MiniUnix/usr/source/c/c10.c
#
/*
C compiler, part 2
*/
#include "c1h.c"
char maprel[] { EQUAL, NEQUAL, GREATEQ, GREAT, LESSEQ,
LESS, GREATQP, GREATP, LESSEQP, LESSP
};
char notrel[] { NEQUAL, EQUAL, GREAT, GREATEQ, LESS,
LESSEQ, GREATP, GREATQP, LESSP, LESSEQP
};
struct tconst czero { CON, INT, 0, 0};
struct tconst cone { CON, INT, 0, 1};
struct tconst fczero { SFCON, DOUBLE, 0, 0 };
struct table *cregtab;
int nreg 3;
int isn 10000;
int namsiz 8;
main(argc, argv)
char *argv[];
{
extern fout;
if (argc<4) {
error("Arg count");
exit(1);
}
if(fopen(argv[1], ascbuf)<0) {
error("Missing temp file");
exit(1);
}
if ((fout = creat(argv[3], 0666)) < 0) {
error("Can't create %s", argv[3]);
exit(1);
}
spacep = treespace;
getree();
/*
* If any floating-point instructions
* were used, generate a reference which
* pulls in the floating-point part of printf.
*/
if (nfloat)
printf(".globl fltused\n");
/*
* tack on the string file.
*/
close(ascbuf[0]);
if (fopen(argv[2], ascbuf)<0) {
error("Missing temp file");
exit(1);
}
printf(".globl\n.data\n");
getree();
flush();
exit(nerror!=0);
}
/*
* Given a tree, a code table, and a
* count of available registers, find the code table
* for the appropriate operator such that the operands
* are of the right type and the number of registers
* required is not too large.
* Return a ptr to the table entry or 0 if none found.
*/
char *match(atree, table, nrleft)
struct tnode *atree;
struct table *table;
{
int op, d1, d2, t1, t2, dope;
struct tnode *p2;
register struct tnode *p1, *tree;
register struct optab *opt;
if ((tree=atree)==0)
return(0);
if (table==lsptab)
table = sptab;
if ((op = tree->op)==0)
return(0);
dope = opdope[op];
if ((dope&LEAF) == 0)
p1 = tree->tr1;
else
p1 = tree;
t1 = p1->type;
d1 = dcalc(p1, nrleft);
if ((dope&BINARY)!=0) {
p2 = tree->tr2;
/*
* If a subtree starts off with a conversion operator,
* try for a match with the conversion eliminated.
* E.g. int = double can be done without generating
* the converted int in a register by
* movf double,fr0; movfi fr0,int .
*/
if(opdope[p1->op]&CNVRT && (opdope[p2->op]&CNVRT)==0) {
tree->tr1 = p1->tr1;
if (opt = match(tree, table, nrleft))
return(opt);
tree->tr1 = p1;
} else if (opdope[p2->op]&CNVRT && (opdope[p1->op]&CNVRT)==0) {
tree->tr2 = p2->tr1;
if (opt = match(tree, table, nrleft))
return(opt);
tree->tr2 = p2;
}
t2 = p2->type;
d2 = dcalc(p2, nrleft);
}
for (; table->op!=op; table++)
if (table->op==0)
return(0);
for (opt = table->tabp; opt->tabdeg1!=0; opt++) {
if (d1 > (opt->tabdeg1&077)
|| (opt->tabdeg1 >= 0100 && (p1->op != STAR)))
continue;
if (notcompat(p1, opt->tabtyp1, op)) {
continue;
}
if ((opdope[op]&BINARY)!=0 && p2!=0) {
if (d2 > (opt->tabdeg2&077)
|| (opt->tabdeg2 >= 0100) && (p2->op != STAR) )
continue;
if (notcompat(p2,opt->tabtyp2, 0))
continue;
}
return(opt);
}
return(0);
}
/*
* Given a tree, a code table, and a register,
* produce code to evaluate the tree with the appropriate table.
* Registers reg and upcan be used.
* If there is a value, it is desired that it appear in reg.
* The routine returns the register in which the value actually appears.
* This routine must work or there is an error.
* If the table called for is cctab, sptab, or efftab,
* and tree can't be done using the called-for table,
* another try is made.
* If the tree can't be compiled using cctab, regtab is
* used and a "tst" instruction is produced.
* If the tree can't be compiled using sptab,
* regtab is used and the register is pushed on the stack.
* If the tree can't be compiled using efftab,
* just use regtab.
* Regtab must succeed or an "op not found" error results.
*
* A number of special cases are recognized, and
* there is an interaction with the optimizer routines.
*/
rcexpr(atree, atable, reg)
struct tnode *atree;
struct table *atable;
{
register r;
int modf, nargs, recurf;
register struct tnode *tree;
register struct table *table;
table = atable;
recurf = 0;
if (reg<0) {
recurf++;
reg = ~reg;
if (reg>=020) {
reg =- 020;
recurf++;
}
}
if((tree=atree)==0)
return(0);
switch (tree->op) {
/*
* A conditional branch
*/
case CBRANCH:
cbranch(optim(tree->btree), tree->lbl, tree->cond, 0);
return(0);
/*
* An initializing expression
*/
case INIT:
if (tree->tr1->op == AMPER)
tree->tr1 = tree->tr1->tr1;
if (tree->tr1->op==NAME)
pname(tree->tr1);
else if (tree->tr1==CON)
psoct(tree->tr1->value);
else
error("Illegal initialization");
putchar('\n');
return(0);
/*
* Put the value of an expression in r0,
* for a switch or a return
*/
case RFORCE:
if((r=rcexpr(tree->tr1, regtab, reg)) != 0)
printf("mov%c r%d,r0\n", isfloat(tree->tr1), r);
return(0);
/*
* sequential execution
*/
case COMMA:
rcexpr(tree->tr1, efftab, reg);
atree = tree = tree->tr2;
break;
/*
* In the generated &~ operator,
* fiddle things so a PDP-11 "bit"
* instruction will be produced when cctab is used.
*/
case NAND:
if (table==cctab) {
tree->op = TAND;
tree->tr2 = optim(block(1, COMPL, INT, 0, tree->tr2));
}
break;
/*
* Handle a subroutine call. It has to be done
* here because if cexpr got called twice, the
* arguments might be compiled twice.
* There is also some fiddling so the
* first argument, in favorable circumstances,
* goes to (sp) instead of -(sp), reducing
* the amount of stack-popping.
*/
case CALL:
r = 0;
nargs = 0;
modf = 0;
if (tree->tr1->op!=NAME) { /* get nargs right */
nargs++;
nstack++;
}
tree = tree->tr2;
if(tree->op) {
while (tree->op==COMMA) {
r =+ comarg(tree->tr2, &modf);
tree = tree->tr1;
nargs++;
}
r =+ comarg(tree, &modf);
nargs++;
}
tree = atree;
tree->op = CALL2;
if (modf && tree->tr1->op==NAME && tree->tr1->class==EXTERN)
tree->op = CALL1;
cexpr(tree, regtab, reg);
popstk(r);
nstack =- nargs;
if (table==efftab || table==regtab)
return(0);
r = 0;
goto fixup;
/*
* Longs need special treatment.
*/
case ASLSH:
case LSHIFT:
if (tree->type==LONG) {
if (tree->tr2->op==ITOL)
tree->tr2 = tree->tr2->tr1;
if (tree->op==ASLSH)
tree->op = ASLSHL;
else
tree->op = LLSHIFT;
}
break;
/*
* Try to change * and / to shifts.
*/
case TIMES:
case DIVIDE:
case ASTIMES:
case ASDIV:
tree = pow2(tree);
}
/*
* Try to find postfix ++ and -- operators that can be
* pulled out and done after the rest of the expression
*/
if (table!=cctab && table!=cregtab && recurf<2
&& (opdope[tree->op]&LEAF)==0) {
if (r=delay(&atree, table, reg)) {
tree = atree;
table = efftab;
reg = r-1;
}
}
/*
* Basically, try to reorder the computation
* so reg = x+y is done as reg = x; reg =+ y
*/
if (recurf==0 && reorder(&atree, table, reg)) {
if (table==cctab && atree->op==NAME)
return(reg);
}
tree = atree;
if (table==efftab && tree->op==NAME)
return(reg);
if ((r=cexpr(tree, table, reg))>=0)
return(r);
if (table!=regtab) {
if((r=cexpr(tree, regtab, reg))>=0) {
fixup:
modf = isfloat(tree);
if (table==sptab || table==lsptab) {
if (tree->type==LONG) {
printf("mov\tr%d,-(sp)\n",r+1);
nstack++;
}
printf("mov%c r%d,%c(sp)\n", modf, r,
table==sptab? '-':0);
nstack++;
}
if (table==cctab)
printf("tst%c r%d\n", modf, r);
return(r);
}
}
if (tree->op>0 && tree->op<RFORCE && opntab[tree->op])
error("No code table for op: %s", opntab[tree->op]);
else
error("No code table for op %d", tree->op);
return(reg);
}
/*
* Try to compile the tree with the code table using
* registers areg and up. If successful,
* return the register where the value actually ended up.
* If unsuccessful, return -1.
*
* Most of the work is the macro-expansion of the
* code table.
*/
cexpr(atree, table, areg)
struct tnode *atree;
struct table *table;
{
int c, r;
register struct tnode *p, *p1, *tree;
struct table *ctable;
struct tnode *p2;
char *string;
int reg, reg1, rreg, flag, opd;
char *opt;
tree = atree;
reg = areg;
p1 = tree->tr2;
c = tree->op;
opd = opdope[c];
/*
* When the value of a relational or a logical expression is
* desired, more work must be done.
*/
if ((opd&RELAT||c==LOGAND||c==LOGOR||c==EXCLA) && table!=cctab) {
cbranch(tree, c=isn++, 1, reg);
rcexpr(&czero, table, reg);
branch(isn, 0);
label(c);
rcexpr(&cone, table, reg);
label(isn++);
return(reg);
}
if(c==QUEST) {
if (table==cctab)
return(-1);
cbranch(tree->tr1, c=isn++, 0, reg);
flag = nstack;
rreg = rcexpr(p1->tr1, table, reg);
nstack = flag;
branch(r=isn++, 0);
label(c);
reg = rcexpr(p1->tr2, table, rreg);
if (rreg!=reg)
printf("mov%c r%d,r%d\n",
isfloat(tree),reg,rreg);
label(r);
return(rreg);
}
reg = oddreg(tree, reg);
reg1 = reg+1;
/*
* long values take 2 registers.
*/
if (tree->type==LONG && tree->op!=ITOL)
reg1++;
/*
* Leaves of the expression tree
*/
if ((r = chkleaf(tree, table, reg)) >= 0)
return(r);
/*
* x + (-1) is better done as x-1.
*/
if ((tree->op==PLUS||tree->op==ASPLUS) &&
(p1=tree->tr2)->op == CON && p1->value == -1) {
p1->value = 1;
tree->op =+ (MINUS-PLUS);
}
if (table==cregtab)
table = regtab;
/*
* The following peculiar code depends on the fact that
* if you just want the codition codes set, efftab
* will generate the right code unless the operator is
* postfix ++ or --. Unravelled, if the table is
* cctab and the operator is not special, try first
* for efftab; if the table isn't, if the operator is,
* or the first match fails, try to match
* with the table actually asked for.
*/
if (table!=cctab || c==INCAFT || c==DECAFT
|| (opt = match(tree, efftab, nreg-reg)) == 0)
if ((opt=match(tree, table, nreg-reg))==0)
return(-1);
string = opt->tabstring;
p1 = tree->tr1;
p2 = 0;
if (opdope[tree->op]&BINARY)
p2 = tree->tr2;
loop:
/*
* The 0200 bit asks for a tab.
*/
if ((c = *string++) & 0200) {
c =& 0177;
putchar('\t');
}
switch (c) {
case '\0':
if (!isfloat(tree))
if (tree->op==DIVIDE || tree->op==ASDIV)
reg--;
return(reg);
/* A1 */
case 'A':
p = p1;
goto adr;
/* A2 */
case 'B':
p = p2;
goto adr;
adr:
c = 0;
if (*string=='\'') {
c = 1;
string++;
} else if (*string=='+') {
c = 2;
string++;
}
pname(p, c);
goto loop;
/* I */
case 'M':
if ((c = *string)=='\'')
string++;
else
c = 0;
prins(tree->op, c, instab);
goto loop;
/* B1 */
case 'C':
if ((opd&LEAF) != 0)
p = tree;
else
p = p1;
goto pbyte;
/* BF */
case 'P':
p = tree;
goto pb1;
/* B2 */
case 'D':
p = p2;
pbyte:
if (p->type==CHAR)
putchar('b');
pb1:
if (isfloat(p))
putchar('f');
goto loop;
/* BE */
case 'L':
if (p1->type==CHAR || p2->type==CHAR)
putchar('b');
p = tree;
goto pb1;
/* F */
case 'G':
p = p1;
flag = 01;
goto subtre;
/* S */
case 'K':
p = p2;
flag = 02;
goto subtre;
/* H */
case 'H':
p = tree;
flag = 04;
subtre:
ctable = regtab;
c = *string++ - 'A';
if (*string=='!') {
string++;
c =| 020; /* force right register */
}
if ((c&02)!=0)
ctable = sptab;
if ((c&04)!=0)
ctable = cctab;
if ((flag&01) && ctable==regtab && (c&01)==0
&& (tree->op==DIVIDE||tree->op==MOD
|| tree->op==ASDIV||tree->op==ASMOD))
ctable = cregtab;
if ((c&01)!=0) {
p = p->tr1;
if(collcon(p) && ctable!=sptab) {
if (p->op==STAR)
p = p->tr1;
p = p->tr1;
}
}
if (table==lsptab && ctable==sptab)
ctable = lsptab;
if (c&010)
r = reg1;
else
if (opdope[p->op]&LEAF || p->degree < 2)
r = reg;
else
r = areg;
rreg = rcexpr(p, ctable, r);
if (ctable!=regtab && ctable!=cregtab)
goto loop;
if (c&010) {
if (c&020 && rreg!=reg1)
printf("mov%c r%d,r%d\n",
isfloat(tree),rreg,reg1);
else
reg1 = rreg;
} else if (rreg!=reg)
if ((c&020)==0 && oddreg(tree, 0)==0 && (flag&04 ||
flag&01
&& xdcalc(p2, nreg-rreg-1) <= (opt->tabdeg2&077)
|| flag&02
&& xdcalc(p1,nreg-rreg-1) <= (opt->tabdeg1&077))) {
reg = rreg;
reg1 = rreg+1;
} else
printf("mov%c\tr%d,r%d\n",
isfloat(tree), rreg, reg);
goto loop;
/* R */
case 'I':
r = reg;
if (*string=='-') {
string++;
r--;
}
goto preg;
/* R1 */
case 'J':
r = reg1;
preg:
if (*string=='+') {
string++;
r++;
}
if (r>nreg)
error("Register overflow: simplify expression");
printf("r%d", r);
goto loop;
case '-': /* check -(sp) */
if (*string=='(') {
nstack++;
if (table!=lsptab)
putchar('-');
goto loop;
}
break;
case ')': /* check (sp)+ */
putchar(')');
if (*string=='+')
nstack--;
goto loop;
/* #1 */
case '#':
p = p1->tr1;
goto nmbr;
/* #2 */
case '"':
p = p2->tr1;
nmbr:
if(collcon(p)) {
if (p->op==STAR) {
printf("*");
p = p->tr1;
}
if ((p = p->tr2)->op == CON) {
if (p->value)
psoct(p->value);
} else if (p->op==AMPER)
pname(p->tr1, 0);
}
goto loop;
case 'T': /* "tst R" if 1st op not in cctab */
if (dcalc(p1, 5)>12 && !match(p1, cctab, 10))
printf("tst r%d\n", reg);
goto loop;
case 'V': /* adc or sbc as required for longs */
switch(tree->op) {
case PLUS:
case ASPLUS:
case INCBEF:
case INCAFT:
printf("adc");
break;
case MINUS:
case ASMINUS:
case NEG:
case DECBEF:
case DECAFT:
printf("sbc");
break;
default:
while ((c = *string++)!='\n' && c!='\0');
break;
}
goto loop;
}
putchar(c);
goto loop;
}
/*
* This routine just calls sreorder (below)
* on the subtrees and then on the tree itself.
* It returns non-zero if anything changed.
*/
reorder(treep, table, reg)
struct tnode **treep;
struct table *table;
{
register r, r1;
register struct tnode *p;
p = *treep;
if (opdope[p->op]&LEAF)
return(0);
r1 = 0;
while(sreorder(&p->tr1, table, reg))
r1++;
if (opdope[p->op]&BINARY)
while(sreorder(&p->tr2, table, reg))
r1++;
r = 0;
while (sreorder(treep, table, reg))
r++;
*treep = optim(*treep);
return(r);
}
/*
* Basically this routine carries out two kinds of optimization.
* First, it observes that "x + (reg = y)" where actually
* the = is any assignment op is better done as "reg=y; x+reg".
* In this case rcexpr is called to do the first part and the
* tree is modified so the name of the register
* replaces the assignment.
* Moreover, expressions like "reg = x+y" are best done as
* "reg = x; reg =+ y" (so long as "reg" and "y" are not the same!).
*/
sreorder(treep, table, reg)
struct tnode **treep;
struct table *table;
{
register struct tnode *p, *p1;
p = *treep;
if (opdope[p->op]&LEAF)
return(0);
if (p->op==PLUS)
if (reorder(&p->tr2, table, reg))
*treep = p = optim(p);
p1 = p->tr1;
if (p->op==STAR || p->op==PLUS) {
if (reorder(&p->tr1, table, reg))
*treep = p = optim(p);
p1 = p->tr1;
}
if (p1->op==NAME) switch(p->op) {
case ASLSH:
case ASRSH:
case ASSIGN:
if (p1->class != REG || isfloat(p->tr2))
return(0);
if (p->op==ASSIGN) switch (p->tr2->op) {
case TIMES:
case DIVIDE:
if (!ispow2(p->tr2))
break;
p->tr2 = pow2(p->tr2);
case PLUS:
case MINUS:
case AND:
case NAND:
case OR:
case EXOR:
case LSHIFT:
case RSHIFT:
p1 = p->tr2->tr2;
if (xdcalc(p1) > 12
|| p1->op==NAME
&&(p1->nloc==p->tr1->nloc
|| p1->regno==p->tr1->nloc))
return(0);
p1 = p->tr2;
p->tr2 = p1->tr1;
if (p1->tr1->op!=NAME
|| p1->tr1->class!=REG
|| p1->tr1->nloc!=p->tr1->nloc)
rcexpr(p, efftab, reg);
p->tr2 = p1->tr2;
p->op = p1->op + ASPLUS - PLUS;
*treep = p;
return(1);
}
goto OK;
case ASTIMES:
case ASDIV:
if (!ispow2(p))
return(0);
case ASPLUS:
case ASMINUS:
case ASSAND:
case ASSNAND:
case ASOR:
case ASXOR:
case DECBEF:
case INCBEF:
OK:
if (table==cctab||table==cregtab)
reg =+ 020;
rcexpr(optim(p), efftab, ~reg);
*treep = p1;
return(1);
}
return(0);
}
/*
* Delay handles postfix ++ and --
* It observes that "x + y++" is better
* treated as "x + y; y++".
* If the operator is ++ or -- itself,
* it calls rcexpr to load the operand, letting
* the calling instance of rcexpr to do the
* ++ using efftab.
* Otherwise it uses sdelay to search for inc/dec
* among the operands.
*/
delay(treep, table, reg)
struct tnode **treep;
{
register struct tnode *p, *p1;
register r;
p = *treep;
if (table!=efftab && (p->op==INCAFT||p->op==DECAFT)
&& p->tr1->op==NAME) {
return(1+rcexpr(p->tr1, table, reg));
}
p1 = 0;
if (opdope[p->op]&BINARY)
p1 = sdelay(&p->tr2);
if (p1==0)
p1 = sdelay(&p->tr1);
if (p1) {
r = rcexpr(optim(p), table, reg);
*treep = p1;
return(r+1);
}
return(0);
}
sdelay(ap)
struct tnode **ap;
{
register struct tnode *p, *p1;
p = *ap;
if ((p->op==INCAFT||p->op==DECAFT) && p->tr1->op==NAME) {
*ap = ncopy(p->tr1);
return(p);
}
if (p->op==STAR || p->op==PLUS)
if (p1=sdelay(&p->tr1))
return(p1);
if (p->op==PLUS)
return(sdelay(&p->tr2));
return(0);
}
/*
* Copy a tree node for a register variable.
* Used by sdelay because if *reg-- is turned
* into *reg; reg-- the *reg will in turn
* be changed to some offset class, accidentally
* modifying the reg--.
*/
ncopy(ap)
struct tname *ap;
{
register struct tname *p;
p = ap;
if (p->class!=REG)
return(p);
return(block(3, NAME, p->type, p->elsize, p->tr1,
p->offset, p->nloc));
}
/*
* If the tree can be immediately loaded into a register,
* produce code to do so and return success.
*/
chkleaf(atree, table, reg)
struct tnode *atree;
{
struct tnode lbuf;
register struct tnode *tree;
tree = atree;
if (tree->op!=STAR && dcalc(tree, nreg-reg) > 12)
return(-1);
lbuf.op = LOAD;
lbuf.type = tree->type;
lbuf.degree = tree->degree;
lbuf.tr1 = tree;
return(rcexpr(&lbuf, table, reg));
}
/*
* Compile a function argument.
* If the stack is currently empty, put it in (sp)
* rather than -(sp); this will save a pop.
* Return the number of bytes pushed,
* for future popping.
*/
comarg(atree, flagp)
int *flagp;
{
register struct tnode *tree;
register retval;
tree = atree;
if (nstack || isfloat(tree) || tree->type==LONG) {
rcexpr(tree, sptab, 0);
retval = arlength(tree->type);
} else {
(*flagp)++;
rcexpr(tree, lsptab, 0);
retval = 0;
}
return(retval);
}