Google
 

Trailing-Edge - PDP-10 Archives - SRI_NIC_PERM_FS_1_19910112 - c/kcc/cceval.c
There are 8 other files named cceval.c in the archive. Click here to see a list.
/*	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 *evaldiscard();
int sideffp();			/* Used here and CCGEN2 */
int istrue();			/* CCGEN1's gfor() */

/* Imported functions */
extern NODE *ndef();		/* CCSTMT */
extern NODE *convbinary(), *convnullcomb();	/* CCTYPE */
extern int binexp();		/* CCOUT */
extern int hash();		/* CCSYM */

/* Internal functions */
static NODE *copyflag(), *setlog(), *evalcast();
static int ecanon();
static NODE *eseqdisc();
static long value();
static NODE *lookup();
/* 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)
static NODE *eval();
static NODE *evalunop(), *evalbinop(), *evalb1op(), *evallog(), *evalfun();
static NODE *evalasop();
static int evalbool();

/* 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(e)
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)))) /* 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);
	e->Nright = eval(e->Nright);
	if (e->Nop == Q_LAND || e->Nop == Q_LOR) {	/* Logical operator? */
	    if (eisconst(e->Nleft))
		return evallog(e);
	} else {
	    /* 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(e)
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(e)
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(e)
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->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.
*/
static NODE *
evalbinop(e)
NODE *e;
{
    long	l, l2;
    unsigned long ul, ul2;
    float	f, f2;
    double	d, d2;
    int log = -1;
    NODE *eleft = e->Nleft;

    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:	l /= l2;	break;
	case Q_MOD:	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;
	return copyflag(eleft, e);

    case TS_UINT:
    case TS_ULONG:
	ul = eleft->Niconst;
	ul2 = e->Nright->Niconst;
	switch (e->Nop) {
	case Q_PLUS:	if (eleft->Ntype != e->Nright->Ntype)
			    return e;		/* Give up if int+ptr */
			ul += ul2;	break;
	case Q_MINUS:	ul -= ul2;	break;	/* ul integ, so ul2 is integ */
	case Q_MPLY:	ul *= ul2;	break;
	case Q_DIV:	ul /= ul2;	break;
	case Q_MOD:	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;
	return copyflag(eleft, e);

    case TS_FLOAT:
	f = eleft->Nfconst;
	f2 = e->Nright->Nfconst;
	switch (e->Nop) {
	case Q_PLUS:	f += f2;	break;
	case Q_MINUS:	f -= f2;	break;
	case Q_MPLY:	f *= f2;	break;
	case Q_DIV:	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;
	return copyflag(eleft, e);

    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:	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;
	return copyflag(eleft, e);

    case TS_PTR:
	/* Operations on constant pointers are tricky because the result
	** can depend on the runtime environment (whether running
	** with extended addressing or not, using OWGBPs or not).  However,
	** compile-time comparisons can be done in a way that
	** duplicates the machine-independent code generated for
	** runtime comparisons.
	*/
	ul = eleft->Niconst;
	ul2 = e->Nright->Niconst;
	l  = (ul  << 6) | (ul  >> (TGSIZ_WORD-6));	/* Set up L and L2 */
	l2 = (ul2 << 6) | (ul2 >> (TGSIZ_WORD-6));	/* for comparisons */
	if (l  & 040) l  ^= 077;
	if (l2 & 040) l2 ^= 077;
	switch (e->Nop) {
	case Q_PLUS:			/* Give up if pointer arith */
	case Q_MINUS:
	default:
	    return e;

	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;
	}
	return setlog(e, log);

    default:
	int_warn("evalbinop: bad type %N", e);
    }
    return e;
}
/* EVALUNOP - Evaluate operators of type TKTY_UNOP (unary operators),
**	where operand is a constant.
**	Unary ops:
**		(cast) ~ -
*/
static NODE *
evalunop(e)
NODE *e;
{
    switch (e->Nop) {
    case N_CAST:
	return evalcast(e);	/* Handles all types */

    case Q_COMPL:
	switch (e->Ntype->Tspec) {
	case TS_INT:
	case TS_LONG:
		e->Nleft->Niconst = ~ e->Nleft->Niconst;
		break;
	case TS_UINT:
	case TS_ULONG:
		e->Nleft->Niconst = ~ (unsigned long)e->Nleft->Niconst;
		break;
	default:
	    int_warn("evalunop: bad ~ %N", e);
	}
	return copyflag(e->Nleft, e);

    case N_NEG:
	switch (e->Ntype->Tspec) {
	case TS_INT:
	case TS_LONG:
		e->Nleft->Niconst = - e->Nleft->Niconst;
		break;
	case TS_UINT:
	case TS_ULONG:
		e->Nleft->Niconst = - (unsigned long)e->Nleft->Niconst;
		break;
	case TS_FLOAT:
		e->Nleft->Nfconst = - (float) e->Nleft->Nfconst;
		break;
	case TS_DOUBLE:
	case TS_LNGDBL:
		e->Nleft->Nfconst = - (double) e->Nleft->Nfconst;
		break;
	default:
	    int_warn("evalunop: bad - %N", e);
	    return e;
	}
	return copyflag(e->Nleft, e);

    case N_PTR:
	break;			/* Don't try to hack pointer constants! */
    default:
	int_warn("evalunop: bad op %N", e);
    }
    return e;
}
/* EVALB1OP - Handle binary ops where one operand isn't a constant.
**	The operator will be one of * / % + - << >> & | ^.
**	Ops which are commutative & associative (+ * & | ^) can still be folded
**	with constants farther along.
**	The bitwise ops (&^|) are always OK for unsigned ops, and OK
**	for signed if twos-complement representation is used. (True for KCC)
**	It is only OK for signed/float + and * if there is no overflow.
**		This is hard to be sure of, though.
** WARNING: non-constant operands with side effects, like "volatile",
** require special handling!
** This is why anything that reduces to a constant value calls eseqdisc()
** to turn the expression into a comma-expr if the non-constant part has
** side effects.
**
** Special cases:
**		e = any valid type (pointer, float, integral)
**		fi = float or integral
**		i = integral type (signed or unsigned)
**		ui = unsigned integral
**		si = signed integral
** Zero		   One		~Zero (-1)		Power-of-2
** fi*0 => (fi,0)  fi*1 => fi	fi*(-1) => -fi		ui*log2(n) => ui<<n
** 0/fi => (fi,0)  fi/1 => fi	fi/(-1) => -fi		ui/log2(n) => ui>>n
** 0%i  => (i,0)   i%1  =>(i,0)	si%(-1) => (si,0)	ui%log2(n) => ui&(n-1)
** fi/0	fi Warning
** i%0	(i,0) Warning
** e+0  => e
** e-0  => e
** 0-fi  => -fi
** 0<<i => (i,0)
** 0>>i => (i,0)
** i<<0 => i
** i>>0 => i
** i&0  => (i,0)		i&-1	=> i
** i|0  => i			i|-1	=> (i,-1)
** i^0  => i			i^-1	=> ~i
*/	
#define ekeepright(e) eseqdisc((e)->Nleft, (e)->Nright)
#define ekeepleft(e)  eseqdisc((e)->Nright, (e)->Nleft)

static NODE *
evalb1op(e)
NODE *e;
{
    NODE *eleft, *elast, *er, *elr;
    int assocf = 0;

    /* First see whether operator is commutative & associative,
    ** and if so we make sure the constant is on the right.  This both
    ** reduces complexity of the rest of the code and helps optimize the
    ** code generation as many PDP10 operators can take immediate
    ** constant values, which CCGEN2 tends to generate because it does the
    ** left-hand node first.
    */
    if (e->Nop == Q_PLUS || e->Nop == Q_MPLY
	  || e->Nop == Q_OR || e->Nop == Q_ANDT || e->Nop == Q_XORT) {
	assocf = 1;
	if (!eisconst(e->Nright)) {	/* Commute to get constant on right */
	    NODE *t;
	    t = e->Nleft;
	    e->Nleft = e->Nright;
	    e->Nright = t;
	}
    }

    /* At this point all we know is that the operator is a binary op and
    ** one of its operands is a constant.  If it is not an associative op, all
    ** we can do is check for special cases (see comments at start of page).
    */
#if NEW > 1
    if (tisinteg(e->Ntype)) {		/* Check integral types */
	long icon;			/* Get value of the constant */
	icon = (e->Nleft->Nop == N_ICONST
			? e->Nleft->Niconst : e->Nright->Niconst);
	if (icon >= -1 && icon <= 1)	/* Test for winning constant vals */
	switch (e->Nop) {
	case Q_MPLY:			/* Cuz commuted, const on right */
		if (icon == 0)
		    return ekeepright(e);	/* i*0 => (i,0) */
		if (icon == 1)
		    return e->Nleft;	/* i*1 => i */
		e->Nop = N_NEG;		/* i*(-1) => -i */
		return e;
		break;
	case Q_DIV:
		if (icon == 0) {
		    if (eisiconst(e->Nleft))
			return ekeepleft(e);	/* 0/i => (i,0)*/
		    advise("Division by zero ignored");
		    return e->Nleft;		/* i/0 => i */
		}
		if (eisiconst(e->Nright)) {
		    if (icon == 1)
			return e->Nleft;	/* i/1 => i */
		    if (tissigned(e->Ntype)) {
			e->Nop = N_NEG;		/* si/(-1) => -si */
			return e;
		    } else break;		/* Cannot do ui/(-1) */
		}
		break;
	case Q_MOD:
		if (icon == 0) {
		    if (eisiconst(e->Nright))
			advise("Division by zero");
		    return ekeepleft(e);	/* 0%i => (i,0)*/
		    				/* i%0 => (i,0) */
		}
		if (!eisiconst(e->Nright))	/* Ensure icon on right */
		    break;
		if (icon == 1 || tissigned(e->Ntype)) {
						/* i%1    => (i,0) */
		    e->Nright->Niconst = 0;	/* si%(-1) => (si,0) */
		    return ekeepright(e);
		}				/* Note ui%(-1) is variable! */
		break;
	case Q_PLUS:
		if (icon == 0)		/* Cuz commuted, const on rt */
		    return e->Nleft;	/* e+0 => e */
		break;
	case Q_MINUS:
		if (icon == 0 && e->Nleft->Nop==N_ICONST) {
		    e->Nop = N_NEG;	/* 0-e => -e */
		    e->Nleft = e->Nright;
		    return e;
		}
		break;
	case Q_LSHFT:			/* << same as >> here */
	case Q_RSHFT:			/* If either operand 0, */
		if (icon == 0)		/* always return left val! */
		    return ekeepleft(e);
		break;			/* Cuz 0<<i is (i,0), i<<0 is (0,i) */
	case Q_ANDT:
		if (icon == 0)		/* Cuz commuted, const on rt */
		    return ekeepright(e);	/* i&0 => (i,0) */
		if (icon == -1)
		    return e->Nleft;	/* i&(-1) => i */
		break;
	case Q_OR:
		if (icon == 0)		/* Cuz commuted, const on rt */
		    return e->Nleft;	/* i|0 => i */
		if (icon == -1)
		    return ekeepright(e);	/* i|(-1) => (i,-1)*/
		break;
	case Q_XORT:
		if (icon == 0)		/* Cuz commuted, const on rt */
		    return e->Nleft;	/* i^0 => i */
		if (icon == -1) {
		    e->Nop = Q_COMPL;	/* i^(-1) => ~i */
		    return e;
		}
		break;

	} else if (e->Nright->Nop == N_ICONST
		 && (icon&(icon-1))==0
		 && tisunsign(e->Ntype)) {
	    /* Last hope - if constant is right-hand and a power of 2,
	    ** (yes, that expression works as a test!) then
	    ** good optimizations are possible for unsigned *,/,%.
	    */
	    if (e->Nop == Q_MPLY) {	/* ui*log2(n) becomes ui<<n */
		e->Nop = Q_LSHFT;
		e->Nright->Niconst = binexp(icon);
		e->Nright->Ntype = inttype;	/* Be safe */
		return e;
	    } else if (e->Nop == Q_DIV) {	/* ui/log2(n) => ui>>n */
		e->Nop = Q_RSHFT;
		e->Nright->Niconst = binexp(icon);
		e->Nright->Ntype = inttype;
		return e;
	    } else if (e->Nop == Q_MOD) {	/* ui%log2(n) => ui&(n-1) */
		e->Nop = Q_ANDT;
		e->Nright->Niconst = icon-1;
		/* preserve prior type of constant */
		return e;
	    }
	}
    } else if (tisfloat(e->Ntype)) {	/* Check floating types */
	if (e->Ntype->Tspec != TS_LNGDBL)	/* Someday may be different */
	    switch (e->Nop) {
	    case Q_DIV:
		if (eisconst(e->Nleft) && e->Nleft->Nfconst == 0.0)
		    return ekeepleft(e);	/* 0/f => (f,0) */

		/* Drop thru to check for f/0, f/1 and f/(-1) */
	    case Q_MPLY:
		if (!eisconst(e->Nright))
		    break;
		if (e->Nright->Nfconst == 0.0) {
		    if (e->Nop == Q_DIV) {
			advise("Division by zero ignored");
			return e->Nleft;
		    }
		    return ekeepright(e);	/* f*0 => (f,0) */
		}
		if (e->Nright->Nfconst == 1.0)
		    return e->Nleft;		/* f*1 or f/1 => f */
		if (e->Nright->Nfconst == -1.0) {
		    e->Nop = N_NEG;		/* f*(-1) or f/(-1) => -f */
		    return e;
		}
		break;
	    case Q_PLUS:
		if (e->Nright->Nfconst == 0.0)
		    return e->Nleft;		/* f+0 => f */
		break;
	    case Q_MINUS:
		if (eisconst(e->Nleft) && e->Nleft->Nfconst == 0.0) {
		    e->Nop = N_NEG;
		    e->Nleft = e->Nright;
		    return e;			/* 0-f => -f */
		}
		break;
	    }
    } else {				/* Assume pointer, check + and - */
	if (eisiconst(e->Nright)	/* 0 will be on right if at all */
	  && e->Nright->Niconst == 0) {	/* (because 0+e already commuted) */
	    /* Won, e+0 or e-0 !!  But beware of funny array type conversion
	    ** sometimes applied to plus/minus pointer arithmetic; result type
	    ** may not be same as operand type.  Keep result type.
	    */
	    e->Nleft->Ntype = e->Ntype;	/* Propagate result type */
	    return e->Nleft;
	}
    }
#endif


    /* See if we can do anything associative.
    ** Move down the left-hand side of expression as long as the operator
    ** is identical to current one, and check for constant on right-hand side.
    ** If one is found, merge it in to existing constant and flush that
    ** operator instance.
    */
    if (!assocf) return e;		/* If op not assoc, that's all */
    elast = e;
    eleft = e->Nleft;
    for (; eleft->Nop == e->Nop; elast = eleft, eleft = eleft->Nleft)
	if (eisconst(eleft->Nright)) {
	    assocf = 0;			/* Zap back */
	    break;
	}
    if (assocf) return e;		/* No further ops */

    /* Win, eleft->Nright and e->Nright can be merged!
    ** do the op on them, replace e->Nright with the new result, and
    ** replace eleft's op with just eleft.  eleft's parent is elast.
    */
    er = e->Nright;
    elr = eleft->Nright;
    switch (e->Ntype->Tspec) {
    case TS_UINT:
    case TS_ULONG:
	switch (e->Nop) {
	case Q_PLUS:	er->Niconst += (unsigned long) elr->Niconst;	break;
	case Q_MPLY:	er->Niconst *= (unsigned long) elr->Niconst;	break;
	case Q_ANDT:	er->Niconst &= (unsigned long) elr->Niconst;	break;
	case Q_OR:	er->Niconst |= (unsigned long) elr->Niconst;	break;
	case Q_XORT:	er->Niconst ^= (unsigned long) elr->Niconst;	break;
	default:	return e;
	}
	break;
    case TS_INT:
    case TS_LONG:
	switch (e->Nop) {
	case Q_PLUS:	er->Niconst += (long) elr->Niconst;	break;
	case Q_MPLY:	er->Niconst *= (long) elr->Niconst;	break;
	case Q_ANDT:	er->Niconst &= (long) elr->Niconst;	break;
	case Q_OR:	er->Niconst |= (long) elr->Niconst;	break;
	case Q_XORT:	er->Niconst ^= (long) elr->Niconst;	break;
	default:	return e;
	}
	break;
    case TS_FLOAT:
	switch (e->Nop) {
	case Q_PLUS:	er->Nfconst += (float) elr->Nfconst;	break;
	case Q_MPLY:	er->Nfconst *= (float) elr->Nfconst;	break;
	default:	return e;
	}
	break;
    case TS_DOUBLE:
    case TS_LNGDBL:
	switch (e->Nop) {
	case Q_PLUS:	er->Nfconst += (double) elr->Nfconst;	break;
	case Q_MPLY:	er->Nfconst *= (double) elr->Nfconst;	break;
	default:	return e;
	}
	break;

    default:
	return e;		/* Can't do operation?? Barf and return. */
    }
    /* Now take out the eleft op */
    elast->Nleft = eleft->Nleft;
    return e;
}
/* EVALASOP - Handle assignment ops where right-hand operand is a constant.
**	The operator will be one of = *= /= %= += -= <<= >>= &= |= ^=.
**	Assignment ops always have side effects and so cannot be eliminated
**	completely -- normally.  But when the right-hand operand is a
**	constant (notably 0 or 1), some improvement is sometimes possible.
**
**	The special cases and cautions here are similar to those for
**	EVALB1OP above.  If the left-hand operand has type "volatile"
**	then improvement is never attempted because it might not result
**	in the behavior the user wanted.
**
** Special cases:
**		e = any valid type (pointer, float, integral)
**		fi = float or integral
**		i = integral type (signed or unsigned)
**		ui = unsigned integral
**		si = signed integral
Zero		One		~Zero (-1)	     Power-of-2
fi*=0 => fi=0	fi*=1	=> fi	fi*=(-1) => fi= -fi  ui*=log2(n) => ui<<=n
fi/=0	fi Warn	fi/=1	=> fi	fi/=(-1) => fi= -fi  ui/=log2(n) => ui>>=n
i%=0   i=0 Warn	i%=1	=> i=0	si%=(-1) => si=0     ui%=log2(n) => ui&=(n-1)
e+=0  => e	e+=1 => ++e	e+=-1 => --e
e-=0  => e	e-=1 => --e	e-=-1 => ++e
i<<=0 => i
i>>=0 => i
i&=0  => i=0			i&=-1	=> i
i|=0  => i			i|=-1	=> i= -1
i^=0  => i			i^=-1	=> i= ~i

Note: for now, we don't actually optimize anything into the two expressions
	fi = -fi	and	i = ~i
because reshaping the expression can only be done if the lefthand
expression has no side effects, and it becomes complicated to copy
the lefthand side into the right-hand side.  Merely copying the pointer
would be prone to lossage since none of the evaluation/generation code
was written with the possibility of shared "pure" nodes in mind.
*/
static NODE *evalincdec();

static NODE *
evalasop(e)
NODE *e;
{

#if NEW > 2
    if (tisvolatile(e->Nleft->Ntype))	/* If volatile dest expr, */
	return e;			/* leave whole thing alone, sigh. */

    if (tisinteg(e->Ntype)) {		/* Check integral types */
	long icon;			/* Get value of the constant */
	if (e->Nright->Nop != N_ICONST)
	    return e;			/* May be an error */
	icon = e->Nright->Niconst;
	if (icon >= -1 && icon <= 1)	/* Test for winning constant vals */
	switch (e->Nop) {
	case Q_ASMPLY:			/* Check integral *= */
		if (icon == 0)
		    e->Nop = Q_ASGN;	/* i*=0 => i=0 */
		else if (icon == 1)
		    return e->Nleft;	/* i*=1 => i */
#if 0	/* -1 case too painful for now (see note at beg of page) */
		else ;			/* i*=(-1) => i=-i (IF NO SIDEFFS) */
#endif
		break;

	case Q_ASDIV:			/* Check integral /= */
		if (icon == 0) {
		    advise("Division by zero ignored"); /* i/=0 => i */
		    return e->Nleft;
		} else if (icon == 1)
		    return e->Nleft;	/* i/=1 => i */
#if 0	/* -1 case too painful for now (see note at beg of page) */
		else if (tissigned(e->Ntype))
		    ;			/* si/=(-1) => si=-si (IF NO SIDEFFS)*/
#endif
		break;			/*	(cannot do ui/(-1)) */

	case Q_ASMOD:			/* Check integral %= */
		if (!icon || icon == 1 || tissigned(e->Ntype)) {
		    if (icon == 0)		/* i%=0 => i=0 */
			advise("Division by zero");
		    e->Nright->Niconst = 0;	/* i%=1    => i=0   */
		    e->Nop = Q_ASGN;		/* si%=(-1) => si=0 */
		}				/* Note ui%(-1) is variable! */
		break;

	case Q_ASPLUS:			/* Check integral += */
		if (icon == 0)
		    return e->Nleft;	/* e+=0 => e */
		return evalincdec(e,
			(icon < 0)
			    ? N_PREDEC		/* e+=(-1) => --e */
			    : N_PREINC);	/* e-=1    => ++e */

	case Q_ASMINUS:			/* Check integral -= */
		if (icon == 0)
		    return e->Nleft;	/* e-=0 => e */
		return evalincdec(e,
			(icon < 0)
			    ? N_PREINC		/* e-=(-1) => ++e */
			    : N_PREDEC);	/* e-=1    => --e */

	case Q_ASLSH:			/* Check integral <<= and >>= */
	case Q_ASRSH:
		if (icon == 0)
		    return e->Nleft;	/* i<<=0 => i */
		break;
	case Q_ASAND:			/* Check integral &= */
		if (icon == 0)
		    e->Nop = Q_ASGN;	/* i&=0 => i=0 */
		else if (icon < 0)
		    return e->Nleft;	/* i&=(-1) => i */
		break;
	case Q_ASOR:			/* Check integral |= */
		if (icon == 0)
		    return e->Nleft;	/* i|=0 => i */
		if (icon < 0)
		    e->Nop = Q_ASGN;	/* i|=(-1) => i=(-1) */
		break;
	case Q_ASXOR:
		if (icon == 0)		/* Check integral ^= */
		    return e->Nleft;	/* i^=0 => i */
#if 0	/* -1 case too painful for now (see note at beg of page) */
		if (icon < 0)
		    ;			/* i^=(-1) => i=~i */
#endif
		break;

	} else if ((icon&(icon-1))==0
		 && tisunsign(e->Ntype)) {
	    /* Last hope - if constant is right-hand and a power of 2,
	    ** (yes, that expression works as a test!) then
	    ** good optimizations are possible for unsigned *,/,%.
	    ** Don't do it for * because multiply-to-memory is easy,
	    ** and we have no shift-to-memory instruction.
	    ** But for division it's probably worthwhile.
	    */
#if 0 /* See above comment */
	    if (e->Nop == Q_ASMPLY) {	/* ui*=log2(n) becomes ui<<=n */
		e->Nop = Q_ASLSH;
		e->Nright->Niconst = binexp(icon);
		e->Nright->Ntype = inttype;	/* Be safe */
		return e;
	    }
#endif
	    if (e->Nop == Q_ASDIV) {	/* ui/=log2(n) => ui>>=n */
		e->Nop = Q_ASRSH;
		e->Nright->Niconst = binexp(icon);
		e->Nright->Ntype = inttype;
		return e;
	    }
	    if (e->Nop == Q_ASMOD) {	/* ui%=log2(n) => ui&=(n-1) */
		e->Nop = Q_ASAND;
		e->Nright->Niconst = icon-1;
		/* preserve prior type of constant */
		return e;
	    }
	}
    } else if (tisfloat(e->Ntype)) {	/* Check floating types */
	if (e->Ntype->Tspec != TS_LNGDBL) {	/* Someday may be different */
	    double fcon;
	    if (e->Nright->Nop != N_FCONST)
		return e;			/* Just in case */
	    fcon = e->Nright->Nfconst;
	    if (fcon == 0.0 || fcon == 1.0 || fcon == -1.0) switch (e->Nop) {
	    case Q_ASMPLY:
		if (fcon == 0.0) {
		    e->Nop = Q_ASGN;		/* f*=0 => f=0 */
		    break;
		}
		/* Drop thru to check for f/0, f/1 and f/(-1) */
	    case Q_ASDIV:
		if (fcon == 0.0) {		/* f/=0 => f */
		    advise("Division by zero ignored");
		    return e->Nleft;
		} else if (fcon > 0.0)
		    return e->Nleft;		/* f*=1 or f/=1 => f */
#if 0	/* -1 case too painful for now (see note at beg of page) */
		else ;				/* f*= -1 or f/= -1  => f=-f */
#endif
		break;
	    case Q_ASPLUS:
		if (fcon == 0.0)
		    return e->Nleft;		/* f+=0 => f */
		return evalincdec(e,
			(fcon > 0)
			    ? N_PREINC		/* f+=1    => ++f */
			    : N_PREDEC);	/* f+=(-1) => --f */
	    case Q_ASMINUS:
		if (fcon == 0.0)
		    return e->Nleft;		/* f-=0 => f */
		return evalincdec(e,
			(fcon > 0)
			    ? N_PREDEC		/* f-=1    => --f */
			    : N_PREINC);	/* f-=(-1) => ++f */
	    }
	}
    } else {	/* Not integral or float, must be pointer. */
	int icon;
	if (e->Nright->Nop == N_ICONST	/* Ensure integer const op */
	  && (icon = e->Nright->Niconst) >= -1
	  && icon <= 1
	  && (e->Nop == Q_ASPLUS || e->Nop == Q_ASMINUS)) {
	    if (icon == 0) {		/* ptr += 0  => ptr  */
		/* Beware of funny array type conversion
		** sometimes applied to plus/minus pointer arithmetic;
		** result type may not be same as operand type.
		** Keep result type!!
		*/
		e->Nleft->Ntype = e->Ntype;	/* Propagate result type */
		return e->Nleft;
	    }
	    return evalincdec(e,
	    	(e->Nop==Q_ASPLUS)
		? (icon > 0 ? N_PREINC : N_PREDEC)	/* p+=1 => ++p */
		: (icon > 0 ? N_PREDEC : N_PREINC));	/* p-=1 => --p */
	}
    }

#endif
    return e;
}

/* EVALINCDEC - Optimize assign op to inc/dec.  Auxiliary for EVALASOP above.
**	Used when about to change an assignment operator (type TKTY_ASOP)
** into an increment/decrement operator, to check for and flush any
** internal casts inserted by the parser.  Although the code generator
** for assignment knows how to deal with these internal casts, the inc/dec
** part doesn't.  The reason for removing them here instead of in CCGEN2's
** gincdec() is just so that if there is a problem, we can still recover
** by not doing the optimization.
** See the info in INTERN.DOC for more details about TKTY_ASOP nodes.
*/
static NODE *
evalincdec(e, op)
NODE *e;		/* Expression node being optimized */
int op;			/* Desired inc/dec op */
{
    if (e->Nleft->Nop == N_CAST) {
	/* Internal cast, must remove it.  Do check to make sure it's
	** really a valid cast (paranoia dept).  Type of operand being
	** cast must be exactly the same as the asop result type.
	*/
	if (e->Ntype != e->Nleft->Nleft->Ntype) {
	    int_warn("evalincdec: bad cast");
	    return e;			/* Don't do it */
	}
	e->Nleft = e->Nleft->Nleft;	/* Snip out the cast */
    }
    e->Nop = op;
    return e;
}
/*
** Copy flags and such across from old top node
** Used when op has been folded out but we still want to keep its info
** WARNING!  This may not work if Nflag is really being used as
** something else.  See the union definition for NODE in cc.h.
*/

static NODE *
copyflag (new, old)
NODE *new, *old;
{
    new->Ntype = old->Ntype;
    new->Nflag = old->Nflag;
    return new;
}

/* SETLOG - Sets and returns a logical operator result.
**	Node given as arg must be that of the operator (not an operand).
*/
static NODE *
setlog(e, res)
NODE *e;		/* Operator node */
int res;		/* Result (either 0 or 1) */
{
    if (e->Ntype != inttype) {
	int_error("setlog: bad type");
	e->Ntype = inttype;
    }
    e->Nop = N_ICONST;
    e->Niconst = res;
    return e;
}
/* EVALCAST - called to evaluate a constant cast expression
**	Arg is a N_CAST node; operand is one of N_ICONST, N_PCONST, N_FCONST.
** To minimize the number of conversion combinations, values are always stored
** in the constant node using the largest possible type.
** For integers this is "long":
**	If the type is unsigned, unused bits in Niconst will be 0.
**	If signed, then sign extension has been done in Niconst.
** For floating-point this is "double":
**	If the type is float, precision will have been truncated (2nd word 0).
**
*/
static long tolong();

static NODE *
evalcast(e)
NODE *e;
{
    NODE *cn = e->Nleft;	/* Get pointer to constant node */
    TYPE *tfrom = cn->Ntype;	/* Converting from this typespec */
    TYPE *tto = e->Ntype;	/* to this one */

    switch (e->Ncast) {
	case CAST_ILL:		/* Illegal cast from error? */
	    return e;		/* Should already have complained */

	case CAST_NONE:		/* no actual conversion needed */
	    break;		/* Just copy flags and set new type */

	case CAST_VOID:		/* Any type to void type (discard constant) */
	    cn->Nop = N_VCONST;	/* Change node op to special value */
	    break;

	case CAST_IT_IT:	/* Integer type to integer type */
	    cn->Niconst = tolong(tto, cn->Niconst);
	    break;
	case CAST_FP_IT:	/* Floating-point type to integer type */
	    cn->Niconst = tolong(tto, (long) cn->Nfconst);
	    cn->Nop = N_ICONST;
	    break;
	case CAST_EN_IT:	/* Enumeration type to integer type */
	case CAST_PT_IT:	/* Pointer type to integer type */
	    cn->Niconst = tolong(tto, (long) cn->Niconst);
	    cn->Nop = N_ICONST;
	    break;

	case CAST_FP_FP:	/* Floating-point type to floating-pt type */
	    switch (castidx(tfrom->Tspec, tto->Tspec)) {
		case castidx(TS_DOUBLE,TS_FLOAT):
		case castidx(TS_LNGDBL,TS_FLOAT):
		    cn->Nfconst = (float) cn->Nfconst;
		    break;
		case castidx(TS_FLOAT,TS_DOUBLE):
		case castidx(TS_FLOAT,TS_LNGDBL):
		    cn->Nfconst = (double)(float) cn->Nfconst;
		    break;
		case castidx(TS_LNGDBL,TS_DOUBLE):
		case castidx(TS_DOUBLE,TS_LNGDBL):
		    break;
		default:
		    int_warn("evalcast: bad fp_fp");
		    break;
	    }
	    break;

	case CAST_IT_FP:	/* Integer type to floating-point type */
	    switch (tto->Tspec) {
		case TS_FLOAT:
		    if (tissigned(tfrom))
			cn->Nfconst = (float) cn->Niconst;
		    else
			cn->Nfconst = (float) (unsigned long) cn->Niconst;
		    break;
		case TS_DOUBLE:
		case TS_LNGDBL:
		    if (tissigned(tfrom))
			cn->Nfconst = (double) cn->Niconst;
		    else
			cn->Nfconst = (double) (unsigned long) cn->Niconst;
		    break;
	    }
	    cn->Nop = N_FCONST;
	    break;

	case CAST_EN_EN:	/* Enumeration type to enumeration type */
	case CAST_IT_EN:	/* Integer type to enumeration type */
	    break;

	/* The only pointer-pointer conversions we can guarantee are
	** those from byte ptrs to word ptrs.  Going the other way
	** depends on the type and the KCC runtime section and will
	** produce different results, so we have to do it at run time.
	*/
	case CAST_PT_PT:	/* Pointer type to pointer type */
	    if (tisbytepointer(tfrom) && !tisbytepointer(tto))
		    cn->Niconst = (long)(int *)(char *)(cn->Niconst);
	    else return e;
	    break;

	case CAST_IT_PT:	/* Integer type to pointer type */
	    cn->Nop = N_PCONST;
	    break;

	default:
	    int_warn("evalcast: bad cast: %d", e->Ncast);
	    return e;
    }
    return copyflag(cn, e);
}

/* TOLONG - Convert a long integer value to some intermediate type and
**	then back to a long value.
**	Note special handling for chars, which KCC allows to
**	have varied byte sizes.
*/
static long
tolong(t, val)
TYPE *t;
long val;
{
    switch(t->Tspec) {
	case TS_SHORT:	return (short) val;
	case TS_INT:	return (int) val;
	case TS_LONG:	return (long) val;
	case TS_USHORT:	return (unsigned short) val;
	case TS_UINT:	return (unsigned int) val;
	case TS_ULONG:	return (unsigned long) val;

	case TS_BITF:
	case TS_CHAR:  /* return (char) val;          */
	    if (val & (1 << (tbitsize(t)-1)))	/* If sign bit set */
		return val | (((long)-1) << tbitsize(t));
	    /* Else drop through to handle like unsigned */

	case TS_UBITF:
	case TS_UCHAR: /* return (unsigned char) val; */
	    return val & ((1<<tbitsize(t))-1);	/* Mask off */

	default:
	    int_error("tolong: bad type");
	    return 0;
    }
}

#if 0
static NODE *
setnode(new, old, nop)
NODE *new, *old;
int nop;
{
    new->Ntype = old->Ntype;
    new->Nflag = old->Nflag;
    new->Nop = nop;
    return new;
}
#endif
/* ---------------------------------------- */
/*      commute tree to canonical form      */
/* ---------------------------------------- */

#define HBAR	64			/* basic unit of complexity */

#define IDMASK	    (HBAR-1)		/* mask for quantum fluctuations */
#define	IDWEIGHT(s) (hash(s->Sname)&IDMASK) /* to permute for common subs */

#define	BWEIGHT	(4*HBAR)		/* weight for binary operator */
#define	IWEIGHT	0			/* weight for integer */
#define	SWEIGHT	(2*HBAR)		/* weight for string const */
#define	MWEIGHT	HBAR			/* weight for struct member */
#define CWEIGHT	(2*HBAR)		/* weight for coercion, unary */
#define	FWEIGHT	(32*HBAR)		/* weight for fun call - v expensive */
#define	QWEIGHT	(2*HBAR)		/* weight for ternary */

static int
ecanon(n)
NODE *n;
{
    NODE *t;
    int x, y;

    /*
    ** Return a weight for the tree, and commute subtrees.
    ** This function has two purposes:
    **   - To rearrange commutative expressions so that the more
    **     expensive operation is performed first, so that fewer
    **     registers need be allocated at once and so that moves
    **     from memory are more likely to be folded into the ops.
    **   - To permute equivalent expressions to the same canonical
    **     form, so that common subexpression elimination may be
    **     more likely to find them.
    */

    if (n) switch (n->Nop) {
    case N_ICONST:
	return IWEIGHT;
    case Q_IDENT:
	return IDWEIGHT(n->Nid)
		/* Hack so new array/funct conversion code
		** comes out looking the same as before --KLH
		*/
		+ (
		((n->Ntype != n->Nid->Stype) || n->Ntype->Tspec == TS_FUNCT)
			? CWEIGHT : 0);

    case N_SCONST:
	return SWEIGHT;
    case Q_DOT:
    case Q_MEMBER:
	return ecanon(n->Nleft) + MWEIGHT;
    case N_EXPRLIST:
	if (n->Nleft == NULL) return ecanon(n->Nright);
	return ecanon(n->Nleft) + ecanon(n->Nright);
    case N_CAST:
	return ecanon(n->Nleft) + CWEIGHT;
    case N_FNCALL:
	return (n->Nright == NULL) ? FWEIGHT : ecanon(n->Nright)+FWEIGHT;
    default:
	switch (tok[n->Nop].tktype) {
	case TKTY_BINOP:
	    x = ecanon(n->Nleft);
	    y = ecanon(n->Nright);
	    if (y > x && (n->Nop == Q_PLUS || n->Nop == Q_MPLY)) {
		t = n->Nleft;
		n->Nleft = n->Nright;
		n->Nright = t;
	    }
            return x + y + BWEIGHT;
	case TKTY_ASOP:
	case TKTY_BOOLOP:
	    return ecanon(n->Nleft) + ecanon(n->Nright) + BWEIGHT;
	case TKTY_UNOP:
	case TKTY_BOOLUN:
            return ecanon(n->Nleft)+CWEIGHT;
	case TKTY_TERNARY:
	    x = ecanon(n->Nright->Nleft);
	    y = ecanon(n->Nright->Nright);
	    if (y > x) x = y;
            return ecanon(n->Nleft) + x + QWEIGHT;
	default:
	    break;
	}
    }
    return 0;
}
#if 0	/* Comments about discarded values */

	There are three ways used internally within KCC to indicate
to the code generator that an expression's value is to be discarded:

(1) Explicit top-level cast operation, specified by user.
	Expressions starting with a N_CAST of CAST_VOID.
(2) Flag set in top-level node by evaldiscard().
	If the NF_DISCARD flag is set, the result of that expression
	is to be thrown away.
(3) Implied by statement context, as per [H&S2 7.13]:
	An expression statement.
	The first operand of a comma expression.
	The initialization and incrementation expressions in a "for" statement.

In practice more than one method is used; for example the parser
checks the statement context cases and ensures that NF_DISCARD is set
for those expressions.  The code generator does a similar
context-dependent setting as a backup.

#endif
/* EVALDISCARD - Used by CCSTMT parsing.
**	Sets flag for given expression to indicate value can be discarded,
** and propagates this flag downwards as far as possible,
** and attempts to reduce expression as much as possible, so as to
** avoid generating any code.
**
** Note: this function returns NULL if the expression was completely flushed.
**
**	A warning is printed only if two conditions hold:
** (1) the expression's type was not "void".  (If it was, then the programmer
**	has coded properly, perhaps with an explicit (void) cast.)
** (2) the top-level operator node is changed, OR the discard count has
**	been bumped.  If not then this top-level operator or its operands
**	had a side effect and thus was preserved.
**
** The sequential (,), conditional (?:), and binary logical (&&,||) operators
** are a little tricky; it is possible for these to have some of their
** sub-expressions discarded without affecting the top-level node.
** This is why the "discnt" variable exists, for an unambiguous indication
** that something was discarded.
**
** Yet another fuzzy situation exists for the cast operator, which can
** exist either due to an explicit user cast or an implicit type coercion.
** We have three choices when discarding a cast operator:
**	(1) always complain
**	(2) never complain
**	(3) complain if discarding an explicit cast; don't if discarding an
**		implicit cast.
** We implement (3) by using the NF_USERCAST flag to distinguish explicit
** from implicit casts, and only bumping the discard count for explicit
** casts.  Both are thrown away, however, which means that even an implicit
** cast will generate a warning if was the top-level node given to
** evaldiscard().  This should never happen, though, as there is no context
** in C for which a top-level implicit coercion is necessary.
*/
static NODE *edisc();	/* Auxiliary routine that does the work */
static int discnt;	/* Count of internal discards (see above comment) */

NODE *
evaldiscard(n)
NODE *n;
{
    NODE *dn;

    if (!n) return n;			/* Check for NULL */
    if (n->Ntype == voidtype)		/* If type is explicitly void, */
	return edisc(n);		/* never give warning message. */

    /* Must check for discards... */
    discnt = 0;			/* Reset internal flush count */
    dn = edisc(n);		/* Run it through, see what we get */
    if (dn != n || discnt)	/* If anything munged, then barf. */
	note("Discarding %s without side effects",
		    dn	? "operator"	/* If still stuff, assume just oper */
			: "expression");	/* else flushed whole expr */
    
    return dn;			/* Note this may or may not be NULL. */
}


/* ESEQDISC - Auxiliary for evalb1op and evalasop, to take care of
**	flushing the non-constant parts of an expression.
** The left and right args should be put into a "(l, r)" sequential
** expression, if the left arg has a side-effect that prevents it
** from being completely flushed.
**	edisc() is called directly to avoid provoking warning messages.
*/
static NODE *
eseqdisc(l, r)
NODE *l, *r;
{
    if (!(l = edisc(l)))	/* If can completely flush left arg, */
	return r;		/* just return right arg. */
    return ndef(N_EXPRLIST, r->Ntype, 0, l, r);
}
/* EDISC - Evaluate node, discarding anything that doesn't have
**	side effects.  This is very similar to sideffp() in CCEVAL!
**
** Note that exprs which can be lvalues must be checked for the "volatile"
** type qualifier, which counts as a side-effect.  See comments in sideffp().
*/
static NODE *
edisc(n)
NODE *n;
{
    NODE *ln, *rn;
    int savcnt;

    if (n == NULL) return n;
    n->Nflag |= NF_DISCARD;		/* Set flag for this node */

    switch(tok[n->Nop].tktype) {
	case TKTY_RWOP:		/* Reserved-word Op, similar to primary */
	    switch (n->Nop) {
		case Q_ASM:		/* asm() has side effects! */
		    return n;
	    }
	    return n;			/* Assume new ops have side effs too */

	case TKTY_PRIMARY:	/* Primary expression */
	    switch (n->Nop) {
		case N_FNCALL:			/* Funct call has side effs! */
		    return n;
		case Q_DOT:			/* May be lvalue, find out */
		    if (!(n->Nflag & NF_LVALUE))
			return edisc(n->Nleft);
		case Q_MEMBER:			/* Always an lvalue */
		    if (tisanyvolat(n->Ntype))
			return n;		/* Volatile, leave alone! */
		    ++discnt;
		    return edisc(n->Nleft);
		case Q_IDENT:
		    if (tisanyvolat(n->Ntype))
			return n;		/* Volatile, leave alone! */
		    break;
	    }
	    ++discnt;
	    return NULL;	/* All other primary exprs have no side-eff */

	case TKTY_UNOP:		/* Unary operator - check single operand */
	    switch (n->Nop) {
		case N_POSTINC: case N_PREINC:
		case N_POSTDEC: case N_PREDEC:
		    return n;		/* These four have side effects! */
		case N_CAST:		/* Cast is a bit special */
		    if ((n->Nflag & NF_USERCAST)==0)	/* If implicit cast, */
			return edisc(n->Nleft);		/* flush quietly, */
							/* without ++discnt! */
		    if (n->Ntype == voidtype) {	/* If explicit cast to void, */
			savcnt = discnt;	/* flush the rest quietly! */
			n = edisc(n->Nleft);
			discnt = savcnt;
			return n;
		    }
		    /* else drop thru to flush non-quietly! */
		    break;

		case N_PTR:			/* Always lvalue, so check */
		    if (tisvolatile(n->Ntype))	/* Check, note type will  */
			return n;		/* never be struct/union */
		    break;		/* so needn't use tisanyvolat */

		case N_ADDR:		/* Special checks to bypass qualifs */
		    switch (n->Nleft->Nop) {
			case Q_IDENT: ++discnt;	/* OK to just flush this */
				return NULL;
			case Q_DOT:
			case Q_MEMBER:
			case N_PTR:
			    ++discnt;
			    return edisc(n->Nleft->Nleft);
		    }
		    break;
	    }
	    /* Drop through to flush unary operator */

	case TKTY_BOOLUN:	/* Unary boolean operator (only '!') */
	    ++discnt;		/* Say something flushed */
	    return edisc(n->Nleft);

	case TKTY_BOOLOP:	/* Binary boolean or logical operator */
	    if (n->Nop == Q_LAND || n->Nop == Q_LOR) {
		/* Binary logical op, right-hand operand may or may not be
		** evaluated.  If it has no side effect we can flush it
		** and check the left-hand operand, but if it does then
		** we have to leave the left-hand operand alone.
		*/
		if (n->Nright = edisc(n->Nright))	/* Check 2nd val */
		    return n;			/* Can't flush it */
		++discnt;			/* Aha, note flushed and */
		return edisc(n->Nleft);		/* can now check 1st val */
	    }
	    /* Not a logical boolean operator, drop thru to treat
	    ** like binary operator
	    */
	case TKTY_BINOP:	/* Binary operator - check both operands */
	    ++discnt;			/* Will always flush something. */
	    ln = edisc(n->Nleft);
	    rn = edisc(n->Nright);
	    if (!rn) return ln;
	    if (!ln) return rn;
	    /* Still have both operands, set up to execute in sequence
	    ** by converting into a comma expression (ln,rn).
	    ** Do left first, then right, just for consistency.
	    ** Note flags already have NF_DISCARD set in them.
	    */
	    return ndef(N_EXPRLIST, rn->Ntype, rn->Nflag,
			ndef(N_EXPRLIST, ln->Ntype, ln->Nflag,
				(NODE *)NULL, ln),
			rn);

	case TKTY_ASOP:		/* Binary assignment operator */
	    break;		/* Always a side-effect operation */

	case TKTY_TERNARY:	/* Ternary operator (only '?') - check 3 ops */
	    /* First check each of the 2 possible values.
	    ** Note either may be replaced by NULL or have their types
	    ** changed so they no longer match the overall result type!
	    ** The gternary() code in CCGEN2 needs to be aware of this.
	    */
	    savcnt = discnt;			/* Remember count */
	    ln = edisc(n->Nright->Nleft);
	    rn = edisc(n->Nright->Nright);
	    if (!ln && !rn) {			/* If both values went away */
		++discnt;
		return edisc(n->Nleft);		/* Then hack condition only! */
	    }
	    /* In an attempt to avoid complaining about cases such
	    ** as (cond ? foo() : 0) which occur in macros, we check to
	    ** see whether a discard was due to the complete flushage of
	    ** a single primary value.  In this case one pointer will have been
	    ** changed to NULL (make sure it wasn't already NULL!) and the
	    ** count of discards will be only 1 greater.  If so, undo the
	    ** count so we don't complain if that was the only problem.
	    */
	    if ((!ln && n->Nright->Nleft)	/* Left changed, now NULL? */
	     || (!rn && n->Nright->Nright))	/* or right? */
		if (discnt == savcnt+1)		/* Yes, flushed just one?*/
		    --discnt;			/* If so, ignore that flush */

	    n->Nright->Nleft  = ln;
	    n->Nright->Nright = rn;
	    break;		/* Still have side effects */

	case TKTY_SEQ:		/* Sequential evaluation operator (only ',') */
	    if (!(n->Nright = edisc(n->Nright))) {	/* If zap top, */
		++discnt;
		return edisc(n->Nleft);			/* flush it. */
	    }

	    /* Keep but check the rest.  Actually it shouldn't be necessary
	    ** to check the rest, as the parser does this when it builds
	    ** the sequential expression.  But it doesn't hurt to do it again
	    ** (someday something else might build those expressions too).
	    */
	    n->Ntype = n->Nright->Ntype;	/* Update overall type */
	    n->Nleft = edisc(n->Nleft);		/* Do rest. */
	    break;

	default:
	    int_warn("edisc: non-expr %N", n);
	    break;
    }
    return n;
}
/* SIDEFFP - Returns TRUE if expression has side effects.
**	Has to trace entire expression tree, so needs to know about
** structure of parse trees.
**
**	Any read reference to a "volatile"-qualified object is also considered
** a side effect!  Read refs can only happen with lvalues, and lvalues are
** only produced by:
**	Q_IDENT (identifier)
**	N_PTR (*)
**	Q_DOT (.)
**	Q_MEMBER (->)
** Note that N_ADDR (&) is a special case because even though it takes an
** lvalue as its operand, the object is not actually referenced.  To avoid
** spuriously flagging these cases, we have to handle N_ADDR by checking
** its operand for the lvalue objects and bypassing the first level.
*/
int
sideffp(n)
NODE *n;
{
    extern char *nopname[];

    switch(tok[n->Nop].tktype) {
	case TKTY_RWOP:		/* Similar to primary expression */
	    switch (n->Nop) {
		case Q_ASM:	/* asm() code has side-eff */ 
		    return 1;
	    }
	    return 1;		/* Use safe default in case of new built-ins */

	case TKTY_PRIMARY:	/* Primary expression */
	    switch (n->Nop) {
		case N_FNCALL:	return 1;	/* Function call has side-eff*/
		case Q_DOT:			/* May be lvalue, find out */
		    if (!(n->Nflag & NF_LVALUE))
			return sideffp(n->Nleft);
		case Q_MEMBER:			/* Always an lvalue */
		    if (tisanyvolat(n->Ntype))
			return 1;
		    return sideffp(n->Nleft);	/* Check these out */
		case Q_IDENT:			/* Ident is always lvalue */
		    return tisanyvolat(n->Ntype);
	    }
	    return 0;			/* Other primaries have no side-eff */

	case TKTY_UNOP:		/* Unary operator - check single operand */
	    switch (n->Nop) {
		case N_POSTINC: case N_POSTDEC:
		case N_PREINC: case N_PREDEC:
		    return 1;
		case N_PTR:			/* Always lvalue, so check */
		    if (tisvolatile(n->Ntype))	/* Check, note type will  */
			return 1;		/* never be struct/union */
		    break;		/* so needn't use tisanyvolat */

		case N_ADDR:		/* Special checking!! */
		    switch (n->Nleft->Nop) {
			case Q_IDENT: return 0;
			case Q_DOT:
			case Q_MEMBER:
			case N_PTR:
			    return sideffp(n->Nleft->Nleft);
		    }
		    break;
	    }
	    return sideffp(n->Nleft);

	case TKTY_BOOLUN:	/* Unary boolean operator (only '!') */
	    return sideffp(n->Nleft);

	case TKTY_BINOP:	/* Binary operator - check both operands */
	case TKTY_BOOLOP:	/* Binary boolean operator - ditto */
	    return (sideffp(n->Nright) || sideffp(n->Nleft));

	case TKTY_ASOP:		/* Binary assignment operator */
	    return 1;		/* Always a side-effect operation */

	case TKTY_TERNARY:	/* Ternary operator (only '?') - check 3 ops */
	    if (sideffp(n->Nleft)) return 1;	/* Check condition */
	    n = n->Nright;	/* Then set up to check the 2 outcomes */
	    return ((n->Nright && sideffp(n->Nright))
		|| (n->Nleft && sideffp(n->Nleft)));
	
	case TKTY_SEQ:		/* Sequential evaluation operator (only ',') */
	    do if (sideffp(n->Nright)) return 1;
	    while (n = n->Nleft);
	    return 0;		/* Nothing had a side effect, so return zero */

	default:
	    int_warn("sideffp: bad op %N", n);
	    return 1;			/* Play it safe */
    }
}
/* ISTRUE - evalute boolean expression as far as we can.
**	Used only by gfor(), to see whether the loop test
**	can be omitted the first time through, and thus moved to the
**	end of the loop.
** The main difference from evalexpr() is that the expression is known
** to be of scalar type, and we also have an expression ("bindings") which
** constitutes the initial expression.  Q_IDENTs encountered during the
** evaluation are looked up in the binding expr to see if they can be resolved,
** and thus produce a constant the first time the loop test is checked.
*/
static int unset;		/* whether an unbound var was found */

int
istrue(test, bindings)
NODE *test, *bindings;
{
    unset = 0;
    return value(test, bindings) && !unset;
}

/* ---------------------------------------- */
/*      recursive expression evaluator      */
/* ---------------------------------------- */

static long
value(test, bindings)
NODE *test, *bindings;
{
    if (test == NULL) return 0;
    switch (test->Nop) {
    case N_ICONST:
	return test->Niconst;

    case Q_IDENT:
	test = lookup(test->Nid, bindings); /* find variable assignment */
	if (test == NULL) break;	/* none found, give up.  Otherwise, */
	return value(test, (NODE *)NULL); /* re-eval hoping no vars */

    case Q_LAND:
	return value(test->Nleft, bindings) && value(test->Nright, bindings);
    case Q_LOR:
	return value(test->Nleft, bindings) || value(test->Nright, bindings);
    case Q_NOT:
	return !value(test->Nleft, bindings);

    case Q_EQUAL:
	return value(test->Nleft, bindings) == value(test->Nright, bindings);
    case Q_LESS:
	return value(test->Nleft, bindings) < value(test->Nright, bindings);
    case Q_GREAT:
	return value(test->Nleft, bindings) > value(test->Nright, bindings);
    case Q_NEQ:
	return value(test->Nleft, bindings) != value(test->Nright, bindings);
    case Q_LEQ:
	return value(test->Nleft, bindings) <= value(test->Nright, bindings);
    case Q_GEQ:
	return value(test->Nleft, bindings) >= value(test->Nright, bindings);

    case Q_PLUS:
	return value(test->Nleft, bindings) + value(test->Nright, bindings);
    case Q_MINUS:
	return value(test->Nleft, bindings) - value(test->Nright, bindings);
    case Q_MPLY:
	return value(test->Nleft, bindings) * value(test->Nright, bindings);
    case Q_DIV:
	return value(test->Nleft, bindings) / value(test->Nright, bindings);
    case Q_MOD:
	return value(test->Nleft, bindings) % value(test->Nright, bindings);
    }

    /* here when unrecognized node or unbound var, remember bad expr */
    unset = 1;				/* tell caller we lost */
    return 0;				/* and pick arbitrary val to return */
}
/* ------------------------------------------------ */
/*      find an assignment to a given variable      */
/* ------------------------------------------------ */

static NODE *
lookup(var, bindings)
SYMBOL *var;
NODE *bindings;
{
    NODE *result;

    if (bindings == NULL) return NULL;
    switch (bindings->Nop) {
    case N_EXPRLIST:
	result = lookup(var, bindings->Nright);
	if (result != NULL) return result;
	else return lookup(var, bindings->Nleft);
    case Q_ASGN:
	if (bindings->Nleft->Nop == Q_IDENT && bindings->Nleft->Nid == var)
	    return bindings->Nright;
	else return NULL;
    default:
	return NULL;
    }
}