MiniUnix/usr/source/c/c12.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));
}