Google
 

Trailing-Edge - PDP-10 Archives - SRI_NIC_PERM_FS_1_19910112 - c/kcc5/ccfold.c
There are 6 other files named ccfold.c in the archive. Click here to see a list.
#define NEW 2

/*	CCFOLD.C - Do optimizations on parse trees
**
**	All changes after version 17 (8/8/85), unless otherwise specified, are
**	Copyright (c) 1985, 1986 by Ken Harrenstien, SRI International.
*/
/* [SRI-NIC]SS:<C.KCC.CC>CCFOLD.C.30, 17-Dec-85 14:53:42, Edit by IAN
    Added float-point (unary and binary) expression evaluator */
/* [SRI-NIC]SS:<C.KCC.CC>CCFOLD.C.21, 17-Dec-85 07:59:05, Edit by KLH */
/*  Rationalized names of constants and structures */
/* <KCC.CC>CCFOLD.C.16, 20-Jun-85 11:39:56, Edit by KRONJ */
/*  Flush addition by zero */
/* <KCC.CC>CCFOLD.C.10, 10-Mar-85 10:12:12, Edit by KRONJ */
/*  Change IDWEIGHT to be independant of memory allocation */
/*  (Otherwise it comes out different with every EXE and makes */
/*  it difficult to tell when there's a real compiler bug) */

/*
** ccfold - perform various minor optimizations on expression trees
** (C) 1981 K. Chen
*/

#include "cc.h"

/* Exported routines */
NODE *fold();		/* Old stuff */
NODE *evalexpr();	/* New stuff */
NODE *evaldiscard();

/* Imported routines */
extern NODE *defnode(), *deficonst();	/* CCSTMT */
extern NODE *convnullcomb();		/* CCTYPE */
extern int binexp();			/* CCOUT */

/* Internal routines */
static NODE *copyflag(), *setlog(),
	*fold_expression(),
	*i_expr_fold(), *ui_expr_fold(),
	*f_expr_fold(), *p_expr_fold(),
	*evalcast();
static int ecanon();
#if !NEW
/* -------------------------------------- */
/*	fold expression  Ref.[2] 5.E      */

NODE *
fold(e)
NODE *e;
{
    if (e == NULL)
	return NULL;

    switch (tok[e->Nop].tktype) {

    case TKTY_PRIMARY:
	if (e->Nop == Q_DOT || e->Nop == Q_MEMBER)
	    e->Nleft = fold(e->Nleft);
	/* Drop through to return e */
    case TKTY_SEQ:
	return e;

    case TKTY_ASOP:
    case TKTY_BINOP:
    case TKTY_BOOLOP:
	e->Nleft = fold(e->Nleft);
	e->Nright = fold(e->Nright);
	return fold_expression(e);

    case TKTY_UNOP:
    case TKTY_BOOLUN:
	e->Nleft = fold(e->Nleft);
	return fold_expression(e);

    case TKTY_TERNARY:			/* Just one op, Q_QUERY */
	e->Nleft = fold(e->Nleft);
	e->Nright->Nleft = fold(e->Nright->Nleft);
	e->Nright->Nright = fold(e->Nright->Nright);
	return fold_expression(e);

    default:
	error(EINTNOD,"bad expr node op", e);
	/* Fall thru to return e */
    }
    return e;
}
#endif
#if NEW

/* 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(), *evalop(), *evalb1op(), *evallog(), *evalfun();
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_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).
    */
    case TKTY_ASOP:			/* Assignment ops */
	e->Nleft  = eval(e->Nleft);	/* have side effect, so must always */
	e->Nright = eval(e->Nright);	/* just fold operands and */
	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 evalop(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.
	    */
	    if (eisconst(e->Nleft) && eisconst(e->Nright))
		return evalop(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:
	error(EINTNOD,"bad expr node op", 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 {
	    error(EINTNOD, "bad funarg list", 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 evalop(e);		/* evaluate (e != 0) */
    return e;
}
/* EVALOP - evaluate operators of type TKTY_UNOP, TKTY_BINOP, TKTY_BOOLOP,
**	with the exception of the logical operators (!, &&, ||).
**	For unary ops, the operand is known to be a good constant.
**		(cast) ~ -
**	For relational/equality ops, both operands are constants.
**		== != < > <= >=
**	For arithmetic & bitwise ops, at least one operand is constant.
**		* / % + - << >> & ^ |
**	("Good" constants are N_ICONST, N_PCONST, N_FCONST).
*/
static NODE *
evalop(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:	l += l2;	break;
	case Q_MINUS:	l -= l2;	break;
	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:	ul += ul2;	break;
	case Q_MINUS:	ul -= ul2;	break;
	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);

    default:
	error(EINTNOD,"bad type in evalop()", e);
    }
    return e;
}
/* EVALUNOP - Evaluate unary operators with a constant operand.
*/
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:
	    error(EINTNOD,"bad type for ~", 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:
	    error(EINTNOD,"bad type for -", e);
	    return e;
	}
	return copyflag(e->Nleft, e);

    case N_PTR:
	break;			/* Don't try to hack pointer constants! */
    default:
	error(EINTNOD, "bad op in eval()", 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.
**
** 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		Neg-One		Power-of-2
**	fi*0 => 0	fi*1	=> fi	fi*(-1) => -fi	ui*log2(n) => ui<<n
**	0/fi => 0	fi/1	=> fi	fi/(-1) => -fi	ui/log2(n) => ui>>n
**	fi/0	Warning
**	0%i  => 0	i%1	=> 0	si%(-1) => 0	ui%log2(n) => ui&(n-1)
**	i%0	Warning
**	e+0  => e
**	e-0  => e
**	0-fi  => -fi
**	0<<i => 0
**	0>>i => 0
**	i<<0 => i
**	i>>0 => i
**	i&0  => 0			i&-1	=> i
**	i|0  => i			i|-1	=> -1
**	i^0  => i			i^-1	=> ~i
*/	

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 e->Nright;	/* i*0 => 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 e->Nleft;	/* 0/i => 0 */
		    warn(EGEN, "Dividing by zero");
		    break;
		}
		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->Nleft))
			return e->Nleft;	/* 0%i => 0 */
		    warn(EGEN, "Dividing by zero");
		    break;
		}
		if (!eisiconst(e->Nright))	/* Ensure icon on rt */
		    break;
		if (icon == 1 || tissigned(e->Ntype)) {
						/* i%1    => 0 */
		    e->Nright->Niconst = 0;	/* si%(-1) => 0 */
		    return e->Nright;		/* 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 (icon == 0)		/* If either operand 0, */
		    return e->Nleft;	/* always return left val! */
		break;			/* Cuz 0<<i is 0, i<<0 is i */
	case Q_ANDT:
		if (icon == 0)		/* Cuz commuted, const on rt */
		    return e->Nright;	/* i&0 => 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 e->Nright;	/* i|(-1) => -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 e->Nleft;		/* 0/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) {
			warn(EGEN, "Dividing by zero");
			break;
		    }
		    return e->Nright;		/* f*0 => 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;
}

#endif /* NEW */
/*
** 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) {
	error(EINT,"logical node not int type");
	e->Ntype = inttype;
    }
    e->Nop = N_ICONST;
    e->Niconst = res;
    return e;
}
#if !NEW
/* FOLD_EXPRESSION(e) - Fold expression.  Assumes that if the expression
**	is a binary operation, any constant will have been commuted to the
**	right-hand node by ecanon().  Thus if a constant appears as the
**	left-hand node, it implies the right-hand is a constant also.
** Any operands of the node have already been folded as far as possible.
*/

static NODE *
fold_expression(e)
NODE *e;
{
    switch (e->Nleft->Nop) {	/* See if left-hand operand is a constant */
    case N_ICONST:		/* Integer constant */
	switch (e->Ntype->Tspec) {
	    case TS_SHORT:
	    case TS_LONG:
	    case TS_INT:	return i_expr_fold(e);

	    case TS_USHORT:
	    case TS_ULONG:
	    case TS_UINT:	return ui_expr_fold(e);
	}
	if (e->Nop == N_CAST) return evalcast(e);
	return e;
    case N_PCONST:		/* Pointer constant */
	return p_expr_fold(e);
    case N_FCONST:		/* Floating-point constant */
	return f_expr_fold(e);
    default:
	return e;
    }
}
#endif
#if !NEW
/*
**	fold integer expressions
**
**	Arg is an expression of signed integral type with the left operand
**	a N_ICONST node.
*/

static NODE *
i_expr_fold(e)
NODE *e;
{
    int op;
    NODE *l, *r;
    long  u,v;

    l = e->Nleft;
    op = e->Nop;
    u = l->Niconst;

    /*---------------------------------------------------------
    ** Check out UNARY and LOGICAL operators
    */
    switch (op) {
    case N_CAST:			/* Type cast */
	return evalcast(e);

    case N_NEG:				/* - Unary negation */
	l->Niconst = -u;
	return copyflag(l, e);

    case Q_COMPL:			/* ~ Bitwise not */
	l->Niconst = ~u;
	return copyflag(l, e);

    case Q_NOT:				/* ! Logical not */
	l->Niconst = !u;
	return copyflag (l, e);

    case Q_LOR:				/* || Logical OR */
	if (u == 0) return copyflag (e->Nright, e);
	return setlog(e, 1);

    case Q_LAND:
	return copyflag((u ? e->Nright : l), e);

    /* Ternary operator (?:) */
    case Q_QUERY:
	return copyflag ((u? e->Nright->Nleft : e->Nright->Nright), e);

    case Q_PLUS:
	if (u == 0) return copyflag (e->Nright, e); /* fold out add by zero */
    }

    r = e->Nright;
    if (r->Nop != N_ICONST) {	/* Is 2nd operand also an integer const? */
	/* Handle binary ops where 2nd operand isn't a signed integer.
	** Ops which are commutative & associative can still be folded
	** with constants farther along.
	** The only allowed ops are +, *, &, ^, and |.
	** The bitwise ops (&^|) are always OK for unsigned ops, and OK
	**	for signed if twos-complement representation is used.
	** It is only OK for + and * if there is no overflow.
	** For now we only handle bitwise ops, to avoid need for overflow
	** checking.
	*/
	if (op == r->Nop		/* See if operators match */
	  && tok[r->Nop].tktype == TKTY_BINOP	/* and if operator binary */
	  && r->Nleft->Nop == N_ICONST) {	/* and operand is const */
	    switch (op) {
	    case Q_OR:
		r->Nleft->Niconst |= u;
		return copyflag(r, e);
	    case Q_ANDT:
		r->Nleft->Niconst &= u;
		return copyflag(r, e);
	    case Q_XORT:
		r->Nleft->Niconst ^= u;
		return copyflag(r, e);
	    }
	}
	return e;		/* Give up */
    }

    /* Both operands are signed integer constants. */
    v = r->Niconst;
    switch (op) {
    case Q_PLUS:	u += v;		break;
    case Q_MINUS:	u -= v;		break;
    case Q_MPLY:	u *= v;		break;
    case Q_DIV:		u /= v;		break;
    case Q_MOD:		u %= v;		break;
    case Q_ANDT:	u &= v;		break;
    case Q_OR:		u |= v;		break;
    case Q_XORT:	u ^= v;		break;
    case Q_LSHFT:	u <<= v;	break;
    case Q_RSHFT:	u >>= v;	break;
    case Q_EQUAL:	u = u == v;	break;
    case Q_NEQ:		u = u != v;	break;
    case Q_LESS:	u = u < v;	break;
    case Q_GREAT:	u = u > v;	break;
    case Q_LEQ:		u = u <= v;	break;
    case Q_GEQ:		u = u >= v;	break;

    default:
	return e;
    }
    l->Niconst = u;
    return copyflag(l, e);
}
/*
**	fold unsigned integer expressions
**
**	Arg is an expression of unsigned integral type with the left operand
**	a N_ICONST node.
*/

static NODE *
ui_expr_fold(e)
NODE *e;
{
    int op;
    NODE *l, *r;
    unsigned int  u,v;

    l = e->Nleft;
    op = e->Nop;
    u = l->Niconst;

    switch (op) {

    /* Unary operators */
    case N_CAST:
	return evalcast(e);

    case N_NEG:
	l->Niconst = -u;
	return copyflag (l, e);

    case Q_COMPL:
	l->Niconst = ~u;
	return copyflag (l, e);

    case Q_NOT:
	l->Niconst = !u;
	return copyflag (l, e);

    case Q_LOR:
	if (u == 0) return copyflag (e->Nright, e);
	l->Niconst = 1;			/* make sure 1 not just non-zero */
	return copyflag (l, e);

    case Q_LAND:
	return copyflag ((u? e->Nright : l), e);

    case Q_QUERY:
	return copyflag ((u? e->Nright->Nleft : e->Nright->Nright), e);

    case Q_PLUS:
	if (u == 0) return copyflag (e->Nright, e); /* fold out add by zero */
    }

    r = e->Nright;
    /* See i_expr_fold() for comments on this code */
    if (r->Nop != N_ICONST) {	/* Is 2nd operand also an integer const? */
	if (op == r->Nop		/* See if operators match */
	  && tok[r->Nop].tktype == TKTY_BINOP	/* and if operator binary */
	  && r->Nleft->Nop == N_ICONST) {	/* and operand is const */
	    switch (op) {
	    case Q_OR:
		r->Nleft->Niconst |= u;
		return copyflag(r, e);
	    case Q_ANDT:
		r->Nleft->Niconst &= u;
		return copyflag(r, e);
	    case Q_XORT:
		r->Nleft->Niconst ^= u;
		return copyflag(r, e);
	    }
	}
	return e;		/* Give up */
    }

    switch (op) {
    case Q_PLUS:	u += v;		break;
    case Q_MINUS:	u -= v;		break;
    case Q_MPLY:	u *= v;		break;
    case Q_DIV:		u /= v;		break;
    case Q_MOD:		u %= v;		break;
    case Q_ANDT:	u &= v;		break;
    case Q_OR:		u |= v;		break;
    case Q_XORT:	u ^= v;		break;
    case Q_LSHFT:	u <<= v;	break;
    case Q_RSHFT:	u >>= v;	break;
    case Q_EQUAL:	u = u == v;	break;
    case Q_NEQ:		u = u != v;	break;
    case Q_LESS:	u = u < v;	break;
    case Q_GREAT:	u = u > v;	break;
    case Q_LEQ:		u = u <= v;	break;
    case Q_GEQ:		u = u >= v;	break;

    default:
	return e;
    }
    l->Niconst = u;
    return copyflag(l, e);
}
/*
**	fold floating expressions
**
**	Arg is an expression of unknown type with the left operand
**	a N_FCONST node.
*/

static NODE *
f_expr_fold(e)
NODE *e;
{
    int op;
    NODE *l, *r;
    double u, v;
    int res;

    l = e->Nleft;
    op = e->Nop;
    u = l->Nfconst;

    /*---------------------------------------------------------
    ** Check out UNARY and LOGICAL operators
    */
    switch (op) {
    case N_CAST:			/* () Type cast */
	return evalcast(e);

    case N_NEG:				/* - Unary negation */
	l->Nfconst = -u;
	return copyflag(l, e);

    case Q_COMPL:			/* can't complement a float */
	error(ENOTINT, "~");
	return e;

    case Q_NOT:				/* ! Logical not */
	return setlog(e, (!u));

    case Q_LOR:				/* || Logical OR */
	if (u)			/* If left-hand is true, res is true */
	    return setlog(e, 1);
	return e;	/* maybe drop thru */

    case Q_LAND:			/* logical AND */
	if (!u)				/* If left is false, res is false */
	    return setlog(e, 0);
	return e;		/* maybe drop thru */

    case Q_QUERY:
	/* The types of the true/false nodes are already set properly. */
	return (u ? e->Nright->Nleft : e->Nright->Nright);

    case Q_PLUS:			 /* fold out add by zero */
	if (u == 0)
	    return copyflag(e->Nright, e);
    }

    /*---------------------------------------------------------
    ** Check out BINARY operators
    */
    r = e->Nright;
    if (r->Nop != N_FCONST)	/* If not also a float constant, forget it */
	return e;
    v = r->Nfconst;
    switch (op) {
    case Q_PLUS:	u += v;	break;
    case Q_MINUS:	u -= v;	break;
    case Q_MPLY:	u *= v;	break;
    case Q_DIV:		u /= v;	break;
    case Q_MOD:		error(ENOTINT, "%");	break;
    case Q_ANDT:	error(ENOTINT, "&");	break;
    case Q_OR:		error(ENOTINT, "|");	break;
    case Q_XORT:	error(ENOTINT, "^");	break;
    case Q_LSHFT:	error(ENOTINT, "<<");	break;
    case Q_RSHFT:	error(ENOTINT, ">>");	break;
    case Q_EQUAL:	return setlog(e, (u == v));
    case Q_NEQ:		return setlog(e, (u != v));
    case Q_LESS:	return setlog(e, (u < v));
    case Q_GREAT:	return setlog(e, (u > v));
    case Q_LEQ:		return setlog(e, (u <= v));
    case Q_GEQ:		return setlog(e, (u >= v));
    default:
	return e;
    }
    l->Nfconst = u;
    return copyflag(l, e);
}
/* P_EXPR_FOLD
**	Temporary hack until this FOLD mess is redone.
**	Arg is an expression of pointer type with the left operand a
**	N_PCONST node.  This is mainly to let casts of NULL work.
*/
static NODE *
p_expr_fold(e)
NODE *e;
{
    switch (e->Nop) {
    case N_CAST:
	return evalcast(e);
    }
    return e;
}
#endif
/* 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:
		    error(EINT,"bad types for fp_fp cast");
		    break;
	    }
	    break;

	case CAST_IT_FP:	/* Integer type to floating-point type */
	    switch (tto->Tspec) {
		case TS_FLOAT:
		    cn->Nfconst = (float) cn->Niconst;
		    break;
		case TS_DOUBLE:
		case TS_LNGDBL:
		    cn->Nfconst = (double) 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 (charpointer(tfrom) && !charpointer(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:
	    error(EINT,"bad cast op in evalcast: %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:
	    error(EINT,"folding illegal constant type");
	    return 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;
}
/* ---------------------------------------- */
/*      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.
    */

    switch (n->Nop) {
    case N_ICONST:
	return IWEIGHT;
    case Q_IDENT:
	return IDWEIGHT(n->Nid)
		/* Temporary hack so new array/funct conversion code
		** comes out looking the same as before --KLH
		*/
		+ (
#if 1
		((n->Ntype != n->Nid->Stype) || n->Ntype->Tspec == TS_FUNCT)
#else
			(n->Nflag&NF_ARRFCONV)
#endif
			? 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:
	    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. */
	warn(EGEN, "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. */
}
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_PRIMARY:	/* Primary expression */
	    switch (n->Nop) {
		case Q_ASM:		/* asm() has side effects! */
		case N_FNCALL:		/* Function call has side effects! */
			return n;
		case Q_DOT:
		case Q_MEMBER:
			++discnt;
			return edisc(n->Nleft);
	    }
	    ++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;
	    }
	    /* 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 defnode(N3, N_EXPRLIST, rn->Ntype, rn->Nflag,
			defnode(N3, N_EXPRLIST, ln->Ntype, ln->Nflag,
				(NODE *)NULL, ln),
			rn);

	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_ASOP:		/* Binary assignment operator */
	    break;		/* Always a side-effect operation */

	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:
	    error(EINTNOD, "non-expr node", n);
	    break;
    }
    return n;
}