Files
Sepp J Morris 31738079c4 Upload
Digital Research
2020-11-06 18:50:37 +01:00

918 lines
18 KiB
C

#
/*
Copyright 1981
Alcyon Corporation
8474 Commerce Av.
San Diego, Ca. 92121
*/
#include "cgen.h"
#include "cskel.h"
/* canon - expression canonicalization*/
/* Top level tree canonicalization. This fixes any bit field*/
/* accesses, then loops on commute and optim until no more*/
/* optimizations are done.*/
char *canon(tp) /* returns pointer to tree*/
struct tnode *tp; /* pointer to tree to canonicalize*/
{
tp = fixbfield(tp);
do {
sucomp(tp,0,1); /*need Sethy-Ullman's for commute*/
tp = commute(tp); /*commute the tree*/
} while( optim(&tp) ); /*keep trying for optimizations*/
#ifndef NODEBUG
if( oflag )
putexpr("canon",tp);
#endif
return(tp);
}
/* fixbfield - fix bit field operators*/
/* Fixes bit field assignment and normal usage*/
char *fixbfield(tp) /* returns pointer to fixed tree*/
struct tnode *tp; /* pointer to tree*/
{
register struct tnode *btp, *stp;
register int foff, fmask, op, flen;
if( leafop(op=tp->t_op) )
return(tp);
if( asgop(op) && tp->t_left->t_op == BFIELD ) {
btp = tp->t_left; /*pointer to BFIELD node*/
stp = btp->t_left; /*pointer to son of BFIELD node*/
foff = (btp->t_su>>8) & 0377;
flen = btp->t_su & 0377;
fmask = (1<<flen)-1;
if( tp->t_right->t_op == CINT && (op == ASSIGN || op == EQXOR) ) {
if( op == EQXOR ) {
tp->t_left = stp;
tp->t_right->t_value =<< foff;
return(tp);
}
if( tp->t_right->t_value == 0 ) {
tp->t_op = EQAND;
tp->t_left = stp;
tp->t_right->t_value = ~ (fmask << foff);
return(tp);
}
if( (tp->t_right->t_value & fmask) == fmask ) {
tp->t_op = EQOR;
tp->t_left = stp;
tp->t_right->t_value = fmask << foff;
return(tp);
}
}
if( fmask == -1 )
tp->t_left = stp;
else {
stp = tcopy(stp);
btp = fixbfield(btp);
op = tp->t_op;
tp = tp->t_right; /*pointer to expression*/
if( op != ASSIGN ) /*=op operator?*/
tp = tnalloc(op-(EQADD-ADD),INT,0,0,btp,tp);
tp = tnalloc(AND,INT,0,0,tp,cnalloc(INT,fmask));
tp = tnalloc(LSH,INT,0,0,tp,cnalloc(INT,foff));
btp = tnalloc(AND,INT,0,0,tcopy(stp),
cnalloc(INT,~(fmask<<foff)));
tp = tnalloc(OR,INT,0,0,btp,tp);
tp = tnalloc(ASSIGN,INT,0,0,stp,tp);
}
}
else if( op == BFIELD ) {
foff = (tp->t_su>>8) & 0377;
fmask = (1<<(tp->t_su&0377))-1;
tp = tnalloc(RSH,INT,0,0,tp->t_left,cnalloc(INT,foff));
tp = tnalloc(AND,INT,0,0,tp,cnalloc(INT,fmask));
}
else {
tp->t_left = fixbfield(tp->t_left);
if( binop(op) )
tp->t_right = fixbfield(tp->t_right);
}
return(tp);
}
/* optim - optimize expression tree*/
/* This takes expression tree and performs the following*/
/* translations: folds auto names to accesses off the local*/
/* environment pointer, performs mostly "machine-independent"*/
/* optimizations, such as multiply by zero and one, etc.,*/
/* turns field accesses into and's and or's, etc.*/
optim(tpp) /* returns pointer to new tree*/
struct tnode **tpp;
{
register struct tnode *ltp, *rtp, *tp;
register char *p;
register int i, cval, changes, op;
tp = *tpp;
if( tp->t_type & ~TYPE ) {
tp->t_type =& TYPE;
tp->t_type =| POINTER;
}
if( leafop(op=tp->t_op) )
return(0);
changes = 0;
if( binop(op) )
changes =+ optim(&tp->t_right);
changes =+ optim(&tp->t_left);
for( ; notleafop(op=tp->t_op); changes++ ) {
#ifndef NODEBUG
if( oflag )
putexpr("optim",tp);
#endif
*tpp = tp;
ltp = tp->t_left;
if( binop(op) ) {
rtp = tp->t_right;
if( tp->t_type == CHAR )
tp->t_type = INT;
p = constant(rtp);
}
else
p = constant(ltp);
ltp = tp->t_left;
if( p )
cval = p->t_value;
switch( op ) {
case ADD:
if( p ) {
if( cval == 0 ) {
tp = ltp;
continue;
}
if( ltp->t_op == ADDR ) {
ltp->t_left->t_offset =+ cval;
tp = ltp;
continue;
}
}
break;
case ASSIGN:
if( rtp->t_op == ADD && indexreg(rtp->t_left) &&
rtp->t_right->t_op == CINT ) {
ltp = rtp->t_left;
ltp->t_sc = REGOFF;
ltp->t_offset =+ rtp->t_right->t_value;
tp->t_right = ltp;
tp->t_op = EQADDR;
continue;
}
break;
case SUB:
if( p ) {
if( cval == 0 ) {
tp = ltp;
continue;
}
tp->t_op = ADD;
p->t_value = -cval;
continue;
}
if( (p=constant(ltp)) && p->t_value == 0 ) { /*0-X=-X*/
tp->t_op = UMINUS;
tp->t_left = ltp = rtp;
continue;
}
break;
case DIV:
case EQDIV:
if( p ) {
if( cval == 0 ) { /*X/0->error*/
error("divide by zero");
tp = rtp;
continue;
}
if( cval == 1 ) {
tp = ltp;
continue;
}
if( cval == -1 && op == DIV ) {
tp->t_op = UMINUS;
continue;
}
}
if( (p=constant(ltp)) && p->t_value == 0 ) {
tp = ltp;
continue;
}
if( multop(tpp) ) {
tp = *tpp;
continue;
}
break;
case EQMOD:
case MOD:
if( p ) {
if( cval == 0 ) { /*X%0->error*/
error("modulus by zero");
tp = rtp;
continue;
}
if( cval == 1 ) { /*X%1->0*/
p->t_value = 0;
tp = rtp;
continue;
}
}
if( (p=constant(ltp)) && p->t_value == 0 ) {
tp = ltp;
continue;
}
if( multop(tpp) ) {
tp = *tpp;
continue;
}
break;
case MULT:
case EQMULT:
if( p ) {
if( cval == 0 ) {
tp = rtp;
continue;
}
if( cval == 1 ) {
tp = ltp;
continue;
}
if( cval == -1 && op == MULT ) {
tp->t_op = UMINUS;
continue;
}
}
if( multop(tpp) ) {
tp = *tpp;
continue;
}
break;
case EQUALS:
case NEQUALS:
if( p && (i=onebit(cval)) >= 0 && ltp->t_op == AND &&
(rtp=constant(ltp->t_right)) &&
i == onebit(rtp->t_value) ) {
tp->t_op = invrel[tp->t_op-EQUALS];
p->t_value = 0;
continue;
}
break;
case GREATEQ:
if( p && cval == 0 && unsign(ltp->t_type) ) {
p->t_value = 1;
tp = p;
continue;
}
break;
case LESS:
if( p && cval == 0 && unsign(ltp->t_type) ) {
tp = p;
continue;
}
break;
case AND:
if( p ) {
if( cval == 0 ) {
tp = rtp;
continue;
}
if( cval == -1 ) {
tp = ltp;
continue;
}
}
break;
case OR:
if( p ) {
if( cval == 0 ) {
tp = ltp;
continue;
}
if( cval == -1 ) {
tp = rtp;
continue;
}
}
break;
case XOR:
if( p ) {
if( cval == 0 ) {
tp = ltp;
continue;
}
if( cval == -1 ) {
tp->t_op = COMPL;
continue;
}
}
break;
case LSH:
case EQLSH:
if( tp->t_type == LONG && rtp->t_op == INT2L &&
unsign(rtp->t_left) ) {
tp->t_right = rtp->t_left;
continue;
}
case RSH:
case EQRSH:
if( p ) {
if( cval == 0 ) {
tp = ltp;
continue;
}
if( p != rtp ) {
tp->t_right = p;
continue;
}
}
break;
case ADDR:
if( ltp->t_op == INDR ) {
tp = ltp->t_left;
continue;
}
if( ltp->t_op == SYMBOL && ltp->t_sc == REGOFF ) {
tp->t_op = ADD;
tp->t_right = cnalloc(INT,ltp->t_offset);
ltp->t_type = tp->t_type;
ltp->t_sc = REGISTER;
ltp->t_offset = 0;
continue;
}
break;
case INDR:
p = ltp->t_left;
switch( ltp->t_op ) {
case ADDR:
tp = p;
continue;
case CINT:
tp = snalloc(tp->t_type,CINDR,ltp->t_value,0,0);
continue;
case CLONG:
tp = snalloc(tp->t_type,CLINDR,ltp->t_lvalue.hiword,0,
ltp->t_lvalue.loword);
continue;
case SYMBOL:
if( indexreg(ltp) ) {
ltp->t_sc = REGOFF;
ltp->t_type = tp->t_type;
ltp->t_offset = 0;
tp = ltp;
continue;
}
break;
case ADD:
if( p->t_op == SYMBOL && p->t_sc == REGOFF &&
(rtp=constant(ltp->t_right)) &&
notpointer(p->t_type) ) {
p->t_offset =+ rtp->t_value;
tp = p;
continue;
}
if( indexreg(p) ) {
if( rtp = constant(ltp->t_right) ) {
/*This combines an address register and a constant into a register*/
/*offset. This relies on 68000 Addressing scheme somewhat.*/
p->t_sc = REGOFF;
p->t_type = tp->t_type;
p->t_offset =+ rtp->t_value;
tp = p;
continue;
}
if( lflag == 0 && ltp->t_right->t_op == ADDR ) {
/*We can fold *(An+&expr) into *(&expr(An)), but note that &expr*/
/*must be 16 bits for 68000, hence we can only do this if short*/
/*addresses are enabled. Note that the storage classes are mapped:*/
/*EXTERNAL->EXTOFF, STATIC->STATOFF, REGISTER->REGOFF*/
ltp = ltp->t_right->t_left;
ltp->t_sc =+ (EXTOFF-EXTERNAL);
ltp->t_type = tp->t_type;
tp = ltp;
continue;
}
}
break;
/*--X and X++ can be folded into -(An) and (An)+ 68000 instructions*/
case PREDEC:
case POSTINC:
if( indexreg(p) && ltp->t_type == p->t_type ) {
p->t_op = (ltp->t_op == PREDEC ? AUTODEC : AUTOINC);
p->t_type = tp->t_type;
tp = p;
continue;
}
}
break;
case NOT:
if( relop(ltp->t_op) ) { /*!(X>Y)->X<=Y*/
tp = ltp;
tp->t_op = invrel[tp->t_op-EQUALS];
continue;
}
break;
case UMINUS:
case COMPL:
if( tp->t_type == CHAR )
tp->t_type = INT;
if( tp->t_op == ltp->t_op ) {
tp = ltp->t_left;
continue;
}
if( ltp->t_op == INT2L ) { /*~(INT2L X)->INT2L (~X)*/
ltp->t_op = tp->t_op;
tp->t_op = INT2L;
ltp->t_type = INT;
continue;
}
break;
case INT2L:
if( ltp->t_op == MULT ) { /*INT2L (X*Y)->X*Y*/
ltp->t_type = (ltp->t_type&(~TYPE))|LONG;
tp = ltp;
continue;
}
break;
}
if( ccexpr(tpp) == 0 )
break;
tp = *tpp;
}
*tpp = tp;
return(changes);
}
/* commute - commutes expression tree to canonical form*/
/* This sorts commutable expression trees so that the most*/
/* difficult expression is the left-most operand. Note that*/
/* canon assumes that commute has placed constant operands in*/
/* the right sub-tree. This also swaps relationals so that the*/
/* most difficult expression is on the left.*/
char *commute(tp) /* returns commuted expression tree*/
struct tnode *tp;
{
struct tnode *clist[20], *plist[19];
register struct tnode **p, **q, *s;
struct tnode **plp, **clp;
register int op;
if( relop(op=tp->t_op) ) {
s = tp->t_left;
if( harder(tp->t_right,s) ) {
#ifndef NODEBUG
if(oflag)
putexpr("swaprel",tp);
#endif
tp->t_op = swaprel[op-EQUALS];
tp->t_left = tp->t_right;
tp->t_right = s;
}
}
if( commop(op) ) {
#ifndef NODEBUG
if(oflag)
putexpr("commute",tp);
#endif
clp = clist;
plp = plist;
addtree(tp,&clp,&plp); /*collect comm. expressions*/
/*see if any sub-trees can also be commuted (with different operator)*/
clp--;
plp--;
for( p = clist; p <= clp; p++ )
*p = commute(*p);
/*this sorts the expressions in decreasing order of Sethy-Ullman*/
/*values.*/
for( p = clist; p <= clp; p++ ) {
for( q = p; q <= clp; q++ ) {
if( harder(*q,*p) ) {
s = *q;
*q = *p;
*p = s;
}
}
}
/*Now, we start at the end of the list and collect any constants*/
/*if possible.*/
for( q = clp, p = plp; p > plist; p-- ) {
s = *p;
s->t_right = *q;
s->t_left = *--q;
if( ccexpr(p) ) {
clp--;
plp--;
}
}
/*this takes the sorted sub-expression pointers and the pointers*/
/*to the commutable operator nodes (plist) and structures the*/
/*tree so that the left sub-expression is the most complex. The*/
/*code generation scheme is very sensitive to this...*/
q = clist;
p = plist;
s = *q++;
while( p <= plp ) {
(*p)->t_left = s;
s = *p++;
s->t_right = *q++;
if( longorptr(s->t_type) == 0 ) {
if( longorptr(s->t_right->t_type) )
s->t_type = s->t_right->t_type;
else if( longorptr(s->t_left->t_type) )
s->t_type = s->t_left->t_type;
}
}
tp = s;
#ifndef NODEBUG
if(oflag)
putexpr("after commute",tp);
#endif
}
else if( binop(op) ) {
tp->t_left = commute(tp->t_left);
tp->t_right = commute(tp->t_right);
}
else if( unaryop(op) )
tp->t_left = commute(tp->t_left);
return(tp);
}
/* harder - test one sub-expression for being "harder" than another*/
/* This requires some special finagling for registers. The reason*/
/* for this is that the code skeletons produce better code if the*/
/* register is on the left. Also note that allowing an address*/
/* register on the right can have disastrous effects for AND and OR.*/
/* The basic point is: don't mess with this routine unless you're*/
/* 100% sure you understand the ramifications...*/
harder(tp,ntp) /* returns 1 if tp > ntp, else 0*/
struct tnode *tp;
struct tnode *ntp;
{
if( ntp->t_su == SU_VHARD )
return(0);
if( tp->t_su == SU_VHARD )
return(1);
if( isreg(ntp) )
return(0);
if( isreg(tp) )
return(1);
if( constant(ntp) )
return(1);
if( constant(tp) )
return(0);
return( tp->t_su > ntp->t_su );
}
/* addtree - collect commutable sub-trees for commute*/
/* This recurses down the sub-trees looking for groups of*/
/* commutable operators. It collects the sub-trees and their*/
/* parent nodes for commute.*/
addtree(tp,clist,plist) /* returns pointer to clist*/
struct tnode *tp; /* pointer to tree*/
struct tnode ***clist; /* commutable sub-trees*/
struct tnode ***plist; /* parent nodes of sub-trees*/
{
register struct tnode ***p, ***c;
c = clist;
p = plist;
if( tp->t_op == tp->t_left->t_op )
addtree(tp->t_left,c,p);
else
*(*c)++ = tp->t_left;
if( tp->t_op == tp->t_right->t_op )
addtree(tp->t_right,c,p);
else
*(*c)++ = tp->t_right;
*(*p)++ = tp;
}
/* constant - test for tree being a constant node*/
char *constant(tp) /* returns 0 or ptr to const node*/
struct tnode *tp; /* pointer to tree*/
{
if( tp->t_op == CINT )
return(tp);
if( tp->t_op == INT2L && tp->t_left->t_op == CINT )
return(tp->t_left);
return(0);
}
/* indexreg - returns whether node is an address register*/
/* For 68000, must be an A register*/
indexreg(tp) /* returns whether node is A reg*/
struct tnode *tp; /* pointer to tree*/
{
if( tp->t_op == SYMBOL && tp->t_sc == REGISTER &&
isdreg(tp->t_reg) == 0 )
return(1);
return(0);
}
/* ccexpr - compute constant expression*/
/* Evaluates constant expressions, including ?: and relationals*/
ccexpr(tpp) /* returns 1 if changes, 0 otherwise*/
struct tnode **tpp; /* pointer to tree*/
{
register struct tnode *ltp, *rtp, *tp;
register int op, i, j, anylong;
register long rval, lval;
tp = *tpp;
op = tp->t_op;
if( leafop(op) )
return(0);
anylong = 0;
ltp = tp->t_left;
if( ltp->t_op == CLONG ) {
lval = ltp->t_lvalue;
anylong++;
}
else if( ltp = constant(ltp) )
lval = ltp->t_value;
else
return(0);
if( binop(op) ) {
rtp = tp->t_right;
if( op == QMARK ) {
ltp = rtp->t_left;
rtp = rtp->t_right;
if( ltp->t_op != CINT || rtp->t_op != CINT )
return(0);
ltp->t_value = (lval?ltp->t_value:rtp->t_value);
*tpp = ltp;
return(1);
}
if( rtp->t_op == CLONG ) {
anylong++;
rval = rtp->t_lvalue;
}
else if( rtp = constant(rtp) )
rval = rtp->t_value;
else
return(0);
}
i = lval;
j = rval;
switch( op ) {
case ADD:
lval =+ rval;
break;
case SUB:
lval =- rval;
break;
case MULT:
case DIV:
case MOD:
case LSH:
case RSH:
case XOR:
if( anylong )
return(0);
switch( op ) {
case MULT:
lval = i * j;
break;
case DIV:
lval = i / j;
break;
case MOD:
lval = i % j;
break;
case RSH:
lval = i >> j;
break;
case LSH:
lval = i << j;
break;
case XOR:
lval = i ^ j;
break;
}
break;
case GREAT:
lval = (lval>rval);
break;
case GREATEQ:
lval = (lval>=rval);
break;
case LESS:
lval = (lval<rval);
break;
case LESSEQ:
lval = (lval<=rval);
break;
case UMINUS:
lval = -lval;
break;
case COMPL:
lval = ~lval;
break;
case NOT:
lval = !lval;
break;
case OR:
lval =| rval;
break;
case AND:
lval =& rval;
break;
default:
return(0);
}
if( anylong )
ltp = lcnalloc(LONG,lval);
else
ltp->t_value = lval;
*tpp = ltp;
return(1);
}
/* power2 - do multiply and divide by powers of two*/
/* This changes multiplies and divides by constant powers of two*/
/* to shifts.*/
power2(tpp) /* returns 0 if not power of two*/
struct tnode **tpp; /* pointer to expression tree*/
{
register char *p;
register int i, j, op;
register struct tnode *tp;
tp = *tpp;
if( p = constant(tp->t_right) ) {
if( (i=onebit(p->t_value)) < 0 )
return(0);
switch( op = tp->t_op ) {
case MULT:
case LMULT:
op = LSH;
break;
case EQMULT:
case LEQMULT:
op = EQLSH;
break;
case DIV:
case LDIV:
op = RSH;
break;
case EQDIV:
case LEQDIV:
op = EQRSH;
break;
case MOD:
case LMOD:
op = AND;
i = p->t_value - 1;
break;
case EQMOD:
case LEQMOD:
op = EQAND;
i = p->t_value - 1;
break;
default:
return(0);
}
tp->t_op = op;
p->t_value = i;
return(1);
}
return(0);
}
/* chklong - check for tree being a long*/
chklong(tp) /* returns 1 if long, 0 otherwise*/
struct tnode *tp; /* pointer to expression tree*/
{
if( tp->t_op == INT2L && unsign(tp->t_left->t_type) == 0 )
return(0);
if( tp->t_op == CLONG )
return(1);
return( longorptr(tp->t_type) );
}
/* multop - handle multiplicative operators*/
/* This checks for powers of two optimizations, then for a hard*/
/* long operation.*/
multop(tpp) /* returns ptr to expression tree*/
struct tnode **tpp; /* pointer to expression tree*/
{
register struct tnode *ltp, *tp, *rtp, *p;
register int change;
register long l;
tp = *tpp;
if( change = power2(tpp) )
tp = *tpp;
if( chklong(rtp=tp->t_right) || chklong(ltp=tp->t_left) ) {
switch( tp->t_op ) {
case MULT:
tp->t_op = LMULT; /*hard if either is long*/
break;
case DIV:
case MOD:
if( chklong(rtp) ) { /*only hard if divisor is long*/
tp->t_op =+ (LMULT-MULT);
if( rtp->t_op == CLONG )
rtp->t_op = DCLONG;
}
break;
case EQDIV:
case EQMOD:
if( chklong(rtp) == 0 )
break;
if( rtp->t_op == CLONG )
rtp->t_op = DCLONG;
case EQMULT:
if( ltp->t_op == SYMBOL && ltp->t_sc == REGISTER )
error("hard long to register");
else {
tp->t_op =+ (LEQMULT-EQMULT);
tp->t_left = tnalloc(ADDR,LONG|POINTER,0,0,ltp,null);
change++;
}
break;
}
}
return(change);
}
/* onebit - returns whether constant is power of two (one bit on)*/
onebit(val) /* returns bit number or -1*/
int val; /* constant value to check*/
{
register int i;
for( i = 15; val != 0; val =<< 1, i-- )
if( val & 0100000 )
break;
if( val != 0100000 )
return(-1);
return(i);
}