/*
 * Copyright (C) 1985-1992  New York University
 * 
 * This file is part of the Ada/Ed-C system.  See the Ada/Ed README file for
 * warranty (none) and distribution info and also the GNU General Public
 * License for more details.

 */

#define GEN

#include "hdr.h"
#include "vars.h"
#include "gvars.h"
#include "ops.h"
#include "set.h"
#include "maincase.h"
#include "misc.h"
#include "smisc.h"
#include "segment.h"
#include "gen.h"
#include "type.h"
#include "stat.h"
#include "expr.h"
#include "gmisc.h"
#include "gutil.h"
#include "axqr.h"
#include "decl.h"

static void gen_structured_object(Node, Symbol, int);

void create_object(Tuple id_list_arg, Symbol type_name, Node init_node,
  int obj_is_constant)										 /*;create_object*/
{
	/*
	 * This procedure is used to create objects (const or var).
	 * id_list is a list (tuple) of name nodes of objects to be created.
	 * The initialization part cannot have side effect, unless id_list
	 * contains a single element (transformation by expander)
	 *
	 * In order to generate not too bad a code, this procedure is organized
	 * as a gigantic if ... elseif ... elseif... structure, checking for the
	 * different configurations. Optimizations may still be added.
	 *
	 * The following cases are considered:
	 *
	 *       1/ Size of object and initial value are known statically.
	 *             a/ Global object or local constant (promoted to global)
	 *                with static initial value.
	 *             b/ Global object initialized with dynamic value.
	 *                Static part is initialized in data segment.
	 *             c/ Uninitialized global object (variable or deferred
	 *                constant).
	 *             d/ Local constant initialized with dynamic value,
	 *                deferred constant, or local variable.
	 *              
	 *       2/ Size of object is not known statically
	 *             a/ Global object with variable size (transformed into
	 *                renaming).
	 *             b/ Local array or record with variable size.
	 *
	 */

#ifdef TRACE
	Node		node;
#endif
	Node	id, first_id, last_id, init_call_node, pre_node;
	Symbol	first_name, obj_name;
	int		obj_is_global, ikind, i, n;
	Fortup	ft1;
	Segment	init_val;	/* type should be Ivalue */
	Node		dyn_node;
	Symbol	model_name, subtype;
	Tuple	tup, id_list;
	Const	ival, small_const;
	int          special_aggregate;

	/* id_list_arg needed since id_list used desctructively  6-25-85 */
	id_list = tup_copy(id_list_arg);
#ifdef TRACE
	if (debug_flag) {
		/*gen_trace("CREATE_OBJECT", id_list);*/
		gen_trace("CREATE_OBJECT");
		FORTUP(node = (Node), id_list, ft1);
			gen_trace_node("  CREATE_OBJECT argument", node);
		ENDFORTUP(ft1);
	}
#endif
	init_val = (Segment)0; /* indicate not yet defined */
	obj_is_global = CURRENT_LEVEL == 1;
	if (N_KIND(init_node) == as_init_call) {
		/* Initialization procedure call */
		init_call_node = init_node;
		init_node      = OPT_NODE;
	}
	else {
		init_call_node = OPT_NODE;
	}

	while (N_KIND(init_node) == as_insert) {
		FORTUP(pre_node = (Node), N_LIST(init_node), ft1);
			compile(pre_node);
		ENDFORTUP(ft1);
		init_node = N_AST1(init_node);
	}

	if (N_KIND(init_node) == as_raise) {
		/* Simplest case, indeed. */
		compile(init_node);
		init_node = OPT_NODE;
	}

	if (has_static_size(type_name) && !(is_array_type(type_name)
	  &&is_unconstrained(type_name))
	  && (init_node == OPT_NODE ||has_static_size(get_type(init_node)))) {
		/*
		 * 1- Size of object is known statically(and also size of initial value)
		 * -------------------------------------
		 */
		if ((obj_is_global || obj_is_constant) && is_ivalue(init_node)) {
			/*
			 *         1a- Global object or local const (promoted to global)
			 *             with static initial value.
			 *             Generate objects in data seg initialized with value
			 *             Generate only one object for multiple constants.
			 */
			if (is_fixed_type(type_name)) {
				init_val = segment_new(SEGMENT_KIND_DATA, 1);
				small_const = small_of(base_type(type_name));
				segment_put_long(init_val , rat_tof(get_ivalue(init_node),
				  small_const, size_of(type_name) ));
			}
			else if (is_simple_type(type_name)) {
				ival = get_ivalue(init_node);
				ikind = ival->const_kind;
				if(ikind == CONST_INT) {
					init_val = segment_new(SEGMENT_KIND_DATA, 1);
					segment_put_word(init_val, ival->const_value.const_int);
				}
				else if(ikind == CONST_REAL) {
					init_val = segment_new(SEGMENT_KIND_DATA, 1);
					segment_put_real(init_val, ival->const_value.const_real);
				}
				else {
#ifdef DEBUG
					printf("const_kind %d\n", ikind);
#endif		
					chaos(__FILE__, __LINE__, "create_object:unsupported kind");
				}
			}
			else if (is_array_type(type_name)) {
				/* build the appropriate vector... */
				init_val = array_ivalue(init_node);
			}
			else if (is_record_type(type_name)) {
				init_val = record_ivalue(init_node);
			}
			else {
				compiler_error_k("Unknown type for constant ", init_node);
				return;
			}
			if (obj_is_constant) {
				first_name = get_constant_name(init_val);
				FORTUP(id = (Node), id_list, ft1);
					obj_name = N_UNQ(id);
					assign_same_reference(obj_name, first_name);
				ENDFORTUP(ft1);
			}
			else {
				FORTUP(id = (Node), id_list, ft1);
					obj_name = N_UNQ(id);
					next_global_reference_segment(obj_name, init_val);
				ENDFORTUP(ft1);
			}
		}
		else if (obj_is_global && init_node != OPT_NODE) {
			/*
			 *          1b- Global object initialized with dynamic value
			 *              Generate first object in data seg with static part
			 *              initialized, compile code to initialize the rest,
			 *              then assign first object to others
			 */
			if (N_KIND(init_node) == as_array_aggregate) {
				init_val = array_ivalue(init_node);
			}
			else if (N_KIND(init_node) == as_record_aggregate) {
				init_val = record_ivalue(init_node);
			}
			else {
				/* TBSL: review translation from SETL */
				/* build segment of desired length, initially all zero */
				n = size_of(type_name);
				init_val = segment_new(SEGMENT_KIND_DATA, n);
				for (i = 1; i <= n; i++) {
					segment_put_word(init_val, 0);
				}
			}
			FORTUP(id = (Node), id_list, ft1);
				obj_name = N_UNQ(id);
				next_global_reference_segment(obj_name, init_val);
			ENDFORTUP(ft1);

			if (is_simple_type(type_name)) {
				gen_value(init_node);
				last_id = (Node) tup_frome(id_list);
				FORTUP(id = (Node), id_list, ft1);
					id = (Node) tup_fromb(id_list);
					obj_name = (Symbol) N_UNQ(id);
					gen_k(I_DUPLICATE, kind_of(type_name));
					gen_ks(I_POP, kind_of(type_name), obj_name);
				ENDFORTUP(ft1);
				obj_name = N_UNQ(last_id);
				gen_ks(I_POP, kind_of(type_name), obj_name);
			}
			else {
				first_id = (Node) tup_fromb(id_list);
				if (is_aggregate(init_node)) {
					init_node = N_AST2(N_AST1(init_node));
					compile(init_node);
				}
				else {
					select_assign(first_id, init_node, type_name);
				}
				FORTUP(id = (Node), id_list, ft1);
					select_assign(id, first_id, type_name);
				ENDFORTUP(ft1);
			}
		}
		else if (obj_is_global) {
			/*
			 *         1c- Uninitialized global object (Variable or deferred
			 *             constant)
			 *             Generate objects in data segment. If initialization
			 *             procedure, call it on first object, then assign first
			 *             object to others.
			 */
			/* build a segment, initially all zeros, of desired length */
			n = size_of(type_name);
			/*
			 * this is a kludge for deferred const EMPTY in VAR_STRING package.
			 */
			if (n== 0) n = 3;
			init_val = segment_new(SEGMENT_KIND_DATA, n);
			for (i = 1; i <= n; i++)
				segment_put_word(init_val, 0);
			FORTUP(id = (Node), id_list, ft1);
				obj_name = N_UNQ(id);
				next_global_reference_segment(obj_name, init_val);
			ENDFORTUP(ft1);
			if (init_call_node != OPT_NODE ) {
				compile(init_call_node);     /* This initializes 1st object */
				first_id = (Node) tup_fromb(id_list);
				FORTUP(id = (Node), id_list, ft1); /* Assign it to other objs */
				select_assign(id, first_id, type_name);
				ENDFORTUP(ft1);
			}
		}
		else {
			/*
			 *     1d- Local constant initialized with dynamic value, deferred
			 *         constant, or local variable, initialized or not.
			 *         Create local references. If no initialization (implicit
			 *         or explicit) create objects, otherwise create and
			 *         initialize first objects, and create copies for others.
			 */
			FORTUP(id = (Node), id_list, ft1);
				next_local_reference(N_UNQ(id));
			ENDFORTUP(ft1);
			if (is_simple_type(type_name)) {
				if (init_node != OPT_NODE) {
					gen_value(init_node);
					last_id = (Node) tup_frome(id_list);
					FORTUP(id = (Node), id_list, ft1);
						gen_k(I_DUPLICATE, kind_of(type_name));
						gen_k(I_CREATE_COPY, kind_of(type_name));
						gen_s(I_UPDATE_AND_DISCARD, N_UNQ(id));
					ENDFORTUP(ft1);
					gen_k(I_CREATE_COPY, kind_of(type_name));
					gen_s(I_UPDATE_AND_DISCARD, N_UNQ(last_id));
				}
				else{
					FORTUP(id = (Node), id_list, ft1);
						gen_ks(I_DECLARE, kind_of(type_name), N_UNQ(id));
					ENDFORTUP(ft1);
				}
			}
			else {  /* Array or record */
				if (!local_reference_map_defined(type_name)
				  && is_constant (N_UNQ ((Node) set_arb (id_list)))
				  && S_SEGMENT(type_name) == -1) {
					/* deferred constant: type not elaborated yet */
					return;
				}
				if (init_node != OPT_NODE) {
					first_id = (Node) tup_fromb(id_list);
					first_name  = N_UNQ(first_id);
					if (is_aggregate(init_node)) {
						/*
						 * Create a static model containing the static part if
						 * there is one, then create a copy and initialize
						 * dynamic part in the copy. Note: the name of the
						 * aggregate is already first_name.
						 */
						/*stat_node=N_AST1(init_node); -- not used   ds 7-8-85*/
						dyn_node = N_AST2(N_AST1(init_node));
						/*nam_node=N_AST3(init_node); -- not used   ds 7-8-85*/
						model_name   = new_unique_name("static_");
						special_aggregate = FALSE;
						/* A special aggregate is an array aggregate whose
						 * unique name is not defined.  In this case we have
						 * to compile the initialization part of the
						 * aggregate first : the assignements refer to
						 * model_name 
						 * This situation occurs when we have an aggregate with
						 * a qualification that appears as an initiliazation of
						 * an object. Expand_decl cannot execute the code that
						 * deals with an aggregate. The qualification is
						 * removed by the expander and therefore the init part
						 * is just an aggregate. But the work in expand_decl
						 * has not be performed...
						 */
						if (is_array_type(type_name)) {
							if (!is_defined (N_UNQ (init_node))) {
								special_aggregate = TRUE;
								model_name   = N_UNQ (init_node); 
							}
							next_global_reference_template(model_name,
							  array_ivalue(init_node));
						}
						else {
							next_global_reference_template(model_name,
							  record_ivalue(init_node));
						}
						if (special_aggregate)
							compile(dyn_node); 
						gen_s(I_PUSH_EFFECTIVE_ADDRESS, model_name);
						gen_s(I_PUSH_EFFECTIVE_ADDRESS, type_name);
						gen(I_CREATE_COPY_STRUC);
						if (is_array_type(type_name))
							gen_ks(I_DISCARD_ADDR, 1, type_name);
						gen_s(I_UPDATE_AND_DISCARD, first_name);
						if (! special_aggregate)
							compile(dyn_node);
					}
					else {
						gen_structured_object(init_node, type_name,
						  obj_is_constant);
						if (is_array_type(type_name))
							gen_ks(I_DISCARD_ADDR, 1, type_name);
						gen_s(I_UPDATE_AND_DISCARD, first_name);
					}

					if (tup_size(id_list)) {
						gen_s(I_PUSH_EFFECTIVE_ADDRESS, first_name);
						FORTUP(id = (Node), id_list, ft1);
							gen_s(I_PUSH_EFFECTIVE_ADDRESS, type_name);
							gen(I_CREATE_COPY_STRUC);
							if (is_array_type(type_name)) /* remove type */
								gen_ks(I_DISCARD_ADDR, 1, type_name);
							gen_s(I_UPDATE, N_UNQ(id));
						ENDFORTUP(ft1);
						gen_ks(I_DISCARD_ADDR, 1, 
						  N_UNQ((Node) id_list[tup_size(id_list)]));
					}
				}
				else if (init_call_node != OPT_NODE ) {
					gen_s(I_PUSH_EFFECTIVE_ADDRESS, type_name);
					first_id = (Node) tup_fromb(id_list);
					first_name  = N_UNQ(first_id);
					gen(I_CREATE_STRUC);
					if (is_array_type(type_name))
						gen_ks(I_DISCARD_ADDR, 1, type_name);
					gen_s(I_UPDATE_AND_DISCARD, first_name);
					compile(init_call_node);  /* First object now initialized */

					if (tup_size(id_list)) {
						gen_s(I_PUSH_EFFECTIVE_ADDRESS, first_name);
						FORTUP(id = (Node), id_list, ft1);
							gen_s(I_PUSH_EFFECTIVE_ADDRESS, type_name);
							gen(I_CREATE_COPY_STRUC);
							if (is_array_type(type_name)) /* remove type */
								gen_ks(I_DISCARD_ADDR, 1, type_name);
							gen_s(I_UPDATE, N_UNQ(id));
						ENDFORTUP(ft1);
						gen_ks(I_DISCARD_ADDR, 1,
						  N_UNQ((Node)id_list[tup_size(id_list)]));
					}
				}
				else { /* Absolutely no initialization */
					gen_s(I_PUSH_EFFECTIVE_ADDRESS, type_name);
					last_id = (Node) tup_frome(id_list);
					FORTUP(id = (Node), id_list, ft1);
						obj_name = N_UNQ(id);
						gen_k(I_DUPLICATE, mu_addr);
						gen(I_CREATE_STRUC);
						if (is_array_type(type_name))
							gen_ks(I_DISCARD_ADDR, 1, type_name);
						gen_s(I_UPDATE_AND_DISCARD, obj_name);
					ENDFORTUP(ft1);
					obj_name = N_UNQ(last_id);
					gen(I_CREATE_STRUC);
					if (is_array_type(type_name))
						gen_ks(I_DISCARD_ADDR, 1, type_name);
					gen_s(I_UPDATE_AND_DISCARD, obj_name);
				}
			}
		}
		/* 2- Size of object is not known statically
		 * -----------------------------------------
		 * Also some pathological cases where size of initial value is not known
		 * although size of object is known: V: constrained_type := (F..G => 0);
		 * No use in optimizing that case: Only JBG can write that.
		 */
	}
	else if (obj_is_global) {
		/*    2a- Global object
		 *          Variable size => transformed into renaming
		 *          If initialization, initialize first object and then create
		 *          copies.
		 */
		if (init_node != OPT_NODE) { /* Explicit initialization */
			first_id = (Node) tup_fromb(id_list);
			obj_name = N_UNQ(first_id);
			next_global_reference_z(obj_name);
#ifdef TBSL
			ALIAS(obj_name) = new_unique_name("dyn_global"); /*not used */
#endif
			gen_structured_object(init_node, type_name, obj_is_constant);
			if (is_array_type(type_name) && is_unconstrained(type_name)) {
				/*       Completely dynamic unconstrained constant.
				 *	      Ex: X: constant STRING := F(..) & V;
				 */
				subtype = new_unique_name("typeof_");
				next_global_reference_z(subtype);

				/* Note: no index type list can be given... */
				tup = tup_new(2);
				tup[1] = (char *) tup_new1((char *) symbol_none);
				tup[2] = (char *) COMPONENT_TYPE(type_name);
				new_symbol(subtype, na_subtype, type_name,
				  tup, root_type(type_name));
				TYPE_OF(obj_name) = subtype;
				type_name         = subtype;     /* To be used by other obj. */
				gen_ks(I_POP, mu_addr, subtype);
				gen_ks(I_PUSH, mu_addr, subtype);
			}
			if (is_record_type(type_name)) {
				/* May be useless, but the peep-hole will take care of it */
				gen_s(I_PUSH_EFFECTIVE_ADDRESS, type_name);
			}

			FORTUP(id = (Node), id_list, ft1);
				gen_k(I_DUPLICATE, mu_dble);
				gen(I_CREATE_COPY_STRUC);
				if (is_array_type(type_name))
					gen_ks(I_DISCARD_ADDR, 1, type_name);
				gen_ks(I_POP, mu_addr, obj_name);
				first_id         = id;
				obj_name         = N_UNQ(first_id);
				TYPE_OF(obj_name) = type_name;    /* May have been changed. */
#ifdef TBSL
				ALIAS (obj_name) = new_unique_name("dyn_global"); /*not used */
#endif
			ENDFORTUP(ft1);
			gen_ks(I_DISCARD_ADDR, 1, type_name);
			gen_ks(I_POP, mu_addr, obj_name);
		}
		else if (init_call_node != OPT_NODE) { /* Implicit initialization */
			first_id = (Node) tup_fromb(id_list);
			first_name   = N_UNQ(first_id);
			next_global_reference_z(first_name);
#ifdef TBSL
			ALIAS(first_name) = new_unique_name("dyn_global"); /*not used */
#endif
			gen_s(I_PUSH_EFFECTIVE_ADDRESS, type_name);
			gen(I_CREATE_STRUC);
			gen_ks(I_POP, mu_addr, first_name);
			compile(init_call_node);      /* first object now initialized */

			FORTUP(id = (Node), id_list, ft1);
				obj_name = N_UNQ(id);
				next_global_reference_z(obj_name);
				gen_ks(I_PUSH, mu_addr, first_name);
				gen_s(I_PUSH_EFFECTIVE_ADDRESS, type_name);
				gen(I_CREATE_COPY_STRUC);
				if (is_array_type(type_name))
					gen_ks(I_DISCARD_ADDR, 1, type_name);
				gen_ks(I_POP, mu_addr, obj_name);
#ifdef TBSL
				ALIAS(obj_name) = new_unique_name("dyn_global"); /*not used */
#endif
			ENDFORTUP(ft1);
		}
		else { /* No initialization */
			last_id = (Node) tup_frome(id_list);
			gen_s(I_PUSH_EFFECTIVE_ADDRESS, type_name);
			FORTUP(id = (Node), id_list, ft1);
				obj_name = N_UNQ(id);
				next_global_reference_z(obj_name);
				gen_k(I_DUPLICATE, mu_addr);
				gen(I_CREATE_STRUC);
				gen_ks(I_POP, mu_addr, obj_name);
#ifdef TBSL
				ALIAS(obj_name) = new_unique_name("dyn_global"); /*not used */
#endif
			ENDFORTUP(ft1);
			obj_name = N_UNQ(last_id);
			next_global_reference_z(obj_name);
			gen(I_CREATE_STRUC);
			gen_ks(I_POP, mu_addr, obj_name);
#ifdef TBSL
			ALIAS(obj_name) = new_unique_name("dyn_global"); /*not used */
#endif
		}
	}
	else {
		/*    2b- Local array or record, variable size
		 *          Create local reference and object.
		 *      TBSL optimization
		 */
		FORTUP(id = (Node), id_list, ft1);
			obj_name = N_UNQ(id);
			next_local_reference(obj_name);
			if (init_node != OPT_NODE) {
				gen_structured_object(init_node, type_name, obj_is_constant);
				if (is_array_type(type_name) && is_unconstrained(type_name)) {
					/*
					 *         Completely dynamic unconstrained constant.
					 *	       Ex: X: constant STRING := F(..) & V;
					 */
					subtype = new_unique_name("typeof_");
					next_local_reference(subtype);

					/*  Note: no index type list can be given...  */

					tup = tup_new(2);
					tup[1] = (char *) tup_new1((char *) symbol_none);

					tup[2] = (char *) COMPONENT_TYPE(type_name);
					new_symbol(subtype, na_subtype, type_name,
				      tup, root_type(type_name));
					TYPE_SIZE(subtype) = -1;
					TYPE_OF(obj_name)  = subtype;
					gen_s(I_UPDATE, subtype);
					type_name = subtype;
				}
			}
			else {
				gen_s(I_PUSH_EFFECTIVE_ADDRESS, type_name);
				gen(I_CREATE_STRUC);
			}
			if (is_array_type(type_name))
				gen_ks(I_DISCARD_ADDR, 1, type_name);
			gen_s(I_UPDATE_AND_DISCARD, obj_name);
			if (init_call_node != OPT_NODE) {
				compile(init_call_node);
				/*  This first object will now serve as initial value for
				 *    other objects
				 */
				init_node      = id;
				init_call_node = OPT_NODE;
			}
		ENDFORTUP(ft1);
	}

}

static void gen_structured_object(Node init_node, Symbol type_name,
  int obj_is_constant)							 /*;gen_structured_object*/
{
	/*
	 * This procedure is used in place of GEN_VALUE when it is necessary to
	 * generate a new object, i.e. making a copy in cases where GEN_VALUE may
	 * generate the address of an already existing object.
	 */

	Node	expr_node;
	Symbol	expr_type;
	int		needs_copy, constrained_obj, val_is_constant, constrained_val;

	expr_node = init_node;
	expr_type = get_type(init_node);

	while (N_KIND(expr_node) == as_qual_discr
	  ||   N_KIND(expr_node) == as_qual_index
	  ||   N_KIND(expr_node) == as_qual_sub) {
		expr_node = N_AST1(expr_node);
	}
	needs_copy = is_object(expr_node) | is_ivalue(expr_node);

	gen_value(init_node);

	if (needs_copy) {
		if (is_record_type(expr_type))
			gen_s(I_PUSH_EFFECTIVE_ADDRESS, expr_type);
		gen(I_CREATE_COPY_STRUC);
	}
	if (is_record_type(type_name) ) {/* May need to adjust constrained */
		constrained_obj = ! is_unconstrained(type_name) || obj_is_constant;
		val_is_constant = is_simple_name(expr_node) &&
		  (NATURE(N_UNQ(expr_node)) == na_constant);
		constrained_val = ! is_unconstrained(expr_type) || val_is_constant;
		if (constrained_obj != constrained_val) {
			gen_k(I_DUPLICATE, mu_addr);
			gen_kic(I_PUSH_IMMEDIATE, kind_of(symbol_boolean), 
			  constrained_obj, "constrained bit");
			gen_k(I_MOVE, kind_of(symbol_boolean));
		}
	}
}
