Google
 

Trailing-Edge - PDP-10 Archives - SRI_NIC_PERM_FS_1_19910112 - kcc-5/kcc/ccgen1.c
There are 8 other files named ccgen1.c in the archive. Click here to see a list.
/*	CCGEN1.C - Generate code for parse-tree statement execution
**
**	All changes after version 112 (8/8/85), unless otherwise specified, are
**	Copyright 1985, 1986 by Ken Harrenstien, SRI International.
*/
/* [SRI-NIC]SS:<C.KCC.CC>CCGEN1.C.133, 17-Dec-85 07:59:25, Edit by KLH */
/*  Rationalized names of constants and structures */
/* [SRI-NIC]SS:<C.KCC.CC>CCGEN1.C.117, 13-Dec-85 11:10:14, Edit by KLH */
/*  Fix up code9 calls to provide a double as value */
/* [SRI-NIC]SS:<C.KCC.CC>CCGEN1.C.115, 12-Dec-85 13:49:19, Edit by IAN */
/*  Fix optimized IF (1) and IF (0) not to deposit bogus never-reached code */
/* <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 P_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 SC_SCOPE */
/* <KCC.CC>CCGEN1.C.39, 24-Feb-85 21:59:40, Edit by SATZ */
/* Remove SC_SCOPE */
/* <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 */

/* Statement code generator */

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

/* Imported functions */
extern VREG *genexpr();		/* CCGEN2 for expressions */
extern void genxrelease();	/* CCGEN2 for discarded expressions */
extern void gswitch();		/* CCGSWI for switch statement */
extern void codlabel(), codgolab();	/* CCCODE for label output */
extern SYMBOL *newlabel();	/* CCSYM */
extern void outlab();		/* CCOUT */

/* Exported functions defined here */
void genstmt();

/* Internal functions */
static void genadata();
static void gdo(), gfor(), gif(), gwhile(), greturn();
static SYMBOL *gtoplab();
static int labck();
/* ------------------------------------- */
/*      generate code for statement      */
/* ------------------------------------- */

void
genstmt(n)
NODE *n;
{
    NODE *rv;
    VREG *r;

    if (n == NULL) return;
    switch (n->Nop) {

    case N_STATEMENT:
	{ NODE *beg, *next;
	if (n->Nleft && n->Nleft->Nop == N_DATA) { /* Check for auto inits */
	    genadata(n->Nleft);		/* Yep, do them */
	    n = n->Nright;		/* then move on to real statements */
	}
	for(beg = n; n != NULL; n = n->Nright) {
	    if(n->Nop != N_STATEMENT)
		error(EINTNOD,"bad statement", n);
	    if(n->Nleft == NULL) continue;

	    /* Check out following stmt for possible optimizations */
	    if(n->Nright && (next = n->Nright->Nleft) && optgen) {
		switch(next->Nop) {

		/* Hack to encourage tail recursion */
		case Q_RETURN:			/* If next will be RETURN */
		    if(next->Nright == NULL) {	/* and has no return val */
			NODE *v, *laststmt();	/* Then try to optimize */
			if((v = laststmt(n->Nleft)) && v->Nop == N_FNCALL)
			    v->Nflag |= NF_RETEXPR;
		    }
		    break;

		/* If next stmt is a GOTO, ensure that any jumps
		 * within current stmt to end of stmt will
		 * instead go directly to object of the GOTO.
		 * Avoids jumping to jumps...
		 * We do a similar hack for BREAK and CONTINUE,
		 * which are similar to GOTOs except that their
		 * destination is kept in variables global to the
		 * code generation routines.
		 */
		case Q_CASE:	/* Not sure about this one yet */
		case N_LABEL:

		case Q_GOTO:
		    n->Nleft->Nendlab = next->Nxfsym;
		    break;
		case Q_BREAK:
		    n->Nleft->Nendlab = brklabel;
		    break;
		case Q_CONTINUE:
		    n->Nleft->Nendlab = looplabel;
		    break;
	    }	/* end of Nop switch */
	    }	/* end of next-stmt check */

	    /* Optimize label usage */
	    if(n->Nright == NULL 	/* If this is last stmt in list */
		&& optgen)
		n->Nleft->Nendlab = beg->Nendlab;	/* Copy from 1st */

	    genstmt(n->Nleft);
	}
	break;
	} /* end of N_STATEMENT case block */

    case Q_CASE:
	codlabel(n->Nxfsym);		/* send forward label */
	n->Nleft->Nendlab = n->Nendlab;	/* propagate end label */
	genstmt (n->Nleft);		/* finish rest of body */
	break;

    case N_LABEL:
	codgolab(n->Nxfsym);		/* send goto label */
	n->Nleft->Nendlab = n->Nendlab;	/* propagate end label */
	genstmt(n->Nleft);		/* finish rest of body */
	break;

    case Q_BREAK:	code6(P_JRST, 0, brklabel);	break;
    case Q_GOTO:	code6(P_JRST, 0, n->Nxfsym);	break;
    case Q_CONTINUE:	code6(P_JRST, 0, looplabel);	break;
    case Q_DO:		gdo(n);		break;
    case Q_FOR:		gfor(n);	break;
    case Q_IF:		gif(n);		break;
    case Q_RETURN:	greturn(n);	break;
    case Q_SWITCH:	gswitch(n);	break;
    case Q_WHILE:	gwhile(n);	break;

    case N_EXPRLIST:		/* Same as expression stmt */
    default:			/* None of above, assume expression stmt */
	genxrelease(n);		/* Generate it and flush any result */
	break;
    }
}

/* Hack for statement optimization.  Find last statement in list. */
static NODE *
laststmt(n)
NODE *n;
{
	while(n && n->Nop == N_STATEMENT) {
		while(n->Nright) n = n->Nright;
		n = n->Nleft;
	}
	return(n);
}
/* GENADATA - Generate auto data initializations
**	Should be called only for N_DATA nodes.
*/
static void
genadata(n)
NODE *n;
{
    VREG *r;
    if (n->Nop != N_DATA) {
	error(EINTNOD,"node not N_DATA", n);
	return;
    }
    for (; n && n->Nop == N_DATA; n = n->Nright) {
	if (n->Nleft != NULL && n->Nleft->Nright != NULL) {

	    /* Have an N_IZ node with initializer, process it
	    ** by turning the N_IZ into a Q_ASGN expression.  Note the
	    ** C type of the expression needs to be set (from the Q_IDENT).
	    */
	    n->Nleft->Nop = Q_ASGN;	/* Turn N_IZ into Q_ASGN */
	    n->Nleft->Ntype = n->Nleft->Nleft->Ntype;	/* Set up type */
	    genxrelease(n->Nleft);	/* Generate code, ignore value */
	}
    }
}
/* ---------------------- */
/*	if statement      */
/* ---------------------- */
static void
gif(n)
NODE *n;
{
    SYMBOL *true, *false;
    NODE *nthen, *nelse, *body, *l;

    body = n->Nright;
    nthen = body->Nleft;
    nelse = body->Nright;
    l = n->Nleft;

    /* optimize if to a jump */
    if (nelse == NULL) {
	if (nthen == NULL) {		/* no body of either kind?? */
	    genxrelease(l);		/* yes, just produce condition */
	    return;			/* and return */
	} else if (optgen) switch (nthen->Nop) {
	case Q_BREAK:
	    l->Nendlab = n->Nendlab;
	    gboolean(l, brklabel, 1);
	    return;
	case Q_GOTO:
	    l->Nendlab = n->Nendlab;
	    gboolean(l, nthen->Nxfsym, 1);
	    return;
	case Q_CONTINUE:
	    l->Nendlab = n->Nendlab;
	    gboolean(l, looplabel, 1);
	    return;
	}
    }

    /* Try to optimize when conditional expression is a constant.
    ** We have to be careful about flushing the then/else clauses because
    ** control could jump into them using either case or goto labels.
    ** Hence the labchk() to see whether the parse tree contains such labels...
    */
    if (l->Nop == N_ICONST && optgen) {		/* If cond is constant */
	if (l->Niconst && nthen && !labchk(nelse)) {	/* if (1) */
	    nthen->Nendlab = n->Nendlab;
	    genstmt(nthen);
	    return;
	}
	if (!l->Niconst && nelse && !labchk(nthen)) {	/* if (0) */
	    nelse->Nendlab = n->Nendlab;
	    genstmt(nelse);
	    return;
	}
    }

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

    /* then emit code for test and clauses */
    if (nthen) {
	if (nelse == NULL) false = true;
	else switch (nelse->Nop) {
	case Q_GOTO:
	    false = nelse->Nxfsym;
	    nelse = NULL;
	    break;
	case Q_CONTINUE:
	    false = looplabel;
	    nelse = NULL;
	    break;
	case Q_BREAK:
	    false = brklabel;
	    nelse = NULL;
	    break;
	default:
	    false = newlabel();
	}
	switch(nthen->Nop) {		/* we could invert the boolean here, */
	case Q_GOTO:			/* but instead we merely set label */
	case N_LABEL:			/* at the end of the condition. */
	case Q_CASE:			/* fixes gotos in both clauses. */
	    l->Nendlab = nthen->Nxfsym;
	    break;
	case Q_CONTINUE:
	    l->Nendlab = looplabel;
	    break;
	case Q_BREAK:
	    l->Nendlab = brklabel;
	}
	gboolean(l, false, 0);
	nthen->Nendlab = true;
	genstmt(nthen);
	if (nelse) {
	    code6(P_JRST, 0, true);
	    codlabel(false);
	    nelse->Nendlab = true;
	    genstmt(nelse);
	}
    } else if (nelse) {
	gboolean(l, true, 1);
	nelse->Nendlab = true;
	genstmt(nelse);
    }

    /* then emit exit label */
    if (!n->Nendlab) codlabel(true);	/* emit exit label */
}
/* LABCHK - returns true if parse tree contains any labels.
*/
static int
labchk(n)
NODE *n;
{
    if (n == NULL) return 0;
    switch (n->Nop) {
	case Q_CASE:		/* These three are all labels */
	case N_LABEL:
	case Q_DEFAULT:
	    return 1;

	case N_STATEMENT:		/* Compound statement, scan it. */
	    if (n->Nleft && n->Nleft->Nop == N_DATA)	/* Skip auto inits */
		n = n->Nright;		/* move on to real statements */
	    for(; n != NULL; n = n->Nright) {
		if(n->Nop != N_STATEMENT)
		    error(EINTNOD,"bad statement", n);
		if(n->Nleft && labchk(n->Nleft))
		    return 1;
	    }
	    return 0;

	case Q_IF:		/* Has two substatements */
	    return labchk(n->Nright->Nleft) || labchk(n->Nright->Nright);

	case Q_DO:		/* Standard substatements */
	case Q_FOR:
	case Q_SWITCH:
	case Q_WHILE:
	    return labchk(n->Nright);

	case Q_BREAK:		/* Not labels and no substatements */
	case Q_GOTO:
	case Q_CONTINUE:
	case Q_RETURN:
	case N_EXPRLIST:	/* Same as expression stmt */
	default:		/* None of above, assume expression stmt */
	    return 0;
    }
}
/* ------------------------- */
/*	while statement      */
/* ------------------------- */
static void
gwhile(n)
NODE *n;
{
    SYMBOL *saveb, *savel;

    /* ok, we do, so we need to make a label for the top */
    savel = looplabel;
    looplabel = gtoplab();		/* Get new label and emit */

    /* now, see if there is a body or just the test */
    if (n->Nright == NULL) {
	n->Nleft->Nendlab = n->Nendlab;	/* propagate exit point */
	gboolean(n->Nleft, looplabel, 1); /* no body, just test */
    } else {
	saveb = brklabel;		/* full body, need another label */
	brklabel = (n->Nendlab != NULL)? n->Nendlab : newlabel();
	n->Nright->Nendlab = looplabel;	/* exit from body is to loop top */
	gboolean(n->Nleft, brklabel, 0);	/* first the test, if any */
	genstmt(n->Nright);		/* then the actual body */
	code6(P_JRST, 0, looplabel);	/* body jumps back to test */
	if (n->Nendlab == NULL) codlabel(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 */
}

/* GTOPLAB - auxiliary for loops which need to generate a label at top.
*/
static SYMBOL *
gtoplab()
{
    SYMBOL *lab;
    flushcode();		/* Ensure all previous code forced out */
    lab = newlabel();		/* Get new label */
    outlab(lab);		/* and emit it directly */
    return lab;
}
/* ---------------------- */
/*	do statement      */
/* ---------------------- */
static void
gdo(n)
NODE *n;
{
    SYMBOL *saveb, *savel, *toplabel;

    toplabel = gtoplab();		/* Get new label and emit */
    saveb = brklabel;
    brklabel = (n->Nendlab != NULL) ? n->Nendlab : newlabel();

    if (n->Nright) {
	savel = looplabel;
	n->Nright->Nendlab = looplabel = newlabel();
	genstmt(n->Nright);
	codlabel(looplabel);
	looplabel = savel;		/* restore outer loop label */
    }

    if ((n->Nleft->Nop) != N_ICONST) {
	n->Nleft->Nendlab = brklabel;
	gboolean(n->Nleft, toplabel, 1);
    } else if (n->Nleft->Niconst) code6(P_JRST, 0, toplabel);

    if (n->Nendlab == NULL) codlabel(brklabel);
    brklabel = saveb;			/* restore for outer breaks */
    freelabel (toplabel);		/* no more use for this one */
}
/* ----------------------- */
/*	for statement      */
/* ----------------------- */
static void
gfor(n)
NODE *n;
{
    NODE *cond, *body, *incr, *init;
    SYMBOL *saveb, *savel, *toplabel;
    int endtest;			/* safe to move test to end of loop */

    cond = n->Nleft;
    body = n->Nright;
    incr = cond->Nright->Nleft;
    cond = cond->Nleft;
    init = cond->Nleft;
    cond = cond->Nright;

    /* See if conditional test can be put at the end of the loop, which is
    ** usually more efficient.  endtest is true if so.  Note that it
    ** will not be true if no test exists.
    */
    endtest = optgen &&			/* Never, if not optimizing. */
	cond &&				/* And only if have a condition. */
	((body == NULL && incr == NULL)	/* OK if no body or increment */
	 || istrue(cond, init));	/* or if test exists & is TRUE */

    if (init) genxrelease(init);

    toplabel = gtoplab();	/* Generate top-of-loop label and emit */
    saveb = brklabel;
    brklabel = (n->Nendlab != NULL) ? n->Nendlab : newlabel();

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

     /* Normally generate test at start of loop */
    if (cond && !endtest)		/* If have condition, and at beg, */
	gboolean(cond, brklabel, 0);	/* generate test at start of loop */

    /* Now generate body */
    if (body != NULL) {			/* If we have one, of course */
	body->Nendlab = looplabel;
	genstmt(body);
	if (looplabel != toplabel) codlabel(looplabel);
    }

    /* Now generate increment */
    if (incr != NULL) {
	if (!endtest) incr->Nendlab = toplabel;
	genxrelease(incr);
    }

    /* Finally generate loop back to condition test at top, unless actually
    ** doing the test here.
    */
    if (endtest) {			/* If test comes at end of loop */
	cond->Nendlab = brklabel;	/* set end label for it */
	gboolean(cond,toplabel,1);	/* and gen the conditional jump */
    }
    else code6(P_JRST, 0, toplabel);	/* Otherwise just loop */

    if (n->Nendlab == NULL)
	codlabel(brklabel);
    brklabel = saveb;			/* restore old break label */
    looplabel = savel;			/* restore outer loop continuation */
    freelabel(toplabel);		/* don't need top label any more */
}
/* -------------------------- */
/*	return statement      */
/* -------------------------- */
static void
greturn(n)
NODE *n;
{
    int siz;
    VREG *r;

    if (safejump(previous)) return;	/* if in dead code, do nothing */
    if ((n = n->Nright) != NULL) {	/* if returning val, set it */
	n->Nflag |= NF_RETEXPR;		/* this is to be return value */
	siz = sizetype(n->Ntype);
	if (n->Ntype->Tspec == TS_ARRAY) {	/* Just in case */
	    error(EINT,"returning array type");
	    return;
	}
	switch (siz) {
	case 1:
	    r = genexpr (n);
	    if (safejump(previous)) return;
	    code0 (P_MOVE, R_RETVAL, r);	/* don't need to worry about */
	    break;			/* register allocation, just do it */
	case 2:
	    r = genexpr (n);
	    if (safejump(previous)) return;
	    code0 (P_DMOVE, R_RETVAL, r);
	    break;

	default:
	    r = getreg();
	    code13(P_MOVE, r, -1 - stackoffset);	/* Get 1st arg addr */
	    code4(P_MOVE, r, r);	/* Get ptr to place to return struct */
	    code4s(P_SMOVE, r, genexpr(n), 0, siz);	/* Return it */
	    release(r);
	}
    }
    if (optobj) killstack();		/* get rid of spurious P_MOVEMs */
    code8 (P_ADJSP, R_SP, - stackoffset);	/* flush local vars from stk */
    code5 (P_POPJ, R_SP);			/* emit the return */
}