📄 cceval.c
字号:
/* CCEVAL.C - Evaluate and optimize expression parse trees
**
** (c) Copyright Ken Harrenstien 1989
** All changes after v.24, 11-Jan-1988 including CCFOLD merge
** All CCFOLD changes after v.116, 29-Apr-1988
** (c) Copyright Ken Harrenstien, SRI International 1985, 1986
** All changes after v.7, 8-Aug-1985
** All CCFOLD changes after v.17, 8-Aug-1985
**
** Original CCEVAL by David Eppstein / Stanford University / 28-Jul-84
** Original CCFOLD (C) 1981 K. Chen
*/
#define NEW 3 /* temporary to allow shutting advanced optimiz off if buggy */
#include "cc.h"
/* Exported functions */
NODE *evalexpr(NODE *e);
NODE *evaldiscard(NODE *n);
int sideffp(NODE *n); /* Used here and CCGEN2 */
INT istrue(NODE *test, NODE *bindings); /* CCGEN1's gfor() */
/* Imported functions */
extern NODE *ndef(int op, TYPE *t, int f, NODE *l, NODE *r); /* CCSTMT */
extern NODE *convbinary(NODE *), *convnullcomb(NODE *); /* CCTYPE */
extern INT binexp(unsigned INT); /* CCOUT */
extern int hash(char *); /* CCSYM */
/* Internal functions */
static NODE *copyflag(NODE *, NODE *), *setlog(NODE *, int);
static INT ecanon(NODE *);
static NODE *eseqdisc(NODE *, NODE *), *evalcast(NODE *);
static long value(NODE *, NODE *);
static NODE *lookup(SYMBOL *, NODE *);
static NODE *eval(NODE *), *evalunop(NODE *), *evalbinop(NODE *);
static NODE *evalb1op(NODE *), *evallog(NODE *), *evalfun(NODE *);
static NODE *evalasop(NODE *);
static int evalbool(NODE *);
static NODE *edisc(NODE *); /* Auxiliary routine that does the work */
static long tolong(TYPE *, long);
static NODE *evalincdec(NODE *, int);
#if 0
static NODE *edisc(); /* Auxiliary routine that does the work */
static long tolong();
static NODE *evalincdec();
static NODE *copyflag(), *setlog(), *evalcast(), *eseqdisc();
static INT ecanon();
static long value();
static NODE *lookup();
static NODE *eval();
static NODE *evalunop(), *evalbinop(), *evalb1op(), *evallog(), *evalfun();
static NODE *evalasop();
static int evalbool();
#endif
/* Handy macro to see if an operand node is a hackable constant */
#define eisconst(e) \
(e->Nop == N_ICONST || e->Nop == N_PCONST || e->Nop == N_FCONST)
#define eisiconst(e) (e->Nop == N_ICONST)
/* EVALEXPR(e) - Evaluates a parse-tree expression, folding constants and
** reducing it as far as possible. This includes discarding unused
** values in sequential (comma) expressions.
*/
NODE *
evalexpr(e)
NODE *e;
{
ecanon(e = eval(e));
return e;
}
static NODE *
eval(NODE *e)
{
if (e) switch (tok[e->Nop].tktype) {
case TKTY_RWOP: /* Built-in Reserved-Word ops */
break; /* Only Q_ASM for now */
case TKTY_PRIMARY:
switch (e->Nop) {
case N_FNCALL:
return evalfun(e);
case Q_DOT:
case Q_MEMBER:
e->Nleft = eval(e->Nleft);
}
break; /* Return e */
case TKTY_SEQ: /* Just one op, N_EXPRLIST (,) */
e->Nright = eval(e->Nright); /* Fold current expression */
if ((e->Nleft = evaldiscard(eval(e->Nleft))) == NULL) /* Fold previous */
return e->Nright; /* May have flushed Nleft */
break; /* Return e */
case TKTY_UNOP:
e->Nleft = eval(e->Nleft);
if (eisconst(e->Nleft))
return evalunop(e); /* Do unary op on constant */
break;
/* Assignment ops have side effects, so can't eliminate them completely.
** However, it may be possible to optimize a non-simple assignment into
** a simple assignment, if the right operand is a specific constant
** (notably 0).
** Also, things like +=1 and -=1 should be turned into ++ and -- since the
** code generator has special optimizations for those.
*/
case TKTY_ASOP: /* Assignment ops */
e->Nleft = eval(e->Nleft); /* Fold destination */
e->Nright = eval(e->Nright); /* Fold right operand, and */
if (eisconst(e->Nright)) /* check further if it's a constant */
return evalasop(e);
break; /* return e */
case TKTY_BINOP: /* Ordinary binary ops */
e->Nleft = eval(e->Nleft); /* First fold operands */
e->Nright = eval(e->Nright);
if (eisconst(e->Nleft)) {
if (eisconst(e->Nright))
return evalbinop(e); /* Do binary op on two constants */
/* Have one constant on left -- not on right.
** ecanon() should have put the constant on right, but
** constant evaluation here may have changed things.
*/
return evalb1op(e); /* Do binary op on one constant */
} else if (eisconst(e->Nright)) /* Have one constant on right? */
return evalb1op(e); /* Do binary op on one constant */
break; /* Neither operand is a constant. */
case TKTY_BOOLOP: /* Relational & logical binary ops */
e->Nleft = eval(e->Nleft);
if (e->Nop == Q_LAND || e->Nop == Q_LOR) /* Logical operator? */
if (eisconst(e->Nleft))
return evallog(e);
e->Nright = eval(e->Nright);
/* Relational or equality op. Both operands must be constants
** if we are to evaluate further.
** Actually, there are a few special cases which could still
** be optimized if one operand is a constant. For example,
** unsigned comparison with 0.
*/
if (eisconst(e->Nleft) && eisconst(e->Nright))
return evalbinop(e);
break;
case TKTY_BOOLUN: /* Just one op, Q_NOT (!) */
e->Nleft = eval(e->Nleft);
if (eisconst(e->Nleft)) /* Is operand a constant? */
return setlog(e, !evalbool(e->Nleft)); /* Yes, win */
break; /* Return e */
case TKTY_TERNARY: /* Just one op, Q_QUERY (?:) */
e->Nleft = eval(e->Nleft);
if (eisconst(e->Nleft)) /* Is operand a constant? */
return (evalbool(e->Nleft) /* Yes, return proper branch */
? eval(e->Nright->Nleft)
: eval(e->Nright->Nright));
/* Conditional isn't a constant, so fold both results */
e->Nright->Nleft = eval(e->Nright->Nleft);
e->Nright->Nright = eval(e->Nright->Nright);
break; /* Return e */
default:
int_warn("eval: bad expr op %N", e);
break; /* Return e anyway */
}
return e; /* This returns NULL if e was null */
}
/* EVALFUN - Evaluate function call.
*/
static NODE *
evalfun(NODE *e)
{
NODE *list;
e->Nleft = eval(e->Nleft); /* First do function addr */
for (list = e->Nright; list; list = list->Nleft) {
if (list->Nop == N_EXPRLIST)
list->Nright = eval(list->Nright);
else {
int_warn("evalfun: bad arglist %N", list);
break;
}
}
return e;
}
/* EVALBOOL - find boolean truth value of node.
** Only scalar constants are acceptable. (float, int, pointer, enum)
** +1 - TRUE
** 0 - FALSE
** -1 - not a scalar constant
*/
static int
evalbool(NODE *e)
{
switch (e->Nop) {
case N_PCONST: /* Pointer constant same as int constant */
case N_ICONST: return (e->Niconst ? 1 : 0);
case N_FCONST: return (e->Nfconst ? 1 : 0);
}
return -1;
}
/* EVALLOG - Evaluate a logical binary expression, either && or ||.
*/
static NODE *
evallog(NODE *e)
{
NODE *etmp;
int torf;
if ((torf = evalbool(e->Nleft)) < 0)
return e; /* 1st operand not constant. */
if (e->Nop == Q_LAND) { /* For logical AND, */
if (torf == 0) /* if 1st operand false, */
return setlog(e, 0); /* entire result is false. */
} else { /* For logical OR, */
if (torf != 0) /* if 1st operand true, */
return setlog(e, 1); /* entire result is true. */
}
e->Nright = eval(e->Nright); /* No joy; must now eval right */
e->Nop = Q_NEQ; /* Transform 1&&e or 0||e */
e->Nleft->Ntype = inttype; /* into (0 != e) */
setlog(e->Nleft, 0);
etmp = e->Nleft; /* Swap to make (e != 0) */
e->Nleft = e->Nright;
e->Nright = etmp;
e = convnullcomb(convbinary(e));
e->Ntype = inttype; /* Result of != is int */
if (eisconst(e->Nleft)) /* If both sides now constant, */
return evalbinop(e); /* evaluate (e != 0) */
return e;
}
/* EVALOP - evaluate operators of type TKTY_BINOP and TKTY_BOOLOP
** with the exception of the logical operators (&& and ||).
** All operands are known to be constants; "good" constants
** are N_ICONST, N_PCONST, N_FCONST.
**
** Relational/Equality ops:
** == != < > <= >=
** Arithmetic & Bitwise ops:
** * / % + - << >> & ^ |
**
** Note that the comparison ops can have scalar arguments,
** as can the additive (+ -).
** Also, four ops (+ - << >>) can have operands which differ in type; this
** needs to be checked for.
*/
#if SYS_CSI /* FEW 2A(40) 29-Jul-92 */
static int
chkovf(int unsign, int subtra) /* check for arithmetic overflow */
#else
static int
chkovf(void) /* check for arithmetic overflow */
#endif
{
#ifdef __COMPILER_KCC__
asm("\tSETO 1,\n"); /* PREPARE TO RETURN TRUE (OVERFLOW) */
#if SYS_CSI /* FEW 2A(40) 29-Jul-92 */
if (unsign)
{
asm ("\tJCRY0 .+2\n"); /* for unsigned overflow, check CARRY0 */
if (subtra)
asm ("\tPOPJ 17,\n"); /* unsigned subtraction: true if clear */
}
else
asm("\tJFCL 11, .+2\n"); /* JUMP IF OVERFLOW (AND CLEAR FLAG) */
#else
asm("\tJFCL 11, .+2\n"); /* JUMP IF OVERFLOW (AND CLEAR FLAG) */
#endif
asm("\tSETZ 1,\n"); /* IF NO OVERFLOW, RETURN FALSE */
#else
return 0; /* assumes no overflow */
#endif
}
static NODE *
evalbinop(NODE *e)
{
long l, l2;
unsigned long ul, ul2;
float f, f2;
double d, d2;
int log = -1;
NODE *eleft = e->Nleft;
#if SYS_CSI /* FEW 2A(40) 29-Jul-92 */
int unsign = 0, subtra = 0;
#endif
#if SYS_CSI /* FEW 2A(40) 29-Jul-92 */
chkovf(unsign, subtra); /* clear machine overflow flags */
#else
chkovf(); /* clear machine overflow flags */
#endif
switch (eleft->Ntype->Tspec) {
case TS_INT:
case TS_LONG:
l = eleft->Niconst;
l2 = e->Nright->Niconst;
switch (e->Nop) {
case Q_PLUS: if (eleft->Ntype != e->Nright->Ntype)
return e; /* Give up if int+ptr */
l += l2; break;
case Q_MINUS: l -= l2; break; /* l integ, so l2 is integ */
case Q_MPLY: l *= l2; break;
case Q_DIV: if (l2 == 0)
error("divide by zero");
else
l /= l2;
break;
case Q_MOD: if (l2 == 0)
error("divide by zero");
else
l %= l2;
break;
case Q_ANDT: l &= l2; break;
case Q_OR: l |= l2; break;
case Q_XORT: l ^= l2; break;
case Q_LSHFT: l <<= l2; break;
case Q_RSHFT: l >>= l2; break;
case Q_EQUAL: log = l == l2; break;
case Q_NEQ: log = l != l2; break;
case Q_LESS: log = l < l2; break;
case Q_GREAT: log = l > l2; break;
case Q_LEQ: log = l <= l2; break;
case Q_GEQ: log = l >= l2; break;
default:
return e;
}
if (log >= 0) return setlog(e, log);
eleft->Niconst = l;
break;
case TS_UINT:
case TS_ULONG:
ul = eleft->Niconst;
ul2 = e->Nright->Niconst;
switch (e->Nop) {
case Q_PLUS:
#if SYS_CSI /* FEW 2A(40) 29-Jul-92 */
unsign = 1;
#endif
if (eleft->Ntype != e->Nright->Ntype)
return e; /* Give up if int+ptr */
ul += ul2; break;
case Q_MINUS:
#if SYS_CSI /* FEW 2A(40) 29-Jul-92 */
unsign = 1;
subtra = 1;
#endif
ul -= ul2; break; /* ul integ, so ul2 is integ */
case Q_MPLY: ul *= ul2; break;
case Q_DIV: if (ul2 == 0)
error("divide by zero");
else
ul /= ul2;
break;
case Q_MOD: if (ul2 == 0)
error("divide by zero");
else
ul %= ul2;
break;
case Q_ANDT: ul &= ul2; break;
case Q_OR: ul |= ul2; break;
case Q_XORT: ul ^= ul2; break;
/* For shifts, don't apply convs to right operand! */
case Q_LSHFT: ul <<= e->Nright->Niconst; break;
case Q_RSHFT: ul >>= e->Nright->Niconst; break;
case Q_EQUAL: log = ul == ul2; break;
case Q_NEQ: log = ul != ul2; break;
case Q_LESS: log = ul < ul2; break;
case Q_GREAT: log = ul > ul2; break;
case Q_LEQ: log = ul <= ul2; break;
case Q_GEQ: log = ul >= ul2; break;
default:
return e;
}
if (log >= 0) return setlog(e, log);
eleft->Niconst = ul;
break;
case TS_FLOAT:
f = (float) eleft->Nfconst; // FW KCC-NT
f2 = (float) e->Nright->Nfconst; // FW KCC-NT
switch (e->Nop) {
case Q_PLUS: f += f2; break;
case Q_MINUS: f -= f2; break;
case Q_MPLY: f *= f2; break;
case Q_DIV: if (f2 == 0)
error("divide by zero");
else
f /= f2;
break;
case Q_EQUAL: log = f == f2; break;
case Q_NEQ: log = f != f2; break;
case Q_LESS: log = f < f2; break;
case Q_GREAT: log = f > f2; break;
case Q_LEQ: log = f <= f2; break;
case Q_GEQ: log = f >= f2; break;
default:
return e;
}
if (log >= 0) return setlog(e, log);
eleft->Nfconst = f;
break;
case TS_DOUBLE:
case TS_LNGDBL:
d = eleft->Nfconst;
d2 = e->Nright->Nfconst;
switch (e->Nop) {
case Q_PLUS: d += d2; break;
case Q_MINUS: d -= d2; break;
case Q_MPLY: d *= d2; break;
case Q_DIV: if (d2 == 0)
error("divide by zero");
else
d /= d2;
break;
case Q_EQUAL: log = d == d2; break;
case Q_NEQ: log = d != d2; break;
case Q_LESS: log = d < d2; break;
case Q_GREAT: log = d > d2; break;
case Q_LEQ: log = d <= d2; break;
case Q_GEQ: log = d >= d2; break;
default:
return e;
}
if (log >= 0) return setlog(e, log);
eleft->Nfconst = d;
break;
case TS_PTR:
/* Operations on constant pointers are tricky because the result
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -