Google
 

Trailing-Edge - PDP-10 Archives - SRI_NIC_PERM_FS_1_19910112 - c/old/kcc/ccgen1.c
There are 8 other files named ccgen1.c in the archive. Click here to see a list.
/* <KCC.CC>CCGEN1.C.111,  8-Aug-85 10:11:41, Edit by KRONJ */
/*  Fix tail recursion to variable functions */
/* <KCC.CC>CCGEN1.C.107, 12-Jul-85 15:43:06, Edit by KRONJ */
/*  Should be safe now not to skip in glogical() */
/* <KCC.CC>CCGEN1.C.92, 28-May-85 14:02:40, Edit by KRONJ */
/*  Fix increment to use AOS instead of obsolete INC */
/* <KCC.CC>CCGEN1.C.85, 21-May-85 16:16:07, Edit by KRONJ */
/*  ++ for floating point */
/* <KCC.CC>CCGEN1.C.84, 20-May-85 17:03:20, Edit by KRONJ */
/*  Count spushes for that and gident() calling same routine */
/* <KCC.CC>CCGEN1.C.81, 20-May-85 16:50:59, Edit by KRONJ */
/*  Make deref of struct ptr work for multi-word struct */
/* <KCC.CC>CCGEN1.C.75, 17-Mar-85 13:48:35, Edit by KRONJ */
/*  Try doing tail recursion in gcall() rather than greturn() */
/* <KCC.CC>CCGEN1.C.53, 11-Mar-85 11:24:49, Edit by KRONJ */
/*  Extract members from stacked structs */
/* <KCC.CC>CCGEN1.C.49, 11-Mar-85 09:12:46, Edit by KRONJ */
/*  Clean up struct assignment */
/* <KCC.CC>CCGEN1.C.47, 10-Mar-85 11:15:38, Edit by KRONJ */
/*  Fix *"x" - was losing the fact that "x" is a char ptr */
/* <KCC.CC>CCGEN1.C.44,  9-Mar-85 16:00:49, Edit by KRONJ */
/*  Allow null terminated comma lists of fn args */
/* <KCC.CC>CCGEN1.C.42,  9-Mar-85 10:54:21, Edit by KRONJ */
/*  reinstall SSCOPE */
/* <KCC.CC>CCGEN1.C.39, 24-Feb-85 21:59:40, Edit by SATZ */
/* Remove SSCOPE */
/* <KCC.CC>CCGEN1.C.38, 14-Jan-85 11:16:20, Edit by SATZ */
/* handle two word structures right */
/* <KCC.CC>CCGEN1.C.37, 13-Jan-85 20:15:48, Edit by SATZ */
/* Rewrite to make a little neater */
/* <KCC.CC>CCGEN1.C.36, 13-Jan-85 20:01:13, Edit by SATZ */
/* Second stab: try to push doubles or two word structs onto the */
/* stack; remember to optimize */
/* stack if they are used as function/procedure arguments */
/* <KCC.CC>CCGEN1.C.33, 11-Jan-85 16:29:50, Edit by SATZ */
/* First stab at fixing double arguments to procedures */
/* <KCC.CC>CCGEN1.C.26,  3-Jan-85 17:56:34, Edit by KRONJ */
/*  struct assignment and function args */
/* <KCC.CC>CCGEN1.C.24,  2-Jan-85 15:21:03, Edit by KRONJ */
/*  stack strings for output in litstrings */
/* <KCC.CC>CCGEN1.C.20,  2-Jan-85 13:17:48, Edit by KRONJ */
/*  non-global struct members */
/* <KCC.CC>CCGEN1.C.19, 29-Dec-84 13:01:52, Edit by KRONJ */
/*  generate condition for if with null body */
/* SCORE:<KCC.CC>CCGEN1.C.8, 29-Jul-84 12:02:03, Edit by KRONJ */
/*  glogical() knows to put result in return val */
/* SCORE:<KCC.CC>CCGEN1.C.6, 24-Jul-84 17:38:35, Edit by KRONJ */
/*  undo failed changereg() from findconst() in gconst() */
/* SCORE:<KCC.CC>CCGEN1.C.4, 18-Jul-84 15:43:24, Edit by KRONJ */
/*  uncalled function identifier returns address not first instruction */
/* SCORE:<KCC.CC>CCGEN1.C.2, 16-Jul-84 12:25:09, Edit by KRONJ */
/*  clean up optimizations in greturn() */
/* SCORE:<KCC.CC>CC82.C.49,  2-Jul-84 12:27:04, Edit by KRONJ */
/*  undo failed changereg made by foldmove before it propagates */
/* SCORE:<KCC.CC>CC82.C.5, 12-May-84 22:36:53, Edit by KRONJ */
/*  clean up RETURN */
/* SCORE:<KCC.CC>CC82.C.2,  5-May-84 13:41:50, Edit by KRONJ */
/*  improved code for logical NOT */

/* cc82.c -- Code generator TOPS-20 (contd)   (C) 1981  K. Chen */

#include "cc.h"
#include "ccgen.h"

struct vreg *genstmt(), *gaddress();

/* extract offset from dot or member op */
#define goffset(n) ((int) n->right)
/* -------------------------- */
/*	return statement      */
/* -------------------------- */

greturn(n)
struct NODE *n;
{
    int siz;
    struct vreg *r;

    if (safejump(previous)) return;	/* if in dead code, do nothing */
    if ((n = n->right) != NULL) {	/* if returning val, set it */
	n->nflag |= RETEXPR;		/* this is to be return value */
	siz = tsize (n->ntype);
	if (n->ntype->ttype == ARRAY) siz = PTRSIZE;
	switch (siz) {
	case 1:
	    r = genstmt (n);
	    if (safejump(previous)) return;
	    code0 (MOVE, RETVAL, r);	/* don't need to worry about */
	    break;			/* register allocation, just do it */
	case 2:
	    r = genstmt (n);
	    if (safejump(previous)) return;
	    code0 (DMOVE, RETVAL, r);
	    break;
	default:
	    genstmt (n);		/* stack struct */
	    if (safejump(previous)) return; /* turned into tail recursion */
	    r = getreg();		/* get const */
	    code1 (MOVE, r, siz);	/* of struct size */
	    code7 (SPOP, r, NULL, - stackoffset - siz, SP);
	    spushes++;			/* (let ADJSP below drop it) */
	    release(r);
	}
    }
    if (optimize) killstack();		/* get rid of spurious MOVEMs */
    code8 (ADJSP, SP, - stackoffset);	/* flush local vars from stack */
    code5 (POPJ, SP);			/* emit the return */
}
/* ------------------------------ */
/*	assignment statement      */
/* ------------------------------ */

struct vreg *
gassign (n)
struct NODE *n;
{
    struct vreg *r1, *r2;
    int ptr, siz, flt;
    node nod, defnode();

    nod = n->left;
    siz = tsize (nod->ntype);
    flt = (nod->ntype->ttype == FLOAT);

    switch (nod->nop) {
    case DOT:
    case MEMBER:
	ptr = (goffset (nod) < 0);	/* bitfield */
	break;

    case PTR:
	ptr = charpointer (nod->left->ntype); /* byte pointer deposit */
	break;

    default:
	emsg (EASGN, nod->nop);
    case IDENT:
	ptr = 0;
    }

    if (n->nop != ASGN && (ptr || siz > 1 || charpointer (nod->ntype) ||
	 n->nop==ASMOD || n->nop==ASLSH || n->nop==ASRSH || n->nop==ASDIV)) {

	/* not one instruction, turn  A op= B  into  A = A op B */
	nod = defnode (N3, ASGN, n->ntype, 0, nod, n);
	switch (n->nop) {
	case ASMINUS: 
	    n->nop = MINUS;
	    break;
	case ASPLUS:
	    n->nop = PLUS;
	    break;
	case ASMPLY:
	    n->nop = MPLY;
	    break;
	case ASAND:
	    n->nop = ANDT;
	    break;
	case ASOR:
	    n->nop = OR;
	    break;
	case ASXOR:
	    n->nop = XORT;
	    break;
	case ASDIV:
	    n->nop = DIV;
	    break;
	case ASMOD:
	    n->nop = MOD;
	    break;
	case ASLSH:
	    n->nop = LSHFT;
	    break;
	case ASRSH:
	    n->nop = RSHFT;
	    break;
	default:
	    emsg (EASOP, n->nop);
	    return 0;
	}

	/* recompute munged quantities */
	n = nod;
	nod = n->left;
    }

    switch (siz) {
    case 1:
	r1 = genstmt(n->right);		/* calculate value to store */
	if (n->nop == ASMINUS) {
	    r2 = getreg();		/* new reg makes opt work better */
	    code0 (MOVN, r2, r1);	/* negate so we can use ADDB */
	    r1 = r2;			/* back into appropriate reg */
	}
	r2 = gaddress(nod);		/* find where to put the value */

	switch (n->nop) {
	case ASPLUS:
	case ASMINUS:			/* negated above */
	    code4 (flt? FADR+BOTH : ADD+BOTH, r1, r2);
	    return r1;

	case ASMPLY:
	    code4 (flt? FMPR+BOTH : IMUL+BOTH, r1, r2);
	    return r1;

	case ASAND:
	    code4 (AND+BOTH, r1, r2);
	    return r1;

	case ASOR:
	    code4 (IOR+BOTH, r1, r2);
	    return r1;

	case ASXOR:
	    code4 (XOR+BOTH, r1, r2);
	    return r1;

	case ASGN:
	    if (ptr) code0 (DPB, r1, r2);
	    else code4 (MOVEM, r1, r2);
	    return r1;
	}

    case 2:
	r1 = genstmt (n->right);	/* make doubleword */
	code4 (DMOVEM, r1, gaddress (nod)); /* put it away */
	return r1;			/* return what we got */

    default:
	genstmt (n->right);		/* stack struct */
	r1 = getreg();			/* get const */
	code1 (MOVE, r1, siz);		/* of struct size */
	code4 (SPOP, r1, gaddress(nod)); /* unstack into result */
	spushes++;			/* used a SPUSH (SPOP actually) */
	release (r1);			/* don't need size any more */
	return 0;			/* result still on stack (not reg) */
    }
}
/* ----------------------- */
/*	fetch address      */
/* ----------------------- */

struct vreg *
gaddress (n)
struct NODE *n;
{
    int op, more, offset;
    struct vreg *r, *p;
    struct SYMBOL *s;
    node m;

    switch (n->nop) {
    case PTR:
	return genstmt (n->left);

    case DOT:
    case MEMBER:
	r = (n->nop == MEMBER? genstmt (n->left) : gaddress (n->left));
	offset = goffset (n);		/* calculate offset */
	if (offset < 0) {		/* bitfield? */
	    p = getreg();		/* yes, need another reg */
	    code2 (MOVE, p, ((-offset)&07777) << 6, r, NULL, (-offset) >> 12);
	    release (r);		/* don't need struct address anymore */
	    return p;			/* construct local byte pointer */
	}
	if (offset > 0) code1 (ADD, r, offset);	/* perform offset */
	if (chararray (n->ntype)) pitopc (r, 0, 1); /* convert to byte ptr */
	return r;

    case IDENT:
	r = getreg();
	s = n->nid;
	switch (s->sclass) {
	case SAUTO:
	case SSCOPE:
	    code13 (MOVE, r, (s->svalue + 1) - stackoffset);
	    break;

	case SARG:
	    code13 (MOVE, r, (- s->svalue) - stackoffset);
	    break;

	case SISTATIC:
	case SSSTATIC:
	    code3 (MOVE, r, s->sval.ssym);
	    break;

	case SENUM:
	    emsg (EENUM, s->sname);

	default:
	    code3 (MOVE, r, s);
	}
	if (chararray (n->ntype)) pitopc (r, 0, 1);
	return r;

    default:
	emsg (EADDR, n->nop);
	return 0;
    }
}
/* -------------------------------------- */
/*	fetch constant into register      */
/* -------------------------------------- */

struct vreg *
gconst(n)
node n;
{
    struct vreg *r;

    r = getreg();
    switch (n->nop) {
    case SCONST:
	n->nsclab = newlabel (1);
	code3 (MOVE, r, n->nsclab);	/* XMOVEI R,$l */
	pitopc (r, 4, 1);		/* then into byte pointer */
	n->nscnext = litstrings;	/* link on string stack */
	litstrings = n;			/* include this one */
	return r;

    case ICONST:
	code1 (MOVE, r, n->niconst);
	return r;

    case DCONST:
	code9 (MOVE, r, n->nmantissa, n->nexponent);
	return r;

    default:
	fatal(ECUNIMP, n->nop);
    }
}
/* ---------------------------------------------- */
/*      Indirect through address in register      */
/* ---------------------------------------------- */

static struct vreg *
getmem (reg, siz, byte)
struct vreg *reg;
{
    struct vreg *q;

    switch (siz) {
    case 1:
	q = getreg();
	if (byte) code0 (LDB, q, reg);
	else code4 (MOVE, q, reg);
	return q;

    case 2:
	q = getpair();
	code4 (DMOVE, q, reg);
	return q;

    default:
	q = getreg();
	code1 (MOVE, q, siz);		/* get register with struct size */
	code4 (SPUSH, q, reg);		/* stack up the struct */
	release (q);			/* don't need size anymore */
	stackoffset += siz;		/* remember where we are on stack */
	spushes++;			/* used a SPUSH here */
	return 0;			/* this is too big for a register */
    }
}
/* -------------------------------------- */
/*	get identifier into register      */
/* -------------------------------------- */

struct vreg *gident(n)
struct NODE *n;
{
    struct vreg *q, *r;
    int siz, ssiz;

    /* deal with extraction of fields from unaddressable structs */    
    if (n->nop == DOT && !(n->left->nflag & LVALUE)) {
	switch (siz = tsize (n->left->ntype)) {
	case 1:
	case 2:
	    r = genstmt (n->left);	/* get struct into reg */
	    switch (goffset (n)) {	/* see where we want */
	    case 0:
		if (siz == 2 && tsize (n->ntype) == 1) narrow (r, 0);
		return r;
	    case 1:
		narrow (r, 1);		/* just keep second word */
		return r;
	    default:
		q = getreg();		/* bitfield from register */
		code2 (LDB, q, ((- goffset (n)) & 07777) << 6,
		       0, NULL, realreg (r) + ((- goffset (n)) >> 12));
		release (r);		/* don't need rest of struct */
		return q;
	    }
	default:
	    genstmt (n->left);		/* put struct on stack */
	    if (goffset (n) < 0) {
		r = getreg();		/* bitfield from stack */
		code2 (LDB, r, ((- goffset (n)) & 07777) << 6,
		       SP, NULL, 1 + ((- goffset (n)) >> 12) - siz); /* ugh */
	    } else switch (ssiz = tsize (n->ntype)) {
	    case 1:
		r = getreg();
		code7 (MOVE, r, NULL, 1 + goffset (n) - siz, SP);
		break;
	    case 2:
		r = getpair();
		code7 (DMOVE, r, NULL, 1 + goffset (n) - siz, SP);
		break;
	    default:
		if (goffset (n) == 0) {	/* bottom of stacked struct? */
		    code8 (ADJSP, SP, ssiz - siz); /* yes, flush rest */
		} else {
		    code8 (ADJSP, SP, ssiz - siz + goffset (n));
		    q = getreg();	/* drop stack to top of substruct */
		    code1 (MOVE, q, ssiz); /* get size of substruct */
		    code7 (SPOP, q, NULL, 1 - goffset (n) - ssiz, SP);
		    code8 (ADJSP, SP, - goffset (n));
		    spushes++;		/* used a SPUSH here */
		}
		stackoffset += ssiz - siz;
		return 0;		/* struct is still stacked */
	    }
	    code8 (ADJSP, SP, -siz);	/* LDB, MOVE, AND DMOVEM all need */
	    stackoffset -= siz;		/* same fixup done to stack */
	    return r;
	}
    }

    /* dereference pointer from gaddress() for vars and addressable structs */
    return getmem (gaddress (n), tsize (n->ntype),
		   ((n->nop == DOT || n->nop == MEMBER) && goffset (n) < 0));
}
/* -------------------------------------------- */
/*	boolean binary and unary operators      */
/* -------------------------------------------- */

struct vreg *
glogical (n)
node n;
{
    struct vreg *reg;
    struct SYMBOL *false, *true, *temp;
    int reverse, skipped = 0;

    reverse = (optimize && n->nop == LOR);
    n->endlab = true = newlabel (1);	/* get label for true case */
    false = newlabel (1);		/* get label for false case */

    gboolean (n, false, reverse);	/* make the boolean code */
    if (optimize && unjump (false)) {	/* can put false case first? */
	temp = false;			/* yes, swap meaning of false */
	false = true;			/* and true, so labels go out */
	true = temp;			/* in correct order. */
	reverse = !reverse;		/* also invert reversal switch */
	skipped = 1;			/* remember we are now skipped over */
    }

    if (n->nflag & RETEXPR) reg = getret(); /* get value in return reg */
    else reg = getreg();		/* not for return, use normal reg */
    emitlabel (true);			/* true label goes here */
    if (reverse) code0 (TDZ+ISSKIP+SKPA, reg, reg); /* make zero, skip */
    else code1 (SKIP+ISSKIP+SKPA, reg, 1); /* makes one and skip */
    if (skipped) setskip (previous);	/* remember if skipped over */
    emitlabel (false);			/* now make false label */
    if (reverse) code1 (MOVE, reg, 1);	/* reversed false makes one */
    else code5 (SETZ, reg);		/* normal false makes zero */
    setskip (previous);			/* MOVEI or SETZ is skipped over */
    return reg;				/* return the register */
}
/* ------------------------------------------- */
/*	generate code for unary operators      */
/* ------------------------------------------- */

struct vreg *
gunary (n)
node n;
{
    struct vreg *r, *incdec();

    switch (n->nop) {
    case PINC:
	return incdec (n, AOS, 1);
    case PDEC:
	return incdec (n, SOS, 1);
    case INC:
	return incdec (n, AOS, 0);
    case DEC:
	return incdec (n, SOS, 0);

    case PTR:
	return getmem (genstmt (n->left), tsize (n->ntype),
		       charpointer (n->left->ntype));

    case ADDR:
	n = n->left;
	r = gaddress(n);
	if (n->ntype == chartype) pitopc(r, 3, 1);
	return r;

    case NEG:
	r = genstmt(n->left);
	if (n->ntype->ttype == DOUBLE) code0 (DMOVN, r, r);
	else code0 (MOVN, r, r);
	return r;

    case COMPL:
	r = genstmt(n->left);
	code0(SETCM, r, r);
	return r;

    default:
	emsg(EUNARY, n->nop);
	return 0;
    }
}
/* ----------------------- */
/*	function calls     */
/* ----------------------- */

struct vreg *
gcall (n)
node n;
{
    node l;
    int narg, siz;
    struct vreg *r;
    struct SYMBOL *arg;

    /* check for args in same order - if ok, can tail recurse */
    if (!optimize) n->nflag &=~ RETEXPR;
    l = n->right;
    siz = tsize (n->ntype);		/* calculate size of return value */
    if (n->ntype->ttype == ARRAY) siz = PTRSIZE;
    narg = -1;
    while ((n->nflag & RETEXPR) && l != NULL) {
	if (l->nop == EXPRESS) {
	    arg = (l->right->nop == IDENT? l->right->nid : NULL);
	    l = l->left;
	} else {
	    arg = (l->nop == IDENT? l->nid : NULL);
	    l = NULL;
	}
	if (arg == NULL || arg->sclass != SARG) n->nflag &=~ RETEXPR;
	else {
	    if (narg == -1) narg = arg->svalue;
	    else if (narg != arg->svalue) n->nflag &=~ RETEXPR;
	    narg -= tsize (arg->sptr);
	    if (narg < 0) n->nflag &=~ RETEXPR;
	}
    }
    if (siz > 2) narg -= siz;		/* account for retval space */
    if (n->right == NULL) narg = 0;	/* no args always matches */

    /* if we still think we can tail recurse, do it */
    if (narg == 0 && (n->nflag & RETEXPR)) {
	r = gaddress (n->left);		/* get address of function first */
	code8 (ADJSP, SP, - stackoffset); /* ... before we lose our marbles */
	code4 (JRST, 0, r);		/* now we can jump to it */
	return NULL;			/* can't want a return value */
    }

    spill();				/* save active registers */

    /* next push function arguments */
    l = n->right;
    narg = stackoffset;			/* remember argument block start */
    while (l != NULL) {
	if (l->nop == EXPRESS) {
	    fnarg(l->right);
	    l = l->left;
	} else {
	    fnarg(l);
	    break;
	}
    }
    narg -= stackoffset;		/* calculate neg number of arg words */

    if (siz > 2) {
	code8 (ADJSP, SP, siz);		/* leave space for return struct */
	stackoffset += siz;
    }

    code4(PUSHJ, SP, gaddress(n->left)); /* call function or expression */

    /* flush args off stack */
    if (narg) {
	if (siz > 2) {
	    r = getreg();
	    code1 (MOVE, r, siz);
	    code7 (SPOP, r, NULL, 1 + narg - siz, SP);
	    spushes++;
	    release (r);
	}
	code8(ADJSP, SP, narg);
	stackoffset += narg;
    }

    switch (siz) {
    case 1: return getret();
    case 2: return getrpair();
    default: return 0;
    }
}
/* ------------------------------------------------- */
/*      push expression as argument to function      */
/* ------------------------------------------------- */

fnarg(n)
node n;
{
    struct vreg *reg;

    switch (n->ntype->ttype == ARRAY? 1 : tsize (n->ntype)) {
    case 1:
	code0 (PUSH, SP, genstmt (n));
	stackoffset++;
	break;
    case 2:
	reg = genstmt (n);
	code0 (PUSH, SP, realreg (reg));
	code0 (PUSH, SP, realreg (reg) + 1);
	release (reg);
	stackoffset += 2;
	break;
    default:
	genstmt (n);			/* this already stacks it */
    }
}
/* ---------------------- */
/*	if statement      */
/* ---------------------- */

gif(n)
struct NODE *n;
{
    struct SYMBOL *true, *false;
    struct NODE *nthen, *nelse, *body, *l;

    body = n->right;
    nthen = body->left;
    nelse = body->right;
    l = n->left;

    /* optimize if to a jump */
    if (nelse == NULL) {
	if (nthen == NULL) {		/* no body of either kind?? */
	    genrelease(l);		/* yes, just produce condition */
	    return;			/* and return */
	} else if (optimize) switch (nthen->nop) {
	case BREAK:
	    l->endlab = n->endlab;
	    gboolean(l, brklabel, 1);
	    return;
	case GOTO:
	    l->endlab = n->endlab;
	    gboolean(l, nthen->nflag, 1);
	    return;
	case CONTINUE:
	    l->endlab = n->endlab;
	    gboolean(l, looplabel, 1);
	    return;
	}
    }

    /* optimize if(0) and if(1) */
    if (l->nop == ICONST && optimize) {
	if (l->niconst && nthen != NULL) {
	    nthen->endlab = n->endlab;
	    genrelease(nthen);
	} else if (nelse != NULL) {
	    nelse->endlab = n->endlab;
	    genrelease(nelse);
	}
	return;
    }

    /* do unoptimized if statement - first get exit label */
    true = ((n->endlab == NULL)? newlabel (1) : n->endlab);

    /* then emit code for test and clauses */
    if (nthen) {
	if (nelse == NULL) false = true;
	else switch (nelse->nop) {
	case GOTO:
	    false = (struct SYMBOL *) nelse->nflag;
	    nelse = NULL;
	    break;
	case CONTINUE:
	    false = looplabel;
	    nelse = NULL;
	    break;
	case BREAK:
	    false = brklabel;
	    nelse = NULL;
	    break;
	default:
	    false = newlabel (1);
	}
	switch(nthen->nop) {		/* we could invert the boolean here, */
	case GOTO:			/* but instead we merely set label */
	case LABEL:			/* at the end of the condition. */
	case CASE:			/* fixes gotos in both clauses. */
	    l->endlab = (struct SYMBOL *) nthen->nflag;
	    break;
	case CONTINUE:
	    l->endlab = looplabel;
	    break;
	case BREAK:
	    l->endlab = brklabel;
	}
	gboolean(l, false, 0);
	nthen->endlab = true;
	genrelease(nthen);
	if (nelse) {
	    code6(JRST, 0, true);
	    emitlabel (false);
	    nelse->endlab = true;
	    genrelease(nelse);
	}
    } else if (nelse) {
	gboolean(l, true, 1);
	nelse->endlab = true;
	genrelease(nelse);
    }

    /* then emit exit label */
    if (!n->endlab) emitlabel (true);	/* emit exit label */
}
/* ------------------------- */
/*	while statement      */
/* ------------------------- */

gwhile(n)
struct NODE *n;
{
    struct SYMBOL *saveb, *savel;

    /* ok, we do, so we need to make a label for the top */
    savel = looplabel;
    looplabel = newlabel (0);

    /* now, see if there is a body or just the test */
    if (n->right == NULL) {
	n->left->endlab = n->endlab;	/* propagate exit point */
	gboolean(n->left, looplabel, 1); /* no body, just test */
    } else {
	saveb = brklabel;		/* full body, need another label */
	brklabel = (n->endlab != NULL)? n->endlab : newlabel (1);
	n->right->endlab = looplabel;	/* exit from body is to loop top */
	gboolean(n->left, brklabel, 0);	/* first the test, if any */
	genrelease(n->right);		/* then the actual body */
	code6(JRST, 0, looplabel);	/* body jumps back to test */
	if (n->endlab == NULL) emitlabel(brklabel); /* emit end label */
	brklabel = saveb;		/* restore label for outer loop */
    }

    /* in either case we need to restore the outer loop top label */
    freelabel (looplabel);
    looplabel = savel;			/* fix the label */
}
/* ---------------------- */
/*	do statement      */
/* ---------------------- */

gdo(n)
struct NODE *n;
{
    struct SYMBOL *saveb, *savel, *toplabel;

    toplabel = newlabel (0);
    saveb = brklabel;
    brklabel = (n->endlab != NULL) ? n->endlab : newlabel (1);

    if (n->right) {
	savel = looplabel;
	n->right->endlab = looplabel = newlabel (1);
	genrelease(n->right);
	emitlabel (looplabel);
	looplabel = savel;		/* restore outer loop label */
    }

    if ((n->left->nop) != ICONST) {
	n->left->endlab = brklabel;
	gboolean(n->left, toplabel, 1);
    } else if (n->left->niconst) code6(JRST, 0, toplabel);

    if (n->endlab == NULL) emitlabel (brklabel);
    brklabel = saveb;			/* restore for outer breaks */
    freelabel (toplabel);		/* no more use for this one */
}
/* ----------------------- */
/*	for statement      */
/* ----------------------- */

gfor(n)
struct NODE *n;
{
    struct NODE *cond, *body, *init, *incr;
    struct SYMBOL *saveb, *savel, *toplabel;
    int endtest;			/* safe to move test to end of loop */

    cond = n->left;
    body = n->right;
    incr = cond->right->left;
    cond = cond->left;
    init = cond->left;
    cond = cond->right;

    genrelease(init);

    toplabel =  newlabel (0);
    endtest = ((body == NULL && incr == NULL) || istrue(cond, init));

    saveb = brklabel;
    brklabel = (n->endlab != NULL)? n->endlab : newlabel (1);

    savel = looplabel;			/* remember prev outer label */
    looplabel = (body == NULL || (incr == NULL && (cond == NULL || !endtest)))?
		toplabel : newlabel (1);

    if (!endtest) gboolean(cond, brklabel, 0); /* test at start of loop */
    if (body != NULL) {
	body->endlab = looplabel;
	genrelease (body);
	if (looplabel != toplabel) emitlabel (looplabel);
    }
    if (incr != NULL) {
	if (cond == NULL || !endtest) incr->endlab = toplabel;
	genrelease (incr);
    }
    if (cond == NULL || !endtest) code6(JRST, 0, toplabel); /* just loop */
    else {				/* test comes at end of loop */
	cond->endlab = brklabel;	/* set end label for it */
	gboolean(cond,toplabel,1);	/* conditional loop */
    }

    if (n->endlab == NULL) emitlabel (brklabel);
    brklabel = saveb;			/* restore old break label */
    looplabel = savel;			/* restore outer loop continuation */
    freelabel (toplabel);		/* don't need top label any more */
}
/* ------------------------------------------ */
/*	increment/decrement instructions      */
/* ------------------------------------------ */

struct vreg *
incdec (n, op, pre)
node n;
{
    struct PSEUDO *p;
    struct vreg *r, *s, *increment();
    int ptr, size;

    size = n->nsize;
    n = n->left;
    ptr = charpointer(n->ntype);

    if (n->ntype->ttype == FLOAT) {
	r = getreg();
	code9 (MOVE, r, (op == AOS? 1 : -1), 0);
	code4 (FADR, r, gaddress (n));
	code4 (MOVEM, r, gaddress (n));
	if (!pre) code9 (FSBR, r, (op == AOS? 1 : -1), 0);
	return r;
    } else if (n->ntype->ttype == DOUBLE) {
	r = getpair();
	code9 (MOVE, r, (op == AOS? 1 : -1), 0);
	code5 (SETZ, realreg (r) + 1);
	code4 (DFAD, r, gaddress (n));
	code4 (DMOVEM, r, gaddress (n));
	if (!pre) {
	    s = getpair();
	    code9 (MOVE, s, (op == AOS? 1 : -1), 0);
	    code5 (SETZ, realreg (s) + 1);
	    code0 (DFSB, r, s);
	}
	return r;
    }

    /* pre-increment, very easy */
    if (pre) return increment(op, ptr, size, n, 1);
  
    /* post-increment, pre-increment then undo change to reg */
    if (!ptr) {
	r = increment(op, ptr, size, n, 1);
	code1((op == AOS)? SUB : ADD, r, size); /* undo change */
	return r;
    }

    /* post-increment, the hard way for byte pointers */
    r = getreg();
    code4 (MOVE, r, gaddress(n));	/* post-incr, get ptr first */
    (void) increment (op, ptr, size, n, 0);
    return r;
}
/* ---------------------------------------------------- */
/*	output code to indirectly increment object      */
/* ---------------------------------------------------- */

struct vreg *
increment (op, ptr, size, n, wanted)
node n;
{
    struct vreg *r;

    r = getreg();
    if (ptr) {
	/* do the increment according to size/direction */
	if (op == SOS) size = -size;
	if (size == 1) {		/* bumping by one? */
	    code4 (IBP, 0, gaddress(n)); /* yes, do so then load into reg */
	    if (wanted) code4 (MOVE, r, gaddress(n));
	} else {
	    code1 (MOVE, r, size);	/* not by one, get how much */
	    code4 (ADJBP, r, gaddress(n)); /* bump by that much */
	    code4 (MOVEM, r, gaddress(n)); /* store back in memory */
	}
    } else if (size == 1) code4 (op, r, gaddress(n)); /* AOS/SOS */
    else {				/* inc/dec by non-1 integer */
	code1 (MOVE, r, (op == SOS)? - size : size);
	code4 (ADD+BOTH, r, gaddress(n));
    }

    if (!wanted) release(r);
    return r;
}