Google
 

Trailing-Edge - PDP-10 Archives - SRI_NIC_PERM_FS_1_19910112 - c/kcc/ccgen.c
There are 8 other files named ccgen.c in the archive. Click here to see a list.
/*	CCGEN.C - Generate code for parse-tree data declarations
**
**	(c) Copyright Ken Harrenstien 1989
**		All changes after v.221, 25-Apr-1988
**	(c) Copyright Ken Harrenstien, SRI International 1985, 1986
**		All changes after v.84, 8-Aug-1985
**
**	Original version (C) 1981  K. Chen
*/

#include "cc.h"
#include "ccgen.h"
#include "ccchar.h"
#include <string.h>

/* Imported (external) functions used herein */
extern void ridlsym();			/* CCSYM */
extern SYMBOL *newlabel();
extern void freelabel();
extern int				/* CCSYM */
	sizearray(), elembsize(), sizetype();
extern void				/* CCCODE */
	codgolab(), code5(), code6(), codemdx(), code8(), flushcode();
extern VREG *genexpr();
extern void genstmt();
extern void genxrelease();
extern void				/* CCOUT */
	outmidef(), outmiref(), outid(), outptr(), outscon(), outlab(),
	outnum(), outpnum(), outnl(), outtab(), outstr();
extern int				/* CCOUT */
	outflt(), codeseg(), dataseg(), prevseg();
#if SYS_CSI				/* profiling, 09/15/89 by MVS */
extern void outprolog();		/* CCOUT */
#endif

/* Exported functions defined here */
void gencode();		/* Called by CC mainline */

/* Internal Functions */
static void inicode(), endcode(), gendata(), genfunct(), gliterals();
static void giz(), gizword(), giznull(), gizexpr();
static int gizptr(), gizconst();

static void gizlist();
static void gizbytes();
static void bytbeg(), wdalign(), outval(), outbyte(), outzvals(),
	outzbs(), outzwds(), outptwd();
static int bytend();

/* OUT data emission vars. */
static int bsiz;	/* 0 if in word mode, else byte size in bits */
static int bpw;		/* # bytes per word */
static int bpos;	/* P of last byte deposited (TGSIZ_WORD at beg of wd)*/
static int savlct;	/* Saved loc ctr at start of byte mode */
static int locctr;	/* Location counter (only for tracking # wds output) */
/* GENCODE - Generate code/data from parse-tree node
*/
void
gencode(n)
NODE *n;
{
    if (n)				/* Ignore null stmts/defs */
	switch (n->Nop) {
	    case N_DATA:
		if (!nerrors) gendata(n); 	/* Generate data definition */
		break;
	    case N_FUNCTION:
		if (!nerrors) genfunct(n);	/* Generate function instrs */
		ridlsym((SYMBOL *)NULL);	/* Flush any local symbols */
		break;
	    default:
		int_error("gencode: bad node %N", n);
 	}
}
/* GENFUNCT - Generate machine instruction code for function
*/
static void
genfunct(n)
NODE *n;
{
    if (n->Nleft->Nright)		/* Any local-scope static data defs? */
	gendata(n->Nleft->Nright);	/* Yes, generate them first */
    codeseg();				/* Ensure in code segment */
    inicode();				/* Start making code */
    outmidef(n->Nleft->Nleft->Nid);	/* Output function label */
#if SYS_CSI
    if (curfn != n->Nleft->Nleft->Nid)
	int_error("Bad funct Nid \"%S\"", n->Nleft->Nleft->Nid);
    if (profbliss)			/* for BLISS profiler */
	outprolog(curfn);		/* added 09/15/89 by MVS */
#endif /* SYS_CSI */

#if 1
    outnl();	/* For temp compat with older KCC, flush later */
#endif
    if (maxauto) {			/* If any auto vars, */
	code8(P_ADJSP, VR_SP, maxauto);	/* make room for them on stack */
	stackoffset += maxauto;		/* and remember stack bumped */
    }
    genstmt(n->Nright);			/* Generate code for body */
    endcode();				/* Wrap up code */
}

/* INICODE - Common code generation inits
*/
static void
inicode()
{
    previous = NULL;
    litstrings = NULL;
    litnodes = NULL;
    looplabel = brklabel = NULL;
    stackoffset = maxcode = mincode = 0;
    vrinit();
}

/* ENDCODE - Common code generation wrap-ups
*/
static void
endcode()
{
    flushcode();	/* Flush out peephole buffer */
    gliterals();	/* Generate any accumulated literals */
    vrendchk();		/* Check to make sure no regs active */
}
/* GENDATA - Generate data definitions
**
** This routine is only called to process static-extent data definitions
** of global or local scope, as opposed to local-extent (automatic) defs
** which are generated by genadata() in CCGEN1.
** Note that the Ntype of the symbol's Q_IDENT node is never examined here;
** the symbol's Stype is used instead.  They are identical except for
** array and function names, when the Ntype is "pointer to <Stype>".
*/
static void
gendata(n)
NODE *n;
{
    NODE *var;
    SYMBOL *s;

    for (; n != NULL; n = n->Nright) {
	if (n->Nop != N_DATA) {
	    int_error("gendata: bad N_DATA %N", n);
	    break;
	}
	if (var = n->Nleft) {		/* For each item on N_DATA list */
	    if (var->Nop != N_IZ) {
		int_error("gendata: bad datum %N", n);
		break;
	    }
	    s = var->Nleft->Nid;		/* get symbol */
	    if (tispure(s->Stype))		/* If obj can be pure, */
		codeseg();			/* put it in pure code seg */
	    else dataseg();			/* Else ensure in data seg */
	    outmidef(s);			/* make label for variable */
	    giz(var->Nright, s->Stype, s);	/* do the initialization */
	}
    }
    gliterals();		/* Put literals into code (pure) segment */
}

/* GLITERALS - Emit all accumulated literals
**	Forces use of code segment as literals are expected to be pure,
**	although this is not mandatory.
*/
static void
gliterals()
{
    if (litstrings || litnodes) {		
	codeseg();
	flushcode();			/* Make sure all code forced out */
    }
    /* Do node literals first since they may generate more string literals! */
    while (litnodes != NULL) {
	outlab(litnodes->Nendlab);	/* Emit internal label */
	giz(litnodes->Nleft, litnodes->Nleft->Ntype, litnodes->Nendlab);
	freelabel(litnodes->Nendlab);
	litnodes = litnodes->Nright;
    }
    while (litstrings != NULL) {	/* Output literal strings */
	outlab(litstrings->Nsclab);	/* Emit generated label */
	freelabel(litstrings->Nsclab);	/* and then can free it. */
	outtab();			/* spaced out from string. */
	outscon(litstrings->Nsconst,	/* Output string literal, */
		    litstrings->Nsclen,	/* this long */
		    elembsize(litstrings->Ntype));	/* of this bytesize. */
	outnl();				/* End with final newline */
	litstrings = litstrings->Nscnext;	/* chain through list */
    }
}
/* GIZ - Generate initialization value for an object
*/
static void
giz(n, t, s)
NODE *n;
TYPE *t;
SYMBOL *s;
{
    if (n == NULL) {
	giznull(t);	/* nothing there, just make block */
	return;
    }

    switch (t->Tspec) {
    case TS_ARRAY:
    case TS_STRUCT:
    case TS_UNION:
	gizlist(n, t, s);
	return;

    default:				/* initializing simple object */
	gizword(n, t, s);		/* make just one or two words */
	return;
    }
}
/* GIZWORD - emit initialization for a simple var (not array or structure)
**	This closely follows the nisconst() routine in CCDECL which
**	checked for legality while parsing.
*/
static void
gizword(n, t, s)
NODE *n;
TYPE *t;
SYMBOL *s;
{
    if (n->Nop == N_IZLIST) {		/* something in brackets? */
	if (n->Nright != NULL)		/* no more than one allowed */
	    int_error("gizword: izer mismatch for %S %N", s, n);
	gizword(n->Nleft, t, s);	/* Just use inner part */
    } else
	if (!gizconst(n))		/* Try new stuff.  If not constant, */
	    gizexpr(n, t);		/* sigh, make at runtime. */
}
/* GIZCONST - Returns true if expression is an allowable initializer constant,
**	with appropriate code generated.  Otherwise, caller must generate.
*/
/* Return value indicates something about the type of constant: */
#define CT_NOTCON 0	/* not a constant, caller must generate. */
#define CT_CON	1	/* definitely a constant (arith, or a cast pointer) */
#define CT_ADDR	2	/* address of some kind */
#define CT_FUNC	3	/* function address (cannot add or sub from this) */

static struct pointerval {
	SYMBOL *pv_id;		/* Identifier (if any) */
	long pv_off;		/* Offset from identifier (words or bytes) */
	int pv_bsize;		/* Byte size of pointer (0 = word) */
} pv;

static int
gizconst(e)
NODE *e;
{
    long res;

    switch(e->Nop) {

	case N_ICONST:
	    if (tisbyte(e->Ntype)) {	/* Special handling for byte vals */
		res = e->Niconst & ((1<<tbitsize(e->Ntype))-1);	/* Mask off */
		res <<= (TGSIZ_WORD % tbitsize(e->Ntype));	/* Shift */
		outval(res);
		return CT_CON;
	    }
	    /* Normal word value, drop through */
	case N_PCONST:
	    outval(e->Niconst);		/* Just emit integer constant */
	    return CT_CON;		/* Say simple constant generated */

	case N_FCONST:			/* Invoke rtn from CCOUT */
	    locctr += outflt(e->Ntype->Tspec, (int *)&e->Nfconst, 0);
	    return CT_CON;		/* Say simple constant generated */


	/* Only the most likely cast conversions are supported here,
	** the others aren't common enough to be worth the
	** extra trouble.
	*/
	case N_CAST:
	    switch (e->Ncast) {
	    case CAST_NONE:		/* Most trivial cast */
		return gizconst(e->Nleft);	/* just pass on. */
	    case CAST_PT_PT:		/* Most likely cast here */
		break;			/* Drop thru to handle */
	    default:
		return CT_NOTCON;	/* Not a constant */
	    }
	    /* Drop through to check for ptr */

	default:
	    if (e->Ntype->Tspec == TS_PTR) {	/* Is this a pointer? */
		pv.pv_id = NULL;		/* Initialize arg struct */
		pv.pv_off = pv.pv_bsize = 0;
		if (res = gizptr(e)) {		/* Fill in the struct */
		    /* Won, output pointer word. */
		    outptwd(pv.pv_id, pv.pv_bsize, pv.pv_off);
		    return res;
		}
	    }
	    return CT_NOTCON;		/* Must generate instructions */
    }
    return CT_NOTCON;
}
/* GIZPTR - auxiliary for GIZCONST */
static int
gizptr(n)
NODE *n;
{
    int addoff, i, off;
    TYPE *t;

    switch (n->Nop) {
	case N_CAST:
	    switch (n->Ncast) {
		case CAST_PT_PT:	/* Only ptr-ptr supported */
		    i = gizptr(n->Nleft);	/* Get values for operand */
		    if (i == CT_FUNC		/* Function addr?  If so, */
		      && n->Ntype->Tspec == TS_PTR	/* and converting to */
		      && n->Ntype->Tsubt->Tspec == TS_FUNCT)	/* same, */
			return CT_FUNC;		/* No further conv needed! */

		    if (i != 2)		/* Only normal addrs allowed now */
			return CT_NOTCON;

		    /* First see whether a conversion is actually needed */
		    i = elembsize(n->Ntype);	/* Desired bytesize of ptr */
		    if (i == 0) {		/* Casting to (void *)? */
			if (tischarpointer(n->Nleft->Ntype)) /* from (char*)?*/
			    return CT_ADDR;	/* Yes, no change */
			i = TGSIZ_CHAR;		/* Else cvt to this bsize */
		    } else if (!elembsize(n->Nleft->Ntype)) { /* fm (void*)? */
			if (tischarpointer(n->Ntype))	      /* to (char*)?*/
			    return CT_ADDR;	/* Yes, no change */
		    }
		    if (i >= TGSIZ_WORD) i = 0;
		    if (i == pv.pv_bsize)	/* If already OK, */
			return CT_ADDR;		/* just return success */

		    /* Different sizes.  Check to see if boundaries match.
		    ** This takes care of 9<->18 bit conversions
		    ** (as well as any others)
		    */
		    if (i && pv.pv_bsize) {	/* Both are byte ptrs? */
			if (pv.pv_bsize < i && (i%pv.pv_bsize == 0)) {
			    pv.pv_off /= (i/pv.pv_bsize);
			    pv.pv_bsize = i;
			    return CT_ADDR;
			}
			if (i < pv.pv_bsize && (pv.pv_bsize%i == 0)) {
			    pv.pv_off *= (pv.pv_bsize/i);
			    pv.pv_bsize = i;
			    return CT_ADDR;
			}
		    }

		    /* Odd sizes.  First must always cvt to a word pointer */
		    if (pv.pv_bsize) {
			pv.pv_off /= (TGSIZ_WORD/pv.pv_bsize);
			pv.pv_bsize = 0;
		    }
		    /* Casting to byte ptr of some kind? */
		    if (i && (i < TGSIZ_WORD)) {
			pv.pv_off *= (TGSIZ_WORD/i);
			pv.pv_bsize = i;
		    }
		    return CT_ADDR;

		default:		/* Only ptr-to-ptr supported for now */
		    break;
	    }
	    return CT_NOTCON;


	case N_SCONST:
	    pv.pv_id = n->Nsclab = newlabel();	/* Get fwd lab for later use */
	    n->Nscnext = litstrings;	/* Link on string stack */
	    litstrings = n;		/* Now on stack */
	    pv.pv_bsize = elembsize(n->Ntype);	/* Set bsize */
	    return CT_ADDR;			/* Say address generated */

	case Q_IDENT:
		/* Identifier.  See documentation for Q_IDENT in cctoks.h
		** for explanation of this method of testing.
		*/
	    pv.pv_id = n->Nid;			/* Remember it */
	    switch (n->Nid->Stype->Tspec) {
		case TS_FUNCT:			/* Function address */
		    return CT_FUNC;		/* Say function address */
		case TS_ARRAY:			/* Array address */
		    if (tisbytearray(n->Nid->Stype))	/* If byte array, */
			pv.pv_bsize = elembsize(n->Nid->Stype);	/* set size */
		    return CT_ADDR;		/* Say array address */
	    }
	    return CT_NOTCON;			/* Barf */

	case N_ADDR:
	    switch (n->Nleft->Nop) {
		case N_PTR:			/* &(*()) is no-op */
		    return gizptr(n->Nleft->Nleft);

#if 0
		/* Allow for conversion of arrays generated by subscripting */
		case Q_PLUS:
		    if (n->Nleft->Ntype->Tspec == TS_ARRAY)
			return gizptr(n->Nleft);	/* OK, continue */
		    return CT_NOTCON;			/* Not array, fail */
#endif

		/* Structure hair.
		** For MEMBER (->) the Nleft must be a constant address.
		**	Can just apply nisconst to this.
		** For DOT (.) the Nleft can be anything that evaluates into
		**	a static structure.  We assume this is only possible
		**	with either Q_IDENT, or N_PTR of a struct addr.
		*/
		case Q_DOT:
		    if (tisbitf(n->Nleft->Ntype))	/* No bitfield ptrs */
			return CT_NOTCON;
		    switch (n->Nleft->Nleft->Nop) {
			case Q_IDENT:
			    switch (n->Nleft->Nleft->Nid->Sclass) {
				case SC_XEXTREF: case SC_EXLINK:
				case SC_EXTDEF: case SC_EXTREF:
				case SC_INTDEF: case SC_INTREF:
				case SC_INLINK: case SC_ISTATIC:
				    pv.pv_id = n->Nleft->Nleft->Nid;
				    goto dostruct; /* Good address of object */
			    }
			    break;
			case N_PTR:
			    if (gizptr(n->Nleft->Nleft->Nleft) == CT_ADDR)
				goto dostruct;
			    break;
		    }
		    return CT_NOTCON;			/* Otherwise fail. */

		case Q_MEMBER:
		    if (tisbitf(n->Nleft->Ntype)	/* No bitfield ptrs */
		      || gizptr(n->Nleft->Nleft) != CT_ADDR)
			return CT_NOTCON;
		dostruct:
		    /* If struct addr is OK, then we're OK */
		    if (pv.pv_bsize)	/* Structaddr never a byteptr */
			return CT_NOTCON;
		    if ((off = n->Nleft->Nxoff) < 0) {	/* Byte object? */
			pv.pv_bsize = off & 077;	/* Get byte size */
			pv.pv_off += (-off >> 12);	/* Add wd offset */
			pv.pv_off *= TGSIZ_WORD/pv.pv_bsize;
			pv.pv_off += (((-off)>>6)&077) / pv.pv_bsize;
		    } else if (tisbytearray(n->Nleft->Ntype)) {
			pv.pv_bsize = elembsize(n->Nleft->Ntype);
			pv.pv_off += off;	/* # of words offset */
			pv.pv_off *= TGSIZ_WORD/pv.pv_bsize;

		    } else {
			pv.pv_off += off;	/* # of words offset */
		    }
		    return CT_ADDR;

		case Q_IDENT:	/* Addr OK if of external or static */
			/* Needn't test type since parser checks it while
			** parsing "&" to verify not function or array.
			*/
		    switch (n->Nleft->Nid->Sclass) {
			case SC_XEXTREF: case SC_EXLINK:
			case SC_EXTDEF: case SC_EXTREF:
			case SC_INTDEF: case SC_INTREF:
			case SC_INLINK: case SC_ISTATIC:
			    pv.pv_id = n->Nleft->Nid;	/* Remember ident */
			    if (tisbyte(n->Nleft->Ntype)) {
				/* Single bytes are right-justified */
				pv.pv_bsize = tbitsize(n->Nleft->Ntype);
				pv.pv_off = (TGSIZ_WORD/pv.pv_bsize) - 1;
			    }
			    return CT_ADDR;	/* Good address of object */
		    }
		    return CT_NOTCON;		/* Bad storage class */
	    }
	    return CT_NOTCON;			/* Bad use of & */

	/* Non-atomic expression checks, for plus and minus. */
	case Q_PLUS:
	    if (n->Nleft->Nop == N_ICONST		/* Integ constant */
		&& gizptr(n->Nright) == CT_ADDR) {	/* + address */
		    addoff = n->Nleft->Niconst;
		    t = n->Nright->Ntype;		/* Ptr has this type */
	     } else if (n->Nright->Nop == N_ICONST	/* Integ constant */
		&& gizptr(n->Nleft) == CT_ADDR) {	/* Address */
		    addoff = n->Nright->Niconst;
		    t = n->Nleft->Ntype;
	    } else return CT_NOTCON;

	    /* See comments for sizeptobj in CCSYM.  Only reason code is
	    ** duplicated here is to handle funny byte sizes right.  Puke!
	    */
	doadd:
#if 1
	    if (tisbytepointer(t)) {
		if (!pv.pv_bsize) pv.pv_bsize = elembsize(t);
		addoff *= sizearray(t->Tsubt);	/* Mult by # bytes in obj */
	    } else
		 addoff *= sizetype(t->Tsubt);	/* Mult by obj size in wds */
#else /* Old buggy code */
	    addoff *= sizetype(t->Tsubt);	/* Mult by obj size in wds */
	    if (tisbytepointer(t) && !tisbyte(t->Tsubt)) {
		if (!pv.pv_bsize) pv.pv_bsize = elembsize(t);
		addoff *= (TGSIZ_WORD / pv.pv_bsize);
	    }
#endif
	    pv.pv_off += addoff;
	    return CT_ADDR;

	case Q_MINUS:
	    if (n->Nright->Nop == N_ICONST	/* minus integ constant */
		&& gizptr(n->Nleft) == CT_ADDR) {	/* Address */
		addoff = - n->Nright->Niconst;
		t = n->Nleft->Ntype;
		goto doadd;
	    }
	    break;

	default:		/* Anything else just fails */
	    break;
    }
    return CT_NOTCON;
}
/* GIZNULL - Initialize an object to nothing.
*/
static void
giznull(t)
TYPE *t;
{
    int i;

    if ((i = sizetype(t)) <= 0)
	int_error("giznull: Bad BLOCK: %d", i);
    else outzwds(i);
}
/* GIZEXPR - Generate code to initialize a static object at runtime.
**	Normally this should never be needed, but the capability is
**	kept here in case the need for cross-compiling ever comes up.
** Note: This will not work for initializing code-segment objects
** that are part of a larger object.  To work right, the init code gen
** needs to be deferred until the top-level object is done.  Don't bother
** fixing this unless it turns out we someday need it.
*/
static void
gizexpr(n, t)
NODE *n;
TYPE *t;
{
    static SYMBOL s;			/* Static to avoid re-initialization */
    SYMBOL *lnk;
    extern NODE *ndef(), *ndefop();
    int oseg;

    s.Sclass = SC_ISTATIC;		/* Set up temp sym for loc to init */
    s.Stype = t;			/* Type for gaddress() & ndefident() */
    s.Ssym = newlabel();		/* Get an internal sym */
    outlab(s.Ssym);			/* and emit it directly */
    strcpy(s.Sname, s.Ssym->Sname);	/* In case of debugging, copy name */
    giznull(t);				/* Emit space for the stuff to init */

    oseg = codeseg();			/* Switch to code segment */
    inicode();				/* Initialize for code generation */
    lnk = newlabel();			/* Get a label for linkage */
    outlab(lnk);			/* and emit it directly */
    outstr("\tBLOCK\t1\n");		/* Make space for linkage */

    /* Fake up an assignment expression setting this symbol */
    n = ndef(Q_ASGN, t, 0, ndefident(&s), n);	/* Use temp for Q_IDENT sym */
    genxrelease(n);			/* Generate code for assignment */

    code6(P_SKIP+POF_ISSKIP+POS_SKPE, VR_RETVAL, lnk); /* see if more inits */
    codemdx(P_JRST, 0, NULL, 1, R_RETVAL);	/* yes, chain to the next */
    code5(P_POPJ, VR_SP);			/* no, back to runtime init */
    endcode();				/* emit literals if any */

    outstr("\t.LINK\t1,");		/* start making link pseudo-op */
    outmiref(lnk);			/* linking through top of routine */
    outnl();				/* finish it off */
    prevseg(oseg);			/* back to previous segment */

    freelabel(s.Ssym);			/* no longer need labels */
    freelabel(lnk);			/* so give them back to freelist */
}
/* GIZLIST - initialize static (not auto) array/struct/union from list
*/
static void
gizlist(n, t, s)
NODE *n;		/* N_IZLIST to initialize from */
TYPE *t;
SYMBOL *s;
{
    SYMBOL *sm;
    int nelts, elwds;
    int wdsleft;
    int savloc;

    if ((wdsleft = sizetype(t)) <= 0) {		/* Paranoia */
	int_error("gizlist: bad size: %d %N", wdsleft, n);
	return;					/* Don't try to fill out */
    }
    if (n->Nop != N_IZLIST) {			/* More paranoia */
	int_error("gizlist: not N_IZLIST %N", n);
	gizword(n, t, s);			/* Emit object expr anyway */
	return;					/* Nothing left on list */
    }

    switch (t->Tspec) {
    case TS_ARRAY:
	if (tisbytearray(t)) {		/* Array of bytes? */
	    gizbytes(n, t, s, 0);	/* Yep, go handle top-lev bytearray */
	    return;			/* Nothing left */
	}
	nelts = t->Tsize;		/* Get # elements in array */
	t = t->Tsubt;			/* Use member type from now on */
	elwds = sizetype(t);		/* Find # wds per element */
	for (; n && --nelts >= 0; n = n->Nright, wdsleft -= elwds)
	    giz(n->Nleft, t, s);	/* Initialize the element */
	break;

    case TS_UNION:
	if (n->Nright) {	/* Union izer should have only 1 element! */
	    int_error("gizlist: > 1 union izer %N", n);
	    n->Nright = NULL;	/* Merciless clobberage to recover */
	}
	/* Then drop thru to handle exactly like struct! */
    case TS_STRUCT:
	sm = t->Tsmtag->Ssmnext;	/* Struct & union have tag */
	savloc = locctr;		/* Remember current loc ctr */
	for (; n && sm; n = n->Nright, sm = sm->Ssmnext) {
	    int p, s, w, o, gap, woff;

	    /* First ensure ready to emit right word for this object */
	    if ((o = sm->Ssmoff) < 0) {	/* Byte or bitf object? */
		w = (-o) >> 12;		/* Decode word offset */
		p = ((-o)&07700) >> 6;	/* Byte pos within word, in bits */
		s = (-o) & 077;		/* Size of object, in bits */
	    } else bytend(), w = o;	/* Word object, leave byte mode */
	    woff = locctr - savloc;	/* Find current offset */
	    if (w != woff) {
		bytend();		/* Align again in case byte mode */
		woff = locctr - savloc;	/* Current offset may have changed */
		if (woff > w)		/* Offset mustn't go backwards!!! */
		    int_error("gizlist: offset clash for %S", sm);
		else outzwds(w - woff);
	    }
	    /* Right word offset, now see if word or byte/bit object */
	    if (o >= 0) {		/* If word object, */
		giz(n->Nleft, sm->Stype, sm);	/* Simply initialize it */
		continue;
	    }

	    /* Handle bitfield (or byte) objects differently from word objs */
	    if (!bsiz) bytbeg(1);	/* Ensure in byte mode */
	    gap = bpos - (p + s);	/* Get space between */
	    if (gap) {
		if (gap < 0) int_error("gizlist: -gap for %S", sm);
		else outbyte(0L, gap);	/* Space out to right place */
	    }
	    if (tisbytearray(sm->Stype))
		gizbytes(n->Nleft, sm->Stype, sm, 1);
	    else {
		if (n->Nleft->Nop != N_ICONST) /* not const? */
		    int_error("gizlist: bitf izer not iconst %N", n);
		outbyte(n->Nleft->Niconst, s);
	    }
	}
	bytend();			/* Done, ensure out of byte mode */
	wdsleft -= (locctr - savloc);	/* Find # words left if any */
	break;

    default:
	int_error("gizlist: bad izer type: %d %N", t->Tspec, n);
	return;
    }

    /*
    ** Fill out remains of initializer.
    **
    ** We might have run off the end of our initializer before coming to
    ** the end of the array or structure we were initializing.  In that
    ** case, we are supposed to fill the rest with zeros; this is done
    ** by counting how much space we have and making a BLOCK that long.
    */
    if (n || wdsleft < 0) {
	int_error("gizlist: too many izers (wlft: %d) %N", wdsleft, n);
	return;
    }
    if (wdsleft)
	outzwds(wdsleft);
}
/* GIZBYTES - Initialize byte array
**	May already be in byte mode.
*/
static void
gizbytes(izl, t, s, lev)
NODE *izl;
TYPE *t;
SYMBOL *s;
int lev;			/* Level (0 is top level) */
{
    register NODE *n = izl;
    int savmode = bsiz;		/* Remember initial mode */
    int nbs = sizearray(t);	/* # of bottom elements (bytes) in array */
    int i;
    char *cp;

    if (n->Nop != N_IZLIST) {
	int_error("gizbytes: izer not list %N", n);
	return;
    }
    if (lev == 0)
	bytbeg(elembsize(t));		/* Get into byte mode with this size */
    for (n = izl; n; n = n->Nright) {
	switch (n->Nleft->Nop) {
	    case N_ICONST:		/* Single byte */
		outval(n->Nleft->Niconst);
		nbs--;			/* count off */
		break;
	    case N_SCONST:		/* String literal */
		if (izl != n || n->Nright) {	/* Must be only thing! */
		    int_error("gizbytes: str not sole node %N", n);
		}
		cp = n->Nleft->Nsconst;
		i = nbs > n->Nleft->Nsclen ? n->Nleft->Nsclen : nbs;
		nbs -= i;
		if (i > 0) do {
		    if (bsiz == 6) outval((long)tosixbit(*cp));
		    else outval((long)*cp);
		    ++cp;
		} while (--i > 0);
		break;
	    case N_IZLIST:		/* Subarray */
		gizbytes(n->Nleft, t->Tsubt,s, lev+1);	/* Do recursively */
		nbs -= sizearray(t->Tsubt);	/* Done with subarray bytes */
		break;
	    default:
		int_error("gizbytes: bad izer for %S %N", s, n);
	}
    }

    /*
    ** Initialization done, fill out rest of array.
    ** Our array might be a subarray of some other char array,
    ** so we must be prepared to leave a ragged end.
    */
    if (nbs > 0)		/* Not enough elements? */
	outzbs(nbs);		/* Fill up this many zero bytes */
    else if (nbs < 0)
	int_error("gizbytes: too many izers, %S", s);

    if (lev == 0 && !savmode)	/* If at top level, leave byte mode now */
	bytend();
    return;
}
/* OUT Data emission stuff.  Tracks whether in byte or word mode,
**	plus count of # words emitted so far.
*/

/* BYTBEG - Initializes to output bytes of given size.
**	If already in byte mode, does nothing except change bytesize.
*/
static void
bytbeg(siz)
{
    if (!bsiz) {
	bpos = TGSIZ_WORD;
	bpw = TGSIZ_WORD/siz;
	savlct = locctr;
    }
    bsiz = siz;
}

/* BYTEND - Leaves byte mode, returns # words output
**	since byte mode was entered (0 if never entered)
*/
static int
bytend()
{
    if (!bsiz) return 0;
    wdalign();			/* Force output to word boundary */
    bsiz = 0;			/* Leave byte mode */
    return locctr - savlct;	/* And return # words done so far */
}

/* WDALIGN - Aligns output to word boundary, doesn't change mode.
*/
static void
wdalign()
{
    if (bsiz && bpos != TGSIZ_WORD) {
	outnl();		/* Force out current word */
	++locctr;		/* and account for it */
	bpos = TGSIZ_WORD;	/* Now at start of new word */
    }
}

/* OUTVAL - Output value in either byte or word mode.
*/
static void
outval(v)
long v;
{
    if (!bsiz) {
	outtab();
	outnum(v);
	outnl();
	++locctr;
    } else outbyte(v, bsiz);
}

/* OUTBYTE - like OUTVAL but byte size is specified (changes default).
**	Must already be in byte mode.
*/
static void
outbyte(v, siz)
long v;
{
    v &= ((unsigned long)1 << siz) - 1;	/* Ensure value masked off */
    if (bpos < siz)
	wdalign();		/* If not enough room, get new wd */
    if (bpos == TGSIZ_WORD)	/* If at start of word, */
	fprintf(out, "\tBYTE (%d) %lo", siz, v);	/* do specially */
    else if (siz == bsiz)		/* can skip size if no change */
	fprintf(out, ",%lo", v);
    else {
	fprintf(out, " (%d) %lo", siz, v);	/* Else just output it */
	bsiz = siz;
    }
    bpos -= siz;
}

/* OUTZVALS - Output zero values to fill up space.
*/
static void
outzvals(zeros)
int zeros;
{
    if (bsiz) outzbs(zeros);
    else outzwds(zeros);
}

/* OUTZBS - Output zero bytes to fill up space.
*/
static void
outzbs(zeros)
int zeros;
{
    if (zeros <= 0) return;
    while (bpos != TGSIZ_WORD && bpos >= bsiz && --zeros >= 0)
	outval(0L);		/* Add filler til at word boundary */
    if (zeros >= (bpw = (TGSIZ_WORD/bsiz))) {	/* Full words left? */
	wdalign();			/* Ensure properly aligned */
	outzwds(zeros/bpw);		/* Zap those words */
	zeros %= bpw;			/* Get remaining # bytes */
    }
    while (--zeros >= 0)		/* Finish off */
	outval(0L);
}

/* OUTZWDS - Output zero words to fill up space.
*/
static void
outzwds(nwds)
int nwds;
{
    if (nwds > 0) {
	fprintf(out, "\tBLOCK %o\n", nwds);	/* This many zero wds */
	locctr += nwds;
    }
}

static void
outptwd(id, bsize, off)
SYMBOL *id;
int bsize, off;
{
    outtab();			/* Won, output it. */
    outptr(pv.pv_id, pv.pv_bsize, pv.pv_off);
    outnl();
    ++locctr;
}