view mc-codegen.c @ 577:7e0076617c76

env switch continue..
author kono
date Sun, 15 Jan 2006 18:28:39 +0900
parents 00e5ce0e341a
children dbde3b869a0f
line wrap: on
line source

/* Micro-C Generic Code Generation Part */
/* $Id$ */

#include <stdio.h>
#include "mc.h"
#include "mc-parse.h"
#include "mc-codegen.h"
#include "mc-code.h"
#include "mc-switch.h"
#include "mc-inline.h"

int use;       /* generated value will be used */
char *init_src;
int size_of_int;
int size_of_short;
int size_of_float;
int size_of_double;
int size_of_longlong;
int bit_of_byte;
int endian;

#define STRUCT_ALIGN 1

static void assign(int e1);
#if ASM_CODE
static void gen_asm(int asm0,int in,int out,int opt,int e);
#endif
static void compatible(int t1, int t2);
static int contains(int e,int type);
static int contains_in_list(int e,int type);
static int contains_in_list_p(int e,int (*p)(int));
static void iassop(int e1);
static int is_same_type(int e1,int e2);
static void jump(int e1, int env);
static void machinop(int e1);
static int register_to_lvar(int e);
static void remove0(int *parent,int e) ;
static void sassign(int e1);

#if FLOAT_CODE

/* floating point */

static void dassop(int e1);
static void dmachinop(int e1,int d);
static void dassign(int e1);

#endif
#if LONGLONG_CODE
static void lassop(int e1);
static void lmachinop(int e1);
static void lassign(int e1);
#endif

#if BIT_FIELD_CODE
static int bit_field_repl(int e1,int e2,int t);
static int bit_field(int e1,int t);
static int bassign(int e1,int e2,int t);
static int bassop(int e1,int e2,int op,int t,int post);
#endif

extern void
codegen_init()
{
    /* called only once */
    code_init();
}

extern void
codegen_reinit()
{
    /* called for each file */
    emit_reinit();
}

extern void
codegen_decl_init()
{
    /* called before each declaration */
    emit_init();
    init_free_lvar_list();
}

extern void 
arg_register(NMTBL *fnptr)
{
    code_arg_register(fnptr);
}

extern int
gexpr(int e1,int use0)
{
    if (chk) return INT;
    gexpr_init();
    use = use0;
    return g_expr0(e1);
}

/* gexpr for value unused */

extern int
g_expr_u(int e1)
{
    int t;
    int suse = use; use=0;
    t=g_expr0(e1);
    code_gexpr(e1);

    use=suse;
    return t;
}

/* gexpr for value used */

extern int
g_expr(int e1)
{
    int t;
    int suse = use; use=1;
    t=g_expr0(e1);
    code_gexpr(e1);

    use=suse;
    return t;
}

/* gexpr for used flag untouched */

extern int
g_expr0(int e1)
{
  int e2,e3,t,d,t1;
  NMTBL *n;

  if (inmode) {
	error(-1);
	// return (parse = list3(ST_COMP,parse,e1));
  }
  if (!control && !IS_STATEMENT(car(e1))) return VOID;

  for(;e1;e1=e2) {
    code_gexpr(e1);

    e2 = cadr(e1);
    switch (car(e1)){
    case GVAR:   
	code_gvar(e1,USE_CREG);
	return ADDRESS;
    case RGVAR:
	code_rgvar(e1,USE_CREG);
	return INT;
    case CRGVAR:
	code_crgvar(e1,USE_CREG,1,1);
	return CHAR;
    case CURGVAR:
	code_crgvar(e1,USE_CREG,0,1);
	return UCHAR;
    case SRGVAR:
	code_crgvar(e1,USE_CREG,1,size_of_short);
	return CHAR;
    case SURGVAR:
	code_crgvar(e1,USE_CREG,0,size_of_short);
	return UCHAR;
    case LVAR:
	code_lvar(e2,USE_CREG);
	return ADDRESS;
    case REGISTER:
	code_register(e2,USE_CREG);
	return INT;
#if FLOAT_CODE
    case DREGISTER:
	code_dregister(e2,USE_CREG,1);
	return DOUBLE;
    case FREGISTER:
	code_dregister(e2,USE_CREG,0);
	return FLOAT;
#endif
#if LONGLONG_CODE
    case LREGISTER:
	code_lregister(e2,USE_CREG);
	return LONGLONG;
#endif
    case RLVAR:
	code_rlvar(e2,USE_CREG);
	return INT;
    case CRLVAR:
	code_crlvar(e2,USE_CREG,1,1);
	return CHAR;
    case CURLVAR:
	code_crlvar(e2,USE_CREG,0,1);
	return UCHAR;
    case SRLVAR:
	code_crlvar(e2,USE_CREG,1,size_of_short);
	return CHAR;
    case SURLVAR:
	code_crlvar(e2,USE_CREG,0,size_of_short);
	return UCHAR;
#if FLOAT_CODE
    case FRLVAR:
	code_drlvar(e2,0,USE_CREG);
	return FLOAT;
    case FRGVAR:
	code_drgvar(e1,0,USE_CREG);
	return FLOAT;
    case DRLVAR:
	code_drlvar(e2,1,USE_CREG);
	return DOUBLE;
    case DRGVAR:
	code_drgvar(e1,1,USE_CREG);
	return DOUBLE;
#endif
#if LONGLONG_CODE
    case LRLVAR:
	code_lrlvar(e2,USE_CREG);
	return LONGLONG;
    case LRGVAR:
	code_lrgvar(e1,USE_CREG);
	return LONGLONG;
    case LURLVAR:
	code_lrlvar(e2,USE_CREG);
	return ULONGLONG;
    case LURGVAR:
	code_lrgvar(e1,USE_CREG);
	return ULONGLONG;
#endif
    case FNAME:
	code_fname((NMTBL *)(e2),USE_CREG);
	return ADDRESS;
    case LABEL:
	if (car(e2)!=LVAR) error(-1);
	code_label_value(cadr(e2),USE_CREG);
	return ADDRESS;
    case CONST:  /* 代入する値が0でも特別な処理はしない */
	code_const(e2,USE_CREG);
	return INT;
#if FLOAT_CODE
    case DCONST:
	code_dconst(e1,USE_CREG,1);
	return DOUBLE;
    case FCONST:
	code_dconst(e1,USE_CREG,0);
	return FLOAT;
#endif
#if LONGLONG_CODE
    case LCONST:
	code_lconst(e1,USE_CREG);
	return LONGLONG;
#endif
    case STRING:
	code_string(e1,USE_CREG);
	return ADDRESS;
    case FUNCTION:
	if (car(e2)==FNAME&&is_code((NMTBL*)cadr(e2))) {
	    // error(FNERR);
	    jump(e1,0);
	    return VOID;
	} 
	t = function(e1);
	return t;
    case CODE:
	if (car(e2)==FNAME&&is_function((NMTBL*)cadr(e2))) {
	    // error(GTERR);
	    return function(e1);
	}
	jump(e2,caddr(e1));
	return VOID;
    case INLINE:
	return gen_inline(e1);
    case INDIRECT:
	return g_expr0(e2);
    case RINDIRECT:  
	code_rindirect(e2,USE_CREG,caddr(e1),1,0); return INT;
    case URINDIRECT:  
	code_rindirect(e2,USE_CREG,caddr(e1),0,0); return UNSIGNED;
    case CRINDIRECT: 
	code_rindirect(e2,USE_CREG,caddr(e1),1,1); return CHAR;
    case CURINDIRECT:
	code_rindirect(e2,USE_CREG,caddr(e1),0,1); return UCHAR;
    case SRINDIRECT: 
	code_rindirect(e2,USE_CREG,caddr(e1),1,size_of_short); return SHORT;
    case SURINDIRECT:
	code_rindirect(e2,USE_CREG,caddr(e1),0,size_of_short); return USHORT;
#if FLOAT_CODE
    case FRINDIRECT:
	return code_drindirect(e2,USE_CREG,caddr(e1),0);
    case DRINDIRECT: 
	return code_drindirect(e2,USE_CREG,caddr(e1),1);
#endif
#if LONGLONG_CODE
    case LRINDIRECT: 
	return code_lrindirect(e2,USE_CREG,caddr(e1),0);
    case LURINDIRECT:
	return code_lrindirect(e2,USE_CREG,caddr(e1),1);
#endif
    case ADDRESS:
	if (car(e2)==REGISTER||car(e2)==DREGISTER||car(e2)==FREGISTER)
	    return register_to_lvar(e2); /* too late? */
	else
	    return g_expr0(e2);
    case MINUS:  /* レジスタに対し、neglを実行すれば実現可能 */
	g_expr0(e2); code_neg(USE_CREG);
	return INT;
#if LONGLONG_CODE
    case LMINUS: 
	g_expr0(e2); code_lneg(USE_CREG);
	return LONGLONG;
#endif
#if FLOAT_CODE
    case DMINUS: 
	g_expr0(e2); code_dneg(USE_CREG,1);
	return DOUBLE;
    case FMINUS: 
	g_expr0(e2); code_dneg(USE_CREG,0);
	return FLOAT;
#endif
    case CONV: 
	g_expr0(e2); 
	switch(caddr(e1)) {
	case I2C: code_i2c(USE_CREG); return INT;
	case I2S: code_i2s(USE_CREG); return INT;
	case U2UC: code_u2uc(USE_CREG); return UNSIGNED;
	case U2US: code_u2us(USE_CREG); return UNSIGNED;
#if FLOAT_CODE
	case I2D: code_i2d(USE_CREG); return DOUBLE;
	case D2I: code_d2i(USE_CREG); return INT;
	case U2D: code_u2d(USE_CREG); return DOUBLE;
	case F2U: code_f2u(USE_CREG); return UNSIGNED;
	case I2F: code_i2f(USE_CREG); return FLOAT;
	case F2I: code_f2i(USE_CREG); return INT;
	case U2F: code_u2f(USE_CREG); return FLOAT;
	case D2U: code_d2u(USE_CREG); return UNSIGNED;
	case D2F: code_d2f(USE_CREG); return FLOAT;
	case F2D: code_f2d(USE_CREG); return DOUBLE;
#endif
#if LONGLONG_CODE
	case  I2LL: code_i2ll(USE_CREG); return LONGLONG;
	case  I2ULL: code_i2ull(USE_CREG); return ULONGLONG;
	case  U2LL: code_u2ll(USE_CREG); return LONGLONG;
	case  U2ULL: code_u2ull(USE_CREG); return ULONGLONG;
	case  LL2I: code_ll2i(USE_CREG); return INT;
	case  LL2U: code_ll2u(USE_CREG); return UNSIGNED;
	case  ULL2I: code_ull2i(USE_CREG); return INT;
	case  ULL2U: code_ull2u(USE_CREG); return UNSIGNED;
#if FLOAT_CODE
	case  D2LL: code_d2ll(USE_CREG); return LONGLONG;
	case  D2ULL: code_d2ull(USE_CREG); return ULONGLONG;
	case  F2LL: code_f2ll(USE_CREG); return LONGLONG;
	case  F2ULL: code_f2ull(USE_CREG); return ULONGLONG;
	case  LL2D: code_ll2d(USE_CREG); return DOUBLE;
	case  LL2F: code_ll2f(USE_CREG); return FLOAT;
	case  ULL2D: code_ull2d(USE_CREG); return DOUBLE;
	case  ULL2F: code_ull2f(USE_CREG); return FLOAT;
#endif
#endif

	default:
	    error(-1); return INT;
	}
    case BNOT:   /* ~ */
	g_expr0(e2); code_not(USE_CREG);
	return INT;
    case LNOT:   /* !  */
	g_expr0(e2); code_lnot(USE_CREG);
	return INT;
    case PREINC:
	code_preinc(e1,e2,caddr(e1),1,cadddr(e1),USE_CREG);
	return INT;
    case UPREINC:
	code_preinc(e1,e2,caddr(e1),0,cadddr(e1),USE_CREG);
	return INT;
    case POSTINC:
	code_postinc(e1,e2,caddr(e1),1,cadddr(e1),USE_CREG);
	return INT;
    case UPOSTINC:
	code_postinc(e1,e2,caddr(e1),0,cadddr(e1),USE_CREG);
	return INT;
#if FLOAT_CODE
    case DPREINC:   /* ++d */
	code_dpreinc(e1,e2,1,USE_CREG);
	return DOUBLE;
    case DPOSTINC:  /* d++ */
	code_dpostinc(e1,e2,1,USE_CREG);
	return DOUBLE;
    case FPREINC:   /* ++f */
	code_dpreinc(e1,e2,0,USE_CREG);
	return FLOAT;
    case FPOSTINC:  /* f++ */
	code_dpostinc(e1,e2,0,USE_CREG);
	return FLOAT;
#endif
#if LONGLONG_CODE
    case LPREINC:   /* ++d */
	code_lpreinc(e1,e2,USE_CREG);
	return LONGLONG;
    case LPOSTINC:  /* d++ */
	code_lpostinc(e1,e2,USE_CREG);
	return LONGLONG;
    case LUPREINC:   /* ++d */
	code_lpreinc(e1,e2,USE_CREG);
	return ULONGLONG;
    case LUPOSTINC:  /* d++ */
	code_lpostinc(e1,e2,USE_CREG);
	return ULONGLONG;
#endif
    case MUL: case UMUL:
    case DIV: case UDIV:	   
    case MOD: case UMOD:
    case LSHIFT: case ULSHIFT: case RSHIFT: case URSHIFT:
    case ADD: case SUB: case BAND: case EOR: case BOR: case CMP: case CMPGE:
    case UCMP: case CMPEQ: case CMPNEQ: case UCMPGE:
	machinop(e1);
	return INT;
#if FLOAT_CODE
    case DMUL: case DDIV:
    case DADD: case DSUB:
    case DCMP: case DCMPGE: case DCMPEQ: case DCMPNEQ:
	dmachinop(e1,1);
	return DOUBLE;
    case FMUL: case FDIV:
    case FADD: case FSUB:
    case FCMP: case FCMPGE: case FCMPEQ: case FCMPNEQ:
	dmachinop(e1,0);
	return FLOAT;
#endif
#if LONGLONG_CODE
    case LMUL: case LUMUL:
    case LDIV: case LUDIV:	   
    case LMOD: case LUMOD:
    case LLSHIFT: case LULSHIFT: case LRSHIFT: case LURSHIFT:
    case LADD: case LSUB: case LBAND: case LEOR: case LBOR: case LCMP:
	lmachinop(e1);
	return INT;
#endif
    case COND:        /* a?0:1 should consider non-brach instruction */
    case UCOND:
	d = INT; goto cond_case;
    case LUCOND:
    case LCOND:
	d = LONGLONG; goto cond_case;
    case DCOND:
	d = DOUBLE; goto cond_case;
    case FCOND:
	d = FLOAT;
cond_case:
	e2=fwdlabel();
	if (caddr(e1)) {
	    b_expr(cadr(e1),0,e2,0);
	    g_expr0(caddr(e1));
	} else {  // gcc extenstion  a?:DEF 
	    bexpr(cadr(e1),0,e2); // value used
	}
	t = code_get_fixed_creg(USE_CREG,d);
	gen_jmp(e3=fwdlabel());
	fwddef(e2);
        t1=g_expr0(cadddr(e1));
	code_set_fixed_creg(t,1,d);
	fwddef(e3);
	return t1;
    case STASS: 
	sassign(e1);
	return RSTRUCT;
    case ASS: case CASS: case SASS:
	assign(e1);
	return INT;
    case SASSOP: case SUASSOP:
    case ASSOP: case CASSOP: case CUASSOP:
	iassop(e1);
	return INT;
#if FLOAT_CODE
    case FASS: 
	dassign(e1);
	return FLOAT;
    case DASS: 
	dassign(e1);
	return DOUBLE;
    case FASSOP:
	dassop(e1);
	return FLOAT;
    case DASSOP: 
	dassop(e1);
	return DOUBLE;
#endif
#if LONGLONG_CODE
    case LASS: 
	lassign(e1);
	return LONGLONG;
    case LASSOP: case LUASSOP:
	lassop(e1);
	return LONGLONG ;
#endif
    case RSTRUCT:
	g_expr0(e2);
	return RSTRUCT;
    case ALLOCA:
	code_alloca(e2,USE_CREG);
	return list2(POINTER,CHAR);
    case BUILTINP:
	/* Too late. Should be evaluated in pexpr. */
	code_const(is_const(e2),USE_CREG);
	return INT;
    case COMMA:
	g_expr_u(e2);
	return g_expr0(caddr(e1));
    case RETURN:
	n = (NMTBL *)e2;
	if (retcont==0)
	    retcont=fwdlabel();
	code_return(USE_CREG);
	return VOID;
    case ENVIRONMENT:
	code_environment(USE_CREG);
	return ADDRESS;
    case LCALL:
	code_save_stacks();
	gen_jmp(e2);
	fwddef(caddr(e1));
	return VOID;
#if BIT_FIELD_CODE
    case RBIT_FIELD:
	return bit_field(e2,caddr(e1) /* type */);
    case BASS:
	return bassign(e2,caddr(e1),cadr(cadddr(e1))/* type */);
    case BPREINC:
	return bassop(e2,list2(CONST,caddr(e1)),ADD,
				    cadddr(e1)/* type */,0);
    case BPOSTINC:
	return bassop(e2,list2(CONST,caddr(e1)),ADD,
				    cadddr(e1)/* type */,1);
    case BASSOP:
	return bassop(e2,caddr(e1),car(cadddr(e1)),/* op */
				    cadr(cadddr(e1))/* type */,0);
#endif
#if ASM_CODE
    case ASM:
	gen_asm(car(e2),cadr(e2),caddr(e2),cadddr(e2),caddr(e1));
        /*        asm    in (str) out (str) opt(str)   expr */
	return VOID;
#endif
    case ST_DECL:         st_decl(e1);	break;
    case ST_IF:           st_if(e1);	break;
    case ST_DO:           st_do(e1);	break;
    case ST_WHILE:        st_while(e1);	break;
    case ST_FOR:          st_for(e1);	break;
    case ST_SWITCH:       st_switch(e1);	break;
    case ST_COMP:         st_comp(e1);	break;
    case ST_BREAK:        st_break(e1);	break;
    case ST_CONTINUE:     st_continue(e1);	break;
    case ST_CASE:         st_case(e1);	break;
    case ST_DEFAULT:      st_default(e1);	break;
    case ST_RETURN:       st_return(e1);	break;
    case ST_GOTO:         st_goto(e1);	break;
    case ST_ASM:          st_asm(e1);	break;
    case ST_LABEL:        st_label(e1);	break;
    case ST_COMMENT:      st_comment(e1);	break;
    case IVAR:      	  error(-1);	break;
    case 0:               break; // empty case
    default:
	code_bool(e1,USE_CREG); /* type? */
	return INT;
    }
  }
  return VOID;
}

static int
rop_dual(int op)
{
    //   x op y => y dual(op) x
    switch(op) {
    case GT: return LT;
    case UGT: return ULT;
    case GE: return LE;
    case UGE: return ULE;
    case LT: return GT;
    case ULT: return UGT;
    case LE: return GE;
    case ULE: return UGE;
    case DOP+GT: return DOP+LT;
    case DOP+GE: return DOP+LE;
    case DOP+LT: return DOP+GT;
    case DOP+LE: return DOP+GE;
    case FOP+GT: return FOP+LT;
    case FOP+GE: return FOP+LE;
    case FOP+LT: return FOP+GT;
    case FOP+LE: return FOP+GE;

    case LOP+GT: return LOP+LT;
    case LOP+GE: return LOP+LE;
    case LOP+LT: return LOP+GT;
    case LOP+LE: return LOP+GE;
    case LOP+UGT: return FOP+ULT;
    case LOP+UGE: return FOP+ULE;
    case LOP+ULT: return FOP+UGT;
    case LOP+ULE: return FOP+UGE;
    }
    return op;
}

/* bexpr for value unused */
/*   l1 ... label for branch */
/*   return 0 if l1 is not used, otherwise return l1 */

extern int
bexpr_u(int e1, char cond, int l1)
{
    int op = car(e1);
    if (chk) return l1;
    // gexpr_init();
    switch(op) {
	case GT: case UGT: case GE: case UGE: case LT: 
	case ULT: case LE: case ULE:  
	case DOP+GT: case DOP+GE: case DOP+LT: case DOP+LE:  
	case FOP+GT: case FOP+GE: case FOP+LT: case FOP+LE:  
        case FOP+EQ: case FOP+NEQ:  
        case EQ: case NEQ: case DOP+EQ: case DOP+NEQ:
	switch(car(cadr(e1))) {
	case CONST: case DCONST: case FCONST: case LCONST:
	    return b_expr(list3(rop_dual(op),caddr(e1),cadr(e1)),cond,l1,0);
	}
    }
    return b_expr(e1,cond,l1,0);
}

/* bexpr for value used */

extern int
bexpr(int e1, char cond, int l1)
{
    int uses = use; use=1;
    l1 = bexpr_u(e1, cond, l1);
    use = uses;
    return l1;
}

/* branch expression generator    */
/*    if (cond?e1:!e1) goto  l1   */
/* 1 or 0 is return for code_bool */

extern int
b_expr(int e1, char cond, int l1,int err)
{
    int e2,l2,t;
    if (!control) return l1;
    l2 = 0;
    e2=cadr(e1);
    switch(car(e1)) {
    case LNOT:
	return b_expr(e2,!cond,l1,0);
    case GT: case GE: case LT: case LE:
    case EQ: case NEQ:
	return rexpr(e1,l1,cond,INT);
	return l1;
    case UGT: case UGE: case ULT: case ULE:
	return rexpr(e1,l1,cond,UNSIGNED);
#if FLOAT_CODE
    case DOP+GT:
    case DOP+GE:
    case DOP+EQ:
    case DOP+NEQ:
    case FOP+GT:
    case FOP+GE:
    case FOP+EQ:
    case FOP+NEQ:
	return drexpr(cadr(e1),caddr(e1),l1,car(e1),cond);
    case FOP+LT:
    case FOP+LE:
    case DOP+LT:
    case DOP+LE:
	return drexpr(caddr(e1),cadr(e1),l1,rop_dual(car(e1)),cond);
#endif
#if LONGLONG_CODE
    case LOP+GT:
    case LOP+GE:
    case LOP+EQ:
    case LOP+NEQ:
    case LOP+UGT:
    case LOP+UGE:
	return lrexpr(cadr(e1),caddr(e1),l1,car(e1),cond);
    case LOP+LT:
    case LOP+LE:
    case LOP+ULT:
    case LOP+ULE:
	return lrexpr(caddr(e1),cadr(e1),l1,rop_dual(car(e1)),cond);
#endif
    case LAND:
	l2=bexpr(e2,0,cond?(l2=fwdlabel()):l1);
	l1=bexpr_u(caddr(e1),cond,l1);
	if(cond) fwddef(l2);
	return l1;
    case LOR:
	l2=bexpr(e2,1,cond?l1:(l2=fwdlabel()));
	l1=bexpr_u(caddr(e1),cond,l1);
	if(!cond) fwddef(l2);
	return l1;
    case CRGVAR: case CURGVAR:
	code_cmp_crgvar(e1,USE_CREG,1,l1,cond);
	return l1;
    case SRGVAR: case SURGVAR:
	code_cmp_crgvar(e1,USE_CREG,size_of_short,l1,cond);
	return l1;
    case CRLVAR: case CURLVAR:
	code_cmp_crlvar(e2,USE_CREG,1,l1,cond);
	return l1;
    case SRLVAR: case SURLVAR:
	code_cmp_crlvar(e2,USE_CREG,size_of_short,l1,cond);
	return l1;
    case RGVAR:
	code_cmp_rgvar(e1,USE_CREG,l1,cond);
	return l1;
    case RLVAR:
	code_cmp_rlvar(e2,USE_CREG,l1,cond);
	return l1;
#if 0 && FLOAT_CODE
    case DRLVAR:
	code_cmp_drlvar(e2,USE_CREG,1,l1,cond);
	return l1;
    case FRLVAR:
	code_cmp_drlvar(e2,USE_CREG,0,l1,cond);
	return l1;
    case DRGVAR:
	code_cmp_drgvar(e2,USE_CREG,1,l1,cond);
	return l1;
    case FRGVAR:
	code_cmp_drgvar(e2,USE_CREG,0,l1,cond);
	return l1;
    case FREGISTER:
	code_cmp_dregister(e2,0,l1,cond);
	return l1;
    case DREGISTER:
	code_cmp_dregister(e2,1,l1,cond);
	return l1;
    case DCONST:
    case FCONST:
	if(control&&((dcadr(e2)!=0.0)^cond)) {
	    gen_jmp(l1); return l1;
	} else return 0;
#endif
#if 0 && LONGLONG_CODE
    case LRLVAR:
	code_cmp_lrlvar(e2,USE_CREG,l1,cond);
	return l1;
    case LRGVAR:
	code_cmp_lrgvar(e2,USE_CREG,l1,cond);
	return l1;
    case LREGISTER:
	code_cmp_lregister(e2,l1,cond);
	return l1;
    case LCONST:
	if(control&&((lcadr(e2)!=0)^cond)) {
	    gen_jmp(l1); return l1;
	} else return 0;
#endif
    case REGISTER:
	code_cmp_register(e2,l1,cond);
	return l1;
    case CONST:
	if(control&&((cond&&e2)||(!cond&&!e2))) {
	    gen_jmp(l1); return l1;
	} else return 0;
    default:
	if(err) {
	    error(-1); return l1; /* recursive g_expr/b_expr */
	}
	t=g_expr(e1);
	if (!use) return l1;
	if (0) ;
#if FLOAT_CODE
	else if(t==FLOAT)
	    code_cmp_dregister(USE_CREG,0,l1,cond);
	else if(t==DOUBLE)
	    code_cmp_dregister(USE_CREG,1,l1,cond);
#endif
#if LONGLONG_CODE
	else if(t==LONGLONG||t==ULONGLONG)
	    code_cmp_lregister(USE_CREG,l1,cond);
#endif
	else
	    code_cmp_register(USE_CREG,l1,cond);
	return l1;
    }
}

extern int 
is_const(int e)
{
    switch(car(e)) {
	case ADDRESS:
	    e = cadr(e);
	    return (car(e)==GVAR||car(e)==FNAME||car(e)==LVAR);
	case STRING: case GVAR: 
	// case FUNCTION: case CODE: 
	case EXTRN: case EXTRN1: case FNAME:
	case CONST: case LCONST: case FCONST: case DCONST:
	return 1;
    default:
	return 0;
    }
}

extern int 
is_code(NMTBL *fnptr)
{
    int type = type_value(fnptr->ty);
    return (type==CODE|| (type>0 && car(type)==CODE));
}

extern int 
is_function(NMTBL *fnptr)
{
    int type = type_value(fnptr->ty);
    return (type==FUNCTION || (type>0 && car(type)==FUNCTION));
}

extern int 
is_inline(NMTBL *f)
{
    return (f && attr_value(f,INLINE));
}

extern int 
function_type(int e1,int *dots)
{
    int ret_type,t;
    ret_type = type_value(cadr(e1));
    if (ret_type==CHAR) ret_type=INT;

    /* check argments type is DOTS? */
    t = type_value(caddr(e1));
    if (/* t==0 || */ t==DOTS) *dots = 1;
    else {
        *dots = 0;
        for(;t;t = type_value(cadr(t))) {
            if (car(t)==DOTS) *dots = 1;
        }
    }

    return ret_type;
}

static int
register_to_lvar(int e)
{
    error(REG_ERR);
    return 0;
#if 0
    途中でレジスタからLVARに変更しても、間に合わない。

    NMTBL *n = (NMTBL*)caddr(e);
    int reg = cadr(e);
    int tag = car(e);
    int lvar;
    int t;
    if (!n||n==&null_nptr) error(REG_ERR);
    switch(tag) {
    case REGISTER:
	n->dsp = new_lvar(size_of_int); t = INT; break;
    case DREGISTER:
	n->dsp = new_lvar(size_of_double); t = DOUBLE; break;
    case FREGISTER:
	n->dsp = new_lvar(size_of_float); t = DOUBLE; break;
    case LREGISTER:
	n->dsp = new_lvar(size_of_longlong); t = LONGLONG; break;
    default:
	error(-1);
    }
    n->sc  = LVAR;
    lvar = list3(LVAR,n->dsp,(int)n);
    g_expr_u(assign_expr0(list3(LVAR,n->dsp,(int)n),list3(tag,reg,(int)n),t,t));
    if (tag==REGISTER||tag==DREGISTER||tag==FREGISTER||tag==LREGISTER) {
	free_register(reg);
    return g_expr0(lvar);
#endif
}

// parallel assignment of registers.
//
//  target = list3(target_regnum,next,source_regnum);

extern void
parallel_rassign(int assigns)
{
    int free,tmp,remains,t0=0,t2,src;
    tmp = 0;
    for(;;) {
	remains = 0;
	// find free target
	for(free=assigns;free;free=cadr(free)) {
	    if (!caddr(free)) continue;       // already done
	    if (car(free)==caddr(free)) {
		caddr(free)=0;
		continue;
	    }
	    remains++;
	    t0 = car(free);                   // target register
	    // check target is free
	    for(src=assigns;src;src=cadr(src)) {
		if ((t2=caddr(src)) && t0==t2) break;  // target is in source
	    }
	    if (src==0) {
		break;                      // free is a free target
	    } 
	}
	if (remains==0) {
	    if (tmp) free_lvar(tmp);
	    return;
	}
	if (free) {  // free target
	    if (t0!=caddr(free)) {
		if (caddr(free)>=0) {
		    code_assign_register(t0,0,caddr(free));
		} else {
		    code_rlvar(caddr(free),t0);
		}
	    }
	    caddr(free)=0;       // mark it done
	} else {  // no free target
	    for(free=assigns;free;free=cadr(free)) {
		if (caddr(free)) break; // not yet done
	    }
	    if (!free) error(-1);
	    tmp = new_lvar(size_of_int);
	    if (tmp>0) error(-1);
	    code_assign_lvar(tmp,caddr(free),0);
	    caddr(free) = tmp;
	}
    }
}

/* goto arguments list                                      */
/* target         list4(list2(tag,disp),cdr,ty,source_expr) */
/*     source         expr=listn(tag,...)                   */
/*     source (after) list2(tag,disp)                       */
/* source list    list3(e,cdr,sz)                           */

#define DEBUG_PARALLEL_ASSIGN 0

static int is_writable(int);

/* overlap 
      return list of overlapped target
 */

static int
overlap(int t,int sz,int target)
{
    int s,s0,s1;
    int t0=cadr(t);
    int t1=t0+sz;
    int source;
    int result=0;
    if (!is_writable(t)) error(-1);
    for(;target;target=cadr(target)) {
	for(source=caddddr(target);source;source=cadr(source)) {
	    s=car(source); s0=cadr(s); 
	    switch(car(s)) {
	    case REGISTER: case DREGISTER: case FREGISTER: case LREGISTER:
		if (code_register_overlap(s,t)) {
		    result = list2(target,result);
		}
		break;
	    default:
		if (is_same_type(s,t)) {
		    s1=s0+caddr(source);
#if DEBUG_PARALLEL_ASSIGN>1 
printf("## overlap source %d t0 %d t1 %d\n",car(car(t)),t0,t1);
printf("## overlap target %d s0 %d s1 %d\n",car(car(source)),s0,s1);
printf("## overlap   equal = %d\n",((t0<=s0&&s0<t1)||(t0<s1&&s1<=t1)));
#endif
		    if((t0<=s0&&s0<t1)||(t0<s1&&s1<=t1))
			result = list2(target,result);
		}
	    }
	}
    }
    return result;
}

static void
remove_target(int *target,int t,int *use)
{
    int use0=*use;
    int reg;
    while(use0) {
	if (car(use0)==t) {
	    reg = car(caddr(use0));
	    if (reg==REGISTER||reg==FREGISTER||reg==DREGISTER||reg==LREGISTER)
		free_register(cadr(caddr(use0)));
	    break;
	}
	use0 = cadr(use0);
    }
    remove0(target,t);
}

static void
save_target(int t,int s,int *target,int *use,int sz,int ty)
{
    int e1;
    /*新しいレジスタ(or スタック)を取得する*/
    if (scalar(ty) && sz==size_of_int && (e1=get_register_var(0))!=-1) {
	// e1=list3(REGISTER,e1,0);
	if (code_register_overlap(s,e1)) goto use_lvar;
	*use=list3(t,*use,e1);
	g_expr_u(assign_expr0(e1,s,ty,ty));
	*target = append5(*target,t,ty,e1,list3(e1,0,sz));
#if FLOAT_CODE
    } else if (ty==DOUBLE && sz==size_of_double && (e1=get_dregister_var(0,1))!=-1) {
	// e1=list3(DREGISTER,e1,0);
	if (code_register_overlap(s,e1)) goto use_lvar;
	*use=list3(t,*use,e1);
	g_expr_u(assign_expr0(e1,s,ty,ty));
	*target = append5(*target,t,ty,e1,list3(e1,0,sz));
    } else if (ty==FLOAT && sz==size_of_float && (e1=get_dregister_var(0,0))!=-1) {
	// e1=list3(FREGISTER,e1,0);
	if (code_register_overlap(s,e1)) goto use_lvar;
	*use=list3(t,*use,e1);
	g_expr_u(assign_expr0(e1,s,ty,ty));
	*target = append5(*target,t,ty,e1,list3(e1,0,sz));
#endif
#if LONGLONG_CODE
    } else if ((ty==LONGLONG||ty==ULONGLONG)&&(e1=get_lregister_var(0))!=-1) {
	// e1=list3(LREGISTER,e1,0);
	if (code_register_overlap(s,e1)) goto use_lvar;
	*use=list3(t,*use,e1);
	g_expr_u(assign_expr0(e1,s,ty,ty));
	*target = append5(*target,t,ty,e1,list3(e1,0,sz));
#endif
    } else {
	if (0) {
use_lvar:;
#if DEBUG_PARALLEL_ASSIGN>1
printf("## register overrap in save_target\n");
#endif
	}
	g_expr_u(assign_expr0((e1=list3(LVAR,new_lvar(sz),0)),s,ty,ty));
	*target = append5(*target,t,ty,e1,list3(e1,0,sz));
	*use=list3(t,*use,e1);
    }
}

static int
circular_dependency(int t,int clist,int target,int history)
{
    int t1,h,sz,s,clist1,t2;

    for(;clist;clist=cadr(clist)) {          /* conflict list */
loop:
	t1 = car(clist);
	for(h=history;h;h=cadr(h)) {
	    if (t1==car(h)) {
#if DEBUG_PARALLEL_ASSIGN
printf("## circular dependency %d ty %d\n",car(t1),cadr(t1));
#endif
		return t1;
	    }
	}
	for(s=caddddr(t1);s;s=cadr(s)) {    /* dependent memory sources */
	    sz=caddr(s);
	    if ((clist1=overlap(car(s),sz,target))) {
		if (!cadr(t1)&&!cadr(s)) {
		    history = list2(t,history);
		    t = t1;
		    clist = clist1; 
		    goto loop;  // tail recursion
		} else {
		    if ((t2=circular_dependency(t1,
				clist1,target,list2(t,history)))) {
			return t2;
		    }
		}
	    }
	}
    }
    return 0;
}

// static void remove_a(int source,int s);  // remove all child

static void
parallel_assign(int *target,int *processing,int *use)
{
    int t,s,sz,ty,target0,s1,progress;
    while(*target) {
	progress = 0;
	for(target0=*target;target0; target0=cadr(target0)) {
	    t=car(target0); s=cadddr(target0);
	    sz=size(ty=caddr(target0)); 
	    if(car(t)==car(s) && cadr(t)==cadr(s)) {
		/*書き込み先が自分自身*/
#if DEBUG_PARALLEL_ASSIGN
printf("## remove same %d ty %d+%d sz %d\n",car(t),ty,cadr(t),sz);
#endif
		remove_target(target,t,use);
		progress = 1;
	    } else if (!(s1=overlap(t,sz,*target)) || 
		    (cadr(s1)==0 && car(car(s1))==t)) {
		/* 重なってないので安心して書き込める */
#if DEBUG_PARALLEL_ASSIGN
if (s1 && cadr(s1)==0)
printf("## singleton %d ty %d+%d sz %d\n",car(t),ty,cadr(t),sz);
else
printf("## normal assign %d ty %d+%d sz %d\n",car(t),ty,cadr(t),sz);
#endif
		g_expr_u(assign_expr0(t,s,ty,ty));
		remove_target(target,t,use);
		progress = 1;
	    } else if((t=circular_dependency(target0,s1,*target,0))) {
		remove_target(target,car(t),use);
		sz=size(ty=caddr(t)); 
		save_target(car(t),cadddr(t),target,use,sz,ty);
		progress = 1;
#if DEBUG_PARALLEL_ASSIGN
    printf("## saving %d ty %d+%d sz %d\n",car(car(t)),ty,cadr(car(t)),sz);
#endif
		break;
	    }
	}
	if (!progress) {
	    // can't performe parallel assign
	    // error(-1);
	    target0 = *target;
	    t=car(target0); s=cadddr(target0);
	    sz=size(ty=caddr(target0)); 
#if DEBUG_PARALLEL_ASSIGN
printf("## can't progress save any %d ty %d+%d sz %d\n",car(s),ty,cadr(t),sz);
#endif
	    remove_target(target,t,use);
	    save_target(t,s,target,use,sz,ty);
	}
    }
}

static void 
remove0(int *parent,int e) 
{
    int list;
    while ((list=*parent)) {
	if (car(list)==e) {
	    *parent= cadr(list); return;
	} else {
	     parent=&cadr(list);
	}
    }
}

/*

static int
remove_1(int source,int e)
{
    int sz;
    if ((sz=is_memory(e))) {
	remove0((int*)source,e);
    }
    return source;
}

static void
remove_a(int source,int s)
{
    contains_p1(source,s,remove_1);
}
*/

// #define SAVE_ALL_NON_MEMORY

#ifdef SAVE_ALL_NON_MEMORY
static int
is_simple(int e1) 
{
    switch(e1) {
        case CONST: case FNAME: case LVAR: case REGISTER: case DREGISTER:
	case FREGISTER: case LREGISTER:
	case GVAR: case RGVAR: case RLVAR: case CRLVAR: case CRGVAR:
	case DRLVAR: case FRLVAR: case LRLVAR:
	case CURLVAR: case SURLVAR: case CURGVAR: case SURGVAR:
	return 1;
    }
    return 0;
}
#endif

static int
is_same_type(int e1,int e2)
{
    int ce1=car(e1);
    int ce2=car(e2);
    if (ce1==LVAR) {
	switch(ce2) {
	    case RLVAR: case CRLVAR: case FRLVAR: case DRLVAR:
	    case SRLVAR: case SURLVAR: case CURLVAR: case LVAR:
	    return 1;
	}
    } else if (ce2==LVAR) {
	switch(ce1) {
	    case RLVAR: case CRLVAR: case FRLVAR: case DRLVAR:
	    case SRLVAR: case SURLVAR: case CURLVAR: case LRLVAR: case LVAR:
	    return 1;
	}
    } else if (ce1==GVAR) {
	return 0;
#if 0
	switch(ce2) {
	    case RGVAR: case CRGVAR: case FRGVAR: case DRGVAR:
	    case SRGVAR: case SURGVAR: case CURGVAR:
	    return 1;
	}
#endif
    } else if (ce2==GVAR) {
	return 0;
#if 0
	switch(ce1) {
	    case RGVAR: case CRGVAR: case FRGVAR: case DRGVAR:
	    case SRGVAR: case SURGVAR: case CURGVAR: case LRGVAR:
	    return 1;
	}
#endif
    }
    return 0;
}

static int
is_writable(int e1)
{
    switch(car(e1)) {
    case GVAR :
    case LVAR :
    case RLVAR :        // this is wrong, but ia32 generates this.
    case REGISTER :
	return size_of_int;
    case FREGISTER :
	return size_of_float;
    case DREGISTER  :
	return size_of_double;
    case LREGISTER:
	return size_of_longlong;
    }
    return 0;
}

extern int
reference(int e1)
{
    switch(car(e1)) {
    case GVAR :
    case CRGVAR  :
    case CURGVAR :
    case DRGVAR  :
    case FRGVAR :
    case LRGVAR :
    case LURGVAR :
    case RGVAR:
    case SRGVAR :
    case SURGVAR:
	return list3(GVAR,cadr(e1),caddr(e1));
    case LVAR :
    case CRLVAR  :
    case CURLVAR  :
    case DRLVAR  :
    case FRLVAR  :
    case LRLVAR:
    case LURLVAR:
    case RLVAR:
    case SRLVAR :
    case SURLVAR :
	return list3(LVAR,cadr(e1),caddr(e1));
    case FREGISTER :
    case REGISTER:
    case LREGISTER:
    case DREGISTER:
	return e1;
	break;
    default: error(-1);
    }
    return e1;
}

static int
is_memory0(int e1,int *global)
{
    *global=0;
    switch(car(e1)) {
    case CRGVAR  :
    case CURGVAR :
	*global=1;
    case CRLVAR  :
    case CURLVAR  :
	return 1;
    case SRGVAR :
    case SURGVAR:
	*global=1;
    case SRLVAR :
    case SURLVAR :
	return size_of_short;
    // case GVAR :
    case RGVAR:
	*global=1;
    // case LVAR :
    case RLVAR:
    case REGISTER :
	return size_of_int;
    case FRGVAR :
	*global=1;
    case FRLVAR  :
    case FREGISTER :
	return size_of_float;
    case DRGVAR  :
	*global=1;
    case DRLVAR  :
    case DREGISTER  :
	return size_of_double;
    case LRGVAR :
    case LURGVAR :
	*global=1;
    case LRLVAR :
    case LURLVAR :
    case LREGISTER:
	return size_of_longlong;
    }
    return 0;
}

extern int
is_memory(int e1)
{
    int global;
    return is_memory0(e1,&global);
}

extern int
is_local_memory(int e1)
{
    int global;
    return is_memory0(e1,&global) && !global;
}

static int
check_source(int source,int e)
{
    int sz;
    if ((sz=is_memory(e))) {
	source = list3(e,source,sz);
    }
    return source;
}

//
// CbC goto statement with environment
//

// maximum size of struct divide (don't make it large)

#define ASSIGN_STRUCT_DIVIDE 40

static void
jump(int e1, int env)
{
    int e2,e3,e4,sz,arg_size,ty,regs,fregs;
    int t0,s0,r,reg;
    NMTBL *code0 = 0;
    int target = 0;
    int processing = 0;
    int use = 0;
    int envreg = 0;

    /* e1 = list4(FUNCTION,code_segment,arglist,ftype); */

    if (env) {
	envreg = get_register_var(0);
	g_expr_u(assign_expr0(envreg,env,INT,INT));
    }

    /* まず、サイズを計算しながら、target を決まった形に落す。 */
    /*    list5(target,next,ty,source,source_dependency)       */

    arg_size = 0; regs = 0;
    fregs = 0;
    for (e3 = reverse0(caddr(e1)); e3; e3 = cadr(e3)) {	
	e2 = car(e3); sz = size(ty=caddr(e3)); 
	if (scalar(ty) && (r = get_input_register_var(regs,0,1))) {
	    target=list5(r,target,ty,e2,0); regs++;
	} else if (ty==FLOAT  && (r = get_input_dregister_var(fregs,0,1,0))) {
	    target=list5(r, target,ty,e2,0); fregs++;
	} else if (ty==DOUBLE && (r = get_input_dregister_var(fregs,0,1,1))) {
	    target=list5(r, target,ty,e2,0); fregs++;
	} else if ((ty==LONGLONG||ty==ULONGLONG) && (r = get_input_lregister_var(fregs,0,1))) {
	    target=list5(r, target,ty,e2,0); regs+=2;
	} else if (env) {
	    while(car(e2)==RSTRUCT) e2=cadr(e2);
	    g_expr_u(assign_expr0(
		list2(INDIRECT,
		    list3(ADD,rvalue_t(envreg,INT),list2(CONST,-arg_size-sz))
		),
		e2,ty,ty));
	} else {
	    while(car(e2)==RSTRUCT) e2=cadr(e2);
	    target=list5(list3(LVAR,0,0), target,ty,e2,0);
	}
        /* keep arg space for register variables */
        arg_size += sz;
#if DEBUG_PARALLEL_ASSIGN
printf("## target %d ty %d+%d sz %d\n",car(car(target)),ty,cadr(car(target)),sz);
#endif
    }
    if (env) {
	/* change the frame pointer during parallel assignment */
	    target=list5(code_frame_pointer_register(), target,INT,rvalue_t(envreg,INT),0);
    } 


    /* disp を飛び先似合わせて修正 */
    if (is_code(fnptr)) {
	if (-arg_size<disp) disp = -arg_size;
    } else {
	if (disp_offset-arg_size<disp) disp = disp_offset-arg_size;
    }

    /*  複雑な式を前もって計算しておく     */
    /*  必要なら局所変数を用いる。         */
    /*  局所変数へのオフセットを覚えておく */

    for (e2 = target; e2; e2 = cadr(e2)) {	
	t0=car(e2); s0=cadddr(e2);
	sz=size(ty=caddr(e2));
	if(car(t0)==LVAR) {
	    /* ここで、書込先アドレスを決める */
	    if (envreg) error(-1);
	    cadr(t0)=-arg_size;
	}
        arg_size-=sz;
#ifdef SAVE_ALL_NON_MEMORY
	if (!is_simple(car(s0))) {
#else
	if (contains_p(s0,not_simple_p)) {
#endif
	    /* complex case */
	    g_expr_u(assign_expr0((e4=list3(LVAR,new_lvar(sz),0)),s0,ty,ty));
	    use=list3(ty,use,e1);
	    cadddr(e2)=e4;
	    caddddr(e2)=list3(e4,0,sz);
	    s0=e4;
        } else if (is_same_type(t0,s0)) {
            if(cadr(t0)==cadr(s0)) {
		if(is_writable(s0)) {
		    caddddr(e2)=list3(s0,0,sz);
		    continue;
		} else
		    error(-1);
	    }
        }
	if(is_writable(s0)) {
	    if (sz>8 && car(s0)==LVAR && car(t0)==LVAR 
		    &&sz<ASSIGN_STRUCT_DIVIDE) {
		/* large struct generate large save/restore */
		/* divide it to avoid large copy */
#if DEBUG_PARALLEL_ASSIGN
printf("## division sz %d\n",sz);
#endif
		caddr(e2) = UNSIGNED;
		caddddr(e2) = list3(
			cadddr(e2)=list3(LVAR,cadr(s0),0),
				0, size_of_int);
#if DEBUG_PARALLEL_ASSIGN
printf("## div 0 source %d ty %d+%d sz %d\n",car(s0),ty,cadr(s0),size_of_int);
#endif
		for(e4=size_of_int;e4<sz;) {
		    cadr(e2) = list5(car(e2),cadr(e2),
			caddr(e2),cadddr(e2),caddddr(e2));
		    switch(sz-e4) {
		    case 1: caddr(e2) = UCHAR; r = 1; break;
		    case 2:
		    case 3: caddr(e2) = USHORT; r = size_of_short; break;
		    default: caddr(e2) = UNSIGNED; r = size_of_int;
		    }
		    if (e4==size_of_int) e3=cadr(e2);
		    car(e2) =  list3(LVAR,cadr(t0)+e4,0);
		    caddddr(e2) = list3(
			cadddr(e2) = list3(LVAR,cadr(s0)+e4,0),0, r);
		    e4 += r;
#if DEBUG_PARALLEL_ASSIGN
printf("## div 1 source %d ty %d+%d sz %d\n",car(s0),ty,cadr(s0),r);
#endif
		}
		e2 = e3;
		continue;
	    }
	    caddddr(e2)=list3(s0,0,sz);
#if DEBUG_PARALLEL_ASSIGN
printf("## source %d ty %d+%d sz %d\n",car(s0),ty,cadr(s0),sz);
#endif
	} else {
	    /* check used sources in rather complex source */
	    /*   more complex sources are compiled before */
	    caddddr(e2)=contains_p1(0,s0,check_source);
	}
    }
    /* compute jump address */
    e2 = cadr(e1);
    if (car(e2) == FNAME) {	
	code0=(NMTBL *)cadr(e2);
	// if (!is_code(code0)) { error(TYERR); return; }
    } else {	/* indirect */
	g_expr(e2);
	emit_push();
    }

    /* 並列代入を実行 */
    parallel_assign(&target,&processing,&use);
    while (use) {
	reg = car(caddr(use));
	if (reg==REGISTER||reg==FREGISTER||reg==DREGISTER||reg==LREGISTER)
	    free_register(cadr(caddr(use)));
	else if (car(caddr(use))==LVAR)
	    free_lvar(cadr(caddr(use)));
	use=cadr(use);
    }
    if(target) error(-1);
    if(env) {
	if (car(envreg)==REGISTER)
	    free_register(cadr(envreg));
	else free_lvar(cadr(envreg));
    }

    if (car(e2) == FNAME) {	
	if (is_function(fnptr))
	    code_fix_frame_pointer(disp_offset);
	code_jmp(code0->nm);
    } else {
	e2 = emit_pop(0);
	if (is_function(fnptr))
	    code_fix_frame_pointer(disp_offset);
	code_indirect_jmp(e2);
	emit_pop_free(e2);
    }
}

static void
machinop(int e1)
{
    int e2,e3,op;

    e2 = cadr(e1);
    op = car(e1);
    e3 = caddr(e1);
    if (code_const_op_p(op,e3)) {
	g_expr(e2);
	oprtc(op,USE_CREG,e3);
	return;
    }
    g_expr(e3);
    emit_push();
    g_expr(e2);
    tosop(op,USE_CREG,(e2=pop_register()));
    emit_pop_free(e2);
    return;
}

#if FLOAT_CODE
static void
dmachinop(int e1,int d)
{
    int e2,e3,op;

    e2 = cadr(e1);
    op = car(e1);
    e3 = caddr(e1);
    g_expr(e3);
    emit_dpush(d);
    g_expr(e2);
    dtosop(car(e1),USE_CREG,(e2=emit_dpop(d)));
    emit_dpop_free(e2,d);
    return;
}
#endif

#if LONGLONG_CODE
static void
lmachinop(int e1)
{
    int e2,e3,op;

    e2 = cadr(e1);
    op = car(e1);
    e3 = caddr(e1);
    if (code_lconst_op_p(op,e3)) {
	g_expr(e2);
	loprtc(op,USE_CREG,e3);
	return;
    }
    g_expr(e3);
    emit_lpush();
    g_expr(e2);
    ltosop(car(e1),USE_CREG,(e2=emit_lpop()));
    emit_lpop_free(e2);
    return;
}
#endif

static void
sassign(int e1)
{
    int e2,e3,e4,sz,xreg,det,offset;

    /* structure assignment */
    e2 = cadr(e1);  /* pointer variable to the struct */
    e3 = cadr(e2);  /* offset of the variable (distination) */
    e4 = caddr(e1); /* right value (source) */
    sz = cadddr(e1);  /* size of struct or union */
    if (car(e4)==RSTRUCT) {
	e4 = cadr(e4);
    }
    if (is_same_type(e2,e4)) {
	if (cadr(e2)==cadr(e4)) {
	    if (use) g_expr(e4);
	    return;
	}
    }
    g_expr(e4);
    emit_push();
    g_expr(e2);
    xreg = emit_pop(0);
    /* 一般的にはコピーのオーバラップの状況は実行時にしかわからない */
    /* しかし、わかる場合もある */
    if (is_same_type(e2,e4)) {
	if(cadr(e2)<cadr(e4)) { offset=sz; sz=-sz;}
	else offset=0;
	det=1;  
    } else {
	det = 0;  offset=0;
    }
    emit_copy(xreg,USE_CREG,sz,offset,1,det);
    emit_pop_free(xreg);
    return;
}

static void
assign_opt(int e5,int e2,int e4,int byte)
{
    int reg;
    /*    e2=e4 */
    if (e5==REGISTER) {
	reg = cadr(e4);
	switch(car(e2)) {
	case GVAR: code_assign_gvar(e2,reg,byte); return;
	case LVAR: code_assign_lvar(cadr(e2),reg,byte); return;
	case REGISTER: code_assign_register(cadr(e2),byte,reg); return;
	}
	g_expr(e2);
	code_assign(USE_CREG,byte,reg);
	return;
    }
    /* e2 is register now */
    if (car(e2)!=REGISTER) error(-1);
    reg = cadr(e2);
    switch(e5) {
    case CRGVAR:  
    case CURGVAR:  code_crgvar(e4,reg,e5==CRGVAR,1); return;
    case SRGVAR:  
    case SURGVAR:  code_crgvar(e4,reg,e5==SRGVAR,size_of_short); return;
    case RGVAR: case URGVAR:   code_rgvar(e4,reg);  return;
    case CRLVAR:  
    case CURLVAR:  code_crlvar(cadr(e4),reg,e5==CRLVAR,1); return;
    case SRLVAR:  
    case SURLVAR:  code_crlvar(cadr(e4),reg,e5==SRLVAR,size_of_short); return;
    case RLVAR: case URLVAR:   code_rlvar(cadr(e4),reg);  return;
    case GVAR:     code_gvar(e4,reg);   return;
    case LVAR:     code_lvar(cadr(e4),reg);   return;
    case CONST:    code_const(cadr(e4),reg); return;
    case ADDRESS: 
	if (car(cadr(e4))==STRING) code_string(cadr(e4),reg);
	else code_gvar(cadr(e4),reg);   
	return;
    case FNAME:    code_fname((NMTBL*)cadr(e4),reg); return;
    case STRING:   code_string(e4,reg); return;
    default: error(-1);
    }
}

static void
assign(int e1)
{
    int e2,e4,byte,e5;

    byte=(car(e1) == CASS)?1:(car(e1) == SASS)?size_of_short:0;
    /*    e2=e4 */
    e2 = cadr(e1);
    e4 = caddr(e1);e5=car(e4);
    if (is_same_type(e2,e4)&&cadr(e2)==cadr(e4)) {
	if (use) g_expr(e4);
	return;
    }
    if (!use) {
	if (e5==REGISTER) {
	    assign_opt(e5,e2,e4,byte);
	    return;
	} else if (car(e2)==REGISTER) {
	    switch(e5) {
	    case ADDRESS:
		if (!((car(cadr(e4))==STRING) || car(cadr(e4))==GVAR)) 
		    break; 
	    case  CRGVAR  : case  CRLVAR  : case  RGVAR  : case  RLVAR :
	    case  URGVAR  : case  URLVAR :
	    case  CURGVAR  : case  CURLVAR  :
	    case  SURGVAR  : case  SURLVAR  :
	    case  GVAR  : case  LVAR :
	    case  CONST   : case  FNAME  : case  STRING :
		assign_opt(e5,e2,e4,byte);
		return;
	    }
	}
    }
    switch(car(e2)) {
    case GVAR:      /*   i=3 */
            g_expr(e4);
	    code_assign_gvar(e2,USE_CREG,byte);
            return;
    case LVAR:
            g_expr(e4);
	    code_assign_lvar(cadr(e2),USE_CREG,byte);
            return;
    case REGISTER:
            g_expr(e4);
	    code_assign_register(cadr(e2),byte,USE_CREG);
            return;
    }
    g_expr(e2);
    emit_push();
    g_expr(e4);
    e2 = emit_pop(0);
    code_assign(e2,byte,USE_CREG);
    emit_pop_free(e2);
    return;
}

#if FLOAT_CODE

static void
dassign_opt(int e5,int e2,int e4,int d)
{
    int reg;
    /*    e2=e4 */
    if (e5==DREGISTER||e5==FREGISTER) {
	reg = cadr(e4);
	switch(car(e2)) {
	case GVAR:      /*   i=3 */
		code_dassign_gvar(e2,reg,d);
		return;
	case LVAR:
		code_dassign_lvar(cadr(e2),reg,d);
		return;
	case DREGISTER:
	case FREGISTER:
		if (reg!=cadr(e2))
		    code_dassign_dregister(cadr(e2),d,reg);
		return;
	default:
	    error(-1);
	}
    }
    /* e2 is register now */
    if (car(e2)!=DREGISTER && car(e2)!=FREGISTER) error(-1);
    reg = cadr(e2);
    switch(e5) {
    case FRGVAR:
    case DRGVAR: code_drgvar(e4,d,reg); return;
    case FRLVAR:
    case DRLVAR: code_drlvar(cadr(e4),d,reg); return;
    case FCONST:
    case DCONST: code_dconst(e4,reg,d); return;
    default:
	    error(-1);
    }
}

static void
dassign(int e1)
{
    int e2,e3,e4,d=0,e5;

    /*    e2=e4 */
    e2 = cadr(e1);
    e3 = cadr(e2);
    e4 = caddr(e1); e5=car(e4);
    if (is_same_type(e2,e4)&&cadr(e2)==cadr(e4)) {
	if (use) g_expr(e4);
	return;
    }
    if (car(e1)==DASS) d=1;
    else if (car(e1)==FASS) d=0;
    else error(-1); 
    if (!use) {
	switch(e5) {
	    case DRGVAR: case DRLVAR: case DCONST:
		if (car(e2)!=DREGISTER) break;
		dassign_opt(e5,e2,e4,d); return;
	    case FRGVAR: case FRLVAR: case FCONST:
		if (car(e2)!=FREGISTER) break;
	    case DREGISTER: case FREGISTER:
		dassign_opt(e5,e2,e4,d); return;
	}
    }
    switch(car(e2)) {
    case GVAR:
            g_expr(e4);
	    code_dassign_gvar(e2,USE_CREG,d);
            return;
    case LVAR:
            g_expr(e4);
	    code_dassign_lvar(cadr(e2),USE_CREG,d);
            return;
    case DREGISTER:
    case FREGISTER:
            g_expr(e4);
	    code_dassign_dregister(cadr(e2),d,USE_CREG);
            return;
    }
    g_expr(e2);
    emit_push();
    g_expr(e4);
    e2 = emit_pop(0);
    code_dassign(e2,USE_CREG,d);
    emit_pop_free(e2);
    return;
}

#endif

#if LONGLONG_CODE

static void
lassign_opt(int e5,int e2,int e4)
{
    int reg;
    /*    e2=e4 */
    if (e5==LREGISTER) {
	reg = cadr(e4);
	switch(car(e2)) {
	case GVAR:      /*   i=3 */
		code_lassign_gvar(e2,reg);
		return;
	case LVAR:
		code_lassign_lvar(cadr(e2),reg);
		return;
	case LREGISTER:
		if (reg!=cadr(e2))
		    code_lassign_lregister(cadr(e2),reg);
		return;
	default:
	    error(-1);
	}
    }
    /* e2 is register now */
    if (car(e2)!=LREGISTER) error(-1);
    reg = cadr(e2);
    switch(e5) {
    case LRGVAR: case LURGVAR: code_lrgvar(e4,reg); return;
    case LRLVAR: case LURLVAR: code_lrlvar(cadr(e4),reg); return;
    case LCONST: code_lconst(e4,reg); return;
    default:
	    error(-1);
    }
}

static void
lassign(int e1)
{
    int e2,e3,e4,e5;

    /*    e2=e4 */
    e2 = cadr(e1);
    e3 = cadr(e2);
    e4 = caddr(e1); e5=car(e4);
    if (is_same_type(e2,e4)&&cadr(e2)==cadr(e4)) {
	if (use) g_expr(e4);
	return;
    }
    if (!use && (
	(e5==LREGISTER &&(car(e2)==GVAR||car(e2)==LVAR||car(e2)==LREGISTER)) ||
	    (car(e2)==LREGISTER&&
		(e5==LRGVAR||e5==LRLVAR||e5==LURLVAR||e5==LURGVAR||e5==LCONST))
	)) {
	lassign_opt(e5,e2,e4);
	return;
    }
    switch(car(e2)) {
    case GVAR:
            g_expr(e4);
	    code_lassign_gvar(e2,USE_CREG);
            return;
    case LVAR:
            g_expr(e4);
	    code_lassign_lvar(cadr(e2),USE_CREG);
            return;
    case LREGISTER:
	    g_expr(e4);
	    code_lassign_lregister(cadr(e2),USE_CREG);
	    return;
    }
    g_expr(e2);
    emit_push();
    g_expr(e4);
    e2 = emit_pop(0);
    code_lassign(e2,USE_CREG);
    emit_pop_free(e2);
    return;
}

#endif

/* numerical type conversion */

#if FLOAT_CODE
static int
double_value(int e2)
{
    int t = type_value(type);
    if (t>0&&car(t)==BIT_FIELD) e2=rvalue(e2);
    switch(car(e2)) {
    case LCONST:
#if LONGLONG_CODE
	e2 =  dlist2(DCONST,(double)lcadr(e2)); break;
#endif
    case CONST:
	e2 =  dlist2(DCONST,(double)cadr(e2)); break;
    case FCONST:
	e2 = dlist2(DCONST,dcadr(e2)); break;
    default:
	switch(type_value(type)) {
	case DOUBLE: break;
	case FLOAT: e2 =  list3(CONV,rvalue(e2),F2D); break;
	case UNSIGNED: e2 =  list3(CONV,rvalue(e2),U2D); break;
	case LONGLONG: e2 =  list3(CONV,rvalue(e2),LL2D); break;
	case ULONGLONG: e2 =  list3(CONV,rvalue(e2),ULL2D); break;
	default:
	    if(integral(type)) e2 =  list3(CONV,rvalue(e2),I2D);
	    else { error(TYERR); e2 =  dlist2(DCONST,1.0); }
	}
    }
    type = set_type_with_attr(DOUBLE,type);
    return e2;
}

static int
float_value(int e2)
{
    int t = type_value(type);
    if (t>0&&car(t)==BIT_FIELD) e2=rvalue(e2);
    if (0) ;
#if LONGLONG_CODE
    else if (car(e2)==LCONST)  e2 =  dlist2(FCONST,(double)lcadr(e2));
#endif
    else if (car(e2)==CONST)  e2 = dlist2(FCONST,(double)cadr(e2));
    else if (car(e2)==DCONST)  e2 = dlist2(FCONST,dcadr(e2));
    else {
	switch(type_value(type)) {
	case LONGLONG: e2 = list3(CONV,rvalue(e2),LL2F); break;
	case ULONGLONG: e2 = list3(CONV,rvalue(e2),ULL2F); break;
	case FLOAT: break;
	case DOUBLE: e2 =  list3(CONV,rvalue(e2),D2F); break;
	case UNSIGNED: e2 =  list3(CONV,rvalue(e2),U2F); break;
	default:
	    if(integral(type)) e2 =  list3(CONV,rvalue(e2),I2F);
	    else { error(TYERR); e2 =  dlist2(DCONST,1.0); }
	}
    }
    type = set_type_with_attr(FLOAT,type);
    return e2;
}
#endif

#if LONGLONG_CODE
static int
longlong_value(int e2)
{
    int t = type_value(type);
    if (t>0&&car(t)==BIT_FIELD) e2=rvalue(e2);
    if (0) ;
    else if (car(e2)==CONST)  e2 = llist2(LCONST,(long long)cadr(e2));
    else if (car(e2)==LCONST) ;
#if FLOAT_CODE
    else if (car(e2)==DCONST||car(e2)==FCONST)
        e2 = llist2(LCONST,(long long)dcadr(e2));
#endif
    else {
	switch(type_value(type)) {
	case FLOAT: e2 = list3(CONV,rvalue(e2),F2LL); break;
	case DOUBLE: e2 = list3(CONV,rvalue(e2),D2LL); break;
	case UNSIGNED: e2 = list3(CONV,rvalue(e2),U2LL); break;
	case LONGLONG: break;
	case ULONGLONG: break;
	default:
	    if(integral(type)) e2 = list3(CONV,rvalue(e2),I2LL);
	    else { error(TYERR); e2 = llist2(LCONST,0LL); }
	}
    }
    type = set_type_with_attr(LONGLONG,type);
    return e2;
}

static int
ulonglong_value(int e2)
{
    int t = type_value(type);
    if (t>0&&car(t)==BIT_FIELD) e2=rvalue(e2);
    if (0);
    else if (car(e2)==CONST)  e2 = llist2(LCONST,(unsigned long long)cadr(e2));
    else if (car(e2)==LCONST)  ;
#if FLOAT_CODE
    else if (car(e2)==DCONST||car(e2)==FCONST)
        e2 = llist2(LCONST,(unsigned long long)dcadr(e2));
#endif
    else {
	switch(type_value(type)) {
	case FLOAT: e2 = list3(CONV,rvalue(e2),F2ULL); break;
	case DOUBLE: e2 = list3(CONV,rvalue(e2),D2ULL); break;
	case UNSIGNED: e2 = list3(CONV,rvalue(e2),U2ULL); break;
	case LONGLONG: break;
	case ULONGLONG: break;
	default:
	    if(integral(type)) e2 = list3(CONV,rvalue(e2),I2ULL);
	    else { error(TYERR); e2 = llist2(LCONST,0LL); }
	}
    }
    type = set_type_with_attr(ULONGLONG,type);
    return e2;
}
#endif

static int
int_value(int e2)
{
    int t = type_value(type);
    if (t>0&&car(t)==BIT_FIELD) { e2=rvalue(e2); t=type_value(type); }
    if (0);
    else if(t>0&&car(t)==ARRAY) return e2;
    else if(scalar(t)) { type = set_type_with_attr(INT,type); return e2; }
#if FLOAT_CODE
    else if (car(e2)==DCONST||car(e2)==FCONST)  e2 = list2(CONST,(int)dcadr(e2));
#endif
#if LONGLONG_CODE
    else if (car(e2)==LCONST)  e2 = list2(CONST,(int)lcadr(e2));
#endif
    else {
	switch(t) {
	case FLOAT: e2 = list3(CONV,rvalue(e2),F2I); break;
	case DOUBLE: e2 = list3(CONV,rvalue(e2),D2I); break;
	case LONGLONG: e2 = list3(CONV,rvalue(e2),LL2I); break;
	case ULONGLONG: e2 = list3(CONV,rvalue(e2),ULL2I); break;
	default:
	    error(TYERR); e2 = list2(CONST,1);
	}
    }
    type = set_type_with_attr(INT,type);
    return e2;
}

static int
char_value(int e2)
{
    int t = type_value(type);
    if (t!=CHAR&&t!=INT) { 
	e2 = list3(CONV,int_value(rvalue(e2)),I2C); 
	type = set_type_with_attr(INT,type);
    }
    return e2;
}

static int
short_value(int e2)
{
    int t = type_value(type);
    if (t!=SHORT&&t!=INT) { 
	e2 = list3(CONV,int_value(rvalue(e2)),I2S); 
	type = set_type_with_attr(INT,type);
    }
    return e2;
}

static int
unsigned_value(int e2)
{
    int t = type_value(type);
    if (t>0&&car(t)==BIT_FIELD) { e2=rvalue(e2); t=type_value(type); }
    else if(t>0&&car(t)==ARRAY) return e2;
    if (0);
    else if(scalar(t)) { type = set_type_with_attr(UNSIGNED,type); return e2; }
#if FLOAT_CODE
    else if (car(e2)==DCONST||car(e2)==FCONST)  e2 = list2(CONST,(int)dcadr(e2));
#endif
#if LONGLONG_CODE
    else if (car(e2)==LCONST)  e2 = list2(CONST,(unsigned)lcadr(e2));
#endif
    else {
	switch(t) {
	case LONGLONG: e2 = list3(CONV,rvalue(e2),LL2U); break;
	case ULONGLONG: e2 = list3(CONV,rvalue(e2),ULL2U); break;
	case FLOAT: e2 = list3(CONV,rvalue(e2),F2U); break;
	case DOUBLE: e2 = list3(CONV,rvalue(e2),D2U); break;
	default:
	    error(TYERR); 
	}
    }
    type = set_type_with_attr(UNSIGNED,type);
    return e2;
}

static int
uchar_value(int e2)
{
    int t = type_value(type);
    if (t!=UCHAR&&t!=UNSIGNED) { 
	e2 = list3(CONV,unsigned_value(rvalue(e2)),U2UC);
	type = set_type_with_attr(UNSIGNED,type);
    }
    return e2;
}

static int
ushort_value(int e2)
{
    int t = type_value(type);
    if (t!=USHORT&&t!=UNSIGNED) { 
	e2 = list3(CONV,unsigned_value(rvalue(e2)),U2US); 
	type = set_type_with_attr(UNSIGNED,type);
    }
    return e2;
}

/* assign statement */

/* keep type */

extern int
assign_expr0(int e1,int e2,int t,int type0) {
    int stype;
    stype=type;
    type = type0;
    e2 = rvalue(e2);
    e1=assign_expr(e1,e2,t);
    type=stype;
    return e1;
}



/* with conversion (will destroy type global variable) */

extern int
assign_expr(int e1,int e2,int t) {
    /* we should check const / assign violation here */
    t = type_value(t);
    if (t>0) {
	switch(car(type_value(t))) {
	case BIT_FIELD:
            //        type = list4(BIT_FIELD,type,
            //            list3(type /*store type*/,0 /*bit offset*/,bitsize));
	    e2 = correct_type(e2,cadr(t)); /* value type */
	    return(list4(BASS,e1,e2,list2(BASS,t)));
	case STRUCT:case UNION:
	    if (size(t)!=size(type)) error(TYERR);
	    type=t;    // dispose attr
	    if(car(e2)==RSTRUCT && car(cadr(e2))==FUNCTION) {
		replace_return_struct(cadr(e2),e1);
		return cadr(e2);
	    } else {
		return (list4(STASS,e1,e2,size(t)));
	    }
	default:
	    if(scalar(t)) {
		if (car(t)!=POINTER) {
		    e2=(type_value(t)==UNSIGNED)?
			unsigned_value(e2):int_value(e2);
		}
		return(list3(ASS,e1,e2));
	    }
	    error(TYERR); return list3(ASS,e1,e2);
	}
    }
    switch(t) {
    case VOID:
	break;
    case CHAR:case UCHAR: 
        e2=(t==UCHAR)?unsigned_value(e2):int_value(e2);
	return(list3(CASS,e1,e2));
    case SHORT:case USHORT:
        e2=(t==USHORT)?unsigned_value(e2):int_value(e2);
	return(list3(SASS,e1,e2));
    case INT:case UNSIGNED: case ENUM:
        e2=(t==UNSIGNED)?unsigned_value(e2):int_value(e2);
	return(list3(ASS,e1,e2));
#if FLOAT_CODE
    case DOUBLE:
        e2=double_value(e2);
	return(list3(DASS,e1,e2));
    case FLOAT:
        e2=float_value(e2);
	return(list3(FASS,e1,e2));
#endif
#if LONGLONG_CODE
    case LONGLONG:
        e2=longlong_value(e2);
	return(list3(LASS,e1,e2));
    case ULONGLONG:
        e2=ulonglong_value(e2);
	return(list3(LASS,e1,e2));
#endif
    }
    error(TYERR); return list3(ASS,e1,e2);
}

extern int
cond(int t,int e1,int e2,int e3)
{
    int t0 = type_value(t);
    int t1 = type_value(type);
    if(car(e1)==CONST) {
	if(cadr(e1)) {type=set_type_with_attr(t,type);return e2?e2:e1;} else return e3;
    }
#if FLOAT_CODE
    if(car(e1)==DCONST) {
	if(dcadr(e1)) {type=t;return e2?e2:e1;} else return e3;
    }
    if(t1==DOUBLE||t0==DOUBLE) {
	e3=double_value(e3);
	type = t; if (e2) e2=double_value(e2);
	return(list4(DCOND,e1,e2,e3));
    }
    if(t1==FLOAT||t0==FLOAT) {
	e3=float_value(e3);
	type = t; if (e2) e2=float_value(e2);
	return(list4(FCOND,e1,e2,e3));
    }
#endif
#if LONGLONG_CODE
    if(car(e1)==LCONST) {
	if(lcadr(e1)) {type=set_type_with_attr(t,type);return e2?e2:e1;} else return e3;
    }
    if(t1==LONGLONG||t0==LONGLONG) {
	e3=longlong_value(e3);
	type = t; if (e2) e2=longlong_value(e2);
	return(list4(LCOND,e1,e2,e3));
    }
    if(t1==ULONGLONG||t0==ULONGLONG) {
	e3=ulonglong_value(e3);
	type = t; if (e2) e2=ulonglong_value(e2);
	return(list4(LUCOND,e1,e2,e3));
    }
#endif
    if(t1==INT||t0==INT) {
	e3=int_value(e3);
	type = t; if (e2) e2=int_value(e2);
	return(list4(COND,e1,e2,e3));
    }
    e3=unsigned_value(e3);
    type = t; if (e2) e2=unsigned_value(e2);
    /* if (t!=type) error(TYERR); */
    return(list4(UCOND,e1,e2,e3));
}

/*
   assop
      parse tree generation
 */
extern int
assop(int e1,int e2,int op,int t,int no_float)
{
    int ass=0,u = 0;
    int t0 = type_value(type);
    if(!(integral(t0)||t0==FLOAT||t0==DOUBLE||
	t0==LONGLONG||t0==ULONGLONG
	    )) error(TYERR);
    switch(t) {
#if FLOAT_CODE
    case FLOAT:
	if (no_float) error(TYERR);
	e2=float_value(e2);
	return(list4(FASSOP,e1,e2,op+FOP));
    case DOUBLE:
	if (no_float) error(TYERR);
	e2=double_value(e2);
	return(list4(DASSOP,e1,e2,op+DOP));
#endif
#if LONGLONG_CODE
    case LONGLONG:
	e2=longlong_value(e2);
	return(list4(LASSOP,e1,e2,op+LOP));
    case ULONGLONG:
	e2=ulonglong_value(e2);
	return(list4(LASSOP,e1,e2,op+LOP+((op==MUL+AS||op==DIV+AS)?US:0)));
#endif
    case CHAR:
	e2=correct_type(e2,INT); ass = CASSOP;  break;
    case SHORT:
	e2=correct_type(e2,INT); ass = SASSOP; break;
    case INT:
	e2=correct_type(e2,INT); ass = ASSOP;  break;
    case UCHAR:
	e2=correct_type(e2,UNSIGNED); ass = CUASSOP; u=1; break;
    case USHORT:
	e2=correct_type(e2,UNSIGNED); ass = SUASSOP; u=1; break;
    case UNSIGNED:
	e2=correct_type(e2,UNSIGNED); ass = ASSOP;  u=1; break;
    default:
	if (t>0 && car(t)==BIT_FIELD) {
            //        type = list4(BIT_FIELD,type,
            //            list3(type /*store type*/,0 /*bit offset*/,symval));
	    e2 = correct_type(e2,car(caddr(t))); /* store type */
	    type = set_type_with_attr(cadr(t),type); /* value type */
	    return(list4(BASSOP,e1,e2,list2(op,t)));
	}
    }
    if (u) {
	if (op==RSHIFT||op==LSHIFT) op=op+US;
	else {
	    switch(type) {
	    case UCHAR: case USHORT: case UNSIGNED:
		if (op==MUL||op==DIV||op==MOD) op=op+US;
	    }
	}
    }
    type = set_type_with_attr(t,type);
    if(integral(t)) return(list4(ass,e1,e2,op));
    /* pointer += ... */
    if((op!=ADD&&op!=SUB)||car(t)!=POINTER) error(TYERR);
    e2=binop(MUL,e2,list2(CONST,size(cadr(t))),INT,UNSIGNED);
    type = set_type_with_attr(t,type);

    return list4(ASSOP,e1,e2,op);
}


/*
   assop
      code generation
 */

static void
iassop(int e1)
{
    int e2,e3,byte,op,sign,size;
    int n,t;

    /*   e2 op= e3 */
    switch(car(e1)) {
    case  CUASSOP: byte = 1; sign = 0; size = 1; break;
    case  CASSOP:  byte = 1; sign = 1; size = 1; break;
    case  SUASSOP: byte = size_of_short; sign = 0; size = size_of_short; break;
    case  SASSOP:  byte = size_of_short; sign = 1; size = size_of_short; break;
    default:       byte = 0; sign = 1; size = size_of_int;
    }
    e2 = cadr(e1);
    if (car(e2)==INDIRECT) e2=cadr(e2);
    e3 = caddr(e1);
    op = cadddr(e1);

    if (car(e2)==REGISTER) {
	if (code_const_op_p(op,e3)) {
	    oprtc(op,cadr(e2),e3);
	} else {
	    g_expr(e3);
	    code_register_assop(cadr(e2),USE_CREG,op,byte);
	}
	if (use) {
	    code_register(cadr(e2),USE_CREG);
	}
	return;
    }
    if (car(e3)==CONST) {
	/*  e2 = e2 op e3; */
	t = sign?INT:UNSIGNED;
	// oprtc expected
	if (car(e2)==LVAR||car(e2)==GVAR) {
	    g_expr(assign_expr0(e2,list3(op,rvalue_t(e2,t),e3),t,t));
	    return;
	}
	/*  new = &e2 */
	/*  *new = *new op e3 */
	n = list3(LVAR,new_lvar(size_of_int),0);
	g_expr_u(assign_expr0(n,list2(ADDRESS,e2),INT,INT));
	g_expr(assign_expr0(rvalue_t(n,INT),
	    list3(op,rvalue_t(list2(INDIRECT,rvalue_t(n,INT)),t),e3),t,t));
	free_lvar(cadr(n));
	return;
    }
    g_expr(e3);
    emit_push();
    g_expr(e2);
    code_assop(op,USE_CREG,byte,sign);
    return;
}

#if FLOAT_CODE

static void
dassop(int e1)
{
    int e2,e3,op,d;

    /*   e2 op= e3 */
    d = (car(e1) == DASSOP);
    e2 = cadr(e1);
    if (car(e2)==INDIRECT) e2=cadr(e2);
    e3 = caddr(e1);
    op = cadddr(e1);

    g_expr(e3);
    if (car(e2)==DREGISTER||car(e2)==FREGISTER) {
        emit_dpush(d);
	code_register_dassop(cadr(e2),op,d);
        if (use)
            code_dregister(cadr(e2),USE_CREG,d);
        return;
    }
    emit_dpush(d);
    g_expr(e2);
    code_dassop(op,USE_CREG,d);
    return;
}

#endif 

#if LONGLONG_CODE

static int
long_sign(int op)
{
    return (op==LUDIV||op==LUMOD||op==LULSHIFT||op==LURSHIFT)?ULONGLONG:LONGLONG;
}

static void
lassop(int e1)
{
    int e2,e3,op;
    int n,t;

    /*   e2 op= e3 */
    e2 = cadr(e1);
    if (car(e2)==INDIRECT) e2=cadr(e2);
    e3 = caddr(e1);
    op = cadddr(e1);

    if (car(e2)==LREGISTER) {
        if (code_lconst_op_p(op,e3)) {
            loprtc(op,cadr(e2),e3);
	    if (use) {
		code_lregister(cadr(e2),USE_CREG);
	    }
	    return;
	}
        if (code_lassop_p) {
            g_expr(e3);
	    emit_lpush();
            code_register_lassop(cadr(e2),op);
	    if (use) {
		code_lregister(cadr(e2),USE_CREG);
	    }
	    return;
        }
    }
    if (!code_lassop_p||car(e3)==LCONST) {
	/*  e2 = e2 op e3; */
	t = long_sign(op);
	if (car(e2)==LREGISTER||car(e2)==LVAR||car(e2)==GVAR) {
	    g_expr(assign_expr0(e2,list3(op,rvalue_t(e2,t),e3),t,t));
	    return;
	}
	/*  new = &e2 */
	/*  *new = *new op e3 */
	n = list3(LVAR,new_lvar(size_of_int),0);
	g_expr_u(assign_expr0(n,list2(ADDRESS,e2),INT,INT));
        g_expr(assign_expr0(rvalue_t(n,INT),
            list3(op,rvalue_t(list2(INDIRECT,rvalue_t(n,INT)),t),e3),t,t));
        free_lvar(cadr(n));
	return;
    }

    g_expr(e3);
    if (car(e2)==LREGISTER) {
        emit_lpush();
        code_register_lassop(cadr(e2),op);
        if (use)
            code_lregister(cadr(e2),USE_CREG);
        return;
    }
    emit_lpush();
    g_expr(e2);
    code_lassop(op,USE_CREG);
    return;
}

#endif 

extern void 
cmpdimm(int e, int csreg,int label,int cond)
{
    if (!chk) {
	if (car(csreg)==CONST) {
	    switch(cond) {
	    case 1: case 0: 
	    if (cond ^ (cadr(csreg)==e)) gen_jmp(label);
		break;
	    case LT:
	    if ((cadr(csreg)>e)) gen_jmp(label);
		break;
	    }
	} else if (car(csreg)==REGISTER) {
	    code_cmpdimm(e, cadr(csreg),label,cond);
	} else error(-1);
    }
}

extern int 
csvalue()
{
    return code_csvalue();
}

extern void
gen_ret()
{
    if (chk) return;
    code_ret();
}

extern void
gen_label_call(int l)
{
    if (chk) return;
    code_label_call(l);
}

extern int
fwdlabel(void)
{       
    return labelno++;
}

extern void
fwddef(int l)
{       
    if (l==0) return;
    checkjmp(l);
    control=1;
    if (!chk)
	code_label(l);
}

extern int
backdef(void)
{       
    checkjmp(0);
    control=1;
    if (!chk)
	code_label(labelno);
    return labelno++;
}

// define case label with default label for switch statement

extern void
def_label(int cslabel, int dlabel)
{
    int fl;

    checkjmp(0);
    fl = 0;
    if (control) {
	gen_jmp(fl=fwdlabel());
    }
    fwddef(cslabel);
    if (dlabel)
	gen_jmp(dlabel);
    if (fl) {
	fwddef(fl);
    }
}

extern void
ret(void)
{       
    if (!is_inline(fnptr))  {
	if (!chk)
	    code_set_return_register(1);
    }
    gen_jmp(retlabel); 
}

extern void
opening(char *filename)
{
    emit_init();
    if (!chk)
	code_opening(filename);
}

extern void
closing()
{
    int e;
    NMTBL *n;
    for(e=inline_funcs;e;e=cadr(e)) {
	n = (NMTBL*)car(e);
        if (is_code(n)||is_function(n)) {
            if (n->sc!=STATIC || has_attr(n,FNAME)) {
		// global or used as pointer
		// generate possibly called inline function
		pfdecl(n);
	    }
	}
    }
    if (!chk)
	code_closing();
}

static int
contains_in_list(int e,int type)
{
    while(e) {
	if(contains(car(e),type)) return 1;
	e = cadr(e);
    }
    return 0;
}

static int
contains(int e,int type)
{
    while(e) {
	if (car(e)==type) return 1;
	if (!car(e)) return 0;
	if (LIST_ARGS(car(e))){
        /* list arguments */
	    return contains_in_list(caddr(e),type);
	} else if (UNARY_ARGS(car(e))) {
        /* unary operators */
	    e = cadr(e);
	    continue;
	} else if (BINARY_ARGS(car(e))) {
        /* biary operators */
	    if (contains(cadr(e),type)) return 1;
	    e = caddr(e);
	    continue;
	} else if (TARNARY_ARGS(car(e))) {
        /* tarary operators */
	    if (contains(cadr(e), type)) return 1;
	    if (contains(caddr(e),type)) return 1;
	    e = cadddr(e);
	    continue;
	} else if (NULLARY_ARGS(car(e))) {
        /* nullary operators */
	    return 0;
	} else if (IS_STATEMENT(car(e))) {
	    return 1;  // may contain anything
	} else {
	    // fprintf(stderr,"Unknown Tree ID %d\n",car(e));
	    error(-1);
	    return 0;
	}
    }
    return 0;
}

static int
contains_in_list_p(int e,int (*p)(int))
{
    while(e) {
	if(contains_p(car(e),p)) return 1;
	e = cadr(e);
    }
    return 0;
}

extern int
contains_p(int e,int (*p)(int))
{
    while(e) {
	if (!car(e)) return 0;
	if (p(car(e))) return 1;
	if (LIST_ARGS(car(e))){
        /* list arguments */
	    return contains_in_list_p(caddr(e),p);
	} else if (UNARY_ARGS(car(e))) {
        /* unary operators */
	    e = cadr(e);
	    continue;
	} else if (BINARY_ARGS(car(e))) {
        /* biary operators */
	    if (contains_p(cadr(e),p)) return 1;
	    e = caddr(e);
	    continue;
	} else if (TARNARY_ARGS(car(e))) {
        /* tarary operators */
	    if (contains_p(cadr(e), p)) return 1;
	    if (contains_p(caddr(e),p)) return 1;
	    e = cadddr(e);
	    continue;
	} else if (NULLARY_ARGS(car(e))) {
        /* nullary operators */
	    return 0;
	} else if (IS_STATEMENT(car(e))) {
	    return 1;  // may contain anything
	} else {
	    // fprintf(stderr,"Unknown Tree ID %d\n",car(e));
	    error(-1);
	    return 0;
	}
    }
    return 0;
}

/* gahter accumurated list in all parse tree */

static int
contains_in_list_p1(int arg,int e,int (*p)(int,int))
{
    while(e) {
	arg=contains_p1(arg,car(e),p);
	e = cadr(e);
    }
    return arg;
}

extern int
contains_p1(int arg,int e,int (*p)(int,int))
{
    while(e) {
	if (!car(e)) return arg;
	if (LIST_ARGS(car(e))){
        /* list arguments */
	    return contains_in_list_p1(arg,caddr(e),p);
	} else if (UNARY_ARGS(car(e))) {
        /* unary operators */
	    e = cadr(e);
	    continue;
	} else if (BINARY_ARGS(car(e))) {
        /* biary operators */
	    arg=contains_p1(arg,cadr(e),p);
	    e = caddr(e);
	    continue;
	} else if (TARNARY_ARGS(car(e))) {
        /* tarary operators */
	    arg=contains_p1(arg,cadr(e), p);
	    arg=contains_p1(arg,caddr(e),p);
	    e = cadddr(e);
	    continue;
	} else if (NULLARY_ARGS(car(e))) {
        /* nullary operators */
	    arg=p(arg,e);
	    return arg;
	} else if (IS_STATEMENT(car(e))) {
	    return arg;
	} else {
	    // fprintf(stderr,"Unknown Tree ID %d\n",car(e));
	    // error(-1);
	    return arg;
	}
    }
    return arg;
}

#if ASM_CODE


/*
        __asm__ __volatile__ ("sthbrx %1,0,%2" : "=m" (*addr) : "r" (val), "r" (a
ddr));
   asm string : output constraint parameter : input constraint parameter : opt

     1: asm string     %1,%2 will be replaced by register or value
     2: constraint     gcc constraint sting
        prefix
           =    overwrite by this asm for output
           &    overwrite by this asm and can't be used as input register
           ignored in this compiler
        constraints
           m    value expression is modified (no corresponding register)
                        information for compiler
           r    register for input or output 
			input register, output register can be shared
           0-9  same operands as outout register in input constraints
      3: opt     "cc", "memory"
           ignored in this compiler
 */

static void
gen_asm(int asm0,int in,int out,int opt,int e)
{
    int i,e1,n;
    int repl = 0;
    int repl0;
    int assign = 0;
    char *p;

//    printf("## asm\n");
    e = reverse0(e);
    for(i=out;i;i=cadr(i)) {
	p = (char*)cadr(car(i));
	e1 = car(e); e = cadr(e);
	repl = code_asm_operand(p,e1,ASM_OUTPUT,repl,0,0);
	if (car(car(repl))==REGISTER) {
	    assign = list2(assign_expr0(e1,car(repl),INT,INT),assign);
	}
    }
    repl0 = repl;
    n = length(repl0);
    for(i=in;i;i=cadr(i)) {
	p = (char*)cadr(car(i));
	e1 = car(e); e = cadr(e);
	repl = code_asm_operand(p,e1,ASM_INPUT,repl,n,repl0);
	if (car(car(repl))==REGISTER) {
	    g_expr_u(assign_expr0(car(repl),e1,INT,INT));
	}
    }
    repl = reverse0(repl);
    code_asm((char*)cadr(asm0),repl);
    for(i=assign;i;i=cadr(i)) {
	g_expr_u(car(i));
    }
    code_free_asm_operand(repl);
    // no check for opt 
}

#endif

static void
set_ctmode(NMTBL *n,int ctmode)
{
    if (ctmode & KONST_BIT) set_attr(n,KONST,0);
    if (ctmode & VOLATILE_BIT) set_attr(n,VOLATILE,0);
    if (ctmode & RESTRICT_BIT) set_attr(n,RESTRICT,0);
}

/*
    define symbol name contents 
      depending on stmode, mode
      define displacement
 */

extern NMTBL *
def(NMTBL *n,int ctmode)
{
    int sz,nsc,ndsp;
    int sbit_f = bit_field_disp;
    int type0 = type_value(type);
    bit_field_disp = 0;  // default is 0, recover only in bit-field

    if (n==0) {
	n=anonymous_nptr();
	n->nm = "_";
    }
    nsc=ndsp=0;
    if (stmode==EXTRN||mode==GDECL)
	n->ty = type;  /* must be in global table/heap */
    if(type0>0&&(car(type0)==FUNCTION || car(type0)==CODE)) {
	if ((mode==GDECL)) {
	    fcheck(n);
	    set_ctmode(n,ctmode);
	    return n;
	    /* function and code segment are defined using fdecl/code_decl */
            /* in decl() */
	}
    }

    // compute size 

    if (mode==GSDECL||mode==LSDECL) {   // struct
        /* Struct fields name lists are in the struct type or tag. */
        /* Only name in the table is used. Do not set n->ty! */
	/* Struct field may volatile... where do I put? list2(VOLATILE,type)? */
	/* disp is pushded and reset in sdecl */
	if (type0>0 && car(type0)==BIT_FIELD) {
	    bit_field_disp=sbit_f;   // default is 0, recover only here.
            //        type = list4(BIT_FIELD,value type,
            //            list3(store type,bit offset,bit_width));
#if BIT_FIELD_CODE
	    cadr(caddr(type0)) = code_bit_field_disp(
		type,&disp,&bit_field_disp,&sz);
	    /* bit_field_disp is next bit posision */
#else
	    error(-1);
#endif
	}  else {
#if STRUCT_ALIGN 
	    sz = size(type0);
            if (sz%size_of_int==0) {
                disp = ((disp+(size_of_int-1))&~(size_of_int-1));
            }
#endif
	}
	if (n!=&null_nptr)
	    fields = list4(type,fields,(int)(n->nm),disp);
    } else if (mode==GUDECL||mode==LUDECL) { // union
	/* disp is pushded and reset in sdecl */
	if (type0>0 && car(type0)==BIT_FIELD) {
	    cadr(caddr(type0)) = 0; sz = size(cadr(type0));
	}  else {
	    sz = size(type0);
	}
	fields = list4(type,fields,(int)(n->nm),0);
    } else {
	if (n->sc!=EMPTY &&  !(n->sc==EXTRN||n->sc==EXTRN1||n->sc==STATIC)) {
	  /* redefined case */
	  if (mode==ADECL) {  /* K&R arguments case */
	    if (n->sc==LVAR && n->ty==INT);
	    else if ( n->sc==REGISTER && n->ty==INT);
	    else if ( n->sc==TYPE) {
		n = lsearch(n->nm,0);
	    } else error(RDERR);
	  } else {
	    if (mode==GDECL)
		compatible(n->ty,type);
	    else
		error(RDERR); // on different type
	  }
	}
	sz = size(n->ty = type);
    }
    switch(mode) {
    case GDECL:            // global variable
	gen_gdecl(n->nm,gpc);
    case STADECL:          // static variable
	nsc = GVAR;
	ndsp = gpc;
	if (n->dsp!=-1)     /* don't set dsp if initialized static */
	    n->dsp = ndsp;  /* emit_data will override this */
	if (stmode==EXTRN)
	    nsc = EXTRN;
	else if (stmode==STATIC||stmode==LDECL)
	    nsc = STATIC;
	n->sc = nsc;
	if (stmode==LDECL) {
	    // this means local static variable
	    n = new_static_name(n->nm,'.');
	    if (!n->next) {
		n->next = local_static_list; local_static_list = n;
	    }
	} else {
	    if (!n->next) {
		n->next = global_list; global_list = n;
	    }
	}
	gpc +=sz;
	set_ctmode(n,ctmode);
	return n;
    case GSDECL: case LSDECL:   // struct
	disp += sz;
	return n;
    case GUDECL: case LUDECL:   // union
	/* In union case, disp contains max size of the member */
	if (disp < sz) disp = sz;
	return n;
    case GTDECL:                // typedef
	nsc = TYPE;
	gtypedefed=glist2((int)gnptr,gtypedefed);
	break;
    case LTDECL:                // local typedef
	nsc = TYPE;
	break;
    case LLDECL:                // label def (gcc extension)
	nsc = FLABEL;
	if (!inmode)
	    ndsp = fwdlabel();
	else
	    ndsp = --disp;
	// inmode の時は pvartable のoffset を確保する。st_label で
	// nptr が 0 ならば backdef、st_label よりも先に使われたら
	// fwdlabel すれば良い。
	// scope は parse 時に解決される。
	break;
    case ADECL:                 // funcion arguments
	if(!integral(type0)&&type0>0&&(car(type0)==FUNCTION||car(type0)==CODE)) {
	    type=list2(POINTER,type); n->ty = type;
	    type0=type;
	}
	fnptr->dsp=list4(type,fnptr->dsp,(int)n,0);
	n->sc = LVAR;
	if(inmode==INLINE) {
	    n->dsp = args++;
	    n->sc = IVAR;
	} else if(type0==CHAR||type0==UCHAR) {
	    if (n->dsp==0) {
		n->dsp = args;
		if (endian) n->dsp += size_of_int-1;
	    }
	    args += size_of_int;
	} else if(type0==SHORT||type0==USHORT) {
	    if (n->dsp==0) {
		n->dsp = args;
		if (endian) n->dsp += size_of_int-size_of_short;
	    }
	    args += size_of_int;
#if  1
	} else if(type0>0&&(car(type0)==UNION||car(type0)==STRUCT)) {
	    /* alignment in struct in argument */
	    n->dsp = args;
	    args += ((sz+(size_of_int-1))&~(size_of_int-1));
#endif
	} else {
	    /* if (n->dsp==0) (argument list in ADECL is useless, type
               list can be found in type ) */
	    n->dsp = args;
	    args += sz;
	}
	cadddr(fnptr->dsp)=sz;
	if(type0==VOID) {
	} else {
	    n->ty = type;
	}
	set_ctmode(n,ctmode);
	return n;
    case STAT: /* return (struct hoge)f() case? */
    case LDECL:    // local variable
	if (stmode==REGISTER && !inmode) {
	    if(scalar(type0)) {
		ndsp = get_register_var(n);
#if FLOAT_CODE
	    } else if (type0==FLOAT) {
		ndsp = get_dregister_var(n,0);
	    } else if (type0==DOUBLE) {
		ndsp = get_dregister_var(n,1);
#endif
#if LONGLONG_CODE
	    } else if (type0==LONGLONG||type0==ULONGLONG) {
		ndsp = get_lregister_var(n);
#endif
	    } else error(DCERR);
	    nsc = car(ndsp);
	    ndsp = cadr(ndsp);
	} else if (inmode) {
	    nsc = IVAR;
	    ndsp = --disp;
	} else {
	    /* local variable alignment is done by new_lvar */
	    nsc = LVAR;
	    ndsp = new_lvar(sz);
	}
	n->sc = nsc;
	n->dsp = ndsp;
	set_ctmode(n,ctmode);
	return n;
    default:
	error(DCERR);
    }
    n->sc = nsc;
    n->dsp = ndsp;
    set_ctmode(n,ctmode);
    if (stmode==EXTRN)
	n->sc = EXTRN;
    return n;
}


extern void
emit_init_vars(void)
{
    int e;
    if (!init_vars) return;
    e = reverse0(init_vars); init_vars = 0;
    if (inmode) {
	while(e) {
	    parse = list3(ST_COMP,parse,car(e));
	    e = cadr(e);
	}
	return;
    }
    while(e) {
	gexpr(car(e),0);
	e = cadr(e);
    }
}

static int
str_init_eq()
{
    // error(-1);  // duplicate struct field value
    return 2;      // allow override keep unique
}

//
// generate constant on global memory
//

static void
emit_data(int e, int t, NMTBL *n)
{
    t = type_value(t);
    if(mode!=GDECL && mode!=STADECL)  { 
	error(-1); return;
    }
    if (chk) return;
    if (n->dsp != -1) {
	n->dsp = -1;   /* initialized flag */
	emit_global(n,t,e);
    }
    switch(t) {
    case EMPTY:
	if(car(e)!=CONST) error(-1);
	emit_space(cadr(e));
	return;
    case CHAR: case UCHAR:
	if (car(e)!=CONST) error(INERR);
	emit_char(cadr(e));
	data_alignment++;
	return;
    case SHORT: case USHORT:
	if (car(e)!=CONST) error(INERR);
	emit_short(cadr(e));
	data_alignment++;
	return;
    case LONGLONG: case ULONGLONG:
	if (car(e)!=LCONST&&car(e)!=CONST) error(INERR);
	emit_longlong(e);
	return;
    case DOUBLE:
	if (car(e)!=DCONST&&car(e)!=FCONST) error(INERR);
	emit_double(e);
	return;
    case FLOAT:
	if (car(e)!=DCONST&&car(e)!=FCONST) error(INERR);
	emit_float(e);
	data_alignment++;
	return;
    default:
	if (t<0) error(-1);
	if (car(t)==BIT_FIELD) {
	    /* not yet supported */
	    error(-1);
	    return;
	}
	if (car(t)!=POINTER&&car(t)!=ARRAY) error(-1);
    case INT: case UNSIGNED:   /* including address case */
    case ENUM:
	switch(car(e)) {
	case CONST:
	    emit_int(cadr(e));
	    return;
	case ADDRESS:
	    if (car(cadr(e))==GVAR)
		emit_address(((NMTBL *)caddr(cadr(e)))->nm,cadr(cadr(e)));
	    else error(INERR);
	    return;
	case FNAME:
	    emit_address(((NMTBL *)cadr(e))->nm,0);
	    return;
	case GVAR:
	    emit_address(((NMTBL *)caddr(e))->nm,0);
	    return;
	case STRING:
	    emit_string(((NMTBL *)cadr(e))->nm,n->ty);
	    return;
	}
    // fprintf(stderr,"## type= %d\n",t);
    }
    error(INERR);
}

//
// local variable initialization
//

extern int
assign_data(int e, int t, NMTBL *n,int offset)
{
    int ass,sz,bfd;

#if STRUCT_ALIGN
    if (t!=-99) {
	sz = size(t);
	if (sz%size_of_int==0) {
	    offset = ((offset+(size_of_int-1))&~(size_of_int-1));
	}
    }
#endif
    if (car(e)==ADDRESS||car(e)==GVAR) {
	if (scalar(t)) {
	    t = list2(POINTER,VOID); // fake
	} else {
	    error(TYERR);
	}
    }
    if(mode==GDECL) {
	if (!is_const(e)) error(INERR);
 	emit_data(e,t,n);
    } else if(mode==LDECL || (mode==STADECL&&local_nptr&&(n=local_nptr))) {
	if (t==EMPTY) {
	    /* empty space in partial initialization */
	    return offset+cadr(e);
	}
	/* If this is a local declared constant, we don't have to assign.
           But some one may take it's address. We have to generate assign.
         */
	ass = assign_expr0(
	    inmode?(
		offset?
		    list3(ADD,list3(n->sc,n->dsp,(int)n),list2(CONST,offset)):
		    list3(n->sc,n->dsp,(int)n)):
    (n->sc==REGISTER||n->sc==DREGISTER||n->sc==FREGISTER||n->sc==LREGISTER)?
		list3(n->sc,n->dsp,(int)n):
		list3(n->sc,n->dsp+offset,(int)n),
	e,t,type);
	init_vars = list2(ass,init_vars);
    } else if(mode==STADECL) {
	if (!is_const(e)) error(INERR);
	else emit_data(e,t,n);
    } else if(mode==SFDINIT) {
// printf("## %d sfdinit c0(e)=%d type=%d t=%d offset=%d\n",lineno,car(e),type,t,offset);
	decl_str_init=insert_ascend(decl_str_init,
		list4(offset,0,e,list2(t,type)),str_init_eq);
    } else {
	error(DCERR);
	return offset;
    }
    if (t>0&&car(t)==BIT_FIELD) {
	sz = 0; 
	bfd = cadr(caddr(t)); /* bit_field_disp */
#if BIT_FIELD_CODE
	code_bit_field_disp(t,&offset,&bfd,&sz);
#endif
	return offset+sz;
    }
    /* constant value field */
    if (offset==0 && (has_attr(n,KONST))) {
	if (is_const(e))
	    set_attr(n,KONST,e);
    }
    return offset+((t==EMPTY)?cadr(e):size(t));
}

extern void
flush_delayed_decl_data(NMTBL *n)
{
    int offset;
    int offset0=0;
    int e;
    int t,sz,offset1=0;
    int smode=mode;
    mode = STADECL;
    sz = size(n->ty);
    /*
         decl_str_init
            output delayed decl data
         list4(offset,next,expression,list2(type0,type1));
     */
    while (decl_str_init) {
        offset= car(decl_str_init);
        e=caddr(decl_str_init);
        t=car(cadddr(decl_str_init));
        if (offset!=offset0) {
            // make space
            assign_data(list2(CONST,offset-offset0),EMPTY,n,offset0);
        }
        type=cadr(cadddr(decl_str_init));
// printf("## %d flush   c0(e)=%d type=%d t=%d offset=%d\n",lineno,car(e),type,t,offset);
        offset0 = assign_data(e,t,n,offset);
        decl_str_init = cadr(decl_str_init);
    }
    offset = offset0;
    if ((sz=(offset1+sz-offset))>0)
        assign_data(list2(CONST,sz),EMPTY,n,offset0);
    decl_str_init = 0;
    local_nptr = 0;
    mode=smode;
}

extern void
data_closing(NMTBL *n)
{
    if (!chk) {
	if (decl_str_init) flush_delayed_decl_data(n);
	emit_data_closing(n);
    }
}

#define ARG_REORDER_DEBUG 0

/*
     In K&R style, order of argment list and order of argment
     type decl are differnt. Fix them.
 */

extern int
arg_reorder(int arg,int new_arg)
{
    /*	list4(type,fnptr->dsp,(int)n,size); */
    int i,j,sz;
    int dsp = 0;
    NMTBL *n,*n1;

    /* f(a,b,c)  int c; short a; char* b; { } case */
#if ARG_REORDER_DEBUG
 fprintf(stderr,"arg_reorder old:\n");
    for(j=new_arg;j;j=cadr(j)) {
	    n=(NMTBL *)caddr(j);
 fprintf(stderr,"dsp %d %s sz %d type %d\n",n->dsp,n->nm,cadddr(j),car(j));
    }
 fprintf(stderr,"arg_reorder new:\n");
#endif
    for(j=arg;j;j=cadr(j)) {
	n=(NMTBL *)caddr(j);
	for(i=new_arg;i;i=cadr(i)) {
	    n1=(NMTBL *)caddr(i);
	    if (!neqname(n1->nm,n->nm)) break;
	    // if (n1==n) break;
	}
#if ARG_REORDER_DEBUG
 fprintf(stderr,"dsp %d %s %s sz %d type %d\n",dsp,n->nm,n1->nm,cadddr(i),car(i));
#endif
	if (!i) {
	    /* f(a,b,c) int c; { } case (what?!) */
	    i = j;
	    n1 = n;
	}
	if(n->sc==LVAR) {
	    n->dsp = dsp;
	    car(j)=car(i);
	    caddr(j)=caddr(i);
	    n1->dsp = n->dsp;
	    n->ty =  n1->ty;
	    n->sc =  n1->sc;
	    n->attr =  n1->attr;
	    cadddr(j)=sz= cadddr(i);
	    if (sz==1||sz==size_of_short) sz = size_of_int;
	    dsp += sz;
	}
    }
#if ARG_REORDER_DEBUG
 fprintf(stderr,"arg_reorder end:\n");
#endif
    return arg;
}


static NMTBL str_ret;

/*
    If function has structure return value, it has an extra
    argument for where to write the structure. It have to be
    a first argument. We add it here and we have to fix all arguments'
    offset. If it is the last value, we don't have to fix, but
    gcc has a first argument convention.
 */

extern void
fdecl_struct(int fntype)
{
    int type_save,mode_save,t,sz;
    NMTBL *n;
    int sargs = args;

    t = cadr(fntype);
    if (t>0 && (car(t)==STRUCT||car(t)==UNION)) {
	mode_save = mode;
	mode=ADECL;
	type_save = type;
	/* extra argument for struct return */
	/* this extra dummy arguments are set at calling sequence */
	str_ret.nm = "str_ret"; str_ret.sc = EMPTY;
	str_ret.dsp = 0; str_ret.ty = 0;
	type=list2(POINTER,t);
	/* fix all argument's offset */
	sz = inmode?1:size(type);
	for(t=fnptr->dsp;t;t=cadr(t)) {
	    n=(NMTBL *)caddr(t);
	    n->dsp += inmode?1:sz;
	}
	fnptr->dsp = reverse0(fnptr->dsp);
	if ((sz=size(cadr(fntype)))==-1) error(TYERR);
	else {
	    args=0;  // set struct var dsp = 0
	    def(&str_ret,0); 
	    args = sargs + (inmode?1:size_of_int);
	    struct_return = inmode
		?list3(list3(IVAR,str_ret.dsp,0),sz,type)
		:list3(list3(LVAR,str_ret.dsp,0),sz,type);
	    caddr(fnptr->ty) = glist2(POINTER,caddr(fnptr->ty));
	}
	type = type_save;
	mode = mode_save;
    } else {
	struct_return = 0;
	fnptr->dsp = reverse0(fnptr->dsp);
    }
}

extern void
fcheck(NMTBL *n)
{
    int type0 = type_value(type);
    if(!(mode==GDECL||mode==ADECL)||
             (car(type0)!=FUNCTION&&car(type0)!=CODE)) error(DCERR);
    if (n->sc==EMPTY) {
	n->sc=EXTRN;
	n->ty=type;
    } else if(is_code(n)) {
	// if (car(type0)!=CODE) error(TYERR);
	compatible(cadr(n->ty),cadr(type));
    } else if(is_function(n)) {
	// if (car(type0)!=FUNCTION) error(TYERR);
	compatible(cadr(n->ty),cadr(type));
    } else {
	error(DCERR);
    }
}

static void
compatible(int t1, int t2)
{
    t1 = type_value(t1);
    t2 = type_value(t2);
    if(integral(t1)) {
	    if(t1!=t2) error(TYERR);
    }
    else if(t1<0 || t2<0) {
	if(t1!=t2) error(TYERR);
    } else if(car(t1)!=car(t2))
	    error(TYERR);
    else if((car(t1)==STRUCT || car(t1)==UNION) && cadr(t1)!=cadr(t2))
	    error(TYERR);
    else if(car(t1)==POINTER || car(t1)==ARRAY ||car(t1)==FUNCTION)
	    compatible(cadr(t1),cadr(t2));
}

extern int
scalar(int t)
{
    t = type_value(t);
    return(integral(t)||(t>0 && car(t)==POINTER));
}

extern int
integral(int t)
{
    t = type_value(t);
    return(t==INT||t==SIGNED||t==CHAR||t==UNSIGNED||
        t==UCHAR||t==SHORT||t==USHORT||t==ENUM);
}

extern void
checkjmp(int l)
{
    int p = pending_jmp;
    pending_jmp = 0;
    if (p) {
	if (p!=l) {
	    control=0;
	    if (!chk)
		jmp(p);
	}
    }
}

extern void
checkret(void)
{
    int lastexp0;
    if (!inmode) {
	if (cslabel==0) {
	    if (!control) error(CSERR); // no execute code in switch
	    checkjmp(0);
	    control=0;
	    jmp(cslabel=fwdlabel());
	} else if (retpending) {
	    ret();
	    control=0;
	    retpending=0;
	}
	if (lastexp) {
	    if(!control) error(-1);
	    lastexp0 = lastexp;
	    lastexp = 0;   // checkret can be nest?
	    gexpr(lastexp0,0);
	}
    } else if (lastexp) {
	parse = list3(ST_COMP,parse,lastexp);
	lastexp = 0;
    }
}


extern void
replace_return_struct(int func,int left) {
    int e = caddr(func);      /* arg lists */
    while(cadr(e)) e=cadr(e); /* find first arg */
    e = car(e);               /* return_struct arg */
    cadr(e) = left;
}


/* right value , get the value of the variable */

static int
indirect(int t,int e1,int type)
{
    int e2,e3,e4,offset;
    e2 = e1;
    offset = 0;
    e3 = cadr(e2);
    if (car(e2)==ADD) {
        e4=caddr(e2);
        if (car(e4)==CONST) {
            offset=cadr(e4);
            e1=e3;
        }
    }
    return list4(t,e1,offset,type);
}

extern int
rvalue(int e)
{
    int op,c;
    NMTBL *n;    
    int type0 = type_value(type);

    if (e==0) error(-1);
    op = 0;
    switch(type0) {
    case INT:		break;
    case UNSIGNED:	break;
    case VOID:		break;
    case CHAR:		op=COP; type=set_type_with_attr(INT,type); break;
    case UCHAR:		op=COP+US; type=set_type_with_attr(UNSIGNED,type); break;
    case SHORT:		op=SOP; type=set_type_with_attr(SHORT,type); break;
    case USHORT:	op=SOP+US; type=set_type_with_attr(UNSIGNED,type); break;
    case LONGLONG:	op=LOP; break;
    case ULONGLONG:	op=LOP+US; break;
    case FLOAT:		op=FOP; break;
    case DOUBLE:	op=DOP; break;
    case CODE:	return e;
    case 0:	error(-1); return e;
    default:
	if (integral(type0)) break;
	switch(car(type0)) {
	case ARRAY:
	    type=set_type_with_attr(list2(POINTER,cadr(type)),type);
	    if(car(e)==INDIRECT) return cadr(e);
	    return list2(ADDRESS,e);
	case STRUCT: case UNION:
	    if(car(e)==RSTRUCT) return e; /* ??? */
	    return list3(RSTRUCT,e,cadr(type) /* size */);
	case FUNCTION:
	    type=set_type_with_attr(cadr(type0),type);
	    return e;
	case CODE:
	    return e;
	case POINTER:
	    break;
	case BIT_FIELD:
	    e =  list3(RBIT_FIELD,cadr(e),type);
            /*                         byte rvalue,   store type */
	    type=set_type_with_attr(cadr(type0),type);/* value type */
	    return e;
	    break;
	default:
	    error(TYERR);
	}
    }
    switch(car(e)) {
    case GVAR:
	n = (NMTBL*)caddr(e);
	if (cadr(e)==0 && (c=attr_value(n,KONST))) {
	    if (!has_attr(n,VOLATILE))
	    return c;
	}
	return(list3(RGVAR+op,cadr(e),caddr(e)));
    case LVAR:
	n = (NMTBL*)caddr(e);
	if (cadr(e)==0 && n && (c=attr_value(n,KONST))) {
	    if (!has_attr(n,VOLATILE))
	    return c;
	}
	return(list3(RLVAR+op,cadr(e),caddr(e)));
    case INDIRECT:
	return(indirect(RINDIRECT+op,cadr(e),type0));
    case IVAR:
	return(indirect(RINDIRECT+op,e,type0));
    default:return(e); /* idempotent case? */
    }
}

extern int
rvalue_t(int e,int t)
{
    int stype = type;
    type = t;
    e = rvalue(e);
    type = stype;
    return e;
}

extern void
lcheck(int e)
{
    int t;
    int type0 = type_value(type);
    if(scalar(type0)) return;
    switch(type0) {
    case DOUBLE: case FLOAT : case LONGLONG: case ULONGLONG:
	return;
    default:
	switch(car(e)) {
	case GVAR: case LVAR: case INDIRECT  :
	case REGISTER  : case DREGISTER  : case FREGISTER  : case LREGISTER:
	return;
	}
    }
    if ((t=car(type0))<0 && t!=STRUCT && t!=UNION)
	error(LVERR);
}

extern int
indop(int e)
{
    int type0 = type_value(type);
    if(type0!=INT&&type0!=UNSIGNED) {
	if(car(type0)==POINTER)
	    type=set_type_with_attr(cadr(type),type);
	else if(car(type0)==CODE || car(type0)==FUNCTION) {
	    // type=type;
	} else error(TYERR);
    } else
	type= set_type_with_attr(CHAR,type);  // ?!
    if(car(e)==ADDRESS)
	return(cadr(e));
    return(list2(INDIRECT,e));
}

/* filed name search */

    /* type = list4(s,disp,fields,tag_nptr); */

extern int
search_struct_type(int type,char *name,int *dsp)
{
    int t;
    NMTBL *nptr0;
    t = caddr(type_value(type));
    if (t==0) {
	nptr0=(NMTBL*)cadddr(type);
	t = caddr(type) = caddr(nptr0->ty);
    }
    for(;t;t = cadr(t)) {
	if (neqname((char *)caddr(t),name)==0) {
	    *dsp = cadddr(t);
	    return car(t);
	}
    }
    return 0;
}

extern int
strop(int e,int ind)
{
    int dsp = 0;
    int type0;

    if (ind) e = indop(rvalue(e));
    type0 = type_value(type);
    if (integral(type0)||(car(type0)!=STRUCT && car(type0)!=UNION)) {
	e=rvalue(e); type0 = type_value(type);
    }
    if ((car(type0)!=STRUCT && car(type0)!=UNION)) {
	error(TYERR); type=INT; return e; 
    }
    /* type = list4(s,disp,fields,tag_nptr); */
    /* print_fields(caddr(type),"strop"); */
    type = search_struct_type(type,nptr->nm,&dsp);
    if (!type) { error(TYERR); type=INT; return e; }
    if(dsp) {
	switch(car(e)) {
	case GVAR:
	    // e=list2(INDIRECT,list3(ADD,e,list2(CONST,dsp)));
	    e=list3(GVAR,cadr(e)+dsp,caddr(e));
	    break;
	case LVAR:
	    e=list3(LVAR,cadr(e) + dsp,0);  /* may have attribute */
	    break;
	case INDIRECT:
	    e=list2(INDIRECT,list3(ADD,cadr(e),list2(CONST,dsp)));
	    break;
	default:
	    e=list2(INDIRECT,list3(ADD,e,list2(CONST,dsp)));
	}
    } else {
	switch(car(e)) {
	case GVAR: case LVAR: case INDIRECT:
	    break;
	default:
	    e=list2(INDIRECT,e);
	}
    }
    type0 = type_value(type);
    if (type>0&&car(type)==BIT_FIELD) {
	// n->ty = list4(BIT_FIELD,type,bit_offset, bit_size);
	e=list3(BIT_FIELD,e,type);  //  ???
    }
    return e;
}

/*
   I don't know how to handle type attribute (const/volatie) in binop
 */

#if FLOAT_CODE
/* binary floating computation */

#define DTYPE(dop) (dop==DOP?DOUBLE:FLOAT)

static int
fdbinop(int op, int e1, int e2, int t1, int t2, int dop)
{
    double d1,d2,d;
    int b=0,t;

    if (dop==DOP) {
	type=t1; e1=double_value(e1);
	type=t2; e2=double_value(e2);
    } else {
	type=t1; e1=float_value(e1);
	type=t2; e2=float_value(e2);
    }
    if(car(e1)==dop+CONST&&car(e2)==dop+CONST) {
	d1=dcadr(e1);
	d2=dcadr(e2);
	switch(op) {
	case ADD: d=d1+d2; break;
	case SUB: d=d1-d2; break;
	case MUL: d=d1*d2;break;
	case DIV:
	    if(!d2) error(EXERR);d=d1/d2;break;
	default:
	    switch(op) {
		case GT: b=(d1>d2);break;
		case GE: b=(d1>=d2);break;
		case LT: b=(d1<d2);break;
		case LE: b=(d1<=d2);break;
		case EQ: b=(d1==d2);break;
		case NEQ: b=(d1!=d2);break;
		default: error(EXERR);
	    }
	    type = INT;
	    return list2(CONST,b);
	}
	return dlist2(dop+CONST,d);
    }
    if(car(e1)==dop+CONST) {
	if ((op==SUB||op==ADD)&&dcadr(e1)==0.0) {
	    return e2;
	} else if (op==MUL&&dcadr(e1)==1.0) {
	    return e2;
	} else if (op==MUL&&-dcadr(e1)==1.0) {
	    return list2(dop+MINUS,e2);
	}
    }
    if(car(e2)==dop+CONST) {
	if ((op==SUB||op==ADD)&&dcadr(e2)==0.0) {
	    return e1;
	}
	if ((op==DIV||op==MUL)&&dcadr(e2)==1.0) {
	    return e1;
	}
	if ((op==DIV||op==MUL)&&-dcadr(e2)==1.0) {
	    return list2(DMINUS,e1);
	}
	if (op==SUB) {
	    op=ADD; dcadr(e2) = -dcadr(e2);
	} else if(op==DIV) {
	    if(dcadr(e2)==0.0) error(EXERR);
	    op=MUL; dcadr(e2)=1/dcadr(e2);
	}
    }
    if ((op==ADD||op==MUL) && (
	    car(e1)==dop+CONST ||
	    car(e2)==DRLVAR || car(e2)==DRGVAR ||
	    car(e2)==FRLVAR || car(e2)==FRGVAR
	)) {
	return(list3(op+dop,e2,e1));
    }
    if(op==ADD||op==SUB||op==MUL||op==DIV) {
	return(list3(op+dop,e1,e2));
    }
    t = type;
    type=INT;
    if(op==LT) {
	return(list3(GT+dop,e2,e1));
    } else if(op==LE) {
	return(list3(GE+dop,e2,e1));
    } else if(op==GT||op==GE||op==EQ||op==NEQ) {
	return(list3(op+dop,e1,e2));
    } else {
	error(-1);
	return e1;
    }
}

static int
dbinop(int op, int e1, int e2, int t1, int t2)
{
    return fdbinop(op, e1, e2, t1, t2,DOP);
}

static int
fbinop(int op, int e1, int e2, int t1, int t2)
{
    return fdbinop(op, e1, e2, t1, t2,FOP);
}

#endif 

#if LONGLONG_CODE

static int
lbinop(int op, int e1, int e2, int t1, int t2)
{
    int e=0;
    long long le1, le2;
    long long le = 0;
    int us = ((t1==ULONGLONG||t1==UNSIGNED)&&(t2==ULONGLONG||t2==UNSIGNED));

    if (us||(t1==ULONGLONG&&(op==LSHIFT||op==RSHIFT))) {
	type=t1; e1=ulonglong_value(e1);
	type=t2; e2=ulonglong_value(e2);
	t1=t2=ULONGLONG;
    } else {
	type=t1; e1=longlong_value(e1);
	type=t2; e2=longlong_value(e2);
	t1=t2=LONGLONG;
    }
    if(car(e1)==LCONST&&car(e2)==LCONST) {
	le1=lcadr(e1);
	le2=lcadr(e2);
	switch(op) {
	case BOR:
	    le=le1|le2;break;
	case EOR:
	    le=le1^le2;break;
	case BAND:
	    le=le1&le2;break;
	case ADD:
	    le=le1+le2;break;
	case SUB:
	    le=le1-le2; type=LONGLONG; break;
	case MUL:
	    le=le1*le2;break;
	case DIV:
	    if(!le2) error(EXERR);
	    if (us) le=(((unsigned long long )le1)/((unsigned long long )le2)); 
	    else e=(le1/le2);
	case MOD:
	    if(!le2) error(EXERR);
	    if (us) le=(((unsigned long long )le1)%((unsigned long long )le2)); 
	    else e=(le1%le2);
	case RSHIFT:
	    if (t1==ULONGLONG) le=(((unsigned long long)le1)<<le2); else le=le1<<le2;
	    break;
	case LSHIFT:
	    if (t1==ULONGLONG) le=(((unsigned long long)le1)>>le2); else le=le1>>le2;
	    break;
	default:
	    switch(op) {
	    case EQ:
		e=(le1==le2);break;
	    case NEQ:
		e=(le1!=le2);break;
	    case LT:
		le=le1;le1=le2;le2=le;
	    case GT:
		if (us) e=((unsigned long long)le1>(unsigned long long)le2);
		else e=(le1>le2);
		break;
	    case LE:
		le=le1;le1=le2;le2=le;
	    case GE:
		if (us) e=((unsigned long long)le1>=(unsigned long long)le2);
		else e=(le1>=le2);
		break;
	    default:
		error(-1); return list2(CONST,0);
	    }
	    type = INT;
	    return list2(CONST,e);
	}
	return llist2(LCONST,le);
    }
    if(op==SUB) { us = 0; type=LONGLONG; }
    if(op==SUB&&car(e2)==LCONST) { op=ADD; e2=llist2(LCONST,-lcadr(e2)); }
    if((op==ADD||op==MUL||op==BOR||op==EOR||op==BAND)&&
	(car(e1)!=LCONST) && (
	    car(e2)==LRGVAR||car(e2)==LRLVAR||
	    car(e2)==LURGVAR||car(e2)==LURLVAR
		)) {
	e=e1;e1=e2;e2=e;e=t1;t1=t2;t2=e;
    }
    if((op==MUL||op==DIV)&&car(e2)==LCONST&&lcadr(e2)==1) return e1;
    if(op==BOR||op==EOR||op==BAND||op==ADD||op==SUB) {
	return(list3(op+LOP,e1,e2));
    }
    if(op==LSHIFT && car(e2)==LCONST) {
	if (lcadr(e2)==0) return e1;
	else if (lcadr(e2)>63) return llist2(LCONST,0);
    }
    if(op==RSHIFT && car(e2)==LCONST) {
	if (lcadr(e2)==0) return e1;
    }
    if(op==LSHIFT||op==RSHIFT) {
	return(list3(op+LOP+(t1==ULONGLONG),e1,e2));
    }
    if (op==DIV||op==MUL||op==ADD||op==SUB||op==MOD) {
	return(list3(op+LOP,e1,e2));
    }

    type = INT;
    if(op==LT) {
	return(list3(GT+LOP+us,e2,e1));
    } else if(op==LE) {
	return(list3(GE+LOP+us,e2,e1));
    } else if(op==EQ||op==NEQ) {   // EQ/NEQ has no unsign
	return(list3(op+LOP,e1,e2));
    } else if(op==GT||op==GE||op==LT||op==LE) {
	return(list3(op+LOP+us,e1,e2));
    } else
	error(-1);
    return 0; /* not reached */
}
#endif

/* binary integer computation */

static int
binop0(int op, int e1, int e2, int t1, int t2)
{
    int e=0;
    int us = 0;

    // for inmode, destructive modification e1,e2,t1,t2 is not allowed

    if(t1>0&&car(t1)==POINTER) { 
	if(!(op==SUB && t2>0&&car(t2)==POINTER))  {
	    type = t2; e2= int_value(e2); t2=INT; 
	}
    } else if(t2>0&&car(t2)==POINTER) { 
	type = t1; e1= int_value(e1); t1=INT; 
    }
#if FLOAT_CODE
    else if(t1==DOUBLE||t2==DOUBLE)
	return dbinop(op,e1,e2,t1,t2);
    else if(t1==FLOAT||t2==FLOAT)
	return fbinop(op,e1,e2,t1,t2);
#endif
#if LONGLONG_CODE
    else if(t1==LONGLONG||t2==LONGLONG||t1==ULONGLONG||t2==ULONGLONG)
	return lbinop(op,e1,e2,t1,t2);
#endif
    if (t1==UNSIGNED) {
	if (t2==UNSIGNED || (car(e2)==CONST && cadr(e2)>0)) us = 1;
    }
    if (t2==UNSIGNED) {
	if (t1==UNSIGNED || (car(e1)==CONST && cadr(e1)>0)) us = 1;
    }

    if(car(e1)==CONST&&car(e2)==CONST) {
	e1=cadr(e1);
	e2=cadr(e2);
	type= INT;
	switch(op) {
	case BOR:
	    e=e1|e2;break;
	case EOR:
	    e=e1^e2;break;
	case BAND:
	    e=e1&e2;break;
	case ADD:
	    if(integral(t1)) {
		if(integral(t2)) {
			e=e1+e2;
		} else {
			if(car(t2)!=POINTER) error(TYERR);
			e=size(cadr(t2))*e1+e2;
			type=t2;
		}
	    } else {
		if(car(t1)!=POINTER) error(TYERR);
		e=e1+size(cadr(t1))*e2;
		type=t1;
	    }
	    break;
	case SUB:
	    if(integral(t1)) {
		e=e1-e2; type=INT;
	    } else {
		if(car(t1)!=POINTER) error(TYERR);
		if(integral(t2)) {
		    e=e1-size(cadr(t1))*e2;
		    type=t1;
		} else {
		    e=(e1-e2)/size(cadr(t1));
		    type=INT;
		}
	    }
	    break;
	case MUL:
	    e=e1*e2;break;
	case DIV:
	    if(!e2) error(EXERR);
	    if (us) e=(((unsigned)e1)/((unsigned)e2)); else e=e1/e2;
	    break;
	case MOD:
	    if(!e2) error(EXERR);
	    if (us) e=(((unsigned)e1)%((unsigned)e2)); else e=e1%e2;
	    break;
	case RSHIFT:
	    if (t1==UNSIGNED) e=(((unsigned)e1)>>((unsigned)e2)); else e=e1>>e2;
	    break;
	case LSHIFT:
	    if (t1==UNSIGNED) e=(((unsigned)e1)<<((unsigned)e2)); else e=e1<<e2;
	    break;
	case EQ:
	    e=(e1==e2);break;
	case NEQ:
	    e=(e1!=e2);break;
	case LT:
	    e=e1;e1=e2;e2=e;
	case GT:
	    if (us) e=(((unsigned)e1)>((unsigned)e2)); else e=(e1>e2);
	    break;
	case LE:
	    e=e1;e1=e2;e2=e;
	case GE:
	    if (us) e=(((unsigned)e1)>=((unsigned)e2)); else e=(e1>=e2);
	    break;
	default: error(-1); return list2(CONST,0);
	}
	return list2(CONST,e);
    }
    if(op==GT||op==GE||op==LT||op==LE) {
	return(car(e1)==CONST?list3(rop_dual(op)+us,e2,e1):list3(op+us,e1,e2));
    } else if(op==EQ||op==NEQ) {
	return(car(e1)==CONST?list3(op,e2,e1):list3(op,e1,e2));
    }
    if(op==SUB&&car(e2)==CONST) { op=ADD; e2=list2(CONST,-cadr(e2)); }
    if((op==ADD||op==MUL||op==BOR||op==EOR||op==BAND)&& (car(e1)!=CONST)) {
	switch(car(e2)) {
	    case RGVAR: case RLVAR:
	    case URGVAR: case URLVAR:
	    case SRGVAR: case SRLVAR:
	    case SURGVAR: case SURLVAR:
	    case CRGVAR: case CRLVAR:
	    case CURGVAR: case CURLVAR:
	    e=e1;e1=e2;e2=e;e=t1;t1=t2;t2=e;
	}
    }
    if(op==ADD) {
	if(integral(t1)) {
	    if(integral(t2)) {
		// if(t1==INT) type=t2;else type=t1;
		if (us) type=UNSIGNED; else type=INT;
		return(list3(ADD,e1,e2));
	    }
	    if(car(t2)!=POINTER) error(TYERR);
	    e=binop0(MUL,e1,list2(CONST,size(cadr(t2))),t1,INT);
	    type=t2;
	    return(list3(ADD,e,e2));
	}
	if(car(t1)!=POINTER||!integral(t2)) error(TYERR);
	e=binop0(MUL,e2,list2(CONST,size(cadr(t1))),t2,INT);
	type=t1;
	if (car(e)==CONST && cadr(e)==0)
	    return(e1);
	if (car(e)==CONST) {
	    if(car(e1)==ADDRESS) {
		if (car(cadr(e1))==IVAR) {
		} else if (car(cadr(e1))!=GVAR) {
		    // must be lvar
		    if (car(cadr(e1))!=LVAR) error(-1);
		    return(list2(ADDRESS,
			list3(car(cadr(e1)),cadr(cadr(e1))+cadr(e),
				caddr(cadr(e1)))));
		} else {
		    return(list2(ADDRESS,
			list3(GVAR,cadr(cadr(e1))+cadr(e),caddr(cadr(e1)))));
		}
	    } else if(car(e1)==GVAR) {
		return(list3(GVAR,cadr(e1)+cadr(e),caddr(e1)));
	    } else if(car(e1)==LVAR) {
		return(list3(LVAR,cadr(e1)+cadr(e),0));
	    }
	}
	return(list3(ADD,e1,e));
    }
    if(op==SUB) {
	if(integral(t1)) {
	    if(!integral(t2)) error(TYERR);
	    if(t1==INT) type=t2;else type=t1;
	    if (type==UNSIGNED) type=INT;
	    return(list3(SUB,e1,e2));
	}
	if(car(t1)!=POINTER) error(TYERR);
	if(integral(t2)) {
	    e=binop0(MUL,e2,list2(CONST,size(cadr(t1))),t2,INT);
	    type=t1;
	    if (car(e)==CONST) error(-1);
	    return(list3(SUB,e1,e));
	}
	if(car(t2)!=POINTER)
	    error(TYERR);
	compatible(t1,t2);
	e=list3(SUB,e1,e2);
	e=binop0(DIV,e,list2(CONST,size(cadr(t1))),INT,INT);
	type= INT;
	return e;
    }
    if(!integral(t1)||!integral(t2)) error(TYERR);
    // if(t1==INT) type=t2; else type=t1;  /* ??? */
    if (us) type=UNSIGNED; else type=INT;
    if((op==MUL||op==DIV)&&car(e2)==CONST&&cadr(e2)==1) return e1;
    if(op==BOR||op==EOR||op==BAND) return(list3(op,e1,e2));
    if(op==LSHIFT||op==RSHIFT) return(list3(op+(t1==UNSIGNED?US:0),e1,e2));
    // which ops remain?
    return(list3(op+us,e1,e2));
}

extern int
binop(int op, int e1, int e2, int t1, int t2)
{
    int e = binop0(op,e1,e2,t1,t2);
    if (inmode) return  list3(ST_OP,op,list4(e1,e2,t1,t2));
    else return e;
}

/*
       arugment type of binary operator
 */
extern int
type_of_bop(int op)
{
    int us;
    if (op>0) {
	switch(op%200) {
	    case UMUL: case UDIV: case UMOD:
	    case URSHIFT: case ULSHIFT:
	    case ULT: case UCMP: case UCMPGE: case UGE: case UGT: case ULE:
		us = 1; break;
	    case MUL: case DIV: case MOD: case ADD: case SUB: case CMP:
	    case RSHIFT: case LSHIFT:
	    case GT: case GE: case LT: case LE: case EQ: case NEQ:
	    case BAND: case EOR: case BOR:
	    case CMPGE: case CMPEQ: case CMPNEQ:
		us = 0; break;
	    default:
		return 0;
	}
	switch((op/200)*200 + us) {
	case SOP:    return SHORT;
	case SOP+US: return USHORT;
	case COP:    return CHAR;
	case COP+US: return UCHAR;
	case DOP:    return DOUBLE;
	case FOP:    return FLOAT;
	case LOP:    return LONGLONG;
	case LOP+US: return ULONGLONG;
	case US:     return UNSIGNED;
	case 0:      return INT;
	}
    }
    return 0;
}

/*
    type of result of conversion operator
 */
extern int
type_of_conv(int op)
{
    switch(op) {
	case  LL2D: case  ULL2D: case F2D: case I2D: case U2D: 
	    return DOUBLE;
	case  LL2F: case  ULL2F: case D2F: case I2F: case U2F: 
	    return FLOAT;
	case  LL2I: case  ULL2I: case D2I: case F2I: 
	case I2C: case I2S: 
	    return INT;
	case  D2LL: case  F2LL: case  I2LL: case  U2LL: 
	    return LONGLONG;
	case  D2ULL: case  F2ULL: case  I2ULL: case  U2ULL: 
	    return ULONGLONG;
	case  LL2U: case  ULL2U: case D2U: case F2U: 
	case U2UC: case U2US: 
	    return UNSIGNED;
    }
    return 0;
}

extern int
op_of(int op)
{
    return (op%200);
}

/* coarse for function/code segments arguments */

extern int
correct_type(int e,int t0)
{
    int t = type_value(t0);
    /* e = rvalue(e); */
#if BIT_FIELD_CODE
    if (type==BIT_FIELD) e = rvalue(e);
#endif
    if (t==DOTS) {
	if (type==FLOAT) t=DOUBLE; 
	else if (type==CHAR) t=INT;
	else if (type==UCHAR) t=UNSIGNED;
	else if (type==SHORT) t=INT;
	else if (type==USHORT) t=UNSIGNED;
    }
    if (type_value(type)>0 && car(type_value(type))==ARRAY && car(e)==GVAR) {
	e=list2(ADDRESS,e);
    }
    if (t>0) {
	switch(car(t)) {
	case STRUCT: case UNION:
	    if(size(t)!=size(type)) error(TYERR);
	    break;
	}
    } else {
	switch(t) {
	case DOTS: return e;
        case UNSIGNED:  e = unsigned_value(e);  t = UNSIGNED; break;
        case CHAR:      e = char_value(e); t = INT; break;
        case UCHAR:     e = uchar_value(e); t = UNSIGNED; break;
        case SHORT:     e = short_value(e); t = INT; break;
        case USHORT:    e = ushort_value(e); t = UNSIGNED; break;
#if FLOAT_CODE
        case FLOAT:     e = float_value(e); break;
        case DOUBLE:    e = double_value(e); break;
#endif
#if LONGLONG_CODE
        case LONGLONG:  e = longlong_value(e); break;
        case ULONGLONG: e = ulonglong_value(e); break;
#endif
	default:        
	    if (integral(t)) e = int_value(e);
	}
    }
    type = set_type_with_attr(t,t0);
    return e;
}


extern int
cexpr(int e)
{
    if (car(e) == CONV) {
	switch(caddr(e)) {
	case I2C: case I2S: case U2UC: case U2US:
	    e=cadr(e); break;
	default: error(-1); // illeagal int constant
	}
    }
    if (car(e) != CONST) error(CNERR);
    return (cadr(e));
}

#define is_long_type(type)  (type==LONGLONG||type==ULONGLONG)

#if BIT_FIELD_CODE

/* 
    bitfield   struct { char a:1; unsigned int b:10; }
    System dependent bit alignment is defined by code_bit_field_disp.
       type of bitfield represents type of the value
       these values are stored in a stored type which can be different from
       value type.
 */

static int
bit_field(int e1,int t)
{
    int reg;
if (car(e1)==BIT_FIELD) {
//    printf("## bit_field_bug\n");
    e1 = cadr(e1);
}
    g_expr(e1);
    emit_push();
    code_bit_field(t, reg = emit_pop(0), USE_CREG);
    emit_pop_free(reg);
    return cadr(t); /* value type */
}

static int
bit_field_repl(int e1,int e2,int t)
{
    /* e1 = e2 */
    if ((car(e2)==CONST||car(e2)==LCONST)) {
	g_expr(e1);
	code_bit_replace_const(e2,USE_CREG,t);
	return t;
    }
    g_expr(e1);
    emit_push();
    g_expr(e2);
    code_bit_replace(e2=emit_pop(0),USE_CREG,t);
    emit_pop_free(e2);
    return t;
}

static int
bassign(int e2,int e3,int t)
{
    int type  = cadr(t);
    /*  e2 = e3 */
    if (car(e2)==BIT_FIELD) {
	e2 = cadr(e2);
    }
    bit_field_repl(e2,e3,t);
    return type;
}

static int
bassop(int e2,int e3,int op,int t,int post)
{
  /*
	n = bit_field address
	code_bit_field
	if (post) n1 = value
        value op= operand
	n bit-repl value
	if (post) n1;
   */
    /*  e2 = e2 op e3; */
    /*  new = &e2 */
    /*  *new = *new op e3 */
    int suse = use;
    int lo = (type==LONGLONG||type==ULONGLONG);
    int n1=0,n2=0;
    int reg;

    g_expr(list2(ADDRESS,cadr(e2)));
    reg = emit_push();
    code_bit_field(t, reg, USE_CREG);
    use=1;
    if (lo) {
#if LONGLONG_CODE
	if (post) {
	    n1 = list3(LVAR,new_lvar(size_of_longlong),0);
	    code_lassign_lvar(cadr(n1),USE_CREG);
	}
	if (!code_lassop_p) {
	    n2 = list3(LVAR,new_lvar(size_of_longlong),0);
	    code_lassign_lvar(cadr(n2),USE_CREG);
	    lassign(list4(LASSOP,n2,e3,op+LOP));
	} else {
	    emit_lpush();
	    g_expr(e3);
	    code_register_lassop(USE_CREG,op+LOP);
	}
#else
	error(TYERR);
#endif
    } else {
	if (post) {
	    n1 = list3(LVAR,new_lvar(size_of_int),0);
	    code_assign_lvar(cadr(n1),USE_CREG,0);
	}
	emit_push();
	g_expr(e3);
	code_register_assop((e2=emit_pop(0)),USE_CREG,op,size(type));
	if (use) {
	    code_register(e2,USE_CREG);
	}
	emit_pop_free(e2);
    }
    use=post?0:suse;
    code_bit_replace(e2=emit_pop(0),USE_CREG,t);
    emit_pop_free(e2);
    use = suse;
    if (post&&use) { g_expr(rvalue_t(n1,type));} 
    if (n1) free_lvar(cadr(n1));
    if (n2) free_lvar(cadr(n2));
    
    return type;
}

#endif

/* temporal local variable free list */

static int lvar_list,lvar_free_list;

extern int
new_lvar0(int sz)
{
    disp-=sz;
#if 1
    if (sz>=4 && (disp & (4-1))) {
	disp &= ~(4-1);
    }
#endif
    return disp;
}

extern int
new_lvar(int size)
{
    int lvar,plvar;

    for (plvar = 0,lvar = lvar_free_list;lvar;lvar = cadr(lvar)) {
        if (caddr(lvar)==size) {
            if (plvar) cadr(plvar) = cadr(lvar);
            else lvar_free_list = cadr(lvar);
            break;
        }
        plvar = lvar;
    }
    if (!lvar) {
        lvar_list = glist3((lvar=new_lvar0(size)),lvar_list,size);
    } else {
        cadr(lvar) = lvar_list; lvar_list = lvar;
        lvar = car(lvar_list);
    }
    return lvar;
}

extern void
free_lvar(int disp)
{
    int lvar,plvar;

    for (plvar = 0,lvar = lvar_list;lvar;lvar = cadr(lvar)) {
        if (car(lvar)==disp) {
            if (plvar) cadr(plvar) = cadr(lvar);
            else lvar_list = cadr(lvar);
            break;
        }
        plvar = lvar;
    }
    if (!lvar) error(-1);
    cadr(lvar) = lvar_free_list; lvar_free_list = lvar;
}

extern void
init_free_lvar_list()
{
    int lvar;
    while((lvar=lvar_list)) {
        lvar_list=cadr(lvar_list);
        free_glist3(lvar);
    }
    while((lvar=lvar_free_list)) {
        lvar_free_list=cadr(lvar_free_list);
        free_glist3(lvar); 
    }
}

extern void
gen_comment(char *s)
{
    if (!chk)
	code_comment(s);
}

extern void
gen_code_enter(char *name)
{
    code_enter(name);
}

extern void
gen_code_enter1(int args)
{
    code_enter1(args);
}

extern void
gen_code_leave(char *name)
{
    code_leave(name);
}

extern void
gen_enter(char *name)
{
    enter(name);
}

extern void
gen_enter1()
{
    enter1();
}

extern void
gen_leave(int control, char *name)
{
    leave(control,name);
}

extern void
gen_jmp(int l)
{
    control=0;
    if (!pending_jmp) {
	pending_jmp = l;
    }
}

extern void
gen_indirect_goto(int e1)
{
    g_expr(e1);
    code_indirect_jmp(USE_CREG);
}

/*
    make bit mask
      MSB 1 2 3 4 .... 29 30 31 LSB
 */
extern int
make_mask(int from,int to)
{
    int mask = 0;
    int bit = 1;
    int i;
    if (from<0||from>32) error(-1);
    for (i=31;from<=i;i--,bit<<=1) {
        if (i<=to) {
            mask |= bit;
        }
    }
    return mask;
}

#define MAX_PTR_CACHE 10

static int ptr_cache=0;
static int ptr_cache_last=0;

/* 
    global name pointer cache
 */

extern void
init_ptr_cache()
{
    int i;
    for(i=0;i<MAX_PTR_CACHE-1;i++) {
	ptr_cache=glist3(0,ptr_cache,0);
    }
    ptr_cache_last=ptr_cache;
}

extern void
clear_ptr_cache_reg(int r)
{
    int ptcptr=ptr_cache;
    int prev = 0;
    while(ptcptr!=ptr_cache_last) {
	if(car(ptcptr)&&caddr(ptcptr)==r) {
	    free_register(r); caddr(ptcptr)=-1; car(ptcptr)=0;
	    // remove me
	    if (prev) cadr(prev) = cadr(ptcptr);
	    else ptr_cache = cadr(ptcptr);
	    // add me to the next of ptr_cache_last
	    cadr(ptcptr) = cadr(ptr_cache_last);
	    cadr(ptr_cache_last) = ptcptr;
	    return;
	}
	ptcptr=cadr(prev=ptcptr);
    }
}

extern int
last_ptr_cache()
{
    int ptcptr=ptr_cache;
    int r = 0;
    while(ptcptr!=ptr_cache_last) {
	r = caddr(ptcptr);
	ptcptr=cadr(ptcptr);
    }
    return r;
}


extern void
clear_ptr_cache()
{
    int ptcptr=ptr_cache;
    while(ptcptr!=ptr_cache_last) {
	free_register(caddr(ptcptr)); caddr(ptcptr)=-1; car(ptcptr)=0;
	ptcptr=cadr(ptcptr);
    }
    ptr_cache_last = ptr_cache;
}


extern int
get_ptr_cache(NMTBL *nptr)
{
    int r;
    int ptcptr=ptr_cache;
    int g = (int)nptr;
    int prev=0,p;

    // linear search cache 
    while(ptcptr!=ptr_cache_last) {
	if(car(ptcptr)==g) {
	    if (prev) {
		// make this top
		cadr(prev)=cadr(ptcptr);
		cadr(ptcptr) = ptr_cache;
		ptr_cache = ptcptr;
	    }
	    return caddr(ptcptr);
	}
	ptcptr=cadr(prev=ptcptr);
    }
    if (!cadr(ptr_cache_last)) {
	// cache is full
	if (prev) {
	    // remove oldest cache and it becomes the last
	    free_register(caddr(prev)); caddr(ptcptr)=-1; car(ptcptr)=0;
	    ptr_cache_last = prev;
	}
	else error(-1);
    }
    r = get_register(0); // some ptr cache may remove by this
    caddr(p = cadr(ptr_cache_last)) = r;
    car(p) = g;
    use_ptr_cache(r);

    cadr(ptr_cache_last) = cadr(p);
    cadr(p) = ptr_cache;
    ptr_cache = p;
    code_ptr_cache_def(r,nptr);
    return r;
}

extern int
ilog(int i)
{
    /* number of bit (i-1) is better? */
    switch(i) {
    case 2: return 1;
    case 4: return 2;
    case 8: return 3;
    case 16: return 4;
    case 32: return 5;
    case 64: return 6;
    case 128: return 7;
    case 256: return 8;
    case 512: return 9;
    case 1024: return 10;
    case 2048: return 11;
    case 4096: return 12;
    case 8192: return 13;
    case 16384: return 14;
    case 32768: return 15;
    case 65536: return 16;
    case 131072: return 17;
    case 262144: return 18;
    case 524288: return 19;
    }
    return 0;
}

/* end */