/*
 * 68K/386 32-bit C compiler.
 *
 * copyright (c) 1996, David Lindauer
 * 
 * This compiler is intended for educational use.  It may not be used
 * for profit without the express written consent of the author.
 *
 * It may be freely redistributed, as long as this notice remains intact
 * and sources are distributed along with any executables derived from them.
 *
 * The author is not responsible for damages, either direct or consequential,
 * that may arise from use of this software.
 *
 * V1.85 July 2000
 * David Lindauer, gclind01@starbase.spd.louisville.edu
 *
 * Credits to Mathew Brandt for original K&R C compiler
 *
 */
/*
 * iexpr.c 
 *
 * routies to take an enode list and generate icode
 */
#include        <stdio.h>
#include				"utype.h"
#include        "lists.h"
#include        "expr.h"
#include        "c.h"
#include				"iexpr.h"
#include				"iopt.h"
#include 				"diag.h"

/*
 *      this module contains all of the code generation routines
 *      for evaluating expressions and conditions.
 */
extern long nextlabel;
extern long bittab[32];
extern IMODE rret;
extern short intsize, addrsize, regsize;
int chksize(int lsize, int rsize)
/*
 * compare two sizes, ignoring sign during comparison
 */
{
	int l,r;
	l = lsize;
	r = rsize;
	if (l < 0) l = - l;
	if (r < 0) r = - r;
	return(l > r);
}
IMODE *make_imaddress(enum e_node type, SYM *sp)
{
	ENODE *node = makenode(type,sp,0);
	IMODE *ap2 = xalloc(sizeof(IMODE));
  ap2->mode = i_immed;
	ap2->size = addrsize;
  ap2->offset = node;     /* use as constant node */
	return ap2;
}
IMODE *make_imval(enum e_node type, SYM *sp)
{
	ENODE *node = makenode(type,sp,0);
	IMODE *ap1;
                ap1 = xalloc(sizeof(IMODE));
                ap1->mode = i_direct;
								ap1->size = sp->tp->size;
								if (sp->tp->type == bt_char || sp->tp->type == bt_short || sp->tp->type == bt_enum || sp->tp->type == bt_long)
									ap1->size = -ap1->size;
                ap1->offset = makenode(type,sp,0);
								ap1->offset->cflags = sp->tp->cflags;
                return ap1;
}
ENODE		 *makeenimode(SYM *sp)
{
	ENODE *rv;
	if (sp->storage_class == sc_member)
		return;
	rv = xalloc(sizeof(ENODE));
	
	rv->nodetype = en_imode;
                switch( sp->storage_class ) {
                        case sc_static:
                        case sc_global:
                        case sc_external:
                        case sc_externalfunc:
												case sc_abs:
																if ((sp->tp->type == bt_pointer && (sp->tp->btp->type == bt_ifunc || sp->tp->btp->type == bt_func)) || sp->tp->type == bt_ifunc|| sp->tp->type == bt_func) { 
/* make a function node */
isfunc:
																	if (sp->tp->type == bt_pointer) {
                                		rv->v.p[0]= make_imaddress(en_nacon,sp);
                                		rv->v.p[1]= make_imval(en_nacon,sp);
																	}
																	else {
                                		rv->v.p[0] = make_imaddress(en_napccon,sp);
                                		rv->v.p[1] = make_imval(en_napccon,sp);
																	}
																}
																else
/* otherwise make a node for a regular variable */
																	if (sp->absflag) {
                                		rv->v.p[0] = make_imaddress(en_absacon,sp);
                                		rv->v.p[1] = make_imval(en_absacon,sp);
																	}
																	else
																		if (sp->tp->type == bt_func || sp->tp->type == bt_ifunc) {
                                			rv->v.p[0] = make_imaddress(en_napccon,sp);
                                			rv->v.p[1] = make_imval(en_napccon,sp);
																		}
																		else
																			if (sp->staticlabel) {
																				rv->v.p[0] = make_imaddress(en_nalabcon,(char *)sp->value.i);
																				rv->v.p[1] = make_imval(en_nalabcon,(char *)sp->value.i);
																			}
																			else {
                                				rv->v.p[0] = make_imaddress(en_nacon,sp);
                                				rv->v.p[1] = make_imval(en_nacon,sp);
																			}
                                break;
                        case sc_const:
/* constants and enums */
                                rv->v.p[0] = makenode(en_icon,(char *)sp->value.i,0);
																break;
                        default:        /* auto and any errors */
															 		if (sp->storage_class == sc_auto) {
                                		rv->v.p[0] = make_imaddress(en_autocon,sp);
                                		rv->v.p[1] = make_imval(en_autocon,sp);
																	}
																	else if (sp->storage_class == sc_autoreg) {
                                		rv->v.p[0] = make_imaddress(en_autoreg,sp);
                                		rv->v.p[1] = make_imval(en_autoreg,sp);
																	}
																if ((sp->tp->type == bt_pointer && (sp->tp->btp->type == bt_ifunc || sp->tp->btp->type == bt_func)) || sp->tp->type == bt_ifunc|| sp->tp->type == bt_func)
																		goto isfunc;
                                break;
                        }
								rv->cflags = sp->tp->cflags;
								((IMODE *)rv->v.p[0])->offset->cflags = rv->cflags;
								((IMODE *)rv->v.p[1])->offset->cflags = rv->cflags;
								((IMODE *)rv->v.p[1])->vol = rv->cflags & DF_VOL;
	return /* sp->imode = */ rv;
}

	
	
IMODE    *make_bf(ENODE *node, IMODE *ap, int size)
/*
 *      construct a bit field reference for 68020 bit field instructions
 */
{
        IMODE    *ap1;
				if (node->startbit == -1)
					DIAG("Illegal bit field");
				ap->startbit = node->startbit;
				ap->bits = node->bits;
				ap->size = size;
        return ap;
}

IMODE    *make_immed(long i)
/*
 *      make a node to reference an immediate value i.
 */
{       IMODE    *ap = tempreg(4,0);
				gen_iiconst(ap,i);
				return ap;
}
IMODE 	*make_parmadj(long i)
/*
 *			make a direct immediate, e.g. for parmadj
 */
{
				ENODE *node = makenode(en_icon,(char *)i,0);
				IMODE *ap = xalloc(sizeof(IMODE));
				ap->mode = i_immed;
				ap->offset = node;
				return ap;
}
			

IMODE    *make_offset(ENODE *node)
/*
 *      make a direct reference to a node.
 */
{       IMODE    *ap;
        ap = xalloc(sizeof(IMODE));
        ap->mode = i_direct;
				ap->size = natural_size(node);
        ap->offset = node;
        return ap;
}
        
IMODE	*genasn(ENODE *node, int size)
/*
 * Find the targe of an assigment.  if node is 0 we need a temp reg,
 * otherwise we calculate the assignment target
 */
{
	if (!node)
		return tempreg(size,0);
	return gen_expr(0,node,0,size);
}
IMODE *indnode(IMODE *ap1, int size)
/*
 * copy the address mode and change it to an indirect type
 *
 */
{
	IMODE *ap = xalloc(sizeof(IMODE)),*ap2;
	if (ap1->mode == i_ind) {
		ap2 = tempreg(size,0);
		ap2->bits = ap1->bits;
		ap2->startbit = ap1->startbit;
    gen_icode(i_assn,ap2,ap1,0);
		ap1 = ap2;
  }
	ap->bits = ap1->bits;
	ap->startbit = ap1->startbit;
	ap->offset = ap1->offset;
	ap->size = size;
	ap->mode = i_ind;
	return ap;
}
IMODE    *gen_deref(ENODE *node, int flags,int size)
/*
 *      return the addressing mode of a dereferenced node.
 */
{       IMODE    *ap1,*ap2,*ap3,*ap4;
        int             siz1,psize;
				psize = size;
				if (psize < 0)
					psize = - psize;
				/* get size */
        switch( node->nodetype )        /* get load size */
                {
                case en_bool_ref:
                case en_ub_ref:
                        siz1 = 1;
                        break;
                case en_b_ref:
                        siz1 = -1;
                        break;
                case en_uw_ref:
                        siz1 = 2;
                        break;
                case en_w_ref:
                        siz1 = -2;
                        break;
                case en_l_ref:
												siz1 = -4;
												break;
								case en_ptr_ref:
												siz1 = addrsize;
												break;
								case en_add:
												siz1 = addrsize;
												break;
								case en_ul_ref:
                        siz1 = 4;
                        break;
								case en_floatref:
												siz1 = 6;
												break;
								case en_doubleref:
												siz1 = 8;
												break;
								case en_longdoubleref:
												siz1 = 10;
												break;
								default:
												siz1 = intsize;
                }
				/* deref for add nodes */
        if( node->v.p[0]->nodetype == en_add )
                {
                ap1 = gen_expr(0,node->v.p[0],0,size);
								ap1 = indnode(ap1,siz1);
								ap1->offset->cflags = node->v.p[0]->cflags;
                return ap1;
                }
				/* deref for auto variables */
        else if( node->v.p[0]->nodetype == en_imode)
                {
                ap1 = node->v.p[0]->v.p[1];
                return ap1;
                }
				/* deref for cpu registers */
				else if (node->v.p[0]->nodetype == en_regref) {
        	ap1 = gen_expr(0,node->v.p[0],0,regsize);
					ap1->offset->cflags = node->v.p[0]->cflags;
					return ap1;
				}
				/* other deref */
        ap1 = gen_expr(0,node->v.p[0],0,siz1); /* generate address */
				ap1 = indnode(ap1,siz1);
				ap1->offset->cflags = node->v.p[0]->cflags;
        return ap1;
}

IMODE    *gen_unary(ENODE *asn, ENODE *node,int flags,int size,int op)
/*
 *      generate code to evaluate a unary minus or complement.
 */
{       IMODE    *ap,*ap1;
        ap = gen_expr(0,node->v.p[0],F_VOL,size);
        gen_icode(op,ap1=genasn(asn,size),ap,0);
        return ap1;
}

IMODE    *gen_binary(ENODE *asn, ENODE *node,int flags,int size,int op)
/*
 *      generate code to evaluate a binary node and return 
 *      the addressing mode of the result.
 */
{       IMODE    *ap,*ap1, *ap2;
        ap1 = gen_expr(0,node->v.p[0],F_VOL,size);
        ap2 = gen_expr(0,node->v.p[1],0,size);
        gen_icode(op,ap =genasn(asn,size),ap1,ap2);
        return ap;
}

IMODE * gen_pdiv(ENODE *asn, ENODE *node, int flags, int size)
/*
 *			generate code for an array/structure size compensation
 */
{
				IMODE *ap,*ap1, *ap2;
				ap1 = gen_expr(0,node->v.p[0],F_VOL,4);
				ap2 = gen_expr(0,node->v.p[1],0,2);
				gen_icode(i_udiv,ap=genasn(asn,size),ap2,ap1);
        return ap;
}			
IMODE * gen_pmul(ENODE *asn, ENODE *node, int flags, int size)
/*
 *			generate code for an array/structure size compensation
 */
{
				IMODE *ap, *ap1, *ap2;
				ap1 = gen_expr(0,node->v.p[0],F_VOL,intsize);
				ap2 = gen_expr(0,node->v.p[1],0,intsize);
				gen_icode(i_umul,ap=genasn(asn,size),ap2,ap1);
        return ap;
}			

IMODE    *gen_hook(ENODE *asn, ENODE *node, int flags, int size)
/*
 *      generate code to evaluate a condition operator node (?:)
 */
{
        int             false_label, end_label;
        false_label = nextlabel++;
        end_label = nextlabel++;
        flags = flags | F_VOL;
        falsejp(node->v.p[0],false_label);
        node = node->v.p[1];
				asn = genasn(asn,size);
        gen_expr(asn,node->v.p[0],flags,size);
        gen_igoto(end_label);
        gen_label(false_label);
        gen_expr(asn,node->v.p[1],flags,size);
        gen_label(end_label);
        return asn;
}

IMODE    *gen_asbin(ENODE *asn,ENODE *node, int flags, int size, int op)
/*
 *      generate a plus equal or a minus equal node.
 */
{       IMODE    *ap,*ap1, *ap2;
        int             ssize,rsize;
        ssize = natural_size(node->v.p[0]);
        rsize = natural_size(node->v.p[1]);
				ap1 = gen_expr(0,node->v.p[0],0,ssize);
				ap2 = gen_expr(0,node->v.p[1],0,rsize);
				gen_icode(op,ap1,ap1,ap2);
				if (asn)
					gen_icode(i_assn,genasn(asn,size),ap1,0);
				ap = xalloc(sizeof(IMODE));
				ap->bits = ap1->bits;
				ap->startbit = ap1->startbit;
				ap->offset = ap1->offset;
				ap->mode = ap1->mode;
				return(ap);
}

IMODE *gen_moveblock(ENODE *node, int flags, int size)
/*
 * Generate code to copy one structure to another
 */
{
	IMODE *ap1, *ap2;
	if (!node->size)
		return(0);
	ap1 = gen_expr(0,node->v.p[0], F_VOL, 4);
	ap2 = gen_expr(0,node->v.p[1], F_VOL,4);
	gen_icode(i_assnblock,ap1,ap2,(IMODE *)node->size);
	return(ap1);
}
IMODE    *gen_assign(ENODE *asn, ENODE *node, int flags, int size,int ref)
/*
 *      generate code for an assignment node. if the size of the
 *      assignment destination is larger than the size passed then
 *      everything below this node will be evaluated with the
 *      assignment size.
 */
{       IMODE    *ap2;
        int             ssize,rsize;
				int q = node->v.p[0]->nodetype;
				ENODE *enode;
				rsize = natural_size(node->v.p[1]);
				if (q == en_bits)
					q = node->v.p[0]->v.p[0]->nodetype;
        switch( q )        
                { 
                case en_bool_ref: case en_cbool:
                case en_ub_ref:
								case en_cub:
                        ssize = 1;
                        break;
                case en_b_ref:
								case en_cb:
                        ssize = -1;
                        break;
                case en_uw_ref:
								case en_cuw:
                        ssize = 2;
                        break;
                case en_w_ref:
								case en_cw:
                        ssize = -2;
                        break;
                case en_l_ref:
								case en_cl:
												ssize = -4;
												break;
								case en_ptr_ref:
												ssize = addrsize;
												break;
                case en_ul_ref:
								case en_cul:
								case en_cp:
                        ssize = 4;
                        break;
                case en_tempref:
                case en_regref:
												ssize = node->v.p[0]->v.i >> 8;
												break;
								case en_floatref:
								case en_cf:
												ssize = 6;
												break;
								case en_doubleref:
								case en_cd:
												ssize = 8;
												break;
								case en_longdoubleref:
								case en_cld:
												ssize = 10;
												break;	
                }
        if (chksize( size , rsize ))
                rsize = size;
				if (ref) {
					enode = makenode(node->v.p[0]->nodetype, makenode(en_cp,node->v.p[0]->v.p[0],0),0);
				}
				else enode = node->v.p[0];
        ap2 = gen_expr(enode,node->v.p[1],0,rsize);
				if (asn) 
					gen_icode(i_assn,genasn(asn,size),ap2,0);
				return ap2;
}

IMODE    *gen_aincdec(ENODE *asn, ENODE *node, int flags, int size, int op)
/*
 *      generate an auto increment or decrement node. op should be
 *      either op_add (for increment) or op_sub (for decrement).
 */
{       IMODE    *ap1;
        int             siz1;
        siz1 = natural_size(node->v.p[0]);
        if( flags & F_NOVALUE )         /* dont need result */
                {
        				ap1 = gen_expr(0,node->v.p[0],F_ADDR,siz1);
                gen_icode(op,ap1,ap1,make_immed((long)node->v.p[1]));
								return ap1;
								}
        ap1 = gen_expr(asn,node->v.p[0],F_ADDR,siz1);
        gen_icode(op,ap1,ap1,make_immed((long)node->v.p[1]));
        return asn;
}

int push_param(ENODE *ep,int size)
/*
 *      push the operand expression onto the stack.
 */
{       IMODE    *ap;
				int rv;
				switch (ep->nodetype) {
                case en_imode:
                        ap = ep->v.p[0];
												rv = intsize;
												break;
								default:
												rv = natural_size(ep);
      			  					ap = gen_expr(0,ep,F_ADDR,rv);
												gen_icode(i_parm,0,ap,0);
												rv = intsize;
												break;
				}
	return(rv < 0 ? -rv : rv);
}
int push_stackblock(ENODE *ep)
/*
 * Push a structure on the stack
 */
{
	IMODE *ap;
	int sz = ep->size;
	if (!sz)
		return(0);
				switch (ep->nodetype) {
                case en_imode:
                        ap = ep->v.p[0];
												break;
								default:
      			  					ap = gen_expr(0,ep,F_ADDR,sz);
												break;
				}
	gen_icode(i_parmblock,0,ap,(IMODE *)sz);
	sz = sz <0 ? -sz : sz;
	sz+=intsize-1;
	sz/=intsize;
	sz *= intsize;
	return sz;
}

int     gen_parms(ENODE *plist,int size)
/*
 *      push a list of parameters onto the stack and return the
 *      size of parameters pushed.
 */
{       int     i;
        i = 0;
        while( plist != 0 )
                {         	
								if (plist->nodetype == en_stackblock)
									i+=push_stackblock(plist->v.p[0]);
								else
                	i+=push_param(plist->v.p[0],size);               
                plist = plist->v.p[1];
                }
        return i;
}

IMODE    *gen_fcall(ENODE *asn, ENODE *node,int flags, int size)
/*
 *      generate a function call node and return the address mode
 *      of the result.
 */
{       IMODE    *ap;
        int             i,siz1;
				/* if it returns a structure */
				if (node->nodetype == en_callblock) {
					i = gen_parms(node->v.p[1]->v.p[1]->v.p[1]->v.p[0],size);
      		ap = gen_expr(0,node->v.p[0],F_ADDR,addrsize);
					gen_icode(i_parm,0,ap,0);
					i+=addrsize;
					node=node->v.p[1];
					siz1 = 4;
				}
				else  {
	        i = gen_parms(node->v.p[1]->v.p[1]->v.p[0],size);    /* generate parameters */
					siz1 = natural_size(node->v.p[1]);
				}
				if (node->nodetype == en_trapcall) {
					/* trap call */
					gen_igosub(i_trap,make_immed(node->v.p[1]->v.p[0]->v.i));
				}
				else if (node->nodetype == en_intcall) {
					/* int call */
					ap = make_offset(node->v.p[0]);
					ap->mode = i_immed;
					gen_igosub(i_int,ap);
				}
				/* named function */
     	  else if( node->v.p[1]->v.p[0]->nodetype == en_imode) {
								ap= node->v.p[1]->v.p[0]->v.p[0];
       	        gen_igosub(i_gosub,ap);
				}
        else
				/* pointer to function */
                {
                ap = gen_expr(0,node->v.p[1]->v.p[0],0,4);
                gen_igosub(i_gosub,ap);
                }
				/* undo pars and make a temp for the result */
        if( i != 0 ) {
                gen_nodag(i_parmadj,0,make_parmadj(i),0);
				}
				if (!(flags & F_NOVALUE))
        	gen_icode(i_assn,ap=genasn(asn,size),&rret,0);	
        return ap;
}
IMODE    *gen_pfcall(ENODE *asn, ENODE *node,int flags, int size)
/*
 *      generate a function call node and return the address mode
 *      of the result.
 */
{       IMODE    *ap;
        int             i,siz1;
				ENODE * invnode = 0,*anode,*node2;
				
				/* invert the parameter list */
				if (node->nodetype == en_pcallblock)
					anode = node->v.p[1]->v.p[1]->v.p[1]->v.p[0];
				else
					anode = node->v.p[1]->v.p[1]->v.p[0];
				while (anode) {
					invnode = makenode(anode->nodetype,anode->v.p[0],invnode);
					anode = anode->v.p[1];
				}
				/* if it returns a structure */
				if (node->nodetype == en_callblock) {
					i = gen_parms(invnode,size);
      		ap = gen_expr(0,node->v.p[0],F_ADDR,addrsize);
					gen_icode(i_parm,0,ap,0);
					i+=addrsize;
					node=node->v.p[1];
					siz1 = 4;
				}
				else  {
	        i = gen_parms(invnode,size);    /* generate parameters */
					siz1 = natural_size(node->v.p[1]);
				}
				if (node->nodetype == en_trapcall) {
					/* trap call */
					gen_igosub(i_trap,make_immed(node->v.p[1]->v.p[0]->v.i));
				}
				else if (node->nodetype == en_intcall) {
					/* int call */
					ap = make_offset(node->v.p[0]);
					ap->mode = i_immed;
					gen_igosub(i_int,ap);
				}
				/* named function */
     	  else if( node->v.p[1]->v.p[0]->nodetype == en_imode) {
								ap= node->v.p[1]->v.p[0]->v.p[0];
       	        gen_igosub(i_gosub,ap);
				}
        else
				/* pointer to function */
                {
                ap = gen_expr(0,node->v.p[1]->v.p[0],0,4);
                gen_igosub(i_gosub,ap);
                }
				/* undo pars and make a temp for the result */
        if( i != 0 ) {
                gen_nodag(i_parmadj,0,make_parmadj(i),0);
				}
				if (!(flags & F_NOVALUE))
        	gen_icode(i_assn,ap=genasn(asn,size),&rret,0);	
        return ap;
}

IMODE    *gen_expr(ENODE *asn,ENODE *node, int flags, int size)
/*
 *      general expression evaluation. returns the addressing mode
 *      of the result.
 *
 *			The first argument of this and other routines is a target for
 *			assignmennt.  If this is 0 and a target is needed a temp will be
 *			used; otherwise the target will be evaluated annd used.
 */
{
				IMODE    *ap1, *ap2,*ap3;
				int lab1,lab2;
        int             natsize,siz1;
        if( node == 0 )
                {
                DIAG("null node in gen_expr.");
                return 0;
                }
        switch( node->nodetype )
                {
								case en_cb: 
												siz1 = -1;
												goto castjoin;
                case en_cub: case en_cbool:
												siz1 = 1;
												goto castjoin;
								case en_cw: 
												siz1 = -2;
												goto castjoin;
								case en_cuw:
												siz1 = 2;
												goto castjoin;
								case en_cl: 
												siz1 = -4;
												goto castjoin;
								case en_cul:
												siz1 = 4;
												goto castjoin;
								case en_cf: 
												siz1 = 6;
												goto castjoin;
								case en_cd: 
												siz1 = 8;
												goto castjoin;
								case en_cld: 
												siz1 = 10;
												goto castjoin;
								case en_cp:
												siz1 = 4;
castjoin:
												ap1 = gen_expr(0,node->v.p[0],flags,natural_size(node->v.p[0]));
												if (ap1->size == siz1)
													ap2 = ap1;
												else {
													gen_icode(i_assn, ap2 = tempreg(siz1,0),ap1,0);
												}
												if (asn)
												  gen_icode(i_assn,gen_expr(0,asn,flags,size),ap2,0);
												return ap2;
                case en_imode:
												ap2 = node->v.p[0];
												if (asn)
												  gen_icode(i_assn,gen_expr(0,asn,flags,size),ap2,0);
                        return ap2;             /* return reg */
								case en_ccon:
                case en_icon:
                case en_lcon:
								case en_lucon:
								case en_iucon:
												gen_iiconst(ap1=tempreg(size,0),node->v.i);
												if (asn)
												  gen_icode(i_assn,gen_expr(0,asn,flags,size),ap1,0);
                        return ap1;
								case en_fcon:
								case en_rcon:
								case en_lrcon:
												gen_ifconst(ap1=tempreg(size,0),node->v.f);
												if (asn)
												  gen_icode(i_assn,gen_expr(0,asn,flags,size),ap1,0);
                        return ap1;
                case en_bool_ref:
                case en_b_ref:
                case en_w_ref:
                case en_ub_ref:
                case en_uw_ref:
                case en_l_ref:
								case en_ptr_ref:
                case en_ul_ref:
								case en_floatref:
								case en_doubleref:
								case en_longdoubleref:
                        ap1 = gen_deref(node,flags,size);
												if (asn) {
													ap2 = gen_expr(0,asn,flags,size);
													gen_icode(i_assn,ap2,ap1,0);
												}
												return ap1;
                case en_regref:
                        ap1 = xalloc(sizeof(IMODE));
												ap1->offset = node;
												ap1->mode = i_direct;
												ap1->size = intsize;
												if (asn)
												  gen_icode(i_assn,gen_expr(0,asn,flags,size),ap1,0);
                        return ap1;
								case en_bits:
												size = natural_size(node->v.p[0]);
												ap1 = gen_expr(asn,node->v.p[0],0,size);
												ap1 = make_bf(node,ap1,size);
												return ap1;
                case en_uminus:
                        return gen_unary(asn,node,flags,size,i_neg);
                case en_compl:
                        return gen_unary(asn,node,flags,size,i_not);
                case en_add:
                        return gen_binary(asn,node,flags,size,i_add);
                case en_sub:
                        return gen_binary(asn,node,flags,size,i_sub);
                case en_and:
                        return gen_binary(asn,node,flags,size,i_and);
                case en_or:
                        return gen_binary(asn,node,flags,size,i_or);
								case en_xor:
												return gen_binary(asn,node,flags,size,i_eor);
								case en_pmul:
												return gen_pmul(asn,node,flags,size);
								case en_pdiv:
												return gen_pdiv(asn,node,flags,size);
                case en_mul:
                        return gen_binary(asn,node,flags,size,i_smul);
                case en_umul:
                        return gen_binary(asn,node,flags,size,i_smul);
                case en_div:
                        return gen_binary(asn,node,flags,size,i_sdiv);
                case en_udiv:
                        return gen_binary(asn,node,flags,size,i_udiv);
                case en_mod:
                        return gen_binary(asn,node,flags,size,i_smod);
                case en_umod:
                        return gen_binary(asn,node,flags,size,i_umod);
                case en_alsh:
                        return gen_binary(asn,node,flags,size,i_asl);
                case en_arsh:
                        return gen_binary(asn,node,flags,size,i_asr);
                case en_lsh:
                        return gen_binary(asn,node,flags,size,i_lsl);
                case en_rsh:
                        return gen_binary(asn,node,flags,size,i_lsr);
                case en_asadd:
                        return gen_asbin(asn,node,flags,size,i_add);
                case en_assub:
                        return gen_asbin(asn,node,flags,size,i_sub);
                case en_asand:
                        return gen_asbin(asn,node,flags,size,i_and);
                case en_asor:
                        return gen_asbin(asn,node,flags,size,i_or);
                case en_aslsh:
                        return gen_asbin(asn,node,flags,size,i_lsl);
                case en_asrsh:
                        return gen_asbin(asn,node,flags,size,i_lsr);
                case en_asalsh:
                        return gen_asbin(asn,node,flags,size,i_asl);
                case en_asarsh:
                        return gen_asbin(asn,node,flags,size,i_asr);
                case en_asmul:
                        return gen_asbin(asn,node,flags,size, i_smul);
                case en_asumul:
                        return gen_asbin(asn,node,flags,size,i_umul);
                case en_asdiv:
                        return gen_asbin(asn,node,flags,size,i_sdiv);
                case en_asudiv:
                        return gen_asbin(asn,node,flags,size,i_udiv);
                case en_asmod:
                        return gen_asbin(asn,node,flags,size,i_sdiv);
                case en_asumod:
                        return gen_asbin(asn,node,flags,size,i_udiv);
                case en_assign:
                        return gen_assign(asn,node,flags,size,FALSE);
                case en_refassign:
                        return gen_assign(asn,node,flags | F_NOVALUE,size,TRUE);
                case en_moveblock:
                        return gen_moveblock(node,flags,size);
                case en_ainc:
                        return gen_aincdec(asn,node,flags,size,i_add);
                case en_adec:
                        return gen_aincdec(asn,node,flags,size,i_sub);
                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_ult:    case en_ule:
                case en_ugt:    case en_uge:
                case en_not:
                        siz1 = natural_size(node);    
												lab1=nextlabel++;
												lab2=nextlabel++;
                        falsejp(node ,lab1);
												ap2 = genasn(asn,size);
												gen_icode(i_assn,ap2,make_immed(1),0);
												gen_igoto(lab2);
												gen_label(lab1);
												gen_icode(i_assn,ap2,make_immed(0),0);
												gen_label(lab2);
                        return ap2;
                case en_cond:
                        return gen_hook(asn,node,flags,size);
                case en_void:
                        natsize = natural_size(node->v.p[0]);
                        return gen_expr(asn,node->v.p[1],flags,size);
                case en_pfcall:  case en_pfcallb: case en_pcallblock:
                        return gen_pfcall(asn,node,flags,size);
                case en_fcall:  case en_callblock: case en_fcallb:
                case en_intcall:
                case en_trapcall:
                        return gen_fcall(asn,node,flags,size);
                default:
                        DIAG("uncoded node in gen_expr.");
                        return 0;
                }
}

int     natural_size(ENODE *node)
/*
 *      return the natural evaluation size of a node.
 */
{       int     siz0, siz1;
        if( node == 0 )
                return 0;
        switch( node->nodetype )
                {
								case en_bits:
												return natural_size(node->v.p[0]);;
								case en_ccon:
												return 1;
								case en_lcon:
                case en_icon:
												return -intsize;
								case en_lucon:
                case en_iucon:
												return intsize;
								case en_rcon:
								case en_doubleref:
												return 8;
								case en_lrcon:
								case en_longdoubleref:
												return 10;
								case en_floatref:
								case en_fcon:
												return 6;
								case en_trapcall:
								case en_imode:
												return addrsize;
								case en_cl: case en_l_ref:
												return -4;
								case en_ptr_ref:
												return addrsize;
								case en_autoreg:
												return regsize;
								case en_fcall: case en_callblock: case en_fcallb:
								case en_pfcall: case en_pcallblock: case en_pfcallb:
								case en_intcall:
												return natural_size(node->v.p[1]);
								case en_tempref:
												return intsize;
								case en_regref:
												return regsize;
                case en_ul_ref:
								case en_cul:
								case en_cp:
                        return 4;
                case en_bool_ref: case en_cbool:
                case en_ub_ref:
								case en_cub:
												return 1;
                case en_b_ref:
								case en_cb:
                        return -1;
                case en_uw_ref:
								case en_cuw:
												return 2;
                case en_cw:
                case en_w_ref:
                        return -2;
								case en_cd:
												return 8;
								case en_cld:
												return 10;
								case en_cf:
												return 6;
                case en_not:    case en_compl:
                case en_uminus: case en_assign: case en_refassign:
                case en_ainc:   case en_adec:
								case en_moveblock: case en_stackblock:
                        return natural_size(node->v.p[0]);
                case en_add:    case en_sub:
								case en_umul:		case en_udiv:	case en_umod: case en_pmul:
                case en_mul:    case en_div:  case en_pdiv:
                case en_mod:    case en_and:
                case en_or:     case en_xor:
								case en_asalsh: case en_asarsh: case en_alsh: case en_arsh:
                case en_lsh:    case en_rsh:
                case en_eq:     case en_ne:
                case en_lt:     case en_le:
                case en_gt:     case en_ge:
								case en_ugt: case en_uge: case en_ult: case en_ule:
                case en_land:   case en_lor:
                case en_asadd:  case en_assub:
                case en_asmul:  case en_asdiv:
                case en_asmod:  case en_asand:
								case en_asumod: case en_asudiv: case en_asumul:
                case en_asor:   case en_aslsh:
                case en_asrsh:
                        siz0 = natural_size(node->v.p[0]);
                        siz1 = natural_size(node->v.p[1]);
                        if( chksize(siz1,siz0))
                                return siz1;
                        else
                                return siz0;
                case en_void:   case en_cond:
                        return natural_size(node->v.p[1]);
                default:
                        DIAG("natural size error.");
                        break;
                }
        return 0;
}

void gen_compare(ENODE *node, int btype, int label)
/*
 *      generate code to do a comparison of the two operands of
 *      node.
 */
{       IMODE    *ap1, *ap2;
        int             size;
        size = natural_size(node);
        ap1 = gen_expr(0,node->v.p[0],0,size);
        ap2 = gen_expr(0,node->v.p[1],0,size);
        gen_icgoto(btype,label,ap1,ap2);
}

void truejp(ENODE *node, int label)
/*
 *      generate a jump to label if the node passed evaluates to
 *      a true condition.
 */
{       IMODE    *ap1;
        int             siz1;
        int             lab0;
        if( node == 0 )
                return;
        switch( node->nodetype )
                {
                case en_eq:
                        gen_compare(node,i_je,label);
                        break;
                case en_ne:
                        gen_compare(node,i_jne,label);
                        break;
                case en_lt:
                        gen_compare(node,i_jl,label);
                        break;
                case en_le:
                        gen_compare(node,i_jle,label);
                        break;
                case en_gt:
                        gen_compare(node,i_jg,label);
                        break;
                case en_ge:
                        gen_compare(node,i_jge,label);
                        break;
                case en_ult:
                        gen_compare(node,i_jc,label);
                        break;
                case en_ule:
                        gen_compare(node,i_jbe,label);
                        break;
                case en_ugt:
                        gen_compare(node,i_ja,label);
                        break;
                case en_uge:
                        gen_compare(node,i_jnc,label);
                        break;
                case en_land:
                        lab0 = nextlabel++;
                        falsejp(node->v.p[0],lab0);
                        truejp(node->v.p[1],label);
                        gen_label(lab0);
                        break;
                case en_lor:
                        truejp(node->v.p[0],label);
                        truejp(node->v.p[1],label);
                        break;
                case en_not:
                        falsejp(node->v.p[0],label);
                        break;
                default:
                        siz1 = natural_size(node);    
                        ap1 = gen_expr(0,node,0,siz1);
                        gen_icgoto(i_jne,label,ap1,make_immed(0));
                        break;
                }
}

void falsejp(ENODE *node, int label)
/*
 *      generate code to execute a jump to label if the expression
 *      passed is false.
 */
{       IMODE    *ap;
        int             siz1;
        int             lab0;
        if( node == 0 )
                return;
        switch( node->nodetype )
                {
                case en_eq:
                        gen_compare(node,i_jne,label);
                        break;
                case en_ne:
                        gen_compare(node,i_je,label);
                        break;
                case en_lt:
                        gen_compare(node,i_jge,label);
                        break;
                case en_le:
                        gen_compare(node,i_jg,label);
                        break;
                case en_gt:
                        gen_compare(node,i_jle,label);
                        break;
                case en_ge:
                        gen_compare(node,i_jl,label);
                        break;
                case en_ult:
                        gen_compare(node,i_jnc,label);
                        break;
                case en_ule:
                        gen_compare(node,i_ja,label);
                        break;
                case en_ugt:
                        gen_compare(node,i_jbe,label);
                        break;
                case en_uge:
                        gen_compare(node,i_jc,label);
                        break;
                case en_land:
                        falsejp(node->v.p[0],label);
                        falsejp(node->v.p[1],label);
                        break;
                case en_lor:
                        lab0 = nextlabel++;
                        truejp(node->v.p[0],lab0);
                        falsejp(node->v.p[1],label);
                        gen_label(lab0);
                        break;
                case en_not:
                        truejp(node->v.p[0],label);
                        break;
                default:
                        siz1 = natural_size(node);    
                        ap = gen_expr(0,node,0,siz1);
                        gen_icgoto(i_je,label,ap,make_immed(0));
                        break;
                }
}
