/*	$Id$	*/

/*
 * Copyright (c) 2004, 2007 Anders Magnusson (ragge@ludd.ltu.se).
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * Redistributions of source code and documentation must retain the above
 * copyright notice, this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * All advertising materials mentioning features or use of this software
 * must display the following acknowledgement:
 * 	This product includes software developed or owned by Caldera
 *	International, Inc.
 * Neither the name of Caldera International, Inc. nor the names of other
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
 * INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE
 * FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OFLIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "pass1.h"
#include "unicode.h"
#include <string.h>

#define	NODE P1ND
#define	tfree p1tfree
#define	nfree p1nfree
#define	fwalk p1fwalk

/*
 * The following machine-dependent routines may be called during
 * initialization:
 * 
 * zbits(OFFSZ, int)	- sets int bits of zero at position OFFSZ.
 * infld(CONSZ off, int fsz, CONSZ val)
 *			- sets the bitfield val starting at off and size fsz.
 * ninval(CONSZ off, int fsz, NODE *)
 *			- prints an integer constant which may have
 *			  a label associated with it, located at off and
 *			  size fsz.
 *
 * Initialization may be of different kind:
 * - Initialization at compile-time, all values are constants and laid
 *   out in memory. Static or extern variables outside functions.
 * - Initialization at run-time, written to their values as code.
 *
 * Currently run-time-initialized variables are only initialized by using
 * move instructions.  An optimization might be to detect that it is
 * initialized with constants and therefore copied from readonly memory.
 */

/*
 * The base element(s) of an initialized variable is kept in a linked 
 * list, allocated while initialized.
 *
 * When a scalar is found, entries are popped of the instk until it's
 * possible to find an entry for a new scalar; then onstk() is called 
 * to get the correct type and size of that scalar.
 *
 * If a right brace is found, pop the stack until a matching left brace
 * were found while filling the elements with zeros.  This left brace is
 * also marking where the current level is for designated initializations.
 *
 * Position entries are increased when traversing back down into the stack.
 */

/*
 * Good-to-know entries from symtab:
 *	soffset - # of bits from beginning of this structure.
 */

/*
 * TO FIX:
 * - Alignment of structs on like i386 char members.
 */

/*
 * Struct used in array initialisation.
 */
static struct instk {
	struct	instk *in_prev; /* linked list */
	struct	symtab *in_lnk;	/* member in structure initializations */
	struct	symtab *in_sym; /* symtab index */
	union	dimfun *in_df;	/* dimenston of array */
	TWORD	in_t;		/* type for this level */
	int	in_n;		/* number of arrays seen so far */
	int	in_fl;	/* flag which says if this level is controlled by {} */
} *pstk, pbase;

int doing_init, statinit;
static struct symtab *csym;

#ifdef PCC_DEBUG
static void prtstk(struct instk *in);
#define	ID(x) if (idebug) printf x
#else
#define ID(x)
#endif

/*
 * Linked lists for initializations.
 */
struct ilist {
	struct ilist *next;
	CONSZ off;	/* bit offset of this entry */
	int fsz;	/* bit size of this entry */
	NODE *n;	/* node containing this data info */
};

struct llist {
	SLIST_ENTRY(llist) next;
	CONSZ begsz;	/* bit offset of this entry */
	struct ilist *il;
};
static SLIST_HEAD(llh, llist) lpole;
static CONSZ basesz;
static int numents; /* # of array entries allocated */

static struct initctx {
	struct initctx *prev;
	struct instk *pstk;
	struct symtab *psym;
	struct llh lpole;
	CONSZ basesz;
	int numents;
} *inilnk;

static struct ilist *
getil(struct ilist *next, CONSZ b, int sz, NODE *n)
{
	struct ilist *il = tmpalloc(sizeof(struct ilist));

	il->off = b;
	il->fsz = sz;
	il->n = n;
	il->next = next;
	return il;
}

/*
 * Allocate a new struct defining a block of initializers appended to the
 * end of the llist. Return that entry.
 */
static struct llist *
getll(void)
{
	struct llist *ll;

	ll = tmpalloc(sizeof(struct llist));
	ll->begsz = numents * basesz;
	ll->il = NULL;
	SLIST_INSERT_LAST(&lpole, ll, next);
	numents++;
	return ll;
}

/*
 * Return structure containing off bitnumber.
 * Allocate more entries, if needed.
 */
static struct llist *
setll(OFFSZ off)
{
	struct llist *ll = NULL;

	/* Ensure that we have enough entries */
	while (off >= basesz * numents)
		 ll = getll();

	if (ll != NULL && ll->begsz <= off && ll->begsz + basesz > off)
		return ll;

	SLIST_FOREACH(ll, &lpole, next)
		if (ll->begsz <= off && ll->begsz + basesz > off)
			break;
	return ll; /* ``cannot fail'' */
}
char *astypnames[] = { 0, 0, "\t.byte", "\t.byte", "\t.short", "\t.short",
	"\t.word", "\t.word", "\t.long", "\t.long", "\t.quad", "\t.quad",
	"ERR", "ERR", "ERR",
};

void
inval(CONSZ off, int fsz, NODE *p)
{
	struct symtab *sp;
	CONSZ val;
	TWORD t;

#ifndef NO_COMPLEX
	if (ANYCX(p) && p->n_left->n_right->n_right->n_op == FCON &&
	    p->n_left->n_left->n_right->n_op == FCON) {
		NODE *r = p->n_left->n_right->n_right;
		int sz = (int)tsize(r->n_type, r->n_df, r->n_ap);
		ninval(off, sz, p->n_left->n_left->n_right);
		ninval(off, sz, r);
		tfree(p);
		return;
	}
#endif

	if (p->n_op != ICON && p->n_op != FCON) {
		uerror("constant required");
		return;
	}
	if (p->n_type == BOOL) {
		if ((U_CONSZ)glval(p) > 1)
			slval(p, 1);
		p->n_type = BOOL_TYPE;
	}
	if (ninval(off, fsz, p))
		return; /* dealt with in local.c */
	t = p->n_type;
	if (t > BTMASK)
		t = INTPTR;

	val = (CONSZ)(glval(p) & SZMASK(sztable[t]));
	if (t <= ULONGLONG) {
		sp = p->n_sp;
		printf(PRTPREF "%s ",astypnames[t]);
		if (val || sp == NULL)
			printf(CONFMT, val);
		if (val && sp != NULL)
			printf("+");
		if (sp != NULL) {
			if ((sp->sclass == STATIC && sp->slevel > 0)) {
				/* fix problem with &&label not defined yet */
				int o = sp->soffset;
				printf(LABFMT, o < 0 ? -o : o);
				if ((sp->sflags & SMASK) == SSTRING)
					sp->sflags |= SASG;
			} else
				printf("%s", getexname(sp));
		}
		printf("\n");
	} else if (t == FLOAT || t == DOUBLE || t == LDOUBLE) {
		uint32_t *ufp;
		int i, nbits;

		ufp = soft_toush(p->n_scon, t, &nbits);
		for (i = 0; i < sztable[t]; i += SZINT) {
			printf(PRTPREF "%s %u\n", astypnames[INT], 
			    (i < nbits ? ufp[i/SZINT] : 0));
		}
	} else
		cerror("inval: unhandled type %d", (int)t);
}

#ifndef MYBFINIT

static int inbits;
static CONSZ xinval;
/*
 * Initialize a bitfield.
 * XXX - use U_CONSZ?
 */
void
infld(CONSZ off, int fsz, CONSZ val)
{
#ifdef PCC_DEBUG
	if (idebug)
		printf("infld off " CONFMT ", fsz %d, val " CONFMT " inbits %d\n",
		    off, fsz, val, inbits);
#endif
	val &= SZMASK(fsz);
#if TARGET_ENDIAN == TARGET_BE
	while (fsz + inbits >= SZCHAR) {
		int shsz = SZCHAR-inbits;
		xinval = (xinval << shsz) | (val >> (fsz - shsz));
		printf(PRTPREF "%s " CONFMT "\n",
		    astypnames[CHAR], xinval & SZMASK(SZCHAR));
		fsz -= shsz;
		val &= SZMASK(fsz);
		xinval = inbits = 0;
	}
	if (fsz) {
		xinval = (xinval << fsz) | val;
		inbits += fsz;
	}
#else
	while (fsz + inbits >= SZCHAR) {
		int shsz = SZCHAR-inbits;
		xinval |= (val << inbits);
		printf(PRTPREF "%s " CONFMT "\n",
		    astypnames[CHAR], (CONSZ)(xinval & SZMASK(SZCHAR)));
		fsz -= shsz;
		val >>= shsz;
		xinval = inbits = 0;
	}
	if (fsz) {
		xinval |= (val << inbits);
		inbits += fsz;
	}
#endif
}

char *asspace = "\t.space";

/*
 * set fsz bits in sequence to zero.
 */
void
zbits(OFFSZ off, int fsz)
{
	int m;

#ifdef PCC_DEBUG
	if (idebug)
		printf("zbits off " CONFMT ", fsz %d inbits %d\n", off, fsz, inbits);
#endif
#if TARGET_ENDIAN == TARGET_BE
	if ((m = (inbits % SZCHAR))) {
		m = SZCHAR - m;
		if (fsz < m) {
			inbits += fsz;
			xinval <<= fsz;
			return;
		} else {
			fsz -= m;
			xinval <<= m;
			printf(PRTPREF "%s " CONFMT "\n", 
			    astypnames[CHAR], xinval & SZMASK(SZCHAR));
			xinval = inbits = 0;
		}
	}
#else
	if ((m = (inbits % SZCHAR))) {
		m = SZCHAR - m;
		if (fsz < m) {
			inbits += fsz;
			return;
		} else {
			fsz -= m;
			printf(PRTPREF "%s " CONFMT "\n", 
			    astypnames[CHAR], (CONSZ)(xinval & SZMASK(SZCHAR)));
			xinval = inbits = 0;
		}
	}
#endif
	if (fsz >= SZCHAR) {
		printf(PRTPREF "%s " CONFMT "\n", asspace, (CONSZ)(fsz/SZCHAR));
		fsz -= (fsz/SZCHAR) * SZCHAR;
	}
	if (fsz) {
		xinval = 0;
		inbits = fsz;
	}
}
#endif

/*
 * beginning of initialization; allocate space to store initialized data.
 * remember storage class for writeout in endinit().
 * p is the newly declarated type.
 */
void
beginit(struct symtab *sp)
{
	struct initctx *ict;
	struct instk *is = &pbase;

	ID(("beginit(%p), sclass %s\n", sp, scnames(sp->sclass)));

	if (pstk) {
		ID(("beginit: saving ctx pstk %p\n", pstk));

		/* save old context */
		ict = tmpalloc(sizeof(struct initctx));
		ict->prev = inilnk;
		inilnk = ict;
		ict->pstk = pstk;
		ict->psym = csym;
		ict->lpole = lpole;
		ict->basesz = basesz;
		ict->numents = numents;
		is = tmpalloc(sizeof(struct instk));
	}
	csym = sp;

	numents = 0; /* no entries in array list */
	if (ISARY(sp->stype)) {
		basesz = tsize(DECREF(sp->stype), sp->sdf+1, sp->sap);
		if (basesz == 0) {
			uerror("array has incomplete type");
			basesz = SZINT;
		}
	} else
		basesz = tsize(sp->stype, sp->sdf, sp->sap);
	SLIST_INIT(&lpole);

	/* first element */
	if (ISSOU(sp->stype)) {
		is->in_lnk = strmemb(sp->sap);
	} else
		is->in_lnk = NULL;
	is->in_n = 0;
	is->in_t = sp->stype;
	is->in_sym = sp;
	is->in_df = sp->sdf;
	is->in_fl = 0;
	is->in_prev = NULL;
	pstk = is;
	doing_init++;
	if (sp->sclass == STATIC || sp->sclass == EXTDEF)
		statinit++;
	ID(("beginit(%p) end\n", sp));
}

/*
 * Push a new entry on the initializer stack.
 * The new entry will be "decremented" to the new sub-type of the previous
 * entry when called.
 * Popping of entries is done elsewhere.
 */
static void
stkpush(void)
{
	struct instk *is;
	struct symtab *sq, *sp;
	TWORD t;

	if (pstk == NULL) {
		sp = csym;
		t = 0;
	} else {
		t = pstk->in_t;
		sp = pstk->in_sym;
	}

#ifdef PCC_DEBUG
	if (idebug) {
		printf("stkpush: '%s' %s ", sp->sname, scnames(sp->sclass));
		tprint(t, 0);
	}
#endif

	/*
	 * Figure out what the next initializer will be, and push it on 
	 * the stack.  If this is an array, just decrement type, if it
	 * is a struct or union, extract the next element.
	 */
	is = tmpalloc(sizeof(struct instk));
	is->in_fl = 0;
	is->in_n = 0;
	if (pstk == NULL) {
		/* stack empty */
		is->in_lnk = ISSOU(sp->stype) ? strmemb(sp->sap) : NULL;
		is->in_t = sp->stype;
		is->in_sym = sp;
		is->in_df = sp->sdf;
	} else if (ISSOU(t)) {
		sq = pstk->in_lnk;
		if (sq == NULL) {
			uerror("excess of initializing elements");
		} else {
			is->in_lnk = ISSOU(sq->stype) ? strmemb(sq->sap) : NULL;
			is->in_t = sq->stype;
			is->in_sym = sq;
			is->in_df = sq->sdf;
		}
	} else if (ISARY(t)) {
		is->in_lnk = ISSOU(DECREF(t)) ? strmemb(pstk->in_sym->sap) : 0;
		is->in_t = DECREF(t);
		is->in_sym = sp;
		if (pstk->in_df->ddim != NOOFFSET && pstk->in_df->ddim &&
		    pstk->in_n >= pstk->in_df->ddim) {
			werror("excess of initializing elements");
			pstk->in_n--;
		}
		is->in_df = pstk->in_df+1;
	} else
		uerror("too many left braces");
	is->in_prev = pstk;
	pstk = is;

#ifdef PCC_DEBUG
	if (idebug) {
		printf(" newtype ");
		tprint(is->in_t, 0);
		printf("\n");
	}
#endif
}

/*
 * pop down to either next level that can handle a new initializer or
 * to the next braced level.
 */
static void
stkpop(void)
{
#ifdef PCC_DEBUG
	if (idebug)
		printf("stkpop\n");
#endif
	for (; pstk; pstk = pstk->in_prev) {
		if (pstk->in_t == STRTY && pstk->in_lnk != NULL) {
			pstk->in_lnk = pstk->in_lnk->snext;
			if (pstk->in_lnk != NULL)
				break;
		}
		if (ISSOU(pstk->in_t) && pstk->in_fl)
			break; /* need } */
		if (ISARY(pstk->in_t)) {
			pstk->in_n++;
			if (pstk->in_fl)
				break;
			if (pstk->in_df->ddim == NOOFFSET ||
			    pstk->in_n < pstk->in_df->ddim)
				break; /* ger more elements */
		}
	}
#ifdef PCC_DEBUG
	if (idebug > 1)
		prtstk(pstk);
#endif
}

/*
 * Count how many elements an array may consist of.
 */
static int
acalc(struct instk *is, int n)
{
	if (is == NULL || !ISARY(is->in_t))
		return 0;
	return acalc(is->in_prev, n * is->in_df->ddim) + n * is->in_n;
}

/*
 * Find current bit offset of the top element on the stack from
 * the beginning of the aggregate.
 */
static CONSZ
findoff(void)
{
	struct instk *is;
	OFFSZ off;

#ifdef PCC_DEBUG
	if (ISARY(pstk->in_t))
		cerror("findoff on bad type %x", pstk->in_t);
#endif

	/*
	 * Offset calculations. If:
	 * - previous type is STRTY, soffset has in-struct offset.
	 * - this type is ARY, offset is ninit*stsize.
	 */
	for (off = 0, is = pstk; is; is = is->in_prev) {
		if (is->in_prev && is->in_prev->in_t == STRTY)
			off += is->in_sym->soffset;
		if (ISARY(is->in_t)) {
			/* suesize is the basic type, so adjust */
			TWORD t = is->in_t;
			OFFSZ o;
			while (ISARY(t))
				t = DECREF(t);
			if (ISPTR(t)) {
				o = SZPOINT(t); /* XXX use tsize() */
			} else {
				o = tsize(t, is->in_sym->sdf, is->in_sym->sap);
			}
			off += o * acalc(is, 1);
			while (is->in_prev && ISARY(is->in_prev->in_t)) {
				if (is->in_prev->in_prev &&
				    is->in_prev->in_prev->in_t == STRTY)
					off += is->in_sym->soffset;
				is = is->in_prev;
			}
		}
	}
#ifdef PCC_DEBUG
	if (idebug>1) {
		printf("findoff: off " CONFMT "\n", off);
		prtstk(pstk);
	}
#endif
	return off;
}

/*
 * Insert the node p with size fsz at position off.
 * Bit fields are already dealt with, so a node of correct type
 * with correct alignment and correct bit offset is given.
 */
static void
nsetval(CONSZ off, int fsz, NODE *p)
{
	struct llist *ll;
	struct ilist *il;

	if (idebug>1)
		printf("setval: off " CONFMT " fsz %d p %p\n", off, fsz, p);

	if (fsz == 0)
		return;

	ll = setll(off);
	off -= ll->begsz;
	if (ll->il == NULL) {
		ll->il = getil(NULL, off, fsz, p);
	} else {
		il = ll->il;
		if (il->off > off) {
			ll->il = getil(ll->il, off, fsz, p);
		} else {
			for (il = ll->il; il->next; il = il->next)
				if (il->off <= off && il->next->off > off)
					break;
			if (il->off == off) {
				/* replace */
				nfree(il->n);
				il->n = p;
			} else
				il->next = getil(il->next, off, fsz, p);
		}
	}
}

/*
 * take care of generating a value for the initializer p
 * inoff has the current offset (last bit written)
 * in the current word being generated
 * Returns the offset.
 */
CONSZ
scalinit(NODE *p)
{
	CONSZ woff;
	NODE *q;
	int fsz;

	ID(("scalinit(%p)\n", p));
#ifdef PCC_DEBUG
	if (idebug > 2) {
		fwalk(p, eprint, 0);
		prtstk(pstk);
	}
#endif

	if (nerrors)
		return 0;

	p = optim(p);

#ifdef notdef /* leave to the target to decide if useable */
	if (csym->sclass != AUTO && p->n_op != ICON &&
	    p->n_op != FCON && p->n_op != NAME)
		cerror("scalinit not leaf");
#endif

	/* Out of elements? */
	if (pstk == NULL) {
		uerror("excess of initializing elements");
		return 0;
	}

	/*
	 * Get to the simple type if needed.
	 */
	while (ISSOU(pstk->in_t) || ISARY(pstk->in_t)) {
		stkpush();
		/* If we are doing auto struct init */
		if (ISSOU(pstk->in_t) && ISSOU(p->n_type) &&
		    suemeq(pstk->in_sym->sap, p->n_ap)) {
			pstk->in_lnk = NULL; /* this elem is initialized */
			break;
		}
	}

	if (ISSOU(pstk->in_t) == 0) {
		/* let buildtree do typechecking (and casting) */
		q = block(NAME, NIL,NIL, pstk->in_t, pstk->in_df,
		    pstk->in_sym->sap);
		p = buildtree(ASSIGN, q, p);
		nfree(p->n_left);
		q = p->n_right;
		nfree(p);
	} else
		q = p;

	q = optloop(q);

	woff = findoff();

	/* bitfield sizes are special */
	if (pstk->in_sym->sclass & FIELD)
		fsz = -(pstk->in_sym->sclass & FLDSIZ);
	else
		fsz = (int)tsize(pstk->in_t, pstk->in_sym->sdf,
		    pstk->in_sym->sap);

	nsetval(woff, fsz, q);
	if (q->n_op == ICON && q->n_sp &&
	    ((q->n_sp->sflags & SMASK) == SSTRING))
		q->n_sp->sflags |= SASG;

	stkpop();
	ID(("scalinit end(%p)\n", q));
	return woff;
}

/*
 * Generate code to insert a value into a bitfield.
 */
static void
insbf(OFFSZ off, int fsz, int val)
{
	struct symtab sym;
	NODE *p, *r;
	TWORD typ;

#ifdef PCC_DEBUG
	if (idebug > 1)
		printf("insbf: off " CONFMT " fsz %d val %d\n", off, fsz, val);
#endif

	if (fsz == 0)
		return;

	/* small opt: do char instead of bf asg */
	if ((off & (ALCHAR-1)) == 0 && fsz == SZCHAR)
		typ = CHAR;
	else
		typ = INT;
	/* Fake a struct reference */
	p = buildtree(ADDROF, nametree(csym), NIL);
	sym.stype = typ;
	sym.squal = 0;
	sym.sdf = 0;
	sym.sap = NULL;
	sym.soffset = (int)off;
	sym.sclass = (char)(typ == INT ? FIELD | fsz : MOU);
	r = xbcon(0, &sym, typ);
	p = block(STREF, p, r, INT, 0, 0);
	ecomp(buildtree(ASSIGN, stref(p), bcon(val)));
}

/*
 * Clear a bitfield, starting at off and size fsz.
 */
static void
clearbf(OFFSZ off, OFFSZ fsz)
{
	/* Pad up to the next even initializer */
	if ((off & (ALCHAR-1)) || (fsz < SZCHAR)) {
		int ba = (int)(((off + (SZCHAR-1)) & ~(SZCHAR-1)) - off);
		if (ba > fsz)
			ba = (int)fsz;
		insbf(off, ba, 0);
		off += ba;
		fsz -= ba;
	}
	while (fsz >= SZCHAR) {
		insbf(off, SZCHAR, 0);
		off += SZCHAR;
		fsz -= SZCHAR;
	}
	if (fsz)
		insbf(off, fsz, 0);
}

/*
 * final step of initialization.
 * print out init nodes and generate copy code (if needed).
 */
void
endinit(int seg)
{
	struct llist *ll;
	struct ilist *il;
	int fsz;
	OFFSZ lastoff, tbit;

	ID(("endinit()\n"));

	/* Calculate total block size */
	if (ISARY(csym->stype) && csym->sdf->ddim == NOOFFSET) {
		tbit = numents*basesz; /* open-ended arrays */
		csym->sdf->ddim = numents;
		if (csym->sclass == AUTO) { /* Get stack space */
			csym->soffset = NOOFFSET;
			oalloc(csym, &autooff);
		}
	} else
		tbit = tsize(csym->stype, csym->sdf, csym->sap);

	/* Setup symbols */
	if (csym->sclass != AUTO) {
		locctr(seg ? UDATA : DATA, csym);
		defloc(csym);
	}

	/* Traverse all entries and print'em out */
	lastoff = 0;
	SLIST_FOREACH(ll, &lpole, next) {
		for (il = ll->il; il; il = il->next) {
#ifdef PCC_DEBUG
			if (idebug > 1) {
				printf("off " CONFMT " size %d val " CONFMT " type ",
				    ll->begsz+il->off, il->fsz, glval(il->n));
				tprint(il->n->n_type, 0);
				printf("\n");
			}
#endif
			fsz = il->fsz;
			if (csym->sclass == AUTO) {
				struct symtab sym;
				NODE *p, *r, *n;

				if (ll->begsz + il->off > lastoff)
					clearbf(lastoff,
					    (ll->begsz + il->off) - lastoff);

				/* Fake a struct reference */
				p = buildtree(ADDROF, nametree(csym), NIL);
				n = il->n;
				sym.stype = n->n_type;
				sym.squal = n->n_qual;
				sym.sdf = n->n_df;
				sym.sap = n->n_ap;
				sym.soffset = (int)(ll->begsz + il->off);
				sym.sclass = (char)(fsz < 0 ? FIELD | -fsz : 0);
				r = xbcon(0, &sym, INT);
				p = block(STREF, p, r, INT, 0, 0);
				ecomp(buildtree(ASSIGN, stref(p), il->n));
				if (fsz < 0)
					fsz = -fsz;

			} else {
				if (ll->begsz + il->off > lastoff)
					zbits(lastoff,
					    (ll->begsz + il->off) - lastoff);
				if (fsz < 0) {
					fsz = -fsz;
					infld(il->off, fsz, glval(il->n));
				} else
					inval(il->off, fsz, il->n);
				tfree(il->n);
			}
			lastoff = ll->begsz + il->off + fsz;
		}
	}
	if (csym->sclass == AUTO) {
		clearbf(lastoff, tbit-lastoff);
	} else
		zbits(lastoff, tbit-lastoff);
	
	doing_init--;
	if (csym->sclass == STATIC || csym->sclass == EXTDEF)
		statinit--;
	endictx();
	ID(("endinit() end\n"));
}

void
endictx(void)
{
	struct initctx *ict = inilnk;

	if (ict == NULL)
		return;

	pstk = ict->pstk;
	csym = ict->psym;
	lpole = ict->lpole;
	basesz = ict->basesz;
	numents = ict->numents;
	inilnk = inilnk->prev;
#ifdef PCC_DEBUG
	if (idebug)
		printf("endinit: restoring ctx pstk %p\n", pstk);
#endif
}

/*
 * process an initializer's left brace
 */
void
ilbrace(void)
{
#ifdef PCC_DEBUG
	if (idebug)
		printf("ilbrace()\n");
#endif

	if (pstk == NULL)
		return;

	stkpush();
	pstk->in_fl = 1; /* mark lbrace */
#ifdef PCC_DEBUG
	if (idebug > 1)
		prtstk(pstk);
#endif
}

/*
 * called when a '}' is seen
 */
void
irbrace(void)
{
#ifdef PCC_DEBUG
	if (idebug)
		printf("irbrace()\n");
	if (idebug > 2)
		prtstk(pstk);
#endif

	if (pstk == NULL)
		return;

	/* Got right brace, search for corresponding in the stack */
	for (; pstk->in_prev != NULL; pstk = pstk->in_prev) {
		if(!pstk->in_fl)
			continue;

		/* we have one now */

		pstk->in_fl = 0;  /* cancel { */
		if (ISARY(pstk->in_t))
			pstk->in_n = pstk->in_df->ddim;
		else if (pstk->in_t == STRTY) {
			while (pstk->in_lnk != NULL &&
			    pstk->in_lnk->snext != NULL)
				pstk->in_lnk = pstk->in_lnk->snext;
		}
		stkpop();
		return;
	}
}

/*
 * Search for next element given.  If anon structs, must return each 
 * anon struct to make offset calculation happy.
 */
static struct symtab *
felem(struct symtab *sp, char *n)
{
	struct symtab *rs;

	for (; sp; sp = sp->snext) {
		if (sp->sname[0] == '*') {
			if ((rs = felem(strattr(sp->sap)->amlist, n)) != NULL)
				return sp;
		} else if (sp->sname == n)
			return sp;
	}
	return sp;
}

/*
 * Create a new init stack based on given elements.
 */
static void
mkstack(NODE *p)
{

	ID(("mkstack: %p\n", p));
#ifdef PCC_DEBUG
	if (idebug > 1 && p)
		fwalk(p, eprint, 0);
#endif

	if (p == NULL)
		return;
	mkstack(p->n_left);

	switch (p->n_op) {
	case LB: /* Array index */
		if (p->n_right->n_op != ICON)
			cerror("mkstack");
		if (!ISARY(pstk->in_t))
			uerror("array indexing non-array");
		pstk->in_n = (int)glval(p->n_right);
		nfree(p->n_right);
		break;

	case NAME:
		ID(("mkstack name %s\n", (char *)p->n_sp));
		if (pstk->in_lnk) {
			pstk->in_lnk = felem(pstk->in_lnk, (char *)p->n_sp);
			if (pstk->in_lnk == NULL)
				uerror("member missing");
		} else {
			uerror("not a struct/union");
		}
		break;
	default:
		cerror("mkstack2");
	}
	nfree(p);
	stkpush();
	ID(("mkstack end: %p\n", p));
}

/*
 * Initialize a specific element, as per C99.
 */
void
desinit(NODE *p)
{
	int op = p->n_op;

	ID(("desinit beg %p\n", p));
	if (pstk == NULL)
		stkpush(); /* passed end of array */
	while (pstk->in_prev && pstk->in_fl == 0)
		pstk = pstk->in_prev; /* Empty stack */

	if (ISSOU(pstk->in_t))
		pstk->in_lnk = strmemb(pstk->in_sym->sap);

	mkstack(p);	/* Setup for assignment */

	/* pop one step if SOU, ilbrace will push */
	if (op == NAME || op == LB)
		pstk = pstk->in_prev;

	ID(("desinit end %p\n", p));
#ifdef PCC_DEBUG
	if (idebug > 1)
		prtstk(pstk);
#endif
}

/*
 * Convert a string to an array of char/wchar for asginit.
 */
static void
strcvt(NODE *p)
{
	NODE *q = p;
	char *s;
	int i;

#ifdef mach_arm
	/* XXX */
	if (p->n_op == UMUL && p->n_left->n_op == ADDROF)
		p = p->n_left->n_left;
#endif

	for (s = p->n_sp->sname; *s != 0; ) {
		if (p->n_type == ARY+WCHAR_TYPE)
			i = (int)u82cp(&s);
		else if (*s == '\\')
			i = esccon(&s);
		else
			i = (unsigned char)*s++;
		asginit(bcon(i));
	} 
	tfree(q);
}

/*
 * Do an assignment to a struct element.
 */
void
asginit(NODE *p)
{
	int g;

#ifdef PCC_DEBUG
	if (idebug)
		printf("asginit %p\n", p);
	if (idebug > 1 && p)
		fwalk(p, eprint, 0);
#endif

	/* convert string to array of char/wchar */
	if (p && (DEUNSIGN(p->n_type) == ARY+CHAR ||
	    p->n_type == ARY+WCHAR_TYPE)) {
		struct instk *is;
		TWORD t;

		t = p->n_type == ARY+WCHAR_TYPE ? ARY+WCHAR_TYPE : ARY+CHAR;
		/*
		 * ...but only if next element is ARY+CHAR, otherwise 
		 * just fall through.
		 */

		/* HACKHACKHACK */
		is = pstk;

		if (pstk == NULL)
			stkpush();
		while (ISSOU(pstk->in_t) || ISARY(pstk->in_t))
			stkpush();
		if (pstk->in_prev && 
		    (DEUNSIGN(pstk->in_prev->in_t) == t ||
		    pstk->in_prev->in_t == t)) {
			pstk = pstk->in_prev;
			if ((g = pstk->in_fl) == 0)
				pstk->in_fl = 1; /* simulate ilbrace */

			strcvt(p);
			if (g == 0)
				irbrace(); /* will fill with zeroes */
			return;
		} else
			pstk = is; /* no array of char */
		/* END HACKHACKHACK */
	}

	if (p == NULL) { /* only end of compound stmt */
		irbrace();
	} else /* assign next element */
		scalinit(p);
}

#ifdef PCC_DEBUG
void
prtstk(struct instk *in)
{
	int i, o = 0;

	printf("init stack:\n");
	for (; in != NULL; in = in->in_prev) {
		for (i = 0; i < o; i++)
			printf("  ");
		printf("%p) '%s' ", in, in->in_sym->sname);
		tprint(in->in_t, 0);
		printf(" %s ", scnames(in->in_sym->sclass));
		if (in->in_df /* && in->in_df->ddim */)
		    printf("arydim=%d ", in->in_df->ddim);
		printf("ninit=%d ", in->in_n);
		if (BTYPE(in->in_t) == STRTY || ISARY(in->in_t))
			printf("stsize=%d ",
			    (int)tsize(in->in_t, in->in_df, in->in_sym->sap));
		if (in->in_fl) printf("{ ");
		printf("soff=%d ", in->in_sym->soffset);
		if (in->in_t == STRTY) {
			if (in->in_lnk)
				printf("curel %s ", in->in_lnk->sname);
			else
				printf("END struct");
		}
		printf("\n");
		o++;
	}
}
#endif

/*
 * Do a simple initialization.
 * At block 0, just print out the value, at higher levels generate
 * appropriate code.
 */
void
simpleinit(struct symtab *sp, NODE *p)
{
	NODE *q, *r, *nt;
	TWORD t;
	int sz;

	/* May be an initialization of an array of char by a string */
	if ((DEUNSIGN(p->n_type) == ARY+CHAR &&
	    DEUNSIGN(sp->stype) == ARY+CHAR) ||
	    (DEUNSIGN(p->n_type) == DEUNSIGN(ARY+WCHAR_TYPE) &&
	    DEUNSIGN(sp->stype) == DEUNSIGN(ARY+WCHAR_TYPE))) {
		/* Handle "aaa" as { 'a', 'a', 'a' } */
		beginit(sp);
		strcvt(p);
		if (csym->sdf->ddim == NOOFFSET)
			scalinit(bcon(0)); /* Null-term arrays */
		endinit(0);
		return;
	}

	nt = nametree(sp);
	switch (sp->sclass) {
	case STATIC:
	case EXTDEF:
		q = nt;
		locctr(DATA, sp);
		defloc(sp);
#ifndef NO_COMPLEX
		if (ANYCX(q) || ANYCX(p)) {
			r = cxop(ASSIGN, q, p);
			/* XXX must unwind the code generated here */
			/* We can rely on correct code generated */
			p = r->n_left->n_right->n_left;
			r->n_left->n_right->n_left = bcon(0);
			tfree(r);
			r = p->n_left->n_right;
			sz = (int)tsize(r->n_type, r->n_df, r->n_ap);
			inval(0, sz, r);
			inval(0, sz, p->n_right->n_right);
			tfree(p);
			break;
		} else if (ISITY(p->n_type) || ISITY(q->n_type)) {
			/* XXX merge this with code from imop() */
			int li = 0, ri = 0;
			if (ISITY(p->n_type))
				li = 1, p->n_type = p->n_type - (FIMAG-FLOAT);
			if (ISITY(q->n_type))
				ri = 1, q->n_type = q->n_type - (FIMAG-FLOAT);
			if (!(li && ri)) {
				tfree(p);
				p = bcon(0);
			}
			/* continue below */
		}
#endif
#ifdef TARGET_TIMODE
		struct attr *ap;
		if ((ap = attr_find(sp->sap, GCC_ATYP_MODE)) &&
		    strcmp(ap->aa[0].sarg, "TI") == 0) {
			if (p->n_op != ICON)
				uerror("need to handle TImode initializer ");
			sz = (int)tsize(sp->stype, sp->sdf, sp->sap);
			p->n_type = ctype(LONGLONG);
			inval(0, sz/2, p);
			slval(p, 0); /* XXX fix signed types */
			inval(0, sz/2, p);
			tfree(p);
			tfree(q);
			break;
		}
#endif
		if (p->n_op == NAME && p->n_sp &&
		    (p->n_sp->sflags & SMASK) == SSTRING)
			p->n_sp->sflags |= SASG;
		p = optloop(buildtree(ASSIGN, nt, p));
		q = p->n_right;
		t = q->n_type;
		sz = (int)tsize(t, q->n_df, q->n_ap);
		inval(0, sz, q);
		tfree(p);
		break;

	case AUTO:
	case REGISTER:
		if (ISARY(sp->stype))
			cerror("no array init");
		q = nt;
#ifdef TARGET_TIMODE
		if ((r = gcc_eval_timode(ASSIGN, q, p)) != NULL)
			;
		else
#endif
#ifndef NO_COMPLEX

		if (ANYCX(q) || ANYCX(p))
			r = cxop(ASSIGN, q, p);
		else if (ISITY(p->n_type) || ISITY(q->n_type))
			r = imop(ASSIGN, q, p);
		else
#endif
			r = buildtree(ASSIGN, q, p);
		ecomp(r);
		break;

	default:
		uerror("illegal initialization");
	}
}
