mirror of
https://github.com/SEPPDROID/Digital-Research-Source-Code.git
synced 2025-10-23 00:14:25 +00:00
1039 lines
25 KiB
Groff
1039 lines
25 KiB
Groff
/*
|
|
Copyright 1982, 1983
|
|
Alcyon Corporation
|
|
8716 Production Ave.
|
|
San Diego, Ca. 92121
|
|
|
|
@(#)canon.c 1.15 12/30/83
|
|
*/
|
|
|
|
#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*/
|
|
PUTEXPR((cflag>1),"canon return",tp);
|
|
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;
|
|
struct tnode *rhstp, *asgtp;
|
|
register short foff, fmask, op, flen;
|
|
|
|
if( LEAFOP(op=tp->t_op) )
|
|
return(tp);
|
|
if( ISASGOP(op) && tp->t_left->t_op == BFIELD ) {
|
|
tp->t_right = fixbfield(tp->t_right); /* [vlh] 4.0 */
|
|
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 ) {
|
|
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 {
|
|
asgtp = tcopy(stp,A_DOPOST); /*for lhs of assign*/
|
|
op = tp->t_op;
|
|
tp = tp->t_right; /*pointer to expression*/
|
|
if( op != ASSIGN ) { /*=op operator?*/
|
|
rhstp = tcopy(stp,0);
|
|
btp = fixbfield(btp);
|
|
tp = tnalloc(op-(EQADD-ADD),INT,0,0,tcopy(btp,A_DOPRE),tp);
|
|
}
|
|
else
|
|
rhstp = tcopy(stp,A_DOPRE); /*for rhs of assign*/
|
|
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,rhstp,cnalloc(INT,~(fmask<<foff)));
|
|
tp = tnalloc(OR,INT,0,0,btp,tp);
|
|
tp = tnalloc(ASSIGN,INT,0,0,asgtp,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.
|
|
* [vlh] 4.0 added checks for divide/modulos long zero...
|
|
* [vlh] 4.0 optimize multiply/divide 1L, add/sub 0L...
|
|
* [vlh] 4.1 optimize all long constant expressions which
|
|
* previously had integer constant equivilents.
|
|
*/
|
|
optim(tpp) /* returns pointer to new tree*/
|
|
struct tnode **tpp;
|
|
{
|
|
register struct tnode *ltp, *rtp, *tp;
|
|
register char *p;
|
|
register short i, changes, op, tmp;
|
|
short lconst, tlc;
|
|
long cval; /* [vlh] 4.1 handle all integer constants as longs */
|
|
|
|
tp = *tpp;
|
|
if (tp->t_op != STASSIGN && (tp->t_type & ~TYPE)) { /*[vlh]4.2 STASSIGN*/
|
|
tp->t_type &= TYPE;
|
|
tp->t_type |= POINTER;
|
|
}
|
|
if( LEAFOP(op=tp->t_op) )
|
|
return(0);
|
|
lconst = changes = 0;
|
|
if( BINOP(op) )
|
|
changes += optim(&tp->t_right);
|
|
changes += optim(&tp->t_left);
|
|
for( ; NOTLEAFOP(op=tp->t_op); changes++ ) {
|
|
#ifdef DEBUG
|
|
if (oflag>1) printf("optim: op = %d, changes %d\n",op,changes);
|
|
#endif
|
|
PUTEXPR(oflag,"optim",tp);
|
|
*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,&lconst);
|
|
}
|
|
else
|
|
p = constant(ltp,&lconst);
|
|
if( p )
|
|
cval = (lconst) ? p->t_lvalue : p->t_value; /* [vlh] 4.1 */
|
|
switch( op ) {
|
|
|
|
case ADD:
|
|
if( p ) {
|
|
if( !cval ) {
|
|
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;
|
|
}
|
|
if (rtp->t_op==LONG2I &&
|
|
(rtp->t_left->t_op==LDIV || rtp->t_left->t_op==LMOD)) {
|
|
rtp = rtp->t_left;
|
|
if(rtp->t_right->t_op == DCLONG) {
|
|
if(rtp->t_right->t_lvalue < 0) break;
|
|
if(rtp->t_right->t_lvalue > 0xffff) break;
|
|
rtp->t_right->t_op = CINT;
|
|
rtp->t_right->t_type = INT;
|
|
}
|
|
else if(rtp->t_right->t_op == INT2L)
|
|
rtp->t_right = rtp->t_right->t_left;
|
|
rtp->t_op -= (LDIV - DIV);
|
|
tp->t_right = rtp;
|
|
continue;
|
|
}
|
|
case EQSUB: /* [vlh] 4.2 */
|
|
case EQADD: /* [vlh] 4.2 */
|
|
case EQAND: /* [vlh] 4.2 */
|
|
case EQOR: /* [vlh] 4.2 */
|
|
if(p && ltp->t_type==CHAR) { /* [vlh] 4.2 */
|
|
ctrunc:
|
|
if (lconst) {
|
|
tmp = rtp->t_lvalue;
|
|
rtp->t_op = CINT;
|
|
}
|
|
else
|
|
tmp = rtp->t_value;
|
|
if (tmp > 255) {
|
|
warning("value assigned to char truncated");
|
|
rtp->t_value = tmp & 0xff;
|
|
}
|
|
else if ( tmp < -128) {
|
|
warning("value assigned to char truncated");
|
|
rtp->t_value = ((char)(tmp & 0xff));
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case SUB:
|
|
if( p ) {
|
|
if( !cval ) {
|
|
tp = ltp;
|
|
continue;
|
|
}
|
|
tp->t_op = ADD;
|
|
SETVAL(lconst,p,-cval);
|
|
continue;
|
|
}
|
|
if( (p=constant(ltp,&tlc)) && CONSTZERO(tlc,p)) {
|
|
tp->t_op = UMINUS;
|
|
tp->t_left = ltp = rtp;
|
|
continue;
|
|
} /*0-x = -x*/
|
|
break;
|
|
|
|
case DIV:
|
|
case EQDIV:
|
|
if( p ) {
|
|
if( !cval ) { /*X/0->error*/
|
|
error("divide by zero");
|
|
tp = rtp;
|
|
continue;
|
|
}
|
|
if( cval == 1 ) {
|
|
tp = ltp;
|
|
continue;
|
|
}
|
|
if( cval == -1 ) {
|
|
tp->t_op = (tp->t_op == DIV ? UMINUS : EQNEG );
|
|
continue;
|
|
}
|
|
}
|
|
if( (p=constant(ltp,&tlc)) && CONSTZERO(lconst,p)) {
|
|
tp = ltp;
|
|
continue;
|
|
}
|
|
if( multop(tpp) ) {
|
|
tp = *tpp;
|
|
continue;
|
|
}
|
|
break;
|
|
|
|
case EQMOD:
|
|
case MOD:
|
|
if( p ) {
|
|
if( !cval ) { /*X%0->error*/
|
|
error("modulus by zero");
|
|
tp = rtp;
|
|
continue;
|
|
}
|
|
if( cval == 1 ) { /*X%1->0*/
|
|
SETVAL(lconst,p,0);
|
|
if (op == EQMOD) /*[vlh] 4.0*/
|
|
tp->t_op = ASSIGN;
|
|
else
|
|
tp = rtp;
|
|
continue;
|
|
}
|
|
}
|
|
if( (p=constant(ltp,&tlc)) && CONSTZERO(lconst,p)) {
|
|
tp = ltp;
|
|
continue;
|
|
}
|
|
if( multop(tpp) ) {
|
|
tp = *tpp;
|
|
continue;
|
|
}
|
|
break;
|
|
|
|
case MULT:
|
|
case EQMULT:
|
|
if( p ) {
|
|
/* Don't do this optimization !!! case: 0 * f()
|
|
if( !cval ) {
|
|
if (op == EQMULT)
|
|
tp->t_op = ASSIGN;
|
|
else
|
|
tp = rtp;
|
|
continue;
|
|
} Problem: side effects of object multiplied with !!!
|
|
*/
|
|
if( cval == 1L ) {
|
|
tp = ltp;
|
|
continue;
|
|
}
|
|
if( cval == -1L ) {
|
|
tp->t_op = (tp->t_op == MULT ? UMINUS : EQNEG);
|
|
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,&tlc)))
|
|
if ((!tlc && i == onebit((long)rtp->t_value)) ||
|
|
(tlc && i == onebit(rtp->t_lvalue))) {
|
|
tp->t_op = invrel[tp->t_op-EQUALS];
|
|
p->t_value = 0;
|
|
p->t_op = CINT; /* assure const short on bit value... */
|
|
continue;
|
|
}
|
|
break;
|
|
|
|
case GREATEQ:
|
|
if( p && !cval && UNSIGN(ltp->t_type) ) {
|
|
SETVAL(lconst,p,1);
|
|
tp = p;
|
|
continue;
|
|
}
|
|
break;
|
|
|
|
case LESS:
|
|
if( p && !cval && UNSIGN(ltp->t_type) ) {
|
|
tp = p;
|
|
continue;
|
|
}
|
|
break;
|
|
|
|
case AND:
|
|
if( p ) {
|
|
/* Don't do this optimization !!! case: 0 & f()
|
|
if( !cval ) {
|
|
tp = rtp;
|
|
continue;
|
|
} Problem: side effects of object anded with !!!
|
|
*/
|
|
if( cval == -1L ) {
|
|
tp = ltp;
|
|
continue;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case OR:
|
|
if( p ) {
|
|
if( !cval ) {
|
|
tp = ltp;
|
|
continue;
|
|
}
|
|
if( cval == -1 ) {
|
|
tp = rtp;
|
|
continue;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case EQXOR:
|
|
case XOR:
|
|
if( p ) {
|
|
if( !cval ) {
|
|
tp = ltp;
|
|
continue;
|
|
}
|
|
if( cval == -1 ) {
|
|
tp->t_op = (tp->t_op == XOR ? COMPL : EQNOT);
|
|
continue;
|
|
}
|
|
if(ltp->t_type==CHAR) /* [vlh] 4.2 */
|
|
goto ctrunc;
|
|
}
|
|
break;
|
|
|
|
case LSH:
|
|
case EQLSH:
|
|
if(tp->t_type==LONG && rtp->t_op==INT2L &&
|
|
UNSIGN(rtp->t_left->t_type)) {
|
|
tp->t_right = rtp->t_left;
|
|
continue;
|
|
}
|
|
case RSH:
|
|
case EQRSH:
|
|
if( p ) {
|
|
if( !cval ) {
|
|
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,(short)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,(long)ltp->t_value,0,0);
|
|
continue;
|
|
|
|
case CLONG:
|
|
tp = snalloc(tp->t_type,CLINDR,ltp->t_lvalue,0,
|
|
ltp->t_lvalue.loword);
|
|
continue;
|
|
|
|
case CFLOAT: /* [vlh] 3.4 */
|
|
tp = snalloc(tp->t_type,CFINDR,ltp->t_lvalue,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,&tlc)) &&
|
|
!tlc && NOTPOINTER(p->t_type) ) {
|
|
p->t_offset += rtp->t_value;
|
|
tp = p;
|
|
continue;
|
|
} /* [vlh] 4.2 long constant not valid offset !!!! */
|
|
if( indexreg(p) ) {
|
|
if((rtp=constant(ltp->t_right,&tlc)) && !tlc){
|
|
/*
|
|
* 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 && 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;
|
|
}
|
|
} /* INDR switch... */
|
|
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;
|
|
}
|
|
if(ltp->t_op==LONG2I) { /*[vlh] 4.2 ~(LONG2I X)->LONG2I (~X)*/
|
|
ltp->t_op = tp->t_op;
|
|
tp->t_op = LONG2I;
|
|
ltp->t_type = LONG;
|
|
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;
|
|
} /* end of switch */
|
|
if(!ccexpr(tpp))
|
|
break;
|
|
tp = *tpp;
|
|
}
|
|
PUTEXPR(oflag,"optim exit",tp);
|
|
*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 short op;
|
|
|
|
if( RELOP(op=tp->t_op) ) {
|
|
s = tp->t_left;
|
|
if( harder(tp->t_right,s) ) {
|
|
PUTEXPR(oflag,"swaprel",tp);
|
|
tp->t_op = swaprel[op-EQUALS];
|
|
tp->t_left = tp->t_right;
|
|
tp->t_right = s;
|
|
}
|
|
}
|
|
if( COMMOP(op) ) {
|
|
PUTEXPR(oflag,"commute",tp);
|
|
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--;
|
|
*q = *p; /*[vlh]*/
|
|
}
|
|
}
|
|
/*
|
|
* 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)) {
|
|
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;
|
|
PUTEXPR(oflag,"after commute",tp);
|
|
}
|
|
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 poshort 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;
|
|
{
|
|
short lconst;
|
|
|
|
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,&lconst) ) /* [vlh] */
|
|
return(1);
|
|
if( constant(tp,&lconst) ) /* [vlh] */
|
|
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,lconst) /* returns 0 or ptr to const node*/
|
|
struct tnode *tp; /* pointer to tree*/
|
|
short *lconst;
|
|
{
|
|
if (tp->t_op == CLONG) { /* [vlh] */
|
|
*lconst = 1;
|
|
return(tp);
|
|
}
|
|
else
|
|
*lconst = 0;
|
|
if( tp->t_op == CINT )
|
|
return(tp);
|
|
if(tp->t_op==INT2L && (tp->t_left->t_op==CINT||tp->t_left->t_op==CLONG)) {
|
|
tp->t_op = CLONG;
|
|
tp->t_type = LONG;
|
|
if (tp->t_left->t_op == CLONG)
|
|
tp->t_lvalue = tp->t_left->t_lvalue;
|
|
else
|
|
tp->t_lvalue = tp->t_left->t_value;
|
|
*lconst = 1;
|
|
return(tp);
|
|
}
|
|
if (tp->t_op==LONG2I && (tp->t_left->t_op==CINT||tp->t_left->t_op==CLONG)) {
|
|
tp->t_op = CINT;
|
|
tp->t_type = INT;
|
|
if (tp->t_left->t_op == CLONG)
|
|
tp->t_value = tp->t_left->t_lvalue;
|
|
else
|
|
tp->t_value = tp->t_left->t_value;
|
|
return(tp);
|
|
}
|
|
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)) )
|
|
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 short op, anyfloat; /*[vlh] 3.4 anyfloat*/
|
|
register long rval, lval;
|
|
short anylong, lconst;
|
|
|
|
tp = *tpp;
|
|
op = tp->t_op;
|
|
if( LEAFOP(op) )
|
|
return(0);
|
|
anylong = anyfloat = 0;
|
|
ltp = tp->t_left;
|
|
if ( ltp->t_op == CFLOAT) { /* [vlh] 3.4 */
|
|
lval = ltp->t_lvalue;
|
|
anyfloat++;
|
|
}
|
|
else if( ltp = constant(ltp,&anylong) ) /* [vlh] 4.1 */
|
|
lval = (anylong) ? ltp->t_lvalue : 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( lconst && (ltp->t_op != CLONG || rtp->t_op != CLONG))
|
|
return(0); /* [vlh] 4.1 */
|
|
if( !lconst && (ltp->t_op != CINT || rtp->t_op != CINT ))
|
|
return(0);
|
|
if (lconst) /* [vlh] 4.1 */
|
|
ltp->t_lvalue = (lval) ? ltp->t_lvalue : rtp->t_lvalue;
|
|
else
|
|
ltp->t_value = (lval) ? ltp->t_value : rtp->t_value;
|
|
*tpp = ltp;
|
|
return(1);
|
|
}
|
|
if (rtp->t_op == CFLOAT) { /* [vlh] 3.4 */
|
|
anyfloat++;
|
|
rval = rtp->t_lvalue;
|
|
switch (op) {
|
|
case MULT:
|
|
case DIV:
|
|
case MOD:
|
|
case LSH:
|
|
case RSH:
|
|
case XOR:
|
|
return(0);
|
|
}
|
|
}
|
|
else if( rtp = constant(rtp,&lconst) ) {
|
|
rval = (lconst) ? rtp->t_lvalue : rtp->t_value;
|
|
anylong += lconst;
|
|
}
|
|
else
|
|
return(0);
|
|
}
|
|
switch( op ) {
|
|
|
|
case ADD:
|
|
lval += rval;
|
|
break;
|
|
|
|
case SUB:
|
|
lval -= rval;
|
|
break;
|
|
|
|
case MULT:
|
|
lval *= rval;
|
|
break;
|
|
|
|
case DIV:
|
|
lval /= rval;
|
|
break;
|
|
|
|
case MOD:
|
|
lval %= rval;
|
|
break;
|
|
|
|
case RSH:
|
|
lval >>= rval;
|
|
break;
|
|
|
|
case LSH:
|
|
lval <<= rval;
|
|
break;
|
|
|
|
case XOR:
|
|
lval ^= rval;
|
|
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);
|
|
|
|
} /* end of switch */
|
|
if( anylong )
|
|
ltp = lcnalloc(LONG,lval);
|
|
else if ( anyfloat )
|
|
ltp = fpcnalloc(FLOAT,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 short op;
|
|
register struct tnode *tp;
|
|
short lconst;
|
|
short value;
|
|
|
|
tp = *tpp;
|
|
if( p = constant(tp->t_right,&lconst) ) {
|
|
if(!lconst && ((value=onebit((long)p->t_value)) < 0) )
|
|
return(0);
|
|
if(lconst && ((value=onebit(p->t_lvalue)) < 0) )
|
|
return(0);
|
|
op = tp->t_op;
|
|
/* THIS FIX MUST WAIT FOR UNSIGNED LONG IMPLEMENTATION...........*/
|
|
if (op!=MULT && op!=EQMULT && !UNSIGN(tp->t_left->t_type))
|
|
return(0);
|
|
switch( op ) {
|
|
|
|
case MULT:
|
|
op = LSH;
|
|
break;
|
|
|
|
case EQMULT:
|
|
op = EQLSH;
|
|
break;
|
|
|
|
case DIV:
|
|
op = RSH;
|
|
break;
|
|
|
|
case EQDIV:
|
|
op = EQRSH;
|
|
break;
|
|
|
|
case MOD:
|
|
op = AND;
|
|
if (!lconst)
|
|
value = p->t_value - 1;
|
|
else
|
|
value = p->t_lvalue - 1;
|
|
break;
|
|
|
|
case EQMOD:
|
|
op = EQAND;
|
|
if (!lconst)
|
|
value = p->t_value - 1;
|
|
else
|
|
value = p->t_lvalue - 1;
|
|
break;
|
|
|
|
default:
|
|
return(0);
|
|
} /* end of case */
|
|
tp->t_op = op;
|
|
if (lconst)
|
|
p->t_op = CINT; /* [vlh] 4.1 assure constant int.... */
|
|
p->t_value = value;
|
|
#ifdef DEBUG
|
|
if (oflag>1) printf("lconst %d op = %d value = %d\n",lconst,op,value);
|
|
#endif
|
|
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 == 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 *tp, *rtp;
|
|
register short change;
|
|
|
|
tp = *tpp;
|
|
PUTEXPR(oflag,"multop",*tpp);
|
|
if (change = power2(tpp))
|
|
tp = *tpp;
|
|
if( chklong(rtp=tp->t_right) || chklong(tp->t_left) )
|
|
|
|
switch( tp->t_op ) {
|
|
|
|
case MULT:
|
|
tp->t_op = LMULT;
|
|
break;
|
|
|
|
case DIV:
|
|
case MOD:
|
|
if( chklong(tp->t_left) ) { /*only hard if divisor is long*/
|
|
if(rtp->t_type == INT) /*only occurs if changed !!*/
|
|
break;
|
|
tp->t_op += (LDIV-DIV);
|
|
if( rtp->t_op == CLONG )
|
|
rtp->t_op = DCLONG;
|
|
}
|
|
break;
|
|
|
|
case EQDIV:
|
|
case EQMOD:
|
|
if( !chklong(rtp) )
|
|
break;
|
|
if( rtp->t_op == CLONG )
|
|
rtp->t_op = DCLONG;
|
|
case EQMULT:
|
|
tp->t_op += (LMULT-EQMULT);
|
|
*tpp = tnalloc(ASSIGN,tp->t_type,0,0,tcopy(tp->t_left,A_DOPOST),
|
|
tcopy(tp,A_DOPRE));
|
|
change++;
|
|
break;
|
|
} /* end of case... */
|
|
PUTEXPR(oflag,"after multop ",tp);
|
|
return(change);
|
|
}
|
|
|
|
/* onebit - returns whether constant is power of two (one bit on)*/
|
|
onebit(val) /* returns bit number or -1*/
|
|
long val; /* [vlh] int->long, constant value to check*/
|
|
{
|
|
register short i;
|
|
|
|
for( i = 31; val != 0; val <<= 1, i-- )
|
|
if( val & 0x80000000 )
|
|
break;
|
|
if (val != 0x80000000)
|
|
return(-1);
|
|
return(i);
|
|
}
|