Google
 

Trailing-Edge - PDP-10 Archives - SRI_NIC_PERM_FS_1_19910112 - kcc-4/kcc/ccfold.c
There are 6 other files named ccfold.c in the archive. Click here to see a list.
/*	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(), *evaldiscard();
int optexpr();

/* Imported routines */
extern NODE *defnode(), *deficonst();

/* Internal routines */
static NODE *copyflag(), *setlog(),
	*fold_expression(),
	*i_expr_fold(), *ui_expr_fold(),
	*f_expr_fold(), *p_expr_fold(),
	*evalcast();
/* -------------------------------------- */
/*	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:
	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;
}
/*
** 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;
}
/* ---------------------------------- */
/*	fold numeric expressions      */
/* ---------------------------------- */

static NODE *
fold_expression(e)
NODE *e;
{
    switch (e->Nleft->Nop) {
    case N_ICONST:
	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:
	return p_expr_fold(e);
    case N_FCONST:
	return f_expr_fold(e);
    default:
	return e;
    }
}
/*
**	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;
    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;
    switch (r->Nop) {
	case N_ICONST:
	    v = r->Niconst;
	    break;
#ifndef COMMENT	/* This is wrong!  See CARM p. 135 --KLH */
	case N_FCONST:
	    v = (int) r->Nfconst;
	    break;
#endif
	default:
	    return e;
    }
    l->Ntype = e->Ntype;		/* preserve coercions */
    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;
    switch (r->Nop) {
	case N_ICONST:
	    v = r->Niconst;
	    break;
#ifndef COMMENT	/* This is wrong!  See CARM p. 135 --KLH */
	case N_FCONST:
	    v = (int) r->Nfconst;
	    break;
#endif
	default:
	    return e;
    }
    l->Ntype = e->Ntype;		/* preserve coercions */
    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) {			/* do unary operations */
    case N_CAST:			/* Type cast */
	return evalcast(e);

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

    case Q_NOT:				/* logical negation */
	return setlog(e, (!u));

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

    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_ICONST 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;
}
/* 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_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 */
	case CAST_AR_PA:	/* Array -> Pointer to 1st Array element */
	case CAST_FN_PF:	/* Function -> Pointer to Function */
	    cn->Nop = N_PCONST;
	    break;

	default:
	    error(EINT,"bad cast op %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 */

int
optexpr(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
		*/
		+ ((n->Nflag&NF_ARRFCONV) ? CWEIGHT : 0);
    case N_SCONST:
	return SWEIGHT;
    case Q_DOT:
    case Q_MEMBER:
	return optexpr(n->Nleft) + MWEIGHT;
    case N_EXPRLIST:
	if (n->Nleft == NULL) return optexpr(n->Nright);
	return optexpr(n->Nleft) + optexpr(n->Nright);
    case N_CAST:
	return optexpr(n->Nleft) + CWEIGHT;
    case N_FNCALL:
	return (n->Nright == NULL) ? FWEIGHT : optexpr(n->Nright)+FWEIGHT;
    default:
	switch (tok[n->Nop].tktype) {
	case TKTY_BINOP:
	    x = optexpr(n->Nleft);
	    y = optexpr(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 optexpr(n->Nleft) + optexpr(n->Nright) + BWEIGHT;
	case TKTY_UNOP:
	case TKTY_BOOLUN:
            return optexpr(n->Nleft)+CWEIGHT;
	case TKTY_TERNARY:
	    x = optexpr(n->Nright->Nleft);
	    y = optexpr(n->Nright->Nright);
	    if (y > x) x = y;
            return optexpr(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 (!optpar) {			/* If not optimizing, */
	n->Nflag |= NF_DISCARD;		/* just set flag for top-level node */
	return n;
    }
    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;

    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! */
		    /* 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.
	    */
	    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! */
	    }
	    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;
}