V9/cmd/sun/pcc/optim2.c
#ifndef lint
static char sccsid[] = "@(#)optim2.c 1.1 86/02/03 Copyr 1985 Sun Micro";
#endif
/*
* Copyright (c) 1985 by Sun Microsystems, Inc.
*/
#include "cpass2.h"
/*
* routines to detect and to deal with doubly-indexed OREGs.
* Initially, these were all stolen from the vax code.
*/
base( p )
register NODE *p;
{
register int o = p->in.op;
register v,maxoff;
register NODE *lp,*rp;
if ( BTYPE(p->in.type) == DOUBLE )
maxoff = 123;
else
maxoff = 127;
if ( o==REG && isbreg( p->tn.rval ))
return( p->tn.rval );
lp = p->in.left;
rp = p->in.right;
if ((o==PLUS || o==MINUS)
&& lp->in.op==REG && isbreg( lp->tn.rval )
&& rp->in.op==ICON && rp->in.name[0] == '\0'
&& (use68020 || (v=rp->tn.lval) <= maxoff && v >= -128)
)
return( lp->tn.rval );
return( -1 );
}
offset( p, notused )
register NODE *p;
int notused;
{
if ( p->in.op==REG ) {
if ( p->in.type==INT || p->in.type==UNSIGNED || ISPTR(p->in.type) ) {
return( p->tn.rval );
}
if ( p->in.type==SHORT ) {
/*
* result is: short index flag + xreg
*/
return( (1<<8) + p->tn.rval );
}
}
if (use68020) {
/*
* test for scaled indexing
*/
int count;
NODE *lp = p->in.left;
NODE *rp = p->in.right;
if ( p->in.op==LS && lp->in.op==REG
&& (lp->in.type==INT || lp->in.type==UNSIGNED || lp->in.type == SHORT )
&& (rp->in.op==ICON && rp->in.name[0]=='\0')
&& (count = rp->tn.lval) >= 0 && count <= 3 ) {
int scale = (1 << count);
if ( p->in.type == SHORT ) {
/*
* result is: short index flag + scale factor + xreg
*/
return( (1<<8) + (scale<<4) + lp->tn.rval );
}
/*
* result is: scale factor + xreg
*/
return( (scale<<4) + lp->tn.rval );
}
}
return( -1 );
}
makeor2( p, basenode, breg, scale_plus_xreg )
register NODE *p; /* node to be rewritten */
register NODE *basenode; /* base node (usually a ptr) */
int breg; /* base register */
int scale_plus_xreg; /* scale factor + index register */
{
register NODE *t; /* from which tn.lval, tn.name are obtained */
register int i;
NODE *f;
register flags;
int scale, xreg, shortx, ibit;
ibit = 0;
shortx= (scale_plus_xreg >> 8) & 1;
scale = (scale_plus_xreg >> 4) & 017;
xreg = scale_plus_xreg & 017;
p->in.op = OREG;
f = p->in.left; /* have to free this subtree later */
/* init base */
switch (basenode->in.op) {
case ICON:
case REG:
case OREG:
t = basenode;
break;
case MINUS:
basenode->in.right->tn.lval = -basenode->in.right->tn.lval;
case PLUS:
t = basenode->in.right;
break;
case INCR:
case ASG MINUS:
t = basenode->in.left;
break;
case UNARY MUL:
t = basenode->in.left->in.left;
break;
default:
cerror("illegal makeor2");
}
p->tn.lval = t->tn.lval;
#ifndef FLEXNAMES
for(i=0; i<NCHNAM; ++i)
p->in.name[i] = t->in.name[i];
#else
p->in.name = t->in.name;
#endif
/*
* For 68020, R2UPK3 encodes the following data:
* int shortx:1; short index
* int ibit:1; memory indirect mode (unused)
* int scale:4; scale factor (1,2,4,8)
* For 68010, only the short index flag is encoded.
*/
R2PACKFLGS(flags,shortx,ibit,scale);
p->tn.rval = R2PACK( breg, xreg, flags );
tfree(f);
return;
}
/*
* Multiply a value by a constant.
* Multiplies are slow on the 68010, so do some shifting and
* adding here.
* For each 1-bit in the constant the value is shifted
* by n where 2**n equals the bit.
* The shifted value is then added into an accumulated value.
* One tricky part is for runs of three or more consecutive
* bits. For a number like 7, it is cheaper to calculate
* (8 - 1), than it is (3 + 2 + 1)
*/
conmul(p, cookie)
register NODE *p;
{
register const; /* The constant value */
register int curpos; /* Position of last one bit */
register int power; /* The bit being tested */
int run; /* Num of consecutive one bits */
int nbits; /* Num of bits since the last one bit */
int negconst; /* Is the constant <0 ? */
char * destreg; /* name of "AL" register */
char * helper; /* name of "A1" register */
int helperreg;
TWORD ltype;
char typechar;
int t;
/*
* first determine how many instructions we will need to do
* it in line. Cost estimator is: # single bits + 2* # of multi bit
* runs. If this exceeds 5, then it is more compact to use a
* multiply instruction or routine.
*/
const = p->in.right->tn.lval;
if (const<0){
negconst = 1;
const = - const;
} else
negconst = 0;
run = const & (const>>1); /* each run shortened by one bit */
run = cntbits( run ^ (run>>1) ) + (run&1); /* 2*# multibit runs */
nbits = cntbits( const ^ (const>>1)) +(const&1); /* 2* #total runs*/
run += nbits; /* 2* estimator */
ltype = p->in.left->in.type;
if ((ttype(ltype,TUCHAR|TCHAR|TUSHORT|TSHORT) && run>6) || (run > 10)){
/* old-fashioned way */
if (tshape( p->in.right, SSCON ) ){
if (negconst){
expand( p->in.left, INAREG|INTAREG, " negZB AL\nZv");
p->in.right->tn.lval = const;
}
if (ttype(ltype, TSHORT|TCHAR)){
expand( p, cookie, " muls #CR,AL\nZv");
} else if (ttype(ltype, TUSHORT|TUCHAR)){
expand( p, cookie, " mulu #CR,AL\n");
} else if (use68020) {
if (ISUNSIGNED(p->in.type)) {
expand( p, cookie, " mulul #CR,AL\n");
} else {
expand( p, cookie, " mulsl #CR,AL\nZv");
}
} else {
expand( p, cookie,
"\tmovl\tAL,A1\n\tswap\tA1\n\tmulu\t#CR,AL\n");
fputs( ttype(ltype, TULONG|TUNSIGNED|TUCHAR|TPOINT)
? "\tmulu" : "\tmuls", stdout);
expand( p, cookie,
"\t#CR,A1\n\tswap\tA1\n\tclrw\tA1\n\taddl\tA1,AL\nZv");
}
} else if (use68020) {
if (ISUNSIGNED(p->in.type)) {
expand( p, cookie, " mulul #CR,AL\n");
} else {
expand( p, cookie, " mulsl #CR,AL\nZv");
}
} else {
order( p->in.right, INTAREG );
order( p, INAREG|INTAREG );
}
return;
}
typechar = (((t=p->in.type)==CHAR)||t==UCHAR) ? 'b'
: (t==SHORT||t==USHORT) ? 'w'
: 'l';
/*
* if the result type is larger than the operand type, we must expand
* the operand before doing the operation.
*/
switch( p->in.left->tn.type){
case UCHAR:
case USHORT:
if (typechar=='b' || typechar=='w') break;
expand( p->in.left, INAREG|INTAREG, " andl #0xffff,AL\n");
break;
case CHAR:
case SHORT:
if (typechar=='b' || typechar=='w') break;
expand( p->in.left, INAREG|INTAREG, " extl AL\n");
break;
}
p->in.left->tn.type = t;
nbits = ffs( const )-1;
power = 0;
helperreg = resc[0].tn.rval ;
helper = rnames[ helperreg ];
destreg = rnames[ p->in.left->tn.rval];
if (negconst){
expand( p->in.left, INAREG|INTAREG, " negZB AL\nZv");
}
if (nbits>0){
shiftreg( nbits, destreg, helperreg , 1, typechar, t);
const >>= nbits;
}
if (const==1) return;
/*
* the first time is special (ask Ann Landers). If it is a run,
* then we move, negate, shift add. Otherwise, we don't negate.
*/
run = ffs( ~const) -1;
expand(p, cookie, " movZB AL,A1\n");
switch (run){
case 2:
shiftreg( 1, helper, -1, 1, typechar, t);
expand( p, cookie, "\taddZB\tA1,AL\nZv");
/* fall through */
case 1:
curpos = 0;
break;
default:
shiftreg( run, helper, -1, 1, typechar, t);
expand( p->in.left, INAREG|INTAREG, " negZB AL\nZv");
expand( p, cookie, "\taddZB\tA1,AL\nZv");
curpos = 1;
break;
}
for ( const >>= run; const >>= (power=ffs(const))-1; const >>= run){
run = ffs(~const)-1;
switch(run) {
case 1:
shiftreg( power-curpos, helper, -1, 1, typechar, t);
expand( p, cookie, "\taddZB\tA1,AL\nZv");
curpos = 0;
break;
case 2:
shiftreg( power-curpos, helper, -1, 1, typechar, t);
expand( p, cookie, "\taddZB\tA1,AL\nZv");
shiftreg( 1, helper, -1, 1, typechar, t);
expand( p, cookie, "\taddZB\tA1,AL\nZv");
curpos = 0;
break;
default:
shiftreg( power-curpos, helper, -1, 1, typechar, t);
expand( p, cookie, "\tsubZB\tA1,AL\nZv");
shiftreg( run, helper, -1, 1, typechar, t);
expand( p, cookie, "\taddZB\tA1,AL\nZv");
curpos = 1;
break;
}
}
}
/*
* divide a signed number by a constant.
* divides are really slow on this machine, so we really want to avoid them.
* the only interesting case is divide by power-of-two. Others, we really
* have to do the division.
*/
void
condiv( p, cookie ) register NODE *p;
{
char sizechar;
register const;
register TWORD ltype;
int lab1f;
char * lname = rnames[p->in.left->tn.rval];
const = p->in.right->tn.lval;
ltype = p->in.left->tn.type;
if (const & (const-1)){
/* too bad -- not a positive power of two */
switch (ltype){
case CHAR:
print_str_str_nl( " extw ", lname );
/* fall through */
case SHORT:
print_str_str_nl( " extl ", lname );
printf( " divs #%d,%s\n", const, lname);
break;
default:
if (use68020) {
printf( " divsl #%d,%s\n",
const, lname);
} else {
order( p->in.right, INTAREG );
order( p, INTAREG );
}
break;
}
return;
}
switch (ltype){
case CHAR: sizechar = 'b'; break;
case SHORT: sizechar = 'w'; break;
default: sizechar = 'l';
}
lab1f = getlab();
printf(" tst%c %s\n jge L%d\n", sizechar, lname, lab1f);
print_str((const >= 1 && const <= 8) ? " addq" : " add");
printf("%c #%d,%s\n", sizechar, const-1, lname );
deflab(lab1f);
const = ffs(const)-1;
/* like shiftreg(), but for right, arithmetic shifts */
while (const > 0){
if (const> 8){
printf(" asr%c #8,%s\n", sizechar, lname );
const -= 8;
} else {
printf(" asr%c #%d,%s\n", sizechar, const, lname );
const = 0;
}
}
return;
}
/*
* remainder a signed number by a constant.
* remainders are really slow on this machine, so we really want to avoid them.
* the only interesting case is powers-of-two. Others, we really
* have to do the division.
*/
void
conrem( p, cookie ) register NODE *p;
{
char sizechar, opchar;
register const;
register TWORD ltype;
char * lname = rnames[p->in.left->tn.rval];
char * helper, u;
int lab1f, lab2f;
const = p->in.right->tn.lval;
ltype = p->in.left->tn.type;
if (const & (const-1)){
/* too bad -- not a positive power of two */
switch (ltype){
case CHAR:
print_str_str_nl( " extw ", lname );
/* fall through */
case SHORT:
print_str_str_nl( " extl ", lname );
printf( " divs #%d,%s\n", const, lname);
print_str_str_nl( " swap ", lname );
break;
case UCHAR:
print_str_str_nl(" andl #0xff,", lname );
goto divu;
case USHORT:
print_str_str_nl(" andl #0xffff,", lname );
divu: printf(" divu #%d,%s\n", const, lname);
print_str_str_nl(" swap ", lname);
break;
default:
if (use68020) {
helper = rnames[resc[0].tn.rval];
printf(" %s #%d,%s:%s\n",
(ISUNSIGNED(ltype)? "divull" : "divsll"),
const, helper, lname);
printf(" movl %s,%s\n", helper, lname);
} else {
order( p->in.right, INTAREG );
order( p, INTAREG );
}
break;
}
return;
}
helper = rnames[resc[0].tn.rval];
u = 0;
switch (ltype){
case UCHAR: u = 1; /* fall through */
case CHAR: sizechar = 'b'; opchar = 'w'; break;
case USHORT: u = 1; /* fall through */
case SHORT: sizechar = 'w'; opchar = 'w'; break;
case UNSIGNED: u = 1; /* fall through */
default: sizechar = 'l'; opchar = 'l';
}
if (!u){
lab1f = getlab();
lab2f = getlab();
}
if (const<=128 && const >=-128){
printf(" moveq #%d,%s\n", const-1, helper);
if (!u){
printf(" tst%c %s\n jge L%d\n neg%c %s\n",
sizechar, lname, lab1f, sizechar, lname);
printf(" and%c %s,%s\n neg%c %s\n jra L%d\n",
opchar, helper, lname, opchar, lname, lab2f );
deflab( lab1f );
}
printf(" and%c %s,%s\n", opchar, helper, lname);
if (!u){ deflab(lab2f);}
} else {
if (!u){
printf(" mov%c %s,%s\n jge L%d\n neg%c %s\n",
sizechar, lname, helper, lab1f, sizechar, lname);
deflab(lab1f);
}
printf(" and%c #%d,%s\n", opchar, const-1, lname );
if (!u){
printf(" tst%c %s\n jge L%d\n neg%c %s\n",
sizechar, helper, lab2f, opchar, lname );
deflab(lab2f);
}
}
return;
}
optim2( p )
register NODE *p;
{
register NODE *q;
register NODE *r;
register long v;
register op;
int w;
int lsize, rsize;
NODE *rl, *rr, *qq;
TWORD t;
/*
* reduction of strength on integer constant operands
* this is a practical, fruitful area of peephole code optimization,
* since there are so many easy things that can be done.
* we also check for possible single-o OREGs in a place where
* the oreg2() should but does not.
*/
op = p->in.op;
switch (optype(op)) {
case LTYPE:
break;
case UTYPE:
optim2(p->in.left);
break;
case BITYPE:
optim2(p->in.left);
optim2(p->in.right);
break;
}
switch (op){
case LS:
case ASG LS:
case RS:
case ASG RS:
/* if rhs is an extending SCONV, don't bother converting */
r = p->in.right;
if (isconv(r, SHORT, USHORT) || isconv(r, CHAR, UCHAR)) {
q = r->in.left;
*r = *q;
q->in.op = FREE;
}
/* one interesting case: <lhs> <op> 0 */
if (r->tn.op != ICON ) break;
if (r->tn.lval == 0 && r->tn.name[0] == '\0' ){
promoteleft:
t = p->in.type;
q = p->in.left;
*p = *q; /* paint over */
p->in.type = t;
q->in.op = r->in.op = FREE;
}
/* could test for count of 1, too, but this is easier to do later on */
break;
case UNARY MUL:
/* in case this wasn't done earlier... */
if (p->in.left->in.op == ICON) {
q = p->in.left;
t = p->in.type;
*p = *q;
p->in.op = NAME;
p->in.type = t;
q->in.op = FREE;
break;
}
/*
* put potential double index OREG trees into a canonical
* form: ( <base> + <offset> ) + <index>
*/
p = p->in.left;
if (p->in.op != PLUS)
break;
q = p->in.left;
r = p->in.right;
if ( ISPTR(r->in.type) ) {
/*
* put the pointer on the left
*/
p->in.left = r;
p->in.right = q;
q = p->in.left;
r = p->in.right;
}
if ( ISPTR(q->in.type)
&& (r->in.op == PLUS || r->in.op == MINUS)
&& !ISPTR(r->in.left->in.type)
&& r->in.right->in.op == ICON ) {
/*
* <ptr exp> + ( <int exp> [+-] ICON )
* rewrite as
* (<ptr exp> [+-] ICON) + <int exp>
*/
p->in.right = r->in.left;
r->in.left = q;
p->in.left = r;
r->in.type = q->in.type;
break;
}
if (r->in.op == LS
&& ( (rr = r->in.right)->in.op == ICON )
&& ( (rl = r->in.left)->in.op == PLUS
|| rl->in.op == MINUS )
&& !ISPTR(rl->in.left->in.type)
&& rl->in.right->in.op == ICON ) {
/*
* <ptr exp> + ((<int exp> [+-] ICON) << ICON)
* rewrite as
* (<ptr exp> [+-] ICON*) + (<int exp> << ICON)
*/
r->in.left = rl->in.left;
rl->in.left = q;
p->in.left = rl;
rl->in.type = q->in.type;
rl->in.right->tn.lval <<= rr->tn.lval;
break;
}
break;
case PLUS:
case ASG PLUS:
case MINUS:
case ASG MINUS:
/*
* one interesting cases: <lhs> <op> 0.
* <address reg> + SCONV( <short> ) is a good one, too,
* but is best done later on.
*/
if (((r=p->in.right)->tn.op == ICON
&& r->tn.lval == 0 && r->tn.name[0] == '\0' )
|| (r->tn.op == FCON && r->fpn.dval == 0.0 )) {
goto promoteleft;
}
break;
case MUL:
/*
* put constants on the right
*/
if (p->in.left->in.op == ICON && p->in.right->in.op != ICON) {
r = p->in.right;
p->in.right = p->in.left;
p->in.left = r;
}
/* fall through */
case ASG MUL:
case DIV:
case ASG DIV:
case MOD:
case ASG MOD:
/*
* two interesting case: <lhs> <op> 1
* and: small multiplies that can be handled by
* the feeble instructions.
*/
if (((r=p->in.right)->tn.op == ICON
&& r->tn.lval == 1 && r->tn.name[0] == '\0')
|| (r->tn.op == FCON && r->fpn.dval == 1.0 )) {
switch (op){
case MOD:
case ASG MOD:
/*
* x%1 == 0
* BUT x might have side effects, so...
* x%1 ==> (x,0)
*/
p->in.op = COMOP;
if (r->tn.op==FCON)
r->fpn.dval = 0.0;
else
r->tn.lval = 0;
break;
default:
goto promoteleft;
}
break;
}
if (op == DIV || op == ASG DIV){
/*
* a very particular case: a difference of two
* pointers, divided by a power of two should
* always give an integral result (this is
* pointer subtraction, with the implied divide
* by sizeof( *p ) ). So, we can change this
* into a shift, if we ever see it.
* AND...
* a less peculiar case: unsigned divide by a power of 2.
*/
if (
(p->in.type == UNSIGNED && r->in.op ==ICON )
|| ((p->in.type == INT || p->in.type == UNSIGNED)
&& r->in.op == ICON && (q = p->in.left)->in.op == MINUS
&& ISPTR(q->in.left->in.type)
&& ISPTR(q->in.right->in.type))
){
if (cntbits( v=r->tn.lval )==1 && r->in.name[0]=='\0' ){
/* well, looks like we found one */
w = ffs( v ) - 1;
p->in.op = op += RS - DIV; /* keep ASG if present */
r->tn.lval = w;
break;
}
}
}
/* the other good case is multiply by a power of two,
and thats already being handled in the front end */
/*
* many multiplies
* can be done directly in the hardware:
* {SHORT, USHORT, CHAR, UCHAR} x
* {SHORT, USHORT, CHAR, UCHAR, ICON}
*
* so can the corresponding divides.
*/
if (p->in.type != INT && p->in.type != UNSIGNED &&
!ISPTR(p->in.type))
break;
q = p->in.left;
if ( isconv(q, SHORT, USHORT ) ) lsize = 2;
else if (isconv(q, CHAR, UCHAR )) lsize = 1;
else if (q->in.type == SHORT) lsize = 0;
else break;
if ( isconv(r, SHORT, USHORT ) ||
special(r, SSCON) ) rsize = 2;
else if (isconv(r, CHAR, UCHAR )) rsize = 1;
else break;
/* we've looked at both sides and it looks plausable */
/* rearrange lhs */
if (lsize == 1){
/* diddle SCONV, but leave it in place */
q->in.type = (q->in.left->tn.type==UCHAR)?USHORT:SHORT;
} else if(lsize == 2){
p->in.left = q->in.left;
q->in.op = FREE;
}
/* now rearrange rhs */
if (rsize == 1){
r->in.type = (r->in.left->tn.type==UCHAR)?USHORT:SHORT;
} else if (r->tn.op == ICON){
r->tn.type = SHORT;
} else {
p->in.right = r->in.left;
r->in.op = FREE;
}
/*
* The result of the mul[us] instructions are ints
* anyway, so leave the type of the MUL node alone.
* But DIV must be acknowleged as short.
*/
if ( (op==DIV || op== ASG DIV || op==MOD || op==ASG MOD)
&& p->in.type != SHORT && p->in.type != USHORT){
w = (p->in.right->tn.type==SHORT && p->in.left->tn.type==SHORT) ? SHORT : USHORT;
q = talloc();
*q = *p;
p->in.op = SCONV;
q->in.type = w;
p->in.left = q;
p->in.right = 0;
}
break;
case SCONV:
/*
* conversions from float to double
* in a coprocessor register are vacuous
*/
q = p->in.left;
if (p->in.type == DOUBLE && q->in.type == FLOAT
&& q->in.op == REG && iscreg(q->tn.rval)) {
TWORD t = p->in.type;
*p = *q;
p->in.type = t;
q->in.op = FREE;
return;
}
/*
* John Gilmore memorial hack
*
* garbage-can case:
* if this is a shorten of a simple calculation
* whose (2) operands are no larger than the
* result type, then we can do the operation more
* cheaply, no?
* The architypical case looks like:
* (SCONV<short> (+<int> (SCONV<int> NAME<short> ) ICON ))
* p^ q^ r^
*/
if ( q->in.type!=INT && q->in.type!=UNSIGNED ) return;
switch (q->in.op){
case PLUS:
case MINUS:
case MUL:
case DIV:
case MOD:
case LS:
case RS:
case AND:
case OR:
case ER:
break;
default:
return;
}
if ( p->in.type==SHORT || p->in.type==USHORT ){
w = 2;
v = 0x7fff;
}else if ( p->in.type==CHAR || p->in.type==UCHAR ){
w = 1;
v = 0x7f;
}else break;
qq = q;
r = q->in.right;
q = q->in.left;
if ( isconv(q, SHORT, USHORT ) ) lsize = 2;
else if (isconv(q, CHAR, UCHAR )) lsize = 1;
else if (q->in.op == ICON)
if (q->tn.name[0] == '\0'
&& ((q->tn.lval | v)==v || (q->tn.lval|v)==-1))
lsize = 0;
else break;
else if (tlen(q) < sizeof(long)) lsize = -1;
else break;
if ( isconv(r, SHORT, USHORT ) ) rsize = 2;
else if (isconv(r, CHAR, UCHAR )) rsize = 1;
else if (r->in.op == ICON)
if (r->tn.name[0] == '\0'
&& ((r->tn.lval | v)==v || (r->tn.lval|v)==-1))
rsize = 0;
else break;
else if (tlen(r) < sizeof(long)) rsize = -1;
else break;
if (lsize == -1) {
NODE *t = talloc();
t->in.op = SCONV;
t->in.type = qq->in.type;
t->in.left = q;
lsize = tlen(q);
q = t;
qq->in.left = q;
}
if (rsize == -1) {
NODE *t = talloc();
t->in.op = SCONV;
t->in.type = qq->in.type;
t->in.left = r;
rsize = tlen(r);
r = t;
qq->in.right = r;
}
if (rsize > w || lsize > w) {
/* must preserve top SCONV, but weaken operation */
p = p->in.left;
if (rsize >lsize){
p->in.type = r->in.left->in.type ;
w = rsize;
} else {
p->in.type = q->in.left->in.type;
w = lsize;
}
} else {
/* clobber SCONV */
NODE * t = p->in.left;
TWORD tt = p->in.type;
*p = *t; /* paint over */
p->in.type = tt; /* except type */
t->in.op = FREE; /* give a node back */
}
/* now weaken or elide child convs */
switch (lsize){
case 0: /* ICON -- diddle type */
q->in.type = p->in.type; break;
case 1: /* char */
if (lsize <w){
/* retain SCONV but weaken */
q->in.type = p->in.type; break;
}
/* else fall through */
case 2: /* same size -- elide SCONV */
{ NODE * t = q->in.left;
*q = *t;
t->in.op = FREE;
}
}
switch (rsize){
case 0: /* ICON -- diddle type */
r->in.type = p->in.type; break;
case 1: /* char */
if (rsize <w){
/* retain SCONV but weaken */
r->in.type = p->in.type; break;
}
/* else fall through */
case 2: /* same size -- elide SCONV */
{ NODE * t = r->in.left;
*r = *t;
t->in.op = FREE;
}
}
break;
case CHK:
/*
* A CHK with constant bounds, of which the left-hand-side
* is an SCONV that widens a shorter type can be converted
* to an SCONV over a CHK. This lets us use weaker CHK
* instructions.
*/
if (p->in.type != INT && p->in.type != UNSIGNED) break;
q = p->in.left;
if (!isconv(q, CHAR, UCHAR) && !isconv(q, SHORT, USHORT) )break;
r = p->in.right;
rr = r->in.right;
rl = r->in.left;
if (rr->in.op != ICON || rl->in.op != ICON) break;
/*
* make bounds smaller using the type of the
* converted operand
*/
t = q->in.left->in.type;
switch(t) {
case CHAR:
if (reduce_bounds(-128, 127, rl, rr))
goto delete_check;
break;
case UCHAR:
if (reduce_bounds(0, 255, rl, rr))
goto delete_check;
break;
case SHORT:
if (reduce_bounds(-32768, 32767, rl, rr))
goto delete_check;
break;
case USHORT:
if (reduce_bounds(0, 65535, rl, rr))
goto delete_check;
break;
delete_check:
*p = *q;
q->in.op = FREE;
tfree(r);
return;
}
/*
* weaken the conversion by
* exchanging CHK and SCONV nodes
*/
*p = *q; /* p becomes SCONV node */
p->in.left = q;
q->in.op = CHK;
q->in.right = r;
q->in.type = r->in.type = rl->tn.type = rr->tn.type = t;
break;
case INIT:
/*
* not an optimization, but prevents an
* infinite loop later on in code generation...
*/
q = p->in.left;
if (q->in.op != ICON && q->in.op != FCON) {
uerror("Illegal initialization");
tfree(q);
q = talloc();
q->in.type = p->in.type;
if (ISFLOATING(p->in.type)) {
q->in.op = FCON;
q->fpn.dval = 0.0;
} else {
q->in.op = ICON;
q->tn.name = "";
q->tn.lval = 0;
}
p->in.left = q;
}
break;
case FORCE:
/*
* if the type of a FORCE op is {u}char or {u}short,
* extend it into an INT.
*/
switch(p->in.type){
case CHAR:
case UCHAR:
case SHORT:
case USHORT:
q = talloc();
q->in.op = SCONV;
q->in.type = INT;
q->in.left = p->in.left;
q->in.right = NULL;
p->in.left = q;
p->in.type = INT;
break;
}
break;
} /* switch */
}
/*
* reduce bounds of a range check using
* the known bounds of the domain type
*/
int
reduce_bounds(domain_min, domain_max, range_min, range_max)
register CONSZ domain_min, domain_max;
register NODE *range_min, *range_max;
{
/* do the domain and range intersect? */
if (domain_min > range_max->tn.lval || domain_max < range_min->tn.lval) {
/* expression value is known to lie outside required range */
werror("expression value is out of range");
}
/* range := intersection(range, domain) */
if (domain_min > range_min->tn.lval)
range_min->tn.lval = domain_min;
if (domain_max < range_max->tn.lval)
range_max->tn.lval = domain_max;
/* if domain is a subset of range, range check is unnecessary */
if (domain_min >= range_min->tn.lval && domain_max <= range_max->tn.lval) {
return(1);
}
return(0);
}