622 lines
12 KiB
C
622 lines
12 KiB
C
#
|
|
/*
|
|
* C compiler part 2 -- expression optimizer
|
|
*
|
|
*/
|
|
|
|
#include "c1h.c"
|
|
|
|
optim(atree)
|
|
struct tnode *atree;
|
|
{
|
|
register op, dope;
|
|
int d1, d2;
|
|
struct tnode *t;
|
|
register struct tnode *tree;
|
|
|
|
if ((tree=atree)==0)
|
|
return(0);
|
|
if ((op = tree->op)==0)
|
|
return(tree);
|
|
if (op==NAME && tree->class==AUTO) {
|
|
tree->class = OFFS;
|
|
tree->regno = 5;
|
|
tree->offset = tree->nloc;
|
|
}
|
|
dope = opdope[op];
|
|
if ((dope&LEAF) != 0)
|
|
return(tree);
|
|
if ((dope&BINARY) == 0)
|
|
return(unoptim(tree));
|
|
/* is known to be binary */
|
|
if (tree->type==CHAR)
|
|
tree->type = INT;
|
|
if ((dope&COMMUTE)!=0) {
|
|
acomm: d1 = tree->type;
|
|
tree = acommute(tree);
|
|
tree->type = d1;
|
|
/*
|
|
* PDP-11 special:
|
|
* replace a&b by a NAND ~ b.
|
|
* This will be undone when in
|
|
* truth-value context.
|
|
*/
|
|
if (tree->op!=AND)
|
|
return(tree);
|
|
tree->op = NAND;
|
|
tree->tr2 = block(1, COMPL, tree->tr2->type, 0, tree->tr2);
|
|
}
|
|
again:
|
|
tree->tr1 = optim(tree->tr1);
|
|
tree->tr2 = optim(tree->tr2);
|
|
if ((dope&RELAT) != 0) {
|
|
if ((d1=degree(tree->tr1)) < (d2=degree(tree->tr2))
|
|
|| d1==d2 && tree->tr1->op==NAME && tree->tr2->op!=NAME) {
|
|
t = tree->tr1;
|
|
tree->tr1 = tree->tr2;
|
|
tree->tr2 = t;
|
|
tree->op = maprel[op-EQUAL];
|
|
}
|
|
if (tree->tr1->type==CHAR && tree->tr2->op==CON
|
|
&& (dcalc(tree->tr1) <= 12 || tree->tr1->op==STAR)
|
|
&& tree->tr2->value <= 127 && tree->tr2->value >= 0)
|
|
tree->tr2->type = CHAR;
|
|
}
|
|
d1 = max(degree(tree->tr1), islong(tree->type));
|
|
d2 = max(degree(tree->tr2), 0);
|
|
if (tree->tr1->type==LONG && dope&RELAT)
|
|
d1 = 10;
|
|
switch (op) {
|
|
|
|
case LTIMES:
|
|
case LDIV:
|
|
case LMOD:
|
|
case LASTIMES:
|
|
case LASDIV:
|
|
case LASMOD:
|
|
tree->degree = 10;
|
|
break;
|
|
|
|
/*
|
|
* PDP-11 special:
|
|
* generate new =&~ operator out of =&
|
|
* by complementing the RHS.
|
|
*/
|
|
case ASSAND:
|
|
op = ASSNAND;
|
|
tree->op = op;
|
|
tree->tr2 = block(2, COMPL, tree->tr2->type, 0, tree->tr2);
|
|
goto again;
|
|
|
|
case NAND:
|
|
if (isconstant(tree->tr2) && tree->tr2->value==0)
|
|
return(tree->tr1);
|
|
goto def;
|
|
|
|
case CALL:
|
|
tree->degree = 10;
|
|
break;
|
|
|
|
case QUEST:
|
|
case COLON:
|
|
tree->degree = max(d1, d2);
|
|
break;
|
|
|
|
case MINUS:
|
|
if (t = isconstant(tree->tr2)) {
|
|
tree->op = PLUS;
|
|
if (t->type==DOUBLE)
|
|
/* PDP-11 FP representation */
|
|
t->value =^ 0100000;
|
|
else
|
|
t->value = -t->value;
|
|
goto acomm;
|
|
}
|
|
goto def;
|
|
|
|
case DIVIDE:
|
|
case ASDIV:
|
|
case ASTIMES:
|
|
if (tree->tr2->op==CON && tree->tr2->value==1)
|
|
return(tree->tr1);
|
|
if (ispow2(tree) == 0) {
|
|
|
|
case MOD:
|
|
case ASMOD:
|
|
d1 =+ 2;
|
|
d2 =+ 2;
|
|
}
|
|
if (tree->type==LONG)
|
|
return(hardlongs(tree));
|
|
goto constant;
|
|
|
|
case LSHIFT:
|
|
case RSHIFT:
|
|
case ASRSH:
|
|
case ASLSH:
|
|
if (tree->tr2->op==CON && tree->tr2->value==0)
|
|
return(tree->tr1);
|
|
/*
|
|
* PDP-11 special: turn right shifts into negative
|
|
* left shifts
|
|
*/
|
|
if (op==LSHIFT||op==ASLSH)
|
|
goto constant;
|
|
if (tree->tr2->op==CON && tree->tr2->value==1)
|
|
goto constant;
|
|
op =+ (LSHIFT-RSHIFT);
|
|
tree->op = op;
|
|
tree->tr2 = block(1, NEG, tree->type, 0, tree->tr2);
|
|
goto again;
|
|
|
|
constant:
|
|
if (tree->tr1->op==CON && tree->tr2->op==CON) {
|
|
const(op, &tree->tr1->value, tree->tr2->value);
|
|
return(tree->tr1);
|
|
}
|
|
|
|
|
|
def:
|
|
default:
|
|
tree->degree = d1==d2? d1+islong(tree->type): max(d1, d2);
|
|
break;
|
|
}
|
|
return(tree);
|
|
}
|
|
|
|
unoptim(atree)
|
|
struct tnode *atree;
|
|
{
|
|
register struct tnode *subtre, *tree;
|
|
register int *p;
|
|
double static fv;
|
|
struct { int integer; };
|
|
|
|
if ((tree=atree)==0)
|
|
return(0);
|
|
if (tree->op==CBRANCH) {
|
|
tree->btree = optim(tree->btree);
|
|
return(tree);
|
|
}
|
|
subtre = tree->tr1 = optim(tree->tr1);
|
|
switch (tree->op) {
|
|
|
|
case FSEL:
|
|
tree->tr1 = block(2, RSHIFT, INT, 0, subtre,
|
|
block(1, CON, INT, 0, tree->bitoffs));
|
|
tree->op = AND;
|
|
tree->type = INT;
|
|
tree->tr2 = block(1, CON, INT, 0, (1<<tree->flen)-1);
|
|
return(optim(tree));
|
|
|
|
case AMPER:
|
|
if (subtre->op==STAR)
|
|
return(subtre->tr1);
|
|
if (subtre->op==NAME && subtre->class == OFFS) {
|
|
p = block(2, PLUS, tree->type, 1, subtre, tree);
|
|
subtre->type = tree->type;
|
|
tree->op = CON;
|
|
tree->type = INT;
|
|
tree->degree = 0;
|
|
tree->value = subtre->offset;
|
|
subtre->class = REG;
|
|
subtre->nloc = subtre->regno;
|
|
subtre->offset = 0;
|
|
return(p);
|
|
}
|
|
break;
|
|
|
|
case STAR:
|
|
if (subtre->op==AMPER)
|
|
return(subtre->tr1);
|
|
if (subtre->op==NAME && subtre->class==REG) {
|
|
subtre->type = tree->type;
|
|
subtre->class = OFFS;
|
|
subtre->regno = subtre->nloc;
|
|
return(subtre);
|
|
}
|
|
p = subtre->tr1;
|
|
if ((subtre->op==INCAFT||subtre->op==DECBEF)&&tree->type!=LONG
|
|
&& p->op==NAME && p->class==REG && p->type==subtre->type) {
|
|
p->type = tree->type;
|
|
p->op = subtre->op==INCAFT? AUTOI: AUTOD;
|
|
return(p);
|
|
}
|
|
if (subtre->op==PLUS && p->op==NAME && p->class==REG) {
|
|
if (subtre->tr2->op==CON) {
|
|
p->offset =+ subtre->tr2->value;
|
|
p->class = OFFS;
|
|
p->type = tree->type;
|
|
p->regno = p->nloc;
|
|
return(p);
|
|
}
|
|
if (subtre->tr2->op==AMPER) {
|
|
subtre = subtre->tr2->tr1;
|
|
subtre->class =+ XOFFS-EXTERN;
|
|
subtre->regno = p->nloc;
|
|
subtre->type = tree->type;
|
|
return(subtre);
|
|
}
|
|
}
|
|
break;
|
|
case EXCLA:
|
|
if ((opdope[subtre->op]&RELAT)==0)
|
|
break;
|
|
tree = subtre;
|
|
tree->op = notrel[tree->op-EQUAL];
|
|
break;
|
|
|
|
case NEG:
|
|
case COMPL:
|
|
if (tree->type==CHAR)
|
|
tree->type = INT;
|
|
if (tree->op == subtre->op)
|
|
return(subtre->tr1);
|
|
if (subtre->op==ITOL) {
|
|
subtre->op = tree->op;
|
|
subtre->type = INT;
|
|
tree->op = ITOL;
|
|
}
|
|
}
|
|
if (subtre->op == CON) switch(tree->op) {
|
|
|
|
case NEG:
|
|
subtre->value = -subtre->value;
|
|
return(subtre);
|
|
|
|
case COMPL:
|
|
subtre->value = ~subtre->value;
|
|
return(subtre);
|
|
|
|
case ITOF:
|
|
fv = subtre->value;
|
|
p = &fv;
|
|
p++;
|
|
if (*p++==0 && *p++==0 && *p++==0) {
|
|
subtre->type = DOUBLE;
|
|
subtre->value = fv.integer;
|
|
subtre->op = SFCON;
|
|
return(subtre);
|
|
}
|
|
break;
|
|
}
|
|
tree->degree = max(islong(tree->type), degree(subtre));
|
|
return(tree);
|
|
}
|
|
|
|
struct acl {
|
|
int nextl;
|
|
int nextn;
|
|
struct tnode *nlist[20];
|
|
struct tnode *llist[21];
|
|
};
|
|
|
|
acommute(atree)
|
|
{
|
|
struct acl acl;
|
|
int d, i, op, flt;
|
|
register struct tnode *t1, **t2, *tree;
|
|
struct tnode *t;
|
|
|
|
acl.nextl = 0;
|
|
acl.nextn = 0;
|
|
tree = atree;
|
|
op = tree->op;
|
|
flt = isfloat(tree);
|
|
insert(op, tree, &acl);
|
|
acl.nextl--;
|
|
t2 = &acl.llist[acl.nextl];
|
|
if (!flt) {
|
|
/* put constants together */
|
|
for (i=acl.nextl;i>0&&t2[0]->op==CON&&t2[-1]->op==CON;i--) {
|
|
acl.nextl--;
|
|
t2--;
|
|
const(op, &t2[0]->value, t2[1]->value);
|
|
}
|
|
}
|
|
if (op==PLUS || op==OR) {
|
|
/* toss out "+0" */
|
|
if (acl.nextl>0 && (t1 = isconstant(*t2)) && t1->value==0) {
|
|
acl.nextl--;
|
|
t2--;
|
|
}
|
|
if (acl.nextl <= 0)
|
|
return(*t2);
|
|
/* subsume constant in "&x+c" */
|
|
if (op==PLUS && t2[0]->op==CON && t2[-1]->op==AMPER) {
|
|
t2--;
|
|
t2[0]->tr1->offset =+ t2[1]->value;
|
|
acl.nextl--;
|
|
}
|
|
} else if (op==TIMES || op==AND) {
|
|
t1 = acl.llist[acl.nextl];
|
|
if (t1->op==CON) {
|
|
if (t1->value==0)
|
|
return(t1);
|
|
if (op==TIMES && t1->value==1 && acl.nextl>0)
|
|
if (--acl.nextl <= 0)
|
|
return(acl.llist[0]);
|
|
}
|
|
}
|
|
if (op==PLUS && !flt)
|
|
distrib(&acl);
|
|
tree = *(t2 = &acl.llist[0]);
|
|
d = max(degree(tree), islong(tree->type));
|
|
if (op==TIMES && !flt)
|
|
d++;
|
|
for (i=0; i<acl.nextl; i++) {
|
|
t1 = acl.nlist[i];
|
|
t1->tr2 = t = *++t2;
|
|
t1->degree = d = d==degree(t)? d+islong(t1->type): max(d, degree(t));
|
|
t1->tr1 = tree;
|
|
tree = t1;
|
|
if (tree->type==LONG) {
|
|
if (tree->op==TIMES)
|
|
tree = hardlongs(tree);
|
|
else if (tree->op==PLUS && (t = isconstant(tree->tr1))
|
|
&& t->value < 0) {
|
|
tree->op = MINUS;
|
|
t->value = - t->value;
|
|
t = tree->tr1;
|
|
tree->tr1 = tree->tr2;
|
|
tree->tr2 = t;
|
|
}
|
|
}
|
|
}
|
|
if (tree->op==TIMES && ispow2(tree))
|
|
tree->degree = max(degree(tree->tr1), islong(tree->type));
|
|
return(tree);
|
|
}
|
|
|
|
distrib(list)
|
|
struct acl *list;
|
|
{
|
|
/*
|
|
* Find a list member of the form c1c2*x such
|
|
* that c1c2 divides no other such constant, is divided by
|
|
* at least one other (say in the form c1*y), and which has
|
|
* fewest divisors. Reduce this pair to c1*(y+c2*x)
|
|
* and iterate until no reductions occur.
|
|
*/
|
|
register struct tnode **p1, **p2;
|
|
struct tnode *t;
|
|
int ndmaj, ndmin;
|
|
struct tnode **dividend, **divisor;
|
|
struct tnode **maxnod, **mindiv;
|
|
|
|
loop:
|
|
maxnod = &list->llist[list->nextl];
|
|
ndmaj = 1000;
|
|
dividend = 0;
|
|
for (p1 = list->llist; p1 <= maxnod; p1++) {
|
|
if ((*p1)->op!=TIMES || (*p1)->tr2->op!=CON)
|
|
continue;
|
|
ndmin = 0;
|
|
for (p2 = list->llist; p2 <= maxnod; p2++) {
|
|
if (p1==p2 || (*p2)->op!=TIMES || (*p2)->tr2->op!=CON)
|
|
continue;
|
|
if ((*p1)->tr2->value == (*p2)->tr2->value) {
|
|
(*p2)->tr2 = (*p1)->tr1;
|
|
(*p2)->op = PLUS;
|
|
(*p1)->tr1 = (*p2);
|
|
*p1 = optim(*p1);
|
|
squash(p2, maxnod);
|
|
list->nextl--;
|
|
goto loop;
|
|
}
|
|
if (((*p2)->tr2->value % (*p1)->tr2->value) == 0)
|
|
goto contmaj;
|
|
if (((*p1)->tr2->value % (*p2)->tr2->value) == 0) {
|
|
ndmin++;
|
|
mindiv = p2;
|
|
}
|
|
}
|
|
if (ndmin > 0 && ndmin < ndmaj) {
|
|
ndmaj = ndmin;
|
|
dividend = p1;
|
|
divisor = mindiv;
|
|
}
|
|
contmaj:;
|
|
}
|
|
if (dividend==0)
|
|
return;
|
|
t = list->nlist[--list->nextn];
|
|
p1 = dividend;
|
|
p2 = divisor;
|
|
t->op = PLUS;
|
|
t->type = (*p1)->type;
|
|
t->tr1 = (*p1);
|
|
t->tr2 = (*p2)->tr1;
|
|
(*p1)->tr2->value =/ (*p2)->tr2->value;
|
|
(*p2)->tr1 = t;
|
|
t = optim(*p2);
|
|
if (p1 < p2) {
|
|
*p1 = t;
|
|
squash(p2, maxnod);
|
|
} else {
|
|
*p2 = t;
|
|
squash(p1, maxnod);
|
|
}
|
|
list->nextl--;
|
|
goto loop;
|
|
}
|
|
|
|
squash(p, maxp)
|
|
struct tnode **p, **maxp;
|
|
{
|
|
register struct tnode **np;
|
|
|
|
for (np = p; np < maxp; np++)
|
|
*np = *(np+1);
|
|
}
|
|
|
|
const(op, vp, av)
|
|
int *vp;
|
|
{
|
|
register int v;
|
|
|
|
v = av;
|
|
switch (op) {
|
|
|
|
case PLUS:
|
|
*vp =+ v;
|
|
return;
|
|
|
|
case TIMES:
|
|
*vp =* v;
|
|
return;
|
|
|
|
case AND:
|
|
*vp =& v;
|
|
return;
|
|
|
|
case OR:
|
|
*vp =| v;
|
|
return;
|
|
|
|
case EXOR:
|
|
*vp =^ v;
|
|
return;
|
|
|
|
case DIVIDE:
|
|
case MOD:
|
|
if (v==0)
|
|
error("Divide check");
|
|
else
|
|
if (op==DIVIDE)
|
|
*vp =/ v;
|
|
else
|
|
*vp =% v;
|
|
return;
|
|
|
|
case RSHIFT:
|
|
*vp =>> v;
|
|
return;
|
|
|
|
case LSHIFT:
|
|
*vp =<< v;
|
|
return;
|
|
|
|
case NAND:
|
|
*vp =& ~ v;
|
|
return;
|
|
}
|
|
error("C error: const");
|
|
}
|
|
|
|
insert(op, atree, alist)
|
|
struct acl *alist;
|
|
{
|
|
register d;
|
|
register struct acl *list;
|
|
register struct tnode *tree;
|
|
int d1, i;
|
|
struct tnode *t;
|
|
|
|
tree = atree;
|
|
list = alist;
|
|
if (tree->op == op) {
|
|
ins: list->nlist[list->nextn++] = tree;
|
|
insert(op, tree->tr1, list);
|
|
insert(op, tree->tr2, list);
|
|
return;
|
|
}
|
|
tree = optim(tree);
|
|
if (tree->op == op)
|
|
goto ins;
|
|
if (!isfloat(tree)) {
|
|
/* c1*(x+c2) -> c1*x+c1*c2 */
|
|
if ((tree->op==TIMES||tree->op==LSHIFT)
|
|
&& tree->tr2->op==CON && tree->tr2->value>0
|
|
&& tree->tr1->op==PLUS && tree->tr1->tr2->op==CON) {
|
|
d = tree->tr2->value;
|
|
if (tree->op==TIMES)
|
|
tree->tr2->value =* tree->tr1->tr2->value;
|
|
else
|
|
tree->tr2->value = tree->tr1->tr2->value << d;
|
|
tree->tr1->tr2->value = d;
|
|
tree->tr1->op = tree->op;
|
|
tree->op = PLUS;
|
|
if (op==PLUS)
|
|
goto ins;
|
|
}
|
|
}
|
|
d = degree(tree);
|
|
for (i=0; i<list->nextl; i++) {
|
|
if ((d1=degree(list->llist[i]))<d) {
|
|
t = list->llist[i];
|
|
list->llist[i] = tree;
|
|
tree = t;
|
|
d = d1;
|
|
}
|
|
}
|
|
list->llist[list->nextl++] = tree;
|
|
}
|
|
|
|
block(an, args)
|
|
{
|
|
register int *p;
|
|
int *oldp;
|
|
register *argp, n;
|
|
|
|
oldp = p = spacep;
|
|
n = an+3;
|
|
argp = &args;
|
|
do
|
|
*p++ = *argp++;
|
|
while (--n);
|
|
if (p >= &treespace[ossiz]) {
|
|
error("Exp. ov. pass 2");
|
|
exit(1);
|
|
}
|
|
spacep = p;
|
|
return(oldp);
|
|
}
|
|
|
|
islong(t)
|
|
{
|
|
if (t==LONG)
|
|
return(2);
|
|
return(1);
|
|
}
|
|
|
|
isconstant(at)
|
|
struct tnode *at;
|
|
{
|
|
register struct tnode *t;
|
|
|
|
t = at;
|
|
if (t->op==CON || t->op==SFCON)
|
|
return(t);
|
|
if (t->op==ITOL && t->tr1->op==CON)
|
|
return(t->tr1);
|
|
return(0);
|
|
}
|
|
|
|
hardlongs(at)
|
|
struct tnode *at;
|
|
{
|
|
register struct tnode *t;
|
|
|
|
t = at;
|
|
switch(t->op) {
|
|
|
|
case TIMES:
|
|
case DIVIDE:
|
|
case MOD:
|
|
t->op =+ LTIMES-TIMES;
|
|
break;
|
|
|
|
case ASTIMES:
|
|
case ASDIV:
|
|
case ASMOD:
|
|
t->op =+ LASTIMES-ASTIMES;
|
|
t->tr1 = block(1, AMPER, LONG+PTR, 0, t->tr1);
|
|
break;
|
|
|
|
default:
|
|
return(t);
|
|
}
|
|
return(optim(t));
|
|
}
|