/*
 * C compiler
 * ==========
 *
 * Copyright 1989, 1990, 1991 Christoph van Wuellen.
 * Credits to Matthew Brandt.
 * All commercial rights reserved.
 *
 * This compiler may be redistributed as long there is no
 * commercial interest. The compiler must not be redistributed
 * without its full sources. This notice must stay intact.
 *
 * History:
 *
 * 1989   starting an 68000 C compiler, starting with material
 *        originally by M. Brandt
 * 1990   68000 C compiler further bug fixes
 *        started i386 port (December)
 * 1991   i386 port finished (January)
 *        further corrections in the front end and in the 68000
 *        code generator.
 *        The next port will be a SPARC port
 */

#include "chdr.h"
#include "expr.h"
#include "cglbdec.h"
#include "proto.h"
#include "outproto.h"

#if defined(__STDC__) || defined(__cplusplus)
#define P_(s) s
#else
#define P_(s) ()
#endif

/* init.c */
#ifdef CPU_DEFINED
static SIZE	alignfield	P_((SIZE,  SIZE));
#endif /* CPU_DEFINED */

static void	check_brace	P_((BOOL));
static SIZE	inittype	P_((TYP *));
#ifdef TOPSPEED
static SIZE	initfunc	P_((BOOL));
#endif /* TOPSPEED */
static SIZE	initarray	P_((TYP *, BOOL));
static SIZE	initunion	P_((TYP *, BOOL));
static SIZE	initstruct	P_((TYP *, BOOL));
static SIZE	initchar	P_((TYP *));
static SIZE	initshort	P_((TYP *));
static SIZE	initlong	P_((TYP *));
static SIZE	initpointer	P_((TYP *));
static EXPR *	constexpr	P_((TYP *));
#ifdef FLOAT_SUPPORT
#ifndef FLOAT_BOOTSTRAP
static SIZE	initfloat	P_((TYP *));
static SIZE	initdouble	P_((TYP *));
static SIZE	initlongdouble	P_((TYP *));
#endif /* FLOAT_BOOTSTRAP */
#endif /* FLOAT_SUPPORT */

#undef P_

#ifdef CPU_DEFINED
static BOOL was_bitfield = FALSE;
#endif /* CPU_DEFINED */

static IVAL bit_value = 0L;
static int level = 0;

static void
check_brace P1(BOOL, brace_seen)
{
    if (!brace_seen) {
	if (level == 1)
	    message(ERR_BRACE);
	else
	    message(WARN_BRACE);
    }
}


SIZE
align P2(const TYP *, tp,  SIZE, offset)
{
    SIZE	 size = 0L;
#ifdef CPU_DEFINED
     SIZE	 al = alignment(tp);
#else
     SIZE	 al = tp->size;
#endif
    if (al) {
	while ((offset + size) % al)
	    size++;
    }
    return size;
}

#ifdef CPU_DEFINED
/*
 * cast an argument back which has been widened on the caller's side.
 * append the resulting assignment expression to init_node
 */
static void
castback P3( SIZE, offset,  TYP *, tp1,  TYP *, tp2)
{
    EXPR	*ep1, *ep2;

    if (tp1->type == tp2->type)
	return;
    ep2 = mk_autocon(offset);
    ep1 = copynode(ep2);
    ep2 = mk_ref(ep2, tp1);
    ep2 = mk_node(en_cast, ep2, NIL_EXPR, tp2);
    ep1 = mk_ref(ep1, tp2);
    ep1 = mk_node(en_assign, ep1, ep2, tp2);

    if (init_node == NIL_EXPR)
	init_node = ep1;
    else
	init_node = mk_node(en_comma, init_node, ep1, tp2);
}


/*
**	Round 'size' up to a multiple of 'align'
*/
static SIZE
roundup P2(SIZE, size, SIZE, align)
{
    if (size % align) {
	size = ((size / align) + 1) * align;
    }
    return size;
}

SIZE
calculate_offset P4(SYM*, sp, SIZE, offset,  STORAGE, def_sc,  BOOL, promoted)
{
    TYP		*tp = sp->tp;
    SIZE	 size;

    switch (sp->storage_class) {
      case sc_register:
	if (def_sc == sc_parms)
	    goto parms;
	/*FALLTHRU*/
      case sc_auto:
	size = tp->size;
	switch (tp->type) {
	  case bt_struct:
	  case bt_union:
	    size = roundup(size, alignment(tp));
	    break;
	  default:
	    break;
	}
	if (g_is_ascending_stack()) {
	    sp->value.i = offset + align(tp, offset) + 1;
	} else {
	    sp->value.i = -(offset + align(tp, offset) + size);
	}
	offset += align(tp, offset) + size;
	break;
      case sc_static:
	if (sp->value.l == UNDEF_LABEL)
	    sp->value.l = nextlabel++;
	break;
      case sc_parms:
parms:
	/*
	 * If we don't have a prototype in scope (ie. promoted is true)
	 * then parameters may have been widened when passed.  By knowing
	 * whether this is a BIGendian or LOWendian machine we can adjust
	 * the stack offset in order to perform the implicit narrowing cast.
	 */
	switch (tp->type) {
	  case bt_char:
	  case bt_charu:
	  case bt_uchar:
	  case bt_schar:
	    if (g_is_ascending_stack()) {
		sp->value.i = -(offset + tp->size) + 1;
	    } else {
		if (g_is_bigendian()) {
		    tp = promoted ? promote(tp) : tp_short;
		    sp->value.i = offset + tp->size - 1L;
		} else {
		    tp = tp_int;
		    sp->value.i = offset;
		}
	    }
	    break;
	  case bt_short:
	  case bt_ushort:
	  case bt_int16:
	  case bt_uint16:
	    if (g_is_ascending_stack()) {
		sp->value.i = -(offset + tp->size) + 1;
	    } else {
		if (g_is_bigendian()) {
		    tp = promoted ? promote(tp) : tp_short;
		    sp->value.i = offset + tp->size - 2L;
		} else {
		    tp = tp_int;
		    sp->value.i = offset;
		}
	    }
	    break;
	  case bt_float:
	    if (g_is_ascending_stack()) {
		sp->value.i = -(offset+tp->size)+1;
	    } else {
		if (promoted) {
		    castback(offset, tp_double, tp);
		    tp = tp_double;
		}
	    }
	    sp->value.i = offset;
	    break;
	  case bt_pointer16:
	  case bt_pointer32:
	    tp = tp_pointer;
	    goto lab;	/* common code with function */
	  case bt_func:
	    tp = tp_func;
	    /*
	     * arrays and functions are never passed. They are really
	     * Pointers
	     */
lab:	    if (is_derived_type(sp->tp)) {
		global_flag++;
		sp->tp = copy_type(sp->tp);
		global_flag--;
		sp->tp->state &= (STATE)~((unsigned)STATE_DERIVED);
		sp->tp->size = tp->size;
	    }
	    /*FALLTHRU*/
	  default:
	    if (g_is_ascending_stack()) {
		sp->value.i = -(offset + tp->size)+1;
	    } else {
		sp->value.i = offset;
	    }
	    break;
	}
	offset += tp->size;
	break;
      default:
	break;
    }
    return offset;
}
#endif /* CPU_DEFINED */

SIZE
doinit P2(SYM *, sp, SIZE, offset)
{
    TYP		*tp = sp->tp;
#ifdef CPU_DEFINED
    LABEL	 label;
    EXPR	*ep1;
#endif /* CPU_DEFINED */
    EXPR	*ep2;
    int		 brace_level = 0;

    switch (lastst) {
      case tk_assign:
	/* Initializer */
	getsym();
	symbol_set(sp);
	switch (sp->storage_class) {
	  case sc_auto:
	  case sc_register:
	    /* AUTO Iniitialisation */
	    switch (tp->type) {
	      case bt_pointer16:
	      case bt_pointer32:
		if (!is_array_type(tp))
		    goto common;
		/* This must be an array.  However it is possible that the
		 * size of the array isn't known until we have parsed the
		 * initializer.  We must therefore delay calculating the
		 * offset of the item on the stack until we know it's actual
		 * size.
		 */
		/*FALLTHRU*/
	      case bt_struct:
	      case bt_union:
		if (trad_option)
		    message(ERR_ILLINIT);
		if (lastst == tk_id)
		    goto common;
#ifdef CPU_DEFINED
		label = nextlabel++;
		put_kseg(alignment(tp));
		put_label(label);
#endif /* CPU_DEFINED */
		VOIDCAST inittype(tp);
		if (is_array_type(tp)) {
		    tp = copy_type (tp);
		    set_array_assignment(tp);
		}
#ifdef CPU_DEFINED
		ep2 = mk_lcon(label);
		ep2 = mk_ref(ep2, tp);
		uses_structassign = TRUE;
#endif /* CPU_DEFINED */
		break;
	      default:
common:
		while (lastst == tk_begin) {
		    brace_level++;
		    getsym();
		}
		ep2 = exprnc();
		if (ep2 == NIL_EXPR)
		    message(ERR_ILLINIT);
		else
		    ep2 = implicit_castop(ep2, tp);

	    }
#ifdef CPU_DEFINED
	    offset = calculate_offset (sp, offset, sc_auto, FALSE);
	    ep1 = mk_symnode(sp);
	    ep1 = mk_ref(ep1, tp);
	    ep1 = mk_node(en_assign, ep1, ep2, tp);

	    if (init_node == NIL_EXPR) {
		init_node = ep1;
	    } else {
		init_node = mk_node(en_comma, init_node, ep1, tp);
	    }
#endif /* CPU_DEFINED */

	    while (brace_level--)
		needpunc(tk_end);
	    break;

	    /* Normal initializers */
	  case sc_static:
#ifdef CPU_DEFINED
	    offset = calculate_offset (sp, offset, sc_auto, FALSE);
	    put_dseg(alignment(tp));
	    put_label(sp->value.l);
#endif /* CPU_DEFINED */
	    VOIDCAST inittype(tp);
	    break;
	  case sc_external:
	    if (!is_global(sp))
		message (ERR_ILLINIT);
	    sp->storage_class = sc_global;
	    /*FALLTHRU*/
	  case sc_global:
#ifdef CPU_DEFINED
	    put_dseg(alignment(tp));
	    put_name(sp);
#endif /* CPU_DEFINED */
	    VOIDCAST inittype(tp);
	    break;
	  case sc_parms:
	    message(ERR_ILLINIT);
	    break;
	  default:
	    CANNOT_REACH_HERE();
	    break;
	}
	if (is_symbol_defined(sp))
	    message (ERR_REDECL, sp->name);
	symbol_defined(sp);
	break;
      default:
	switch (sp->storage_class) {
	  case sc_auto:
	  case sc_global:
	  case sc_static:
	    if (is_const_qualified(sp->tp))
		message (WARN_CONSTINIT, sp->name);
	    break;
	  default:
	    break;
	}
#ifdef CPU_DEFINED
	offset = calculate_offset (sp, offset, sc_auto, FALSE);
#endif /* CPU_DEFINED */
	break;
    }
    sequence_point();
    return offset;
}

static SIZE
inittype P1(TYP *, tp)
{
    BOOL	 brace_seen = FALSE;
    SIZE	 nbytes;
    level++;
    if (lastst == tk_begin) {
	brace_seen = TRUE;
	getsym();
    }
    switch (tp->type) {
      case bt_char:
      case bt_charu:
      case bt_uchar:
      case bt_schar:
	nbytes = initchar(tp);
	break;
      case bt_short:
      case bt_ushort:
      case bt_int16:
      case bt_uint16:
        nbytes = initshort(tp);
        break;
      case bt_pointer16:
      case bt_pointer32:
	if (is_array_type(tp))
	    nbytes = initarray(tp, brace_seen);
	else
	    nbytes = initpointer(tp);
	break;
      case bt_bitfield:
	bit_value |= (arithexpr(tp_int) << (int)bitfield_offset(tp));
	nbytes = 0L;
#ifdef CPU_DEFINED
	was_bitfield = TRUE;
#endif /* CPU_DEFINED */
	break;
      case bt_ubitfield:
	bit_value |= (arithexpr(tp_uint) << (int)bitfield_offset(tp));
	nbytes = 0L;
#ifdef CPU_DEFINED
	was_bitfield = TRUE;
#endif /* CPU_DEFINED */
	break;
      case bt_int32:
      case bt_uint32:
      case bt_ulong:
      case bt_long:
	nbytes = initlong(tp);
	break;
      case bt_struct:
	nbytes = initstruct(tp, brace_seen);
	break;
      case bt_union:
	nbytes = initunion(tp, brace_seen);
	break;
#ifdef FLOAT_SUPPORT
#ifndef FLOAT_BOOTSTRAP
      case bt_float:
	nbytes = initfloat(tp);
	break;
      case bt_double:
        nbytes = initdouble(tp);
        break;
      case bt_longdouble:
	nbytes = initlongdouble(tp);
	break;
#endif /* FLOAT_BOOTSTRAP */
#endif /* FLOAT_SUPPORT */
#ifdef TOPSPEED
      case bt_func:
	if (topspeed_option) {
	    nbytes = initfunc(brace_seen);
	    break;
	}
	/*FALLTHRU*/
#endif /* TOPSPEED */
      default:
	message(ERR_NOINIT);
	nbytes = 0L;
    }
    if (brace_seen) {
	if (lastst == tk_comma)
	    getsym();
	needpunc(tk_end);
    }
    level--;
    return nbytes;
}

#ifdef TOPSPEED
/*
**	The TopSpeed C Compiler allows function to be "inlined" as
**	a series of initialized bytes.
*/
static SIZE
initfunc P1(BOOL, brace_seen)
{
    SIZE	 nbytes;
    check_brace(brace_seen);
    for (nbytes = 0L; ;) {
	VOIDCAST arithexpr(tp_uchar);
	nbytes += tp_uchar->size;
	if (lastst != tk_comma)
	    break;
	getsym();	/* comma */
	if (lastst == tk_end)
	    break;
    }
    return nbytes;
}
#endif /* TOPSPEED */

static SIZE
initarray P2(TYP *, tp, BOOL, brace_seen)
{
    SIZE	 nbytes;
    TYP	*	rtp = referenced_type(tp);

    if ((( lastst == tk_sconst) &&
         (rtp->type == bt_char || rtp->type == bt_charu ||
 	  rtp->type == bt_uchar || rtp->type == bt_schar)) ||
	(( lastst == tk_wsconst && rtp->type == tp_wchar->type))) {
	nbytes = (SIZE) lastsymlen;
#ifdef CPU_DEFINED
	{
	    SIZE	 len;
	    CHAR	*p;
	    for (len = nbytes, p = lastsym; len--; )
		put_char((int)*p++);
	}
#endif /* CPU_DEFINED */
        if (tp->size == UNKNOWN_SIZE)
            tp->size = nbytes + 1L;
	getsym();		/* skip sconst/wsconst */
    } else {
	check_brace(brace_seen);
	for (nbytes = 0L; ;) {
	    nbytes += inittype(rtp);
	    if (lastst != tk_comma || nbytes == tp->size)
		break;
	    getsym();	/* comma */
	    if (lastst == tk_end)
		break;
	}
	if (tp->size > nbytes)
	    message(WARN_INCOMPLETE);
    }

    if (tp->size == UNKNOWN_SIZE)
	tp->size = nbytes;
    else if (tp->size < nbytes)
	message(ERR_INITSIZE);
#ifdef CPU_DEFINED
    for ( ; tp->size > nbytes; nbytes++)
	put_byte(0);
#endif /* CPU_DEFINED */
    return nbytes;
}

static SIZE
initunion P2(TYP *, tp,  BOOL, brace_seen)
{
     SYM * sp = members(tp)->symbols.head;
    SIZE	 nbytes;
/*
 * Initialize the first branch
 */
    if (sp == NIL_SYM)
        return 0L;
    check_brace(brace_seen);
    nbytes = inittype(sp->tp);
#ifdef CPU_DEFINED
    for ( ; nbytes < tp->size; nbytes++)
	put_byte(0);
#endif /* CPU_DEFINED */
    return nbytes;
}

#ifdef CPU_DEFINED
static SIZE
alignfield P2(SIZE, nbytes,  SIZE, offset)
{
    if (was_bitfield && nbytes < offset) {
	if (short_option)
	    put_word((int)bit_value);
	else
	    put_long(bit_value);
	bit_value = 0;
	was_bitfield = FALSE;
	nbytes += tp_int->size;
    }
    for ( ; nbytes < offset; nbytes++)
	put_byte(0);
    return nbytes;
}
#endif /* CPU_DEFINED */

static SIZE
initstruct P2(TYP *, tp,  BOOL, brace_seen)
{
    SYM		*sp;
#ifdef CPU_DEFINED
    SIZE	 nbytes = 0L;
#endif /* CPU_DEFINED */
    check_brace(brace_seen);
    for (sp = members(tp)->symbols.head; sp!= NIL_SYM ; ) {
	if (sp->storage_class == sc_const) {
	    sp = sp->next;
	    continue;
	}
#ifdef CPU_DEFINED
	nbytes = alignfield (nbytes, sp->value.i);
	nbytes += inittype(sp->tp);
#endif /* CPU_DEFINED */
	sp = sp->next;
	if (lastst != tk_comma || sp == NIL_SYM)
	    break;
	getsym();	/* comma */
	if (lastst == tk_end)
	    break;
    }
    if (sp != NIL_SYM)
	message (WARN_INCOMPLETE);
#ifdef CPU_DEFINED
    nbytes = alignfield (nbytes, tp->size);
#endif /* CPU_DEFINED */
    return tp->size;
}

static SIZE
initchar P1(TYP *, tp)
{
#ifdef CPU_DEFINED
    put_byte((int) arithexpr(tp));
#endif /* CPU_DEFINED */
    return tp->size;
}

static SIZE
initshort P1(TYP *, tp)
{
#ifdef RELOC_BUG
     IVAL	ival = arithexpr(tp));
#ifdef CPU_DEFINED
    put_word((int) ival);
#endif /* CPU_DEFINED */
#else
    /*
     * We allow shorts to be initialized with pointer differences now.
     * Thus, we call constexpr() instead of arithexpr.
     */
    EXPR	*ep;
    if ((ep = constexpr(tp)) == NIL_EXPR) {
	return 0L;
    }
#ifdef CPU_DEFINED
    ep = unsymbolexpr(ep);
    put_short(ep);
#endif /* CPU_DEFINED */
#endif /* RELOC_BUG */
    return tp->size;
}

static SIZE
initlong P1(TYP *, tp)
{
    /*
     * We allow longs to be initialized with pointers now.
     * Thus, we call constexpr() instead of intexpr.
     */
    EXPR	*ep;
    if ((ep = constexpr(tp)) == NIL_EXPR) {
	return 0L;
    }
#ifdef CPU_DEFINED
    ep = unsymbolexpr(ep);
    put_pointer(ep);
#endif /* CPU_DEFINED */
    return tp->size;
}

#ifdef FLOAT_SUPPORT
#ifndef FLOAT_BOOTSTRAP
static SIZE
initfloat P1(TYP *, tp)
{
    RVAL	val;
    floatexpr(tp, &val);
#ifdef CPU_DEFINED
    put_float(&val);
#endif /* CPU_DEFINED */
    return tp->size;
}

static SIZE
initdouble P1(TYP *, tp)
{
    RVAL	val;
    floatexpr(tp, &val);
#ifdef CPU_DEFINED
    put_double(&val);
#endif /* CPU_DEFINED */
    return tp->size;
}

static SIZE
initlongdouble P1(TYP *, tp)
{
    RVAL	val;
    floatexpr(tp, &val);
#ifdef CPU_DEFINED
    put_longdouble(&val);
#endif /* CPU_DEFINED */
    return tp->size;
}
#endif /* FLOAT_BOOTSTRAP */
#endif /* FLOAT_SUPPORT */

static SIZE
initpointer P1(TYP *, tp)
{
    EXPR	*ep;
    if ((ep = constexpr(tp)) == NIL_EXPR) {
	return 0L;
    }
#ifdef CPU_DEFINED
    ep = unsymbolexpr(ep);
    put_pointer(ep);
#endif /* CPU_DEFINED */
    return tp->size;
}

static EXPR *
constexpr P1(TYP *, tp)
{
    EXPR	*ep=exprnc();
    if (ep == NIL_EXPR) {
        message(ERR_CONSTEXPR);
	getsym();
        return NIL_EXPR;
    }
    ep = castop(ep, tp, implicit);
    ep = constantopt(ep);
    /* ep may still contain casts between 32-bit integers and pointers */
    while ((ep->nodetype == en_cast)
	&& (ep->etp->type == bt_long   ||
	    ep->etp->type == bt_ulong  ||
	    ep->etp->type == bt_int32  ||
	    ep->etp->type == bt_uint32 ||
	    ep->etp->type == bt_pointer32)
	&& (ep->v.p[0]->etp->type == bt_long   ||
	    ep->v.p[0]->etp->type == bt_ulong  ||
	    ep->v.p[0]->etp->type == bt_int32  ||
	    ep->v.p[0]->etp->type == bt_uint32 ||
	    ep->v.p[0]->etp->type == bt_pointer32)) {
	ep->v.p[0]->etp = ep->etp;
	ep = ep->v.p[0];
    }

    if (!tst_const(ep)) {
	message(ERR_CONSTEXPR);
	getsym();
	return NIL_EXPR;
    }
    return ep;
}
