Trailing-Edge
-
PDP-10 Archives
-
SRI_NIC_PERM_FS_1_19910112
-
c/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
**
** (c) Copyright Ken Harrenstien 1989
** All changes after v.198, 9-Mar-1988
** (c) Copyright Ken Harrenstien, SRI International 1985, 1986
** All changes after v.112, 8-Aug-1985
**
** Original version (C) 1981 K. Chen
*/
#include "cc.h"
#include "ccgen.h"
/* Imported functions */
extern NODE *ndeflr(); /* CCSTMT */
extern VREG *genexpr(); /* CCGEN2 for expressions */
extern void genxrelease(); /* CCGEN2 for discarded expressions */
extern VREG *getmem(), *stomem(); /* CCGEN2 */
extern void gswitch(); /* CCGSWI for switch statement */
extern void codlabel(), codgolab(); /* CCCODE for label output */
extern SYMBOL *newlabel(); /* CCSYM */
extern int sizetype(); /* CCSYM */
extern void outlab(); /* CCOUT */
extern void code0(), code3(), code4(), code4s(), code5(),
code6(), code8(), code13();
extern void relflush(), gboolean(), flushcode(), freelabel();
extern int istrue(); /* CCEVAL */
extern int deadjump(), killstack(); /* CCJSKP, CCOPT */
#if SYS_CSI /* added for profiling, 09/15/89 by MVS */
extern void outepilog(); /* CCOUT */
#endif
/* Exported functions defined here */
void genstmt();
/* Internal functions */
static void genadata();
static void gdo(), gfor(), gif(), gwhile(), greturn();
static SYMBOL *gtoplab();
static int labchk();
static NODE *laststmt();
/* ------------------------------------- */
/* generate code for statement */
/* ------------------------------------- */
void
genstmt(n)
NODE *n;
{
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)
int_error("genstmt: bad stmt %N", 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; /* 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.
**
** Includes "gizlist" - Generate a struct/union/array constant.
** This can only happen for brace-enclosed initializers.
** Node will be a N_IZLIST, with Ntype set to desired type of the
** constant.
** For the time being, our approach is to chicken out by just inventing
** a new internal label and returning that. This label and the N_IZLIST
** are put together to form a N_LITIZ list and chained onto "litnodes"
** for eventual output as a static data literal.
*/
static void
genadata(n)
NODE *n;
{
if (n->Nop != N_DATA) {
int_error("genadata: node not N_DATA %N", 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. */
if (n->Nleft->Nright->Nop == N_IZLIST) {
/* A struct/union/array initializer! */
SYMBOL *s = n->Nleft->Nleft->Nid; /* Find ident */
NODE *l = n->Nleft->Nright; /* Point to N_IZLIST */
TYPE *t = l->Ntype; /* Find type of constant */
VREG *ra, *r;
/* Put izer node onto literal list for later emission */
litnodes = ndeflr(N_LITIZ, l, litnodes);
litnodes->Nendlab = newlabel(); /* Make label for it */
/* Get the literal and store into auto var. Note that the
** type used is that of literal, not the ident, and it is
** ignored except to find the size in words of the object.
** This takes care of union objects, which only need to
** init the first member (which may be smaller than some
** other members).
*/
r = vrget();
ra = vrget();
code3(P_MOVE, r, litnodes->Nendlab); /* Get lit's addr */
r = getmem(r, t, 0, 0); /* Get izer contents */
code13(P_MOVE, ra, (s->Svalue+1)-stackoffset); /* AUTO addr */
r = stomem(r, ra, sizetype(t), 0); /* Set up the store */
relflush(r); /* Done, flush reg */
continue;
}
/* Normal case, izer not a brace-enclosed list. Handle
** 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 && !labchk(nelse)) { /* if (1) & "else" flushable */
if (nthen) {
nthen->Nendlab = n->Nendlab;
genstmt(nthen);
}
return;
}
if (!l->Niconst && !labchk(nthen)) { /* if (0) & "then" flushable */
if (nelse) {
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)
int_error("labchk: bad stmt %N", 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, *r2;
if (optobj && deadjump()) return; /* If in dead code, do nothing */
if (n = n->Nright) { /* If returning a value, set it */
if (n->Ntype->Tspec == TS_ARRAY) { /* Just in case */
int_error("greturn: returning array");
return;
}
siz = sizetype(n->Ntype); /* Remember size */
n->Nflag |= NF_RETEXPR; /* Tell genexpr this is return value */
if ((r = genexpr(n)) == NULL) { /* Make return expression */
if (!deadjump()) /* If no expr, must be in dead code */
int_error("greturn: null vreg");
return; /* Assume tail-recursed, so win! */
}
switch (siz) {
case 1: /* Return 1-word value */
code0(P_MOVE, VR_RETVAL, r);
break;
case 2: /* Return 2-word value */
code0(P_DMOVE, VR_RETVAL, r);
break;
default: /* Return N-word value */
r2 = vrget();
code13(P_MOVE, r2, -1 - stackoffset); /* Get addr of arg 0 */
code4(P_MOVE, r2, r2); /* Get ptr to place to return block */
code4s(P_SMOVE, r2, r, 0, siz); /* Copy the block */
vrfree(r2);
}
}
if (optobj) killstack(); /* Flush spurious MOVEMs */
code8(P_ADJSP, VR_SP, - stackoffset); /* flush local vars from stk */
#if SYS_CSI
if (profbliss) { /* for BLISS profiler */
flushcode();
outepilog(curfn); /* added 09/15/89 by MVS */
}
#endif
code5(P_POPJ, VR_SP); /* emit the return */
}