/*
 * 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
 */

/*
 * This module will step through the parse tree and find all optimizable
 * expressions.  At present these expressions are limited to expressions
 * that are valid throughout the scope of the function. the list of
 * optimizable expressions is:
 *
 *	constants
 *	global and static addresses
 *	auto addresses
 *	contents of auto addresses.
 *
 * Contents of auto addresses are valid only if the address is never
 * referred to without dereferencing.
 *
 * Scan() will build a list of optimizable expressions which opt1 will
 * replace during the second optimization pass.
 */

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

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

typedef	int	WEIGHT;		/* weighting of expression complexity */

static BOOL	is_equalsym	P_((const SYM *, const SYM *));
static void	walkstmt	P_((STMT *, void (*)(STMT *), EXPR * (*)(EXPR *)));
static CSE *	searchnode	P_((const EXPR *));
static CSE *	enternode	P_((EXPR *, BOOL));
static CSE *	voidauto	P_((EXPR *));
static void	scannode	P_((EXPR *, BOOL));
static void	scan		P_((STMT *));
static void	bsort		P_((CSE **));
static EXPR *	repcsenode	P_((EXPR *));
static void	repcse		P_((STMT *));
static WEIGHT	swapnode	P_((EXPR *));
static void	swap		P_((STMT *));

static int	regptr = 0;
static SYM *	reglst[REG_LIST];
static int	autoptr = 0;
static SYM *	autolst[AUTO_LIST];
static CSE *	olist;	/* list of optimizable expressions */

/*****************************************************************************/

/*
**	copy the node passed into a new enode so it wont get corrupted during
**	substitution.
*/
EXPR   *
copynode P1(const EXPR *, ep)
{
    EXPR   *temp;
    if (ep == NIL_EXPR)
	return NIL_EXPR;
    temp = (EXPR *) xalloc((size_t) sizeof(EXPR));
    *temp = *ep;
    return temp;
}

/*****************************************************************************/

void
addoptinfo P2( SYM*, sp,  STORAGE, sc)
{
     TYP *	tp = sp->tp;

    switch (tp->type) {
      case bt_pointer32:
	if (is_array_type(tp) && sc != sc_parms)
	    break;
	/*FALLTHRU*/
      case bt_char:
      case bt_charu:
      case bt_uchar:
      case bt_schar:
      case bt_short:
      case bt_ushort:
      case bt_int16:
      case bt_uint16:
      case bt_int32:
      case bt_uint32:
      case bt_long:
      case bt_ulong:
      case bt_float:
      case bt_double:
	if (!is_volatile_qualified(tp)) {
	    switch (sp->storage_class) {
	      case sc_register:
		if (regptr < REG_LIST) {
		    reglst[regptr++] = sp;
#ifdef DEBUG
		    if (is_debugging(DEBUG_GLOBAL))
			dprintf ("adding '%s' to reglst\n", sp->name);
#endif /* DEBUG */
		}
		break;
	      case sc_parms:
	      case sc_auto:
		if (autoptr < AUTO_LIST) {
		    autolst[autoptr++] = sp;
#ifdef DEBUG
		    if (is_debugging(DEBUG_GLOBAL))
			dprintf ("adding '%s' to autolst\n", sp->name);
#endif /* DEBUG */
		}
		break;
	      default:
		break;
	    }
	}
	break;
      default:
	break;
    }
}

/*
 * Remove the expression from the register lists
 */
void
deloptinfo P1( EXPR *, ep)
{
    int i;
    assert(ep->nodetype == en_sym);
    for (i=0; i<autoptr; i++) {
	if (is_equalsym(autolst[i], ep->v.sp)) {
	    autolst[i] = NULL ;
#ifdef DEBUG
	    if (is_debugging(DEBUG_GLOBAL))
		dprintf ("removing '%s' from autolst\n", ep->v.sp->name);
#endif /* DEBUG */
	}
    }
    for (i=0; i<regptr; i++) {
	if (is_equalsym(reglst[i], ep->v.sp)) {
	    message(ERR_ADDREGVAR, reglst[i]->name);
	    reglst[i] = NULL;
#ifdef DEBUG
	if (is_debugging(DEBUG_GLOBAL))
	    dprintf ("removing '%s' from reglst\n", ep->v.sp->name);
#endif /* DEBUG */
	}
    }
}

/*****************************************************************************/

/*
**	used as a parameter to walkstmt() when no action is to be
**	performed on the expressions.
*/
static EXPR *
null_expr P1(EXPR *, ep)
{
    return ep;
}

/*
**	walkexpr() decends the expression tree recusively.
*/
EXPR *
#if defined(__STDC__) || defined(__cplusplus)
walkexpr(EXPR *ep, EXPR * (*exprfunc) (EXPR *))
#else
walkexpr(stmt, exprfunc)
    EXPR *	ep;
    EXPR *	(*exprfunc) ();
#endif
{
    if (ep == NIL_EXPR)
	return NIL_EXPR;
    switch (ep->nodetype) {
      case en_add:
      case en_sub:
      case en_mul:
      case en_div:
      case en_mod:
      case en_lsh:
      case en_rsh:
      case en_and:
      case en_or:
      case en_xor:
      case en_land:
      case en_lor:
      case en_eq:
      case en_ne:
      case en_lt:
      case en_le:
      case en_gt:
      case en_ge:
      case en_cond:
      case en_comma:
      case en_list:
      case en_asadd:
      case en_assub:
      case en_asmul:
      case en_asmul2:
      case en_asdiv:
      case en_asdiv2:
      case en_asor:
      case en_asxor:
      case en_asand:
      case en_asmod:
      case en_aslsh:
      case en_asrsh:
      case en_fcall:
      case en_call:
      case en_assign:
	ep->v.p[1] = walkexpr(ep->v.p[1], exprfunc);
	/*FALLTHRU*/
      case en_uminus:
      case en_not:
      case en_test:
      case en_compl:
      case en_ainc:
      case en_adec:
      case en_cast:
      case en_deref:
      case en_ref:
      case en_fieldref:
	ep->v.p[0] = walkexpr(ep->v.p[0], exprfunc);
	/*FALLTHRU*/
      case en_size:
      case en_fcon:
      case en_icon:
      case en_nacon:
      case en_labcon:
      case en_autocon:
      case en_sym:
      case en_register:
      case en_str:
	ep = (*exprfunc) (ep);
	break;
      default:
	CANNOT_REACH_HERE();
	break;
    }
    return ep;
}


/*
**	walkstmt() decends the statement tree recusively.
*/
static void
#if defined(__STDC__) || defined(__cplusplus)
walkstmt(STMT *stmt, void (*stmtfunc) (STMT *), EXPR * (*exprfunc) (EXPR *))
#else
walkstmt(stmt, stmtfunc, walk, exprfunc)
    STMT *	stmt;
    void	(*stmtfunc) ();
    EXPR *	(*exprfunc) ();
#endif
{
    for (; stmt != NIL_STMT; stmt = stmt->next) {
	switch (stmt->stype) {
	  case st_return:
	  case st_expr:
	    stmt->exp  = (*exprfunc)(stmt->exp);
	    break;
	  case st_while:
	  case st_do:
	    stmt->exp  = (*exprfunc)(stmt->exp);
	    (*stmtfunc)(stmt->s1);
	    break;
	  case st_for:
	    stmt->exp  = (*exprfunc)(stmt->exp);
	    stmt->v1.e = (*exprfunc)(stmt->v1.e);
	    (*stmtfunc)(stmt->s1);
	    stmt->v2.e = (*exprfunc)(stmt->v2.e);
	    break;
	  case st_if:
	    stmt->exp  = (*exprfunc)(stmt->exp);
	    (*stmtfunc)(stmt->s1);
	    (*stmtfunc)(stmt->v1.s);
	    break;
	  case st_switch:
	    stmt->exp  = (*exprfunc)(stmt->exp);
	    (*stmtfunc)(stmt->v1.s);
	    break;
	  case st_case:
	  case st_default:
	  case st_label:
	    (*stmtfunc)(stmt->v1.s);
	    break;
	  case st_compound:
	    (*stmtfunc)(stmt->s1);
	    break;
	  case st_goto:
	  case st_break:
	  case st_continue:
#ifdef ASM
	  case st_asm:
#endif /* ASM */
	    break;
	  default:
	    CANNOT_REACH_HERE();
	    break;
	}
    }
}

/*****************************************************************************/

/*
**	is_equalsym() will return TRUE if the symbols pointed to by sp1
**	and sp2 are equivalent.
*/

static BOOL
is_equalsym P2(const SYM *, sp1, const SYM *, sp2)
{
    if (sp1 == NIL_SYM || sp2 == NIL_SYM)
	 return FALSE;
    if (sp1->storage_class != sp2->storage_class)
	return FALSE;
    switch (sp1->storage_class) {
      case sc_auto:
      case sc_parms:
      case sc_register:
	return (sp1->value.i == sp2->value.i);
      default:
	return FALSE;
    }
}

/*
**	is_equalnode() will return TRUE if the expressions pointed to by ep1
**	and ep2 are equivalent.
*/
BOOL
is_equalnode P2(const EXPR *, ep1, const EXPR *, ep2)
{
    if (ep1 == NIL_EXPR || ep2 == NIL_EXPR)
	return FALSE;
    if (ep1->nodetype != ep2->nodetype)
	return FALSE;
    switch (ep1->nodetype) {
      case en_icon:
      case en_autocon:
	return (ep1->v.i == ep2->v.i);
      case en_labcon:
	return (ep1->v.l == ep2->v.l);
      case en_nacon:
	return (ep1->v.str == ep2->v.str);
      case en_sym:
	return is_equalsym(ep1->v.sp, ep2->v.sp);
      case en_ref:
      case en_fieldref:
      case en_uminus:
      case en_cast:
	return is_equalnode(ep1->v.p[0], ep2->v.p[0]);
      case en_add:
      case en_sub:
	return is_equalnode(ep1->v.p[1], ep2->v.p[1]) &&
	       is_equalnode(ep1->v.p[0], ep2->v.p[0]);
      default:
	return FALSE;
    }
}


/*
**	searchnode will search the common expression table for an entry that
**	matches the node passed and return a pointer to it.
**	the top level of is_equalnode is code inline here for speed
*/
static CSE *
searchnode P1(const EXPR *, ep)
{
    register CSE *csp;
    register EXPR *ep1;
    if (ep == NIL_EXPR)
	return NIL_CSE;
    for (csp = olist; csp != NIL_CSE; csp = csp->next) {
	ep1 = csp->exp;
	if (ep1 != NIL_EXPR && ep->nodetype == ep1->nodetype) {
	    switch (ep->nodetype) {
	      case en_icon:
	      case en_autocon:
		if (ep->v.i == ep1->v.i)
		    return csp;
		break;
	      case en_labcon:
		if (ep->v.l == ep1->v.l)
		    return csp;
		break;
	      case en_nacon:
		if (ep->v.str == ep1->v.str)
		    return csp;
		break;
	      case en_sym:
		if (ep->v.sp == ep1->v.sp)
		    return csp;
		break;
	      case en_ref:
	      case en_fieldref:
		if (is_equalnode(ep->v.p[0], ep1->v.p[0]))
		    return csp;
		break;
	      default:
		break;
	    }
	}
    }
    return NIL_CSE;
}

/*****************************************************************************/

/*
**	returns the desirability of optimization for a subexpression.
*/
USES
desire P1(const CSE *, csp)
{
    const EXPR *ep = csp->exp;
    if (csp->voidf || (ep->nodetype == en_icon &&
		       ep->v.i < 32L && ep->v.i >= 0L))
	return (USES)0;
    if (is_lvalue(ep))
	return (USES)((int)csp->uses * 2);
    return csp->uses;
}

/*
**	bsort() implements a bubble sort on the expression list.
*/
static void
bsort P1(CSE**, lst)
{
    CSE     *csp1, *csp2;
    register USES uses;
    csp1 = *lst;
    if (csp1 == NIL_CSE || csp1->next == NIL_CSE)
	return;
    bsort(&(csp1->next));
    uses = desire(csp1);
    while ((csp1 != NIL_CSE) &&
	   (csp2 = csp1->next) != NIL_CSE && uses < desire(csp2)) {
	*lst = csp2;
	csp1->next = csp2->next;
	csp2->next = csp1;
	lst = &(csp2->next);
    }
}

/*****************************************************************************/

/*
**	enternode() will enter a reference to an expression node into
**	the common expression table.
**
**	duse is a flag indicating whether or not this reference will be
**	dereferenced.
*/
static CSE *
enternode P2( EXPR *, ep,  BOOL, duse)
{
    CSE		*csp;
    if ((csp = searchnode(ep)) == NIL_CSE) {	/* add to tree */
	csp = (CSE *) xalloc((size_t) sizeof(CSE));
	csp->next  = olist;
	csp->uses  = (USES)0;
	csp->duses = (USES)0;
	csp->exp   = copynode(ep);
	csp->voidf = FALSE;
	csp->reg   = NO_REG;
	olist = csp;
	return csp;
    }

    /*
    **	Integer constants may be in the table with different sizes --
    **	keep the maximum size
    */
    if (ep->nodetype == en_icon && ep->etp->size > csp->exp->etp->size) {
	csp->exp->etp = ep->etp;
    }
    ++(csp->uses);
    if (duse)
	++(csp->duses);
    return csp;
}


/*
**	voidauto() will void an auto dereference node which points to the
**	same auto constant as ep.
**/

static CSE *
voidauto P1( EXPR *, ep)
{
    CSE		*csp;
    for (csp = olist; csp != NIL_CSE; csp = csp->next) {
	if (is_lvalue(csp->exp) && is_equalnode(ep, csp->exp->v.p[0])) {
	    if (csp->voidf)
		return NIL_CSE;
	    csp->voidf = TRUE;
	    return csp;
	}
    }
    return NIL_CSE;
}


/*
**	scannode() will scan the expression pointed to by node for
**	optimizable subexpressions.
**
**	When an optimizable expression is found it is entered into the tree.
**	If a reference to an auto node is scanned the corresponding
**	auto dereferenced node will be voided.
**
**	duse should be set if the expression will be dereferenced.
*/

static void
scannode P2( EXPR *, ep,  BOOL, duse)
{
    CSE		*csp, *csp1;
    if (ep == NIL_EXPR)
	return;
    switch (ep->nodetype) {
      case en_fcon:
      case en_str:
	break;
      case en_icon:
      case en_autocon:
      case en_sym:
	/*
	**	look if the dereferenced use of the node is in the
	**	list, remove it in this case.
	*/
	if ((csp = voidauto(ep)) != NIL_CSE) {
	    csp1 = enternode(ep, duse);
	    csp1->duses += csp->uses;
	    break;
	}
	/*FALLTHRU*/
      case en_labcon:
      case en_nacon:
	VOIDCAST enternode(ep, duse);
	break;
      case en_ref:
      case en_fieldref:
	if (ep->v.p[0]->nodetype == en_sym &&
	    (ep->v.p[0]->v.sp->storage_class == sc_auto ||
	     ep->v.p[0]->v.sp->storage_class == sc_parms ||
	     ep->v.p[0]->v.sp->storage_class == sc_register)) {
	     BOOL  first = (searchnode(ep) == NIL_CSE);
	    csp = enternode(ep, duse);
	    if (searchnode(ep->v.p[0]) != NIL_CSE) {
		/*
		**	the non-derereferenced use of the auto node
		**	is already in the list.
		*/
		csp->voidf = TRUE;
		scannode(ep->v.p[0], TRUE);
	    } else if (first) {
		/*
		**	look for register nodes
		*/
		int	i;
		SYM * sp = ep->v.p[0]->v.sp;
		for (i = 0; i < regptr; ++i) {
		    if (is_equalsym(reglst[i], sp)) {
			csp->voidf--;	/* this is not in auto_lst */
			csp->uses += (USES)(90 * (100 - i));
			csp->duses += (USES)(30 * (100 - i));
			break;
		    }
		}

		/*
		**	set voidf if the node is not in autolst
		*/
	        csp->voidf++;
		for (i = 0; i < autoptr; ++i) {
		    if (is_equalsym(autolst[i], sp)) {
			csp->voidf--;
			break;
		    }
		}

		/*
		**	Even if that item must not be put in a register,
                **	it is legal to put its address therein
                */
                if (csp->voidf) {
                    scannode(ep->v.p[0], TRUE);
		}
	    }
	} else {
	    scannode(ep->v.p[0], TRUE);
	}
	break;
      case en_add:
      case en_sub:
	scannode(ep->v.p[1], duse);
	scannode(ep->v.p[0], duse);
	break;
      case en_ainc:
      case en_adec:
      case en_deref:
	scannode(ep->v.p[0], duse);
	break;
      case en_asadd:
      case en_assub:
      case en_mul:
      case en_div:
      case en_lsh:
      case en_rsh:
      case en_mod:
      case en_and:
      case en_or:
      case en_xor:
      case en_lor:
      case en_land:
      case en_eq:
      case en_ne:
      case en_gt:
      case en_ge:
      case en_lt:
      case en_le:
      case en_asmul:
      case en_asmul2:
      case en_asdiv:
      case en_asdiv2:
      case en_asmod:
      case en_aslsh:
      case en_asrsh:
      case en_asand:
      case en_asor:
      case en_asxor:
      case en_cond:
      case en_comma:
      case en_list:
      case en_assign:
	scannode(ep->v.p[1], FALSE);
	/*FALLTHRU*/
      case en_uminus:
      case en_compl:
      case en_not:
      case en_test:
      case en_cast:
	scannode(ep->v.p[0], FALSE);
	break;
      case en_fcall:
      case en_call:
	scannode(ep->v.p[0], TRUE);
	scannode(ep->v.p[1], FALSE);
	break;
      default:
	CANNOT_REACH_HERE();
	break;
    }
}

static EXPR *
scanexpr P1(EXPR*, ep)
{
    scannode (ep, FALSE);
    return ep;
}


/*
**	scan() will gather all optimizable expressions into the
**	expression list for a block of statements.
**/

static void
scan P1( STMT *, stmt)
{
    walkstmt (stmt, scan, scanexpr);
}

/*****************************************************************************/

/*
**	constant() will fold all constant expression in the statement
**	tree.
*/
static void
constant P1( STMT *, stmt)
{
    walkstmt (stmt, constant, constantopt);
}

/*****************************************************************************/

static EXPR *
unsymbolnode P1(EXPR*, ep)
{
    SYM *sp;
    TYP *tp;
    switch (ep->nodetype) {
      case en_sym:
	sp = ep->v.sp;
	tp = sp->tp;
	switch (sp->storage_class) {
	  case sc_static:
	    if (tp->type != bt_func) {
		ep->nodetype = en_labcon;
		ep->v.l = sp->value.l;
		ep->etp = mk_type(tp_pointer, tp);
		break;
	    }
	    /*FALLTHRU*/
	  case sc_global:
	  case sc_external:
	    ep->nodetype = en_nacon;
	    ep->v.str = sp->name;
	    ep->etp = mk_type(tp_pointer, tp);
	    break;
	  case sc_const:
	    ep->nodetype = en_icon;
	    ep->v.i = sp->value.i;
	    break;
	  case sc_register:
	  case sc_auto:
	  case sc_parms:
	    ep->etp = mk_type(tp_pointer, tp);
	    ep->nodetype = en_autocon;
	    ep->v.i = sp->value.i;
	    break; 
	  default:
	    CANNOT_REACH_HERE();
	    break;
	}
	break;
      default:
	break;
    }
    return ep;
}

EXPR *
unsymbolexpr P1(EXPR *, ep)
{
    return walkexpr(ep, unsymbolnode);
}

/*
**	 unsymbol() will remove all references to en_sym nodes.
*/
static void
unsymbol P1(STMT*, stmt)
{
    CSE		*csp;
    walkstmt (stmt, unsymbol, unsymbolexpr);
    /* must also change the Common Sub-expression tree */
    for (csp = olist; csp ; csp = csp->next) {
	csp->exp = walkexpr(csp->exp, unsymbolnode);
    }
}

/*****************************************************************************/

static EXPR *
transformnode P1(EXPR *, ep)
{
    return g_transform(ep);
}

static EXPR *
transformexpr P1(EXPR *, ep)
{
    return walkexpr(ep, transformnode);
}

/*
**	transform() will descend the statement tree calling a code
**	generator specific routine to transform all expressions which
**	are performed by run-time support routines.
*/
static void
transform P1(STMT *, stmt)
{
    walkstmt (stmt, transform, transformexpr);
}

/*****************************************************************************/

/*
**	repcsenode() will replace all allocated references within an
**	expression with register nodes.
*/
static EXPR *
repcsenode P1(EXPR *, ep)
{
    CSE     *csp;
    switch (ep->nodetype) {
      case en_register:
	break;
      case en_fcon:
      case en_icon:
      case en_nacon:
      case en_labcon:
      case en_autocon:
      case en_sym:
      case en_ref:
      case en_fieldref:
	if (((csp = searchnode(ep)) != NIL_CSE) && (csp->reg != NO_REG)) {
	    ep->nodetype = en_register;
	    ep->v.r = csp->reg;
	}
	break;
      default:
	break;
    }
    return ep;
}

static EXPR *
repcseexpr P1(EXPR *, ep)
{
    return walkexpr (ep, repcsenode);
}

/*
** 	 repcse() will scan through a block of statements replacing
**	 the optimized expressions with their temporary references.
*/
static void
repcse P1(STMT *, stmt)
{
    walkstmt (stmt, repcse, repcseexpr);
}

/*****************************************************************************/

static WEIGHT
swapnode P1(EXPR *, ep)
{
    WEIGHT lweight, rweight;
    if (ep == NIL_EXPR)
	return (WEIGHT)0;
    ep = opt0(ep);
    switch (ep->nodetype) {
      case en_register:
	return (WEIGHT)1;
      case en_icon:
      case en_fcon:
      case en_nacon:
      case en_labcon:
      case en_autocon:
      case en_sym:
      case en_str:
	return (WEIGHT)2;
      case en_ref:
      case en_fieldref:
	return swapnode(ep->v.p[0]);
      case en_uminus:
      case en_not:
      case en_test:
      case en_compl:
      case en_ainc:
      case en_adec:
      case en_cast:
      case en_deref:
	return swapnode(ep->v.p[0])+1;
      case en_lt:
      case en_le:
      case en_gt:
      case en_ge:
	/* almost cummutative operators */
      case en_eq:
      case en_ne:
      case en_add:
      case en_mul:
      case en_and:
      case en_or:
      case en_xor:
	/* cummutative operators */
	lweight = swapnode(ep->v.p[0]);
	rweight = swapnode(ep->v.p[1]);
	if (rweight > lweight) {
	    swap_nodes(ep);
	}
	return lweight + rweight;
      case en_sub:
      case en_div:
      case en_mod:
      case en_lsh:
      case en_rsh:
      case en_land:
      case en_lor:
      case en_cond:
      case en_comma:
      case en_list:
      case en_asadd:
      case en_assub:
      case en_asmul:
      case en_asmul2:
      case en_asdiv:
      case en_asdiv2:
      case en_asor:
      case en_asxor:
      case en_asand:
      case en_asmod:
      case en_aslsh:
      case en_asrsh:
      case en_assign:
	return swapnode(ep->v.p[0]) + swapnode(ep->v.p[1]);
      case en_fcall:
      case en_call:
	return (WEIGHT)10 + swapnode(ep->v.p[0]) + swapnode(ep->v.p[1]);
      default:
	CANNOT_REACH_HERE();
	break;
    }
    return (WEIGHT)0;
}

static EXPR *
swapexpr P1(EXPR *, ep)
{
    VOIDCAST swapnode (ep);
    return ep;
}

/*
**	swap() will swap commutative expression nodes so that the most
**	complex node will be evaluated first - thus possibly preventing
**	an intermediate value from being temporarily pushed on the stack
**	during expression evaluation.
*/
static void
swap P1(STMT *, stmt)
{
    walkstmt (stmt, swap, swapexpr);
}

/*****************************************************************************/

static EXPR *
ordernode P1(EXPR *, ep)
{
    return g_order(ep);
}

static EXPR *
orderexpr P1(EXPR *, ep)
{
    return walkexpr (ep, ordernode);
}

static void
order P1(STMT *, stmt)
{
    walkstmt (stmt, order, orderexpr);
}

/*****************************************************************************/

/*
**	decend the statement tree recusively and sort consecutive case
**	statements into assending order.
*/
static void
ordercase P1(STMT*, stmt)
{
    walkstmt (stmt, ordercase, null_expr);
    if (stmt) {
	switch (stmt->stype) {
	  case st_case:
	    if (stmt->s1 && stmt->s1->stype == st_case &&
		(stmt->s1 == stmt->v1.s) &&
		(stmt->s1->v2.i < stmt->v2.i)) {
		 IVAL i = stmt->v2.i ;
		stmt->v2.i = stmt->s1->v2.i ;
		stmt->s1->v2.i = i ;
	    }
	    break;
	  default:
	    break;
	}
    }
}

/*****************************************************************************/

/*
**	globalopt() is the externally callable optimization routine.
**	It will collect and allocate common subexpressions and substitute
**	the tempref for all occurrances of the expression within
**	the block.
*/
CSE *
globalopt P1(STMT*, stmt)
{
    olist = NIL_CSE;
#ifdef ICODE
#ifdef DEBUG
    if (icode_option) {
	if (is_debugging(DEBUG_GLOBAL)) {
	    genicode(stmt,0);
	}
    }
#endif /* DEBUG */
#endif /* ICODE */
    constant(stmt);		/* constant folding */
    transform(stmt);
    if (opt_option) {
	scan(stmt);		/* collect expressions */
	bsort(&olist);		/* sort expressions list into usage order */
    }
    unsymbol(stmt);		/* replace all references to symbols */
#ifdef ICODE
#ifdef DEBUG
    if (icode_option) {
	if (is_debugging(DEBUG_GLOBAL)) {
	    genicode(stmt,0);
	}
    }
#endif /* DEBUG */
#endif /* ICODE */
    if (opt_option) {
	g_allocate(olist);	/* allocate registers */
	repcse(stmt);		/* replace allocated expressions */
	swap(stmt);		/* swap commutative expression */
	order(stmt);		/* code generation order */
	ordercase(stmt) ;
    }
#ifdef ICODE
    if (icode_option) {
	genicse(olist);
    }
#endif /* ICODE */
    regptr = autoptr = 0;
    return olist;
}
#undef P_
#endif /* CPU_DEFINED */
