/*
 * C compiler
 * ==========
 *
 * Copyright 1989, 1990, 1991 Christoph van Wuellen.
 * Credits to Matthew Brandt.
 * All commercial rights reserved.
 *
 * This compiler may be redistributed as long there is no
 * commercial interest. The compiler must not be redistributed
 * without its full sources. This notice must stay intact.
 *
 * History:
 *
 * 1989   starting an 68000 C compiler, starting with material
 *        originally by M. Brandt
 * 1990   68000 C compiler further bug fixes
 *        started i386 port (December)
 * 1991   i386 port finished (January)
 *        further corrections in the front end and in the 68000
 *        code generator.
 *        The next port will be a SPARC port
 */

#include "chdr.h"
#include "expr.h"
#include "cglbdec.h"
#include "proto.h"

#if defined(__STDC__) || defined(__cplusplus)
#define P_(s) s
#else
#define P_(s) ()
#endif

static	IVAL	xfold		P_((EXPR *));
static	EXPR *	dooper		P_((EXPR *));
static	EXPR 	*fold_const	P_((EXPR *));

#undef P_

/*
**	dooper() will execute a constant operation in a node and return
**	the node to be the result of the operation.
*/
static EXPR *
dooper P1(EXPR*, ep)
{
    EXPRTYPE     type = ep->nodetype;
#ifdef FLOAT_SUPPORT
    RVAL f;
#endif /* FLOAT_SUPPORT */

    ep->nodetype = ep->v.p[0]->nodetype;
    switch (ep->v.p[0]->nodetype) {
      case en_fcon:
#ifdef FLOAT_SUPPORT
#ifndef FLOAT_BOOTSTRAP
	FASSIGN(f, ep->v.p[0]->v.f);
        switch (type) {
          case en_uminus:
	    FASSIGN(ep->v.f, f);
	    FNEG(ep->v.f);
            break;
	  case en_test:
            ep->v.i = FTST(f) ? (IVAL) 1 : (IVAL) 0;
            ep->nodetype = en_icon;
            break;
          case en_not:
            ep->v.i = FTST(f) ? (IVAL) 0 : (IVAL) 1;
            ep->nodetype = en_icon;
            break;
	  case en_cast:
	    FTOL(ep->v.i, f);
	    ep->nodetype = en_icon;
	    break;
          case en_add:
	    FADD3(ep->v.f, f, ep->v.p[1]->v.f);
            break;
          case en_sub:
	    FSUB3(ep->v.f, f, ep->v.p[1]->v.f);
            break;
          case en_mul:
	    FMUL3(ep->v.f, f, ep->v.p[1]->v.f);
            break;
          case en_div:
	    if (FTST(ep->v.p[1]->v.f)) {
		FDIV3(ep->v.f, f, ep->v.p[1]->v.f);
	    } else {
	        ep->nodetype = en_div;
	    }
            break;
          case en_eq:
            ep->v.i = (IVAL) FEQ(f, ep->v.p[1]->v.f);
            ep->nodetype = en_icon;
            break;
          case en_ne:
            ep->v.i = (IVAL) FNE(f, ep->v.p[1]->v.f);
            ep->nodetype = en_icon;
            break;
          case en_land:
            ep->v.i = (IVAL) (FTST(f) && FTST(ep->v.p[1]->v.f));
            ep->nodetype = en_icon;
            break;
          case en_lor:
            ep->v.i = (IVAL) (FTST(f) || FTST(ep->v.p[1]->v.f));
            ep->nodetype = en_icon;
            break;
          case en_lt:
            ep->v.i = (IVAL) FLT(f, ep->v.p[1]->v.f);
            ep->nodetype = en_icon;
            break;
          case en_le:
            ep->v.i = (IVAL) FLE(f, ep->v.p[1]->v.f);
            ep->nodetype = en_icon;
            break;
          case en_gt:
            ep->v.i = (IVAL) FGT(f, ep->v.p[1]->v.f);
            ep->nodetype = en_icon;
            break;
          case en_ge:
            ep->v.i = (IVAL) FGE(f, ep->v.p[1]->v.f);
            ep->nodetype = en_icon;
            break;
          default:
	    CANNOT_REACH_HERE();
            break;
        }
#endif /* FLOAT_BOOTSTRAP */
#endif /* FLOAT_SUPPORT */
	break;
      case en_icon:
	if (is_unsigned_type(ep->v.p[0]->etp)) {
	    UVAL u = ep->v.p[0]->v.u;
	    switch (type) {
	      case en_uminus:
		/*
		**	unary minus on an unsigned is normally a mistake so we must
		**	fool the compiler into not giving a warning.
		*/
		ep->v.u = (UVAL) (- (IVAL)u);
		break;
	      case en_test:
		ep->v.u = (u ? (UVAL) 1 : (UVAL) 0);
		break;
	      case en_not:
		ep->v.u = (u ? (UVAL) 0 : (UVAL) 1);
		break;
	      case en_compl:
		ep->v.u = (UVAL) strip_icon((IVAL) ~u, ep->etp);
		break;
#ifdef FLOAT_SUPPORT
	      case en_cast:
		ep->nodetype = en_fcon;
		UTOF(ep->v.f, u);
		break;
#endif /* FLOAT_SUPPORT */
	      case en_add:
		ep->v.u = u + ep->v.p[1]->v.u;
		break;
	      case en_sub:
		ep->v.u = u - ep->v.p[1]->v.u;
		break;
	      case en_mul:
		ep->v.u = u * ep->v.p[1]->v.u;
		break;
	      case en_div:
		if (ep->v.p[1]->v.u == (UVAL) 0) {
		    ep->nodetype = en_div;
		} else {
		    ep->v.u = u / ep->v.p[1]->v.u;
		}
		break;
	      case en_mod:
		if (ep->v.p[1]->v.u == (UVAL) 0) {
		    ep->nodetype = en_mod;
		} else {
		    ep->v.u = u % ep->v.p[1]->v.u;
		}
		break;
	      case en_and:
		ep->v.u = u & ep->v.p[1]->v.u;
		break;
	      case en_or:
		ep->v.u = u | ep->v.p[1]->v.u;
		break;
	      case en_xor:
		ep->v.u = u ^ ep->v.p[1]->v.u;
		break;
	      case en_eq:
		ep->v.u = (UVAL) (u == ep->v.p[1]->v.u);
		break;
	      case en_ne:
		ep->v.u = (UVAL) (u != ep->v.p[1]->v.u);
		break;
	      case en_land:
		ep->v.u = (UVAL) (u && ep->v.p[1]->v.u);
		break;
	      case en_lor:
		ep->v.u = (UVAL) (u || ep->v.p[1]->v.u);
		break;
	      case en_lt:
		ep->v.u = (UVAL) (u < ep->v.p[1]->v.u);
		break;
	      case en_le:
		ep->v.u = (UVAL) (u <= ep->v.p[1]->v.u);
		break;
	      case en_gt:
		ep->v.u = (UVAL) (u > ep->v.p[1]->v.u);
		break;
	      case en_ge:
		ep->v.u = (UVAL) (u >= ep->v.p[1]->v.u);
		break;
	      case en_lsh:
		ep->v.u = u << ep->v.p[1]->v.u;
		break;
	      case en_rsh:
		ep->v.u = u >> ep->v.p[1]->v.u;
		break;
	      default:
		CANNOT_REACH_HERE();
		break;
	    }
	} else {
	    IVAL i = ep->v.p[0]->v.i;
	    switch (type) {
	      case en_uminus:
		ep->v.i = - i;
		break;
	      case en_test:
		ep->v.i = (i) ? (IVAL) 1 : (IVAL) 0;
		break;
	      case en_not:
		ep->v.i = (i) ? (IVAL) 0 : (IVAL) 1;
		break;
	      case en_compl:
		ep->v.i = strip_icon(~i, ep->etp);
		break;
#ifdef FLOAT_SUPPORT
	      case en_cast:
		ep->nodetype = en_fcon;
		LTOF(ep->v.f, i);
		break;
#endif /* FLOAT_SUPPORT */
	      case en_add:
		ep->v.i = i + ep->v.p[1]->v.i;
		break;
	      case en_sub:
		ep->v.i = i - ep->v.p[1]->v.i;
		break;
	      case en_mul:
		ep->v.i = i * ep->v.p[1]->v.i;
		break;
	      case en_div:
		if (ep->v.p[1]->v.i == (IVAL) 0) {
		    ep->nodetype = en_div;
		} else {
		    ep->v.i = i / ep->v.p[1]->v.i;
		}
		break;
	      case en_mod:
		if (ep->v.p[1]->v.i == (IVAL) 0) {
		    ep->nodetype = en_mod;
		} else {
		    ep->v.i = i % ep->v.p[1]->v.i;
		}
		break;
	      case en_and:
		ep->v.i = i & ep->v.p[1]->v.i;
		break;
	      case en_or:
		ep->v.i = i | ep->v.p[1]->v.i;
		break;
	      case en_xor:
		ep->v.i = i ^ ep->v.p[1]->v.i;
		break;
	      case en_eq:
		ep->v.i = (IVAL) (i == ep->v.p[1]->v.i);
		break;
	      case en_ne:
		ep->v.i = (IVAL) (i != ep->v.p[1]->v.i);
		break;
	      case en_land:
		ep->v.i = (IVAL) (i && ep->v.p[1]->v.i);
		break;
	      case en_lor:
		ep->v.i = (IVAL) (i || ep->v.p[1]->v.i);
		break;
	      case en_lt:
		ep->v.i = (IVAL) (i < ep->v.p[1]->v.i);
		break;
	      case en_le:
		ep->v.i = (IVAL) (i <= ep->v.p[1]->v.i);
		break;
	      case en_gt:
		ep->v.i = (IVAL) (i > ep->v.p[1]->v.i);
		break;
	      case en_ge:
		ep->v.i = (IVAL) (i >= ep->v.p[1]->v.i);
		break;
	      case en_lsh:
		ep->v.i = i << ep->v.p[1]->v.i;
		break;
	      case en_rsh:
		ep->v.i = i >> ep->v.p[1]->v.i;
		break;
	      default:
		CANNOT_REACH_HERE();
		break;
	    }
	}
	break;
      default:
	CANNOT_REACH_HERE();
	break;
    }
    return ep;
}


/*
**	return which power of two i is or -1.
*/
#ifndef MC68000
static
#endif /* MC68000 */
int
pwrof2 P1( IVAL, i)
{
    int             p;
    IVAL            q;
    q = (IVAL) 2;
    p = 1;
    while (q > (IVAL) 0) {
	if (q == i)
	    return p;
	q <<= 1l;
	++p;
    }
    return -1;
}


/*
**	This routine attempts to optimize bitfield references ... if
**	it makes a change it returns a pointer to the new node,
**	otherwise it returns NULL;
*/
static EXPR *
simplify_fieldref P3(EXPR *, ep, TYP *, tp1, TYP*, tp2)
{
    if (g_is_bigendian()) {
	if (ep->nodetype == en_fieldref) {
	    SIZE algn = alignment(tp1) * 8L;
	    if (((SIZE)ep->v.bit.offset % algn + (SIZE)ep->v.bit.width) <= tp1->size*8L) {
		SIZE adjust = (SIZE)ep->v.bit.offset / algn;
		ep->v.bit.offset -= adjust * 8L;
		adjust = ep->etp->size - (tp1->size + adjust);
		if (adjust != 0L) {
			ep->v.p[0] = opt0(mk_node(en_add, ep->v.p[0],
					     mk_icon(adjust, tp_pointer), ep->etp));
		}
		ep->etp = is_signed_type(ep->etp) ? tp1 : tp2;
		if ((ep->v.bit.offset == 0) && (ep->v.bit.width == (tp1->size*8L))) {
		    /*
		    **	 Fits exactly within the type so change to an
		    **	ordinary reference as this is much more efficient
		    */
		    ep->nodetype = en_ref;
		}
	    }
	}
    }
    return ep;
}

/*
**	opt0() - delete useless expressions and combine constants.
**
**	opt0 will delete expressions such as
**	 x + 0,
**	 x - 0,
**	 x * 0,
**	 x * 1,
**	 0 / x,
**	 x / 1,
**	 x mod 0,
**	 etc from the tree pointed to by node and combine obvious
**	constant operations. It cannot combine name and label constants
**	but will combine icon type nodes.
*/
static EXPR *
optnode P1(EXPR*, ep)
{
    IVAL         val, sc;
    TYP	    	*tp;
    EXPR	*ep0, *ep1;

    tp = ep->etp;
    switch (ep->nodetype) {
      case en_uminus:
      case en_compl:
      case en_test:
	ep0 = ep->v.p[0];
        if (ep0->nodetype == ep->nodetype) {
            ep = ep0->v.p[0];
	    return ep;
	}
        if (ep0->nodetype == en_icon || ep0->nodetype == en_fcon) {
	    ep = dooper(ep);
	    return ep;
	}
	break;
      case en_not:
	ep0 = ep->v.p[0];
        if (ep0->nodetype == en_not) {
            ep->v.p[0] = ep0->v.p[0];
	    ep->nodetype = en_test;
	    return ep;
        }
	if (ep0->nodetype == en_icon || ep0->nodetype == en_fcon) {
	    ep = dooper(ep);
	    return ep;
	}
	if (ep0->nodetype == en_test) {
	    ep->v.p[0] = ep0->v.p[0];
	    return ep;
	}
	if (ep0->nodetype == en_lor) {
	    /*
	     *  !(a || b)  =>  !a && !b
	     */
	    ep = ep0;
	    ep->nodetype = en_land;
	    ep->v.p[0] = mk_node(en_not, ep->v.p[0], NIL_EXPR, tp);
	    ep->v.p[1] = mk_node(en_not, ep->v.p[1], NIL_EXPR, tp);
	    return ep;
	} 
	if (ep0->nodetype == en_land) {
	    /*
	     *  !(a && b)  =>  !a || !b
	     */
	    ep = ep0;
	    ep->nodetype = en_lor;
	    ep->v.p[0] = mk_node(en_not, ep->v.p[0], NIL_EXPR, tp);
	    ep->v.p[1] = mk_node(en_not, ep->v.p[1], NIL_EXPR, tp);
	    return ep;
	}
	break;
      case en_cast:
	ep0 = ep->v.p[0];
	if (ep0->nodetype == en_icon || ep0->nodetype == en_fcon) {
	    ep = dooper(ep);
	    return ep;
	}
	if (ep0->nodetype == en_register &&
	    ep0->etp->size == 4L && ep->etp->size == 4L &&
	    is_integral_type(ep0->etp) && is_integral_type(ep->etp)) {
	    ep0->etp = tp;
	    return ep0;
	}
	break;
      case en_add:
	ep0 = ep->v.p[0];
	ep1 = ep->v.p[1];
        /*
        **	constant expressions
        */
        if ((ep0->nodetype == en_icon && ep1->nodetype == en_icon) ||
            (ep0->nodetype == en_fcon && ep1->nodetype == en_fcon))   {
            ep = dooper(ep);
            return ep;
        }
        /*
         *  a + (-b)  =>  a - b
         *  (-a) + b  =>  b - a
         */
        if (ep1->nodetype == en_uminus) {
            ep->v.p[1] = ep1->v.p[0];
            ep->nodetype = en_sub;
	    return ep;
        }
        if (ep0->nodetype == en_uminus) {
            ep->v.p[0] = ep0->v.p[0];
            swap_nodes(ep);
            ep->nodetype = en_sub;
	    return ep;
        }
	if (ep1->nodetype == en_autocon) {
	    swap_nodes(ep);
	    ep0 = ep->v.p[0];
	    ep1 = ep->v.p[1];
	}
	if ((ep0->nodetype == en_icon) && (ep0->v.i == 0L)) {
	    ep = ep1;
	    ep->etp = tp;
	    return ep;
	}
	if (ep1->nodetype == en_icon) {
	    if (ep0->nodetype == en_autocon) {
		ep = ep0;
		ep->v.i += ep1->v.i;
		ep->etp = tp;
		return ep;
	    }
	    if (ep1->v.i == 0L) {
		ep = ep0;
		ep->etp = tp;
		return ep;
	    }
	}
	break;
      case en_sub:
	ep0 = ep->v.p[0];
	ep1 = ep->v.p[1];
        /*
        **	constant expressions
        */
        if ((ep0->nodetype == en_icon && ep1->nodetype == en_icon) ||
            (ep0->nodetype == en_fcon && ep1->nodetype == en_fcon))   {
            ep = dooper(ep);
            return ep;
        }
        /*
         *	a - (-b)  =>  a + b
         */
        if (ep1->nodetype == en_uminus) {
            ep->v.p[1] = ep1->v.p[0];
            ep->nodetype = en_add;
	    return ep;
        }
	/*
	 *	a - (b - c)  =>  a + (c - b)
	 */
	if (ep1->nodetype == en_sub) {
	    swap_nodes(ep1);
	    ep->nodetype = en_add;
	    return ep;
	}

	/*
	 *	a - 0	=>  a
	 */
	if ((ep1->nodetype == en_icon) && (ep1->v.i == 0L)) {
	    ep = ep0;
	    ep->etp = tp;
	    return ep;
	}

	/*
	 *	0 - a	=>  -a
	 */
	if ((ep0->nodetype == en_icon) && (ep0->v.i == 0L)) {
	    ep->v.p[0] = ep1;
	    ep->nodetype = en_uminus;
	    return ep;
	}
	break;
      case en_mul:
	ep0 = ep->v.p[0];
	ep1 = ep->v.p[1];
        /*
        **	constant expressions
        */
        if ((ep0->nodetype == en_icon && ep1->nodetype == en_icon) ||
            (ep0->nodetype == en_fcon && ep1->nodetype == en_fcon))  {
            ep = dooper(ep);
            return ep;
        }
	if (ep0->nodetype == en_icon) {
	    val = ep0->v.i;
	    if (val == (IVAL) 0) {
		ep = mk_node(en_comma, ep1, ep0, tp);
		return ep;
	    }
	    if (val == (IVAL) 1) {
		ep = ep1;
		break;
	    }
	    sc = (IVAL) pwrof2(val);
	    if (sc != (IVAL) -1) {
		swap_nodes(ep);
		ep->v.p[1]->v.i = sc;
		ep->nodetype = en_lsh;
		return ep;
	    }
	} else if (ep1->nodetype == en_icon) {
	    val = ep1->v.i;
	    if (val == (IVAL) 0) {
		ep = mk_node(en_comma, ep0, ep1, tp);
		return ep;
	    }
	    if (val == (IVAL) 1) {
		ep = ep0;
		return ep;
	    }
	    sc = (IVAL) pwrof2(val);
	    if (sc != (IVAL) -1) {
		ep->v.p[1]->v.i = sc;
		ep->nodetype = en_lsh;
		return ep;
	    }
	}
	break;
      case en_div:
	ep0 = ep->v.p[0];
	ep1 = ep->v.p[1];
        /*
        **	constant expressions
        */
        if ((ep0->nodetype == en_icon && ep1->nodetype == en_icon) ||
            (ep0->nodetype == en_fcon && ep1->nodetype == en_fcon))  {
            ep = dooper(ep);
            return ep;
        }
	if (ep0->nodetype == en_icon) {
	    if (ep0->v.i == 0L) {	/* 0/x */
		ep = mk_node(en_comma, ep1, ep0, tp);
		return ep;
	    }
	} else if (ep1->nodetype == en_icon) {
	    val = ep1->v.i;
	    if (val == (IVAL) 1) {	/* x/1 */
		ep = ep0;
		return ep;
	    }
	    if (is_unsigned_type(tp)) {
	        sc = (IVAL) pwrof2(val);
	        if (sc != (IVAL) -1) {
		    ep->v.p[1]->v.i = sc;
		    ep->nodetype = en_rsh;
		    return ep;
	        }
	    }
	}
	break;
      case en_mod:
	ep0 = ep->v.p[0];
	ep1 = ep->v.p[1];
        /*
        **	constant expressions
        */
        if ((ep0->nodetype == en_icon && ep1->nodetype == en_icon) ||
            (ep0->nodetype == en_fcon && ep1->nodetype == en_fcon))  {
            ep = dooper(ep);
            return ep;
        }
	if (ep1->nodetype == en_icon) {
	    if (is_unsigned_type(tp)) {
		sc = (IVAL) pwrof2(ep1->v.i);
		if (sc != (IVAL) -1) {
		    ep->v.p[1]->v.i = (IVAL)bitmask((int)sc);
		    ep->nodetype = en_and;
		    return ep;
		}
	    }
	}
	break;
      case en_lor:
	ep0 = ep->v.p[0];
	ep1 = ep->v.p[1];
        /*
        **	constant expressions
        */
        if ((ep0->nodetype == en_icon && ep1->nodetype == en_icon) ||
            (ep0->nodetype == en_fcon && ep1->nodetype == en_fcon)) {
            ep = dooper(ep);
	    return ep;
	}

	/*
	 *	1 || a  =>  1
	 *	0 || a	=>  a
	 */
	if (ep0->nodetype == en_icon) {
	    /* short-circuit! */
	    if  (ep0->v.i) {
		ep = ep0;
		ep->v.i = (IVAL)TRUE;
	    } else {
		ep = ep1;
	    }
	    return ep;
	}

	/*
	 *	a || 0	=>  a
	 */
	if ((ep1->nodetype == en_icon) && (ep0->v.i == 0)) {
	    ep = ep0;
	    return ep;
	}

	break;
      case en_land:
	ep0 = ep->v.p[0];
	ep1 = ep->v.p[1];
        /*
        **	constant expressions
        */
        if ((ep0->nodetype == en_icon && ep1->nodetype == en_icon) ||
            (ep0->nodetype == en_fcon && ep1->nodetype == en_fcon)) {
            ep = dooper(ep);
	    return ep;
	}

	/*
	 *	0 && a  =>  0
	 *	1 && a	=>  a
	 */
	if (ep0->nodetype == en_icon) {
	    /* short-circuit! */
	    ep = (ep0->v.i) ? ep1 : ep0;
	    return ep;
	}

	/*
	 *	a && 1	=>  a
	 */
	if ((ep1->nodetype == en_icon)  && (ep1->v.i != 0)) {
	    ep = ep0;
	    return ep;
	}
	
	break;
      case en_eq:
      case en_ne:
	ep0 = ep->v.p[0];
	ep1 = ep->v.p[1];
        /*
        **	constant expressions
        */
        if ((ep0->nodetype == en_icon && ep1->nodetype == en_icon) ||
            (ep0->nodetype == en_fcon && ep1->nodetype == en_fcon)) {
            ep = dooper(ep);
	    return ep;
	}

	/*
	 *	a == 0	=>  !a
	 *	a != 0	=>  a
	 */
	if ((ep1->nodetype == en_icon) && (ep1->v.i == 0)) {
	    ep->nodetype = (ep->nodetype == en_eq) ? en_not : en_test;
	    return ep;
	}
	if ((ep1->nodetype == en_fcon) && (ep1->v.f == F_zero)) {
	    ep->nodetype = (ep->nodetype == en_eq) ? en_not : en_test;
	    return ep;
	}

	/*
	 *	0 == a	=>  !a
	 *	0 != a	=>  a
	 */
	if ((ep0->nodetype == en_icon) && (ep0->v.i == 0)) {
	    ep->v.p[0] = ep1;
	    ep->nodetype = (ep->nodetype == en_eq) ? en_not : en_test;
	    return ep;
	}
	if ((ep0->nodetype == en_fcon) && (ep0->v.i == F_zero)) {
	    ep->v.p[0] = ep1;
	    ep->nodetype = (ep->nodetype == en_eq) ? en_not : en_test;
	    return ep;
	}
	break;
      case en_and:
      case en_or:
      case en_xor:
      case en_lt:
      case en_le:
      case en_gt:
      case en_ge:
	ep0 = ep->v.p[0];
	ep1 = ep->v.p[1];
        /*
        **	constant expressions
        */
        if ((ep0->nodetype == en_icon && ep1->nodetype == en_icon) ||
            (ep0->nodetype == en_fcon && ep1->nodetype == en_fcon)) {
            ep = dooper(ep);
	    return ep;
	}
	break;
      case en_lsh:
      case en_rsh:
	ep0 = ep->v.p[0];
	ep1 = ep->v.p[1];
	if (ep0->nodetype == en_icon && ep1->nodetype == en_icon) {
	    ep = dooper(ep);
            return ep;
        }
        /*
        **	optimize a << 0   and a >> 0
        */
	if (ep1->nodetype == en_icon && ep1->v.i == 0L) {
	    ep = ep0;
	    return ep;
	}
	break;
      case en_cond:
	ep0 = ep->v.p[0];
	ep1 = ep->v.p[1];
        if (ep0->nodetype == en_icon) {
	    if (ep0->v.i)
		ep=ep1->v.p[0];
	    else
		ep=ep1->v.p[1];
	    return ep;
	}
	break;
      case en_asand:
      case en_asor:
      case en_asxor:
      case en_asadd:
      case en_assub:
      case en_asmul:
      case en_asmul2:
      case en_asdiv:
      case en_asdiv2:
      case en_asmod:
      case en_asrsh:
      case en_aslsh:
      case en_fcall:
      case en_call:
      case en_comma:
      case en_list:
      case en_assign:
      case en_ref:
      case en_ainc:
      case en_adec:
      case en_deref:
	break;
      case en_fieldref:
	/*
	 *	If possible reduce bit-field references to ordinary
	 *	references as they are much more efficient
	 */
	ep1 = simplify_fieldref(ep, tp_char, tp_uchar);
	if (ep1 == NIL_EXPR) {
	    ep1 = simplify_fieldref(ep, tp_short, tp_ushort);
	    if (ep1 == NIL_EXPR) {
		ep1 = simplify_fieldref(ep, tp_long, tp_ulong);
	    }
	}
	return ep1 ? ep1 : ep;
      default:
	break;
    }
    return ep;
}

EXPR *
opt0 P1(EXPR*, ep)
{
    return walkexpr(ep, optnode);
}

/*
**	xfold() will remove constant nodes and return the values to the
**	calling routines.
*/
static IVAL
xfold P1(EXPR*, ep)
{
    IVAL            i;
    if (ep == NIL_EXPR)
	return 0L;
    switch (ep->nodetype) {
      case en_icon:
	i = ep->v.i;
	ep->v.i = 0L;
	return i;
      case en_add:
	return xfold(ep->v.p[0]) + xfold(ep->v.p[1]);
      case en_sub:
	return xfold(ep->v.p[0]) - xfold(ep->v.p[1]);
      case en_mul:
	if (ep->v.p[0]->nodetype == en_icon) {
	    return xfold(ep->v.p[1]) * ep->v.p[0]->v.i;
	} else if (ep->v.p[1]->nodetype == en_icon) {
	    return xfold(ep->v.p[0]) * ep->v.p[1]->v.i;
	}
	/*FALLTHRU*/
      case en_lsh:
      case en_rsh:
      case en_div:
      case en_mod:
      case en_asadd:
      case en_assub:
      case en_asmul:
      case en_asmul2:
      case en_asdiv:
      case en_asdiv2:
      case en_asmod:
      case en_and:
      case en_land:
      case en_or:
      case en_lor:
      case en_xor:
      case en_asand:
      case en_asor:
      case en_asxor:
      case en_comma:
      case en_list:
      case en_fcall:
      case en_call:
      case en_assign:
      case en_eq:
      case en_ne:
      case en_lt:
      case en_le:
      case en_gt:
      case en_ge:
	ep->v.p[1] = fold_const(ep->v.p[1]);
	/*FALLTHRU*/
      case en_ref:
      case en_fieldref:
      case en_compl:
      case en_test:
      case en_not:
      case en_deref:
      case en_cast:
	ep->v.p[0] = fold_const(ep->v.p[0]);
	return 0L;
      case en_uminus:
	return -xfold(ep->v.p[0]);
      default:
	break;
    }
    return 0L;
}

/*
**	reorganize an expression for optimal constant grouping.
*/
static EXPR *
fold_const P1(EXPR*, ep)
{
    EXPR	*ep1;
    IVAL         i;
    if (ep == NIL_EXPR)
	return ep;
    switch (ep->nodetype) {
      case en_add:
	if (ep->v.p[0]->nodetype == en_icon) {
	    swap_nodes(ep);
	}
	if (ep->v.p[1]->nodetype == en_icon) {
	    ep->v.p[1]->v.i += xfold(ep->v.p[0]);
	    return ep;
	}
	break;
      case en_sub:
	if (ep->v.p[0]->nodetype == en_icon) {
	    ep->v.p[0]->v.i -= xfold(ep->v.p[1]);
	    return ep;
	}
	if (ep->v.p[1]->nodetype == en_icon) {
	    ep->v.p[1]->v.i -= xfold(ep->v.p[0]);
	    return ep;
	}
	break;
      default:
	break;
    }
    i = xfold(ep);
    if (i != 0L) {
	/*
	**	strip_icon() is in fact harmless here since this value is
	**	just added to ep
	**
	**	consider in 16-bit mode:
	**
	**		int day, year;
	**		day = 365 * (year - 1970);
	**
	**	and look at the code, which is transformed to
	**
	**		day = 365*year + 1846;
	**
	**	which works if the multiplication returns the lower 16 bits
	**	of the result correctly.
	*/
	i = strip_icon(i, ep->etp);
	ep1 = mk_icon(i, ep->etp);
	ep = mk_node(en_add, ep, ep1, ep->etp);
    }
    return ep;
}


/*
**	apply all constant optimizations.
*/
EXPR *
constantopt P1(EXPR*, ep)
{
    ep = fold_const(ep);
    return opt0(ep);
}
