view mc-codegen.c @ 879:528595192871

partial struct init in GDECL
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Thu, 03 Apr 2014 10:34:02 +0900
parents f93bf97bbd1f
children 5313ed059cee
line wrap: on
line source

/* Micro-C Generic Code Generation Part */

/*
************************************************************************
** Copyright (C) 2006 Shinji Kono
** 連絡先: 琉球大学情報工学科 河野 真治  
** (E-Mail Address: kono@ie.u-ryukyu.ac.jp)
**
**    このソースのいかなる複写,改変,修正も許諾します。ただし、
**    その際には、誰が貢献したを示すこの部分を残すこと。
**    再配布や雑誌の付録などの問い合わせも必要ありません。
**    営利利用も上記に反しない範囲で許可します。
**    バイナリの配布の際にはversion messageを保存することを条件とします。
**    このプログラムについては特に何の保証もしない、悪しからず。
**
**    Everyone is permitted to do anything on this program 
**    including copying, modifying, improving,
**    as long as you don't try to pretend that you wrote it.
**    i.e., the above copyright notice has to appear in all copies.  
**    Binary distribution requires original version messages.
**    You don't have to ask before copying, redistribution or publishing.
**    THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE.
***********************************************************************
 */
#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"
#include "conv/conv.h"

extern Converter *conv;

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

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 machinop(int e1);
static int register_to_lvar(int e);
static void remove0(int *parent,int e) ;
static void sassign(int e1);
static int gen_decl_data0(int v,int target_type,int init,int offset);

#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();
}

/**
    make register argments
    and save it into memory (sigh...)
    we should postpone code_save_argument_register
 */
extern void 
arg_register(NMTBL *fnptr, int in)
{
    code_arg_register(fnptr, in);
}

extern int
gexpr(int e1,int use0)
{
    if (chk==1) 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);
    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);
    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 (!e1 ||(!control && !IS_STATEMENT(car(e1)))) return VOID;

  for(;e1;e1=e2) {
    code_gexpr(e1);
    conv->expr_(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 URGVAR:
	code_rgvar(e1,USE_CREG);
	return UNSIGNED;
    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:
	if (lp64) code_crlvar(e2,USE_CREG,1,size_of_int);
	else code_rlvar(e2,USE_CREG);
	return INT;
    case URLVAR:
	if (lp64) code_crlvar(e2,USE_CREG,0,size_of_int);
	else code_rlvar(e2,USE_CREG);
	return UNSIGNED;
    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(ncaddr(e1),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 STRINGS:
	code_strings(e2,USE_CREG) ;
	return ADDRESS;
    case STRING:
	code_string(e1,USE_CREG);
	return ADDRESS;
    case FUNCTION:
	if (car(e2)==FNAME&&is_code(ncaddr(e2))) {
	    // error(FNERR);
	    jump(e1,0);
	    return VOID;
	} 
	t = function(e1);
	return t;
    case JUMP:
	if (car(e2)==FNAME&&is_function(ncaddr(e2))) {
	    // error(GTERR);
	    return function(e1);
	}
	jump(e2,caddr(e1));
	return VOID;
    case ARRAY:
	if (chk==2) { // for generation check (?)
	    indop(e1);
	    t = type;
	    g_expr0(e2);
	    g_expr0(caddr(e1));
	    code_gexpr(e1);
	    return t;
	}
	e1=binop(ADD,e2,caddr(e1),cadddr(e1),caddddr(e1));
	e1 = indop(e1); t = type;
	g_expr0(e1);
	return t;
    case PERIOD:
	nptr = ncadddr(e1);
	type = caddr(e1);
	e1 = strop(e2,0); t = type;
	if (chk) return t;
	g_expr0(e1);
	return t;
    case ARROW:
	nptr = ncadddr(e1);
	type = caddr(e1);
	e1 = strop(e2,1); t = type;
	if (chk) return t;
	g_expr0(e1);
	return t;
    case INLINE:
	return gen_inline(e1,0);
    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 BUILTIN_FABSF:
	code_builtin_fabsf(e2);
        return FLOAT;
    case BUILTIN_FABS:
    case BUILTIN_FABSL:
	code_builtin_fabs(e2);
        return DOUBLE;
    case BUILTIN_INFF:
	code_builtin_inff();
        return FLOAT;
    case BUILTIN_INF:
    case BUILTIN_INFL:
	code_builtin_inf();
        return DOUBLE;
    case COMMA:
	g_expr_u(e2);
	return g_expr0(caddr(e1));
    case RETURN:
	n = ncaddr(e1);
	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 CAST:
	error(-1); // correct_type is too late for contains_p
	type = cadddr(e1);
	e2 = correct_type(e2,caddr(e1));
	continue;
    case DECL_DATA:
	e1 =  gen_decl_data(e1,0);
	return e1;
    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 ST_OP:
        e3=caddr(e1);
        e1=binop(e2,car(e1),cadr(e3),caddr(e3),cadddr(e3));
	return g_expr0(e1);
    case IVAR:      	  error(-1);	break;
    case 0:               
	error(-1);
	break; // empty case
    default:
	code_bool(e1,USE_CREG); /* type? */
	return INT;
    }
  }
  return VOID;
}

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

static int
bexpr_u(int e1, char cond, int l1)
{
    int op = car(e1);
    conv->expr_(e1);
    if (chk) return l1;

    // is this switch really useful?
    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;
    code_save_stacks();

    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:
	conv->bool_(e1);
	code_cmp_crgvar(e1,USE_CREG,1,l1,cond);
	return l1;
    case SRGVAR: case SURGVAR:
	conv->bool_(e1);
	code_cmp_crgvar(e1,USE_CREG,size_of_short,l1,cond);
	return l1;
    case CRLVAR: case CURLVAR:
	conv->bool_(e1);
	code_cmp_crlvar(e2,USE_CREG,1,l1,cond);
	return l1;
    case SRLVAR: case SURLVAR:
	conv->bool_(e1);
	code_cmp_crlvar(e2,USE_CREG,size_of_short,l1,cond);
	return l1;
    case RGVAR:
    case URGVAR:
	conv->bool_(e1);
	code_cmp_rgvar(e1,USE_CREG,l1,cond);
	return l1;
    case RLVAR:
    case URLVAR:
	conv->bool_(e1);
	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:
	conv->bool_(e1);
	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;  // Is this really happen?
	conv->bool_(e1);
	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 STRINGS: case STRING: case GVAR: 
	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,NOINLINE) &&  attr_value(f,INLINE));
}

extern int 
function_type(int e1,int *dots)
{
    int ret_type,t;
    if (e1<0) return INT;
    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 = list3n(LVAR,n->dsp,n);
    g_expr_u(assign_expr0(list3n(LVAR,n->dsp,n),list3n(tag,reg,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);
//
//  register の大きさはsize_of_int とは限らない。むしろ、
//  get_register_var した方が安全...

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 
if (lsrc) printf("## overlap source %d t0 %d t1 %d\n",car(car(t)),t0,t1);
if (lsrc) printf("## overlap target %d s0 %d s1 %d\n",car(car(source)),s0,s1);
if (lsrc) 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 = 0;
    // 使ったら free するべきだよね? code goto の時なら害はないか...
    /*新しいレジスタ(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) {
	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) {
	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) {
	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
if (lsrc) printf("## register overrap in save_target\n");
#endif
	}
	g_expr_u(assign_expr0((e1=list3n(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
if (lsrc) 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(is_same_type(t,s) && cadr(t)==cadr(s)) {
		/*書き込み先が自分自身*/
#if DEBUG_PARALLEL_ASSIGN
if (lsrc) 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) {
if (lsrc) printf("## singleton %d ty %d+%d sz %d\n",car(t),ty,cadr(t),sz);
    // this means singleton struct should be copied safely.
} else
if (lsrc) 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
if (lsrc)    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
if (lsrc)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:
	case URGVAR: case URLVAR:
	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 LRLVAR: case LURLVAR:  return lp64;
	    case RLVAR: case CRLVAR: case FRLVAR: case DRLVAR:
	    case SRLVAR: case SURLVAR: case CURLVAR: case LVAR:
	    case URLVAR:
	    return 1;
	}
    } else if (ce2==LVAR) {
	switch(ce1) {
	    case LRLVAR: case LURLVAR:  return lp64;
	    case RLVAR: case CRLVAR: case FRLVAR: case DRLVAR:
	    case SRLVAR: case SURLVAR: case CURLVAR: case LVAR:
	    case URLVAR:
	    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
    } else if (ce1==REGISTER) {
	if (lp64 && ce2==LREGISTER) return 1;
    } else if (ce2==REGISTER) {
	if (lp64 && ce1==LREGISTER) return 1;
    }
    return 0;
}

static int
is_writable(int e1)
{
    switch(car(e1)) {
    case GVAR :
    case LVAR :
    case RLVAR :  case LRLVAR: case URLVAR: case LURLVAR:     // 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:
    case URGVAR:
	return list3n(GVAR,cadr(e1),ncaddr(e1));
    case LVAR :
    case CRLVAR  :
    case CURLVAR  :
    case DRLVAR  :
    case FRLVAR  :
    case LRLVAR:
    case LURLVAR:
    case RLVAR:
    case SRLVAR :
    case SURLVAR :
    case URLVAR:
	return list3n(LVAR,cadr(e1),ncaddr(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:
    case URGVAR:
	*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
#define ARG_OFFSET_CODE 1

extern 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;
    int int_type = lp64?LONGLONG:INT;

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

    if (env) {
        error(-1); // not supported
	envreg = get_register_var(0);
	g_expr_u(assign_expr0(envreg,env,int_type,int_type));
    }

    /* まず、サイズを計算しながら、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+=lp64?1:2;
	} else if (env) {
	    while(car(e2)==RSTRUCT) e2=cadr(e2);
	/*
	    envreg contains frame pointer, we need disp_offset. disp_offset
	    for code segment and function should be the same value. 
	    If original frame pointer has indeterminate offset, these should
	    fixed in code_fix_frame_pointer.
	 */
	    g_expr_u(assign_expr0(
		list2(INDIRECT,
    lp64?list3(LADD,rvalue_t(envreg,LONGLONG),llist2(LCONST,-arg_size-sz+disp_offset))
         :list3(ADD,rvalue_t(envreg,INT),list2(CONST,-arg_size-sz+disp_offset))
		),
		e2,ty,ty));
	} else {
	    while(car(e2)==RSTRUCT) e2=cadr(e2);
	    target=list5(list3n(LVAR,0,0), target,ty,e2,0);
	}
        /* keep arg space for register variables */
#if ARG_OFFSET_CODE
        NMTBL n;
	arg_size = code_arg_alignment(arg_size, &n, ty, sz,1);
#else
        arg_size += sz;
#endif
#if DEBUG_PARALLEL_ASSIGN
if (lsrc)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;
    }

    /*  複雑な式を前もって計算しておく     */
    /*  必要なら局所変数を用いる。         */
    /*  局所変数へのオフセットを覚えておく */
#if ARG_OFFSET_CODE
    NMTBL n;
    n.dsp = 0;
    int arg_offset = 0;
    target = reverse0(target); 
#endif
    for (e2 = target; e2; e2 = cadr(e2)) {	
	t0=car(e2); s0=cadddr(e2);
	sz=size(ty=caddr(e2));
#if ARG_OFFSET_CODE
	/* ここで、書込先アドレスを決める */
	arg_offset = code_arg_alignment(arg_offset, &n, ty, sz,1);
	if(car(t0)==LVAR) {
	    cadr(t0) = n.dsp;
	}
#else
	if(car(t0)==LVAR) {
	    cadr(t0)=-arg_size;    // disp_offset?!
	}
        arg_size-=sz;
#endif
#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=list3n(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);
	    }
        }
        int int_unsigned = lp64?ULONGLONG:UNSIGNED;
        int int_size = lp64?sizeof(long long):sizeof(int);
	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
if (lsrc)printf("## division sz %d\n",sz);
#endif
		caddr(e2) = int_unsigned;
		caddddr(e2) = list3(
			cadddr(e2)=list3n(LVAR,cadr(s0),0),
				0, int_size);
#if DEBUG_PARALLEL_ASSIGN
if (lsrc)printf("## div 0 source %d ty %d+%d sz %d\n",car(s0),ty,cadr(s0),int_size);
#endif
		for(e4=int_size;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;
		    case 4: if (lp64) { caddr(e2) = UNSIGNED; r = size_of_int; break; }
		    default: caddr(e2) = int_unsigned; r = int_size;
		    }
		    if (e4==int_size) e3=cadr(e2);
		    car(e2) =  list3n(LVAR,cadr(t0)+e4,0);
		    caddddr(e2) = list3(
			cadddr(e2) = list3n(LVAR,cadr(s0)+e4,0),0, r);
		    e4 += r;
#if DEBUG_PARALLEL_ASSIGN
if (lsrc)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
if (lsrc)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=ncaddr(e2);
	// if (!is_code(code0)) { error(TYERR); return; }
    } else {	/* indirect */
	g_expr(e2);
	emit_push();
    }
    if (chk) return;

    /* 並列代入を実行 */
    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));
    }
    code_fix_frame_pointer(env);

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

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

    // e3 op e2

    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;
    }
    if ((op==CMP||op==UCMP) && car(e3)==REGISTER && car(e2)==REGISTER) {
	if (tosop_operand_safe_p(op)==1) {
	    // both operands are safe
	    tosop(op,cadr(e2),cadr(e3));
	    return;
	}
    }
    if (car(e3)==REGISTER) {
	if (tosop_operand_safe_p(op)) {
	    // operand are safe
	    g_expr(e2);
	    tosop(op,USE_CREG,cadr(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);
    switch (op) {
    case DCMPGE: case DCMPEQ: case DCMPNEQ: case DCMP:
    case FCMPGE: case FCMPEQ: case FCMPNEQ: case FCMP:
	if ((car(e3)==DREGISTER||car(e3)==FREGISTER) && 
	   ((car(e2)==DREGISTER||car(e2)==FREGISTER))) { 
	    if (tosop_operand_safe_p(op)==1) {
		dtosop(op,cadr(e2),cadr(e3));
		return;
	    }
	}
    }
    if (car(e3)==DREGISTER||car(e3)==FREGISTER) {
	if (tosop_operand_safe_p(op)) {
	    g_expr(e2);
	    dtosop(op,USE_CREG,cadr(e3));
	    return;
	}
    }
    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;
    }
    if (car(e3)==LREGISTER && tosop_operand_safe_p(op)) {
	g_expr(e2);
	ltosop(op,USE_CREG,cadr(e3));
	return;
    }
    g_expr(e3);
    emit_lpush();
    g_expr(e2);
    ltosop(op,USE_CREG,(e2=emit_lpop()));
    emit_lpop_free(e2);
    return;
}
#endif
static int llvalue_opt(int e0);

static int
lvalue_opt(int e0)
{
    int e,e1;

    if (inmode) return e0;
    if (car(e0)==LADD) return llvalue_opt(e0);
    if (car(e0)!=ADD) return e0;
    if (car(e=caddr(e0))!=CONST) return e0;
    e1 = cadr(e0);
    if(car(e1)==ADDRESS) {
	switch (car(cadr(e1))) {
	case GVAR:
	    return(list2(ADDRESS,
		list3n(GVAR,cadr(cadr(e1))+cadr(e),ncaddr(cadr(e1)))));
	case LVAR:
	    return(list2(ADDRESS,
		list3n(LVAR,cadr(cadr(e1))+cadr(e),
			ncaddr(cadr(e1)))));
	// case INDIRECT:
	case PERIOD:
	case ARROW:
	    if (lp64) return list3(LADD,e1,llist2(LCONST,cadr(e)));
	    return list3(ADD,e1,e);
	default:
	    return e0;
	}
    } else if(car(e1)==GVAR) {
	return(list3n(GVAR,cadr(e1)+cadr(e),ncaddr(e1)));
    } else if(car(e1)==LVAR) {
	return(list3n(LVAR,cadr(e1)+cadr(e),ncaddr(e1)));
    }
    return e0;
}

static int
llvalue_opt(int e0)
{
    int e,e1;

    if (inmode) return e0;
    if (car(e0)!=LADD) return e0;
    if (car(e=caddr(e0))!=LCONST) return e0;
    // should we check LCONST is 32bit
    e1 = cadr(e0);
    if(car(e1)==ADDRESS) {
	switch (car(cadr(e1))) {
	case GVAR:
	    return(list2(ADDRESS,
		list3n(GVAR,cadr(cadr(e1))+lcadr(e),ncaddr(cadr(e1)))));
	case LVAR:
	    return(list2(ADDRESS,
		list3n(LVAR,cadr(cadr(e1))+lcadr(e),
			ncaddr(cadr(e1)))));
	case INDIRECT:
	case PERIOD:
	case ARROW:
	    return list3(LADD,e1,e);
	default:
	    return e0;
	}
    } else if(car(e1)==GVAR) {
	return(list3n(GVAR,cadr(e1)+lcadr(e),ncaddr(e1)));
    } else if(car(e1)==LVAR) {
	return(list3n(LVAR,cadr(e1)+lcadr(e),ncaddr(e1)));
    }
    return e0;
}

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;
	}
    }
    e2 = lvalue_opt(e2);

    if (car(e4)==DECL_DATA && (car(e2)==GVAR || car(e2)==LVAR)) {
	// we can do optimize any other type of destination ...
	// but gen_decl_data0 requires nptr (of course wrong decision)
	int t = caddr(e4);
	int sinit_vars = init_vars; init_vars = 0;
	gen_decl_data0(e2,t,e4,0);
	emit_init_vars();
	init_vars = sinit_vars;
	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 = 0;
	    if (!(sz==1 || sz==2 || sz==3 || (lp64 && sz==8))) {
		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||e5==LREGISTER) {
	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;
	case LREGISTER: code_lassign_lregister(cadr(e2),reg); return;
	}
	g_expr(e2);
	code_assign(USE_CREG,byte,reg);
	return;
    }
    /* e2 is register now */
    if (car(e2)!=REGISTER && car(e2)!=LREGISTER) 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 if (car(cadr(e4))==STRINGS) {
	    code_strings(cadr(e4),reg);
	} else code_gvar(cadr(e4),reg);   
	return;
    case FNAME:    code_fname(ncaddr(e4),reg); return;
    case STRING:   code_string(e4,reg); return;
    case STRINGS:   code_strings(cadr(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: lp64? size_of_int : 0;
    /*    e2=e4 */
    e2 = cadr(e1);
    e2 = lvalue_opt(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) {
	if (e5==REGISTER||e5==LREGISTER) {
	    assign_opt(e5,e2,e4,byte);
	    return;
	} else if (car(e2)==REGISTER||car(e2)==LREGISTER) {
	    switch(e5) {
	    case ADDRESS:
		if (!((car(cadr(e4))==STRING)|| (car(cadr(e4))==STRINGS) || 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 : case STRINGS:
		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;
    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_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||e5==REGISTER) {
	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:
	case REGISTER:
		if (reg!=cadr(e2))
		    code_lassign_lregister(cadr(e2),reg);
		return;
	default:
	    error(-1);
	}
    }
    /* e2 is register now */
    if (!(car(e2)==LREGISTER||car(e2)==REGISTER)) 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||e5==REGISTER) &&(car(e2)==GVAR||car(e2)==LVAR||car(e2)==LREGISTER || car(e2)==REGISTER)) ||
	    ((car(e2)==LREGISTER||car(e2)==REGISTER)&&
		(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:
    case REGISTER:
	    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)
{
    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,e2,F2D); break;
	case UNSIGNED: e2 =  list3(CONV,e2,U2D); break;
	case LONGLONG: e2 =  list3(CONV,e2,LL2D); break;
	case ULONGLONG: e2 =  list3(CONV,e2,ULL2D); break;
	default:
	    if(integral(type)) e2 =  list3(CONV,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)
{
    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,e2,LL2F); break;
	case ULONGLONG: e2 = list3(CONV,e2,ULL2F); break;
	case FLOAT: break;
	case DOUBLE: e2 =  list3(CONV,e2,D2F); break;
	case UNSIGNED: e2 =  list3(CONV,e2,U2F); break;
	default:
	    if(integral(type)) e2 =  list3(CONV,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)
{
    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,e2,F2LL); break;
	case DOUBLE: e2 = list3(CONV,e2,D2LL); break;
	case UNSIGNED: e2 = list3(CONV,e2,U2LL); break;
	case LONGLONG: break;
	case ULONGLONG: break;
	default:
	    if(integral(type)) e2 = list3(CONV,e2,I2LL);
            else if(lp64)
               ;
	    // else { error(TYERR); e2 = llist2(LCONST,0LL); }
	}
    }
    type = set_type_with_attr(LONGLONG,type);
    return e2;
}

static int
ulonglong_value(int 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) {
	if (dcadr(e2)<0) 
	    e2 = llist2(LCONST,0);
        e2 = llist2(LCONST,(unsigned long long)dcadr(e2));
    }
#endif
    else {
	switch(type_value(type)) {
	case FLOAT: e2 = list3(CONV,e2,F2ULL); break;
	case DOUBLE: e2 = list3(CONV,e2,D2ULL); break;
	case UNSIGNED: e2 = list3(CONV,e2,U2ULL); break;
	case LONGLONG: break;
	case ULONGLONG: break;
	default:
	    if(integral(type)) e2 = list3(CONV,e2,I2ULL);
            else if(lp64)
              ;
	    // 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 (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,e2,F2I); break;
	case DOUBLE: e2 = list3(CONV,e2,D2I); break;
	case LONGLONG: e2 = list3(CONV,e2,LL2I); break;
	case ULONGLONG: e2 = list3(CONV,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(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(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)==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)  {
	if (dcadr(e2)<0)
	    return e2 = list2(CONST,0);
	e2 = list2(CONST,(int)dcadr(e2));
    }
#endif
#if LONGLONG_CODE
    else if (car(e2)==LCONST)  {
	if (lcadr(e2)<0)
	    e2 = list2(CONST,0);
	e2 = list2(CONST,(unsigned)lcadr(e2));
    }
#endif
    else {
	switch(t) {
	case LONGLONG: e2 = list3(CONV,e2,LL2U); break;
	case ULONGLONG: e2 = list3(CONV,e2,ULL2U); break;
	case FLOAT: e2 = list3(CONV,e2,F2U); break;
	case DOUBLE: e2 = list3(CONV,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(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(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);
		}
		if (lp64) return(list3(LASS,e1,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);
    if (lp64) {
	e2=binop(MUL,e2,llist2(LCONST,size(cadr(t))),INT,ULONGLONG);
	type = set_type_with_attr(t,type);
	return list4(LASSOP,e1,e2,op+LOP);
    }
    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 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 = lp64?size_of_int:0; sign = 1; size = size_of_int;
    }
    e2 = cadr(e1);
    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||
                 (car(e2)==INDIRECT&&car(cadr(e2))==REGISTER)) {
	    g_expr0(assign_expr0(e2,list3(op,rvalue_t(e2,t),e3),t,t));
	    return;
	}
#if 0
	/*  new = &e2 */
	/*  *new = *new op e3 */
	// e2 が複雑な式でないと却ってだめなこともある
	// register が取れれば常に有効か?
	// たぶん、i386 の時には有効だったんだろうなぁ。
	int n = list3n(LVAR,new_lvar(size_of_int),0); // get register var?
	g_expr_u(assign_expr0(n,list2(ADDRESS,e2),INT,INT));
	g_expr0(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;
#endif
    }
    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);

    if (car(e2)==DREGISTER||car(e2)==FREGISTER) {
	g_expr(e3);
        emit_dpush(d);
	code_register_dassop(cadr(e2),op,d);
        if (use)
            code_dregister(cadr(e2),USE_CREG,d);
        return;
    }
    if (car(e3)==DCONST||car(e3)==FCONST) {
	/*  e2 = e2 op e3; */
	int t = d?DOUBLE:FLOAT;
	// oprtc expected
	if (car(e2)==LVAR||car(e2)==GVAR||
                 (car(e2)==INDIRECT&&car(cadr(e2))==REGISTER)) {
	    g_expr0(assign_expr0(e2,list3(op,rvalue_t(e2,t),e3),t,t));
	    return;
	}
    }
    g_expr(e3);
    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 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||
                 (car(e2)==INDIRECT&&car(cadr(e2))==REGISTER)) {
	    g_expr0(assign_expr0(e2,list3(op,rvalue_t(e2,t),e3),t,t));
	    return;
	}
	if (!code_lassop_p) {
	    /*  new = &e2 */
	    /*  *new = *new op e3 */
	    int n = list3n(LVAR,new_lvar(size_of_int),0);
	    g_expr_u(assign_expr0(n,list2(ADDRESS,e2),INT,INT));
	    g_expr0(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||car(e2)==REGISTER) {
        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
df_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 = ncaddr(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();
    conv->close_();
}

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 (TERNARY_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 {
	    // if (lsrc)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) {
	int e1 = car(e);
	if (!e1) return 0;
	if (p(e1)) return 1;
	if (LIST_ARGS(e1)){
        /* list arguments */
	    return contains_in_list_p(caddr(e),p);
	} else if (UNARY_ARGS(e1)) {
        /* unary operators */
	    e = cadr(e);
	    continue;
	} else if (BINARY_ARGS(e1)) {
        /* biary operators */
	    if (contains_p(cadr(e),p)) return 1;
	    e = caddr(e);
	    continue;
	} else if (TERNARY_ARGS(e1)) {
        /* 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(e1)) {
        /* nullary operators */
	    return 0;
	} else if (IS_STATEMENT(e1)) {
	    return 1;  // may contain anything
	} else {
	    // if (lsrc)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) {
	int e1 = car(e);
	if (!e1) return arg;
	if (LIST_ARGS(e1)){
        /* list arguments */
	    return contains_in_list_p1(arg,caddr(e),p);
	} else if (UNARY_ARGS(e1)) {
        /* unary operators */
	    e = cadr(e);
	    continue;
	} else if (BINARY_ARGS(e1)) {
        /* biary operators */
	    arg=contains_p1(arg,cadr(e),p);
	    e = caddr(e);
	    continue;
	} else if (TERNARY_ARGS(e1)) {
        /* tarary operators */
	    arg=contains_p1(arg,cadr(e), p);
	    arg=contains_p1(arg,caddr(e),p);
	    e = cadddr(e);
	    continue;
	} else if (NULLARY_ARGS(e1)) {
        /* nullary operators */
	    arg=p(arg,e);
	    return arg;
	} else if (IS_STATEMENT(e1)) {
	    return arg;
	} else {
	    // if (lsrc)fprintf(stderr,"Unknown Tree ID %d\n",car(e));
	    // error(-1);
	    return arg;
	}
    }
    return arg;
}

/* gahter accumurated list in all parse tree for all operator */

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

extern int
contains_p2(int arg,int e,int (*p)(int,int))
{
    while(e) {
	if (!car(e)) return arg;
	if (LIST_ARGS(car(e))){
        /* list arguments */
	    arg=p(arg,e);
	    return contains_in_list_p2(arg,caddr(e),p);
	} else if (UNARY_ARGS(car(e))) {
        /* unary operators */
	    arg=p(arg,e);
	    e = cadr(e);
	    continue;
	} else if (BINARY_ARGS(car(e))) {
        /* biary operators */
	    arg=p(arg,e);
	    arg=contains_p2(arg,cadr(e),p);
	    e = caddr(e);
	    continue;
	} else if (TERNARY_ARGS(car(e))) {
        /* tarary operators */
	    arg=p(arg,e);
	    arg=contains_p2(arg,cadr(e), p);
	    arg=contains_p2(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 {
	    // if (lsrc)fprintf(stderr,"Unknown Tree ID %d\n",car(e));
	    // error(-1);
	    return arg;
	}
    }
    return arg;
}

//
// globalize parse tree
//

static int
copy_list(int e)
{
    int e1;
    if (!e) return 0;
    e1 = copy_expr(car(e));
    return glist2(e1,copy_list(cadr(e)));
    return 0;
}

extern int
copy_expr(int e)
{
    int smode = mode;  
	if (!e || !car(e)) return 0;
	if (car(e)<0) {
	    mode = GDECL;
	    switch (car(e)){
	    case CONST: e=list2(car(e),cadr(e)); break;
	    case FCONST: 
	    case DCONST: e=dlist2(car(e),dcadr(e)); break;
	    case LCONST: e=llist2(car(e),lcadr(e)); break;
	    case STRING: e=list3n(car(e),cadr(e),ncaddr(e)); break;
	    case STRINGS: e=list3s(car(e),cadr(e),scaddr(e)); break;
	    case STRUCT:  // for udpcl
		e=glist3(car(e),copy_expr(cadr(e)),copy_expr(caddr(e))); break;
	    case ARRAY:
		e= glist5(car(e),copy_expr(cadr(e)),
		copy_expr(caddr(e)),cadddr(e),caddddr(e)); 
		break;
	    }
	    mode = smode;
	    return e;
	}
	switch (OP(car(e))){
	// special cases
	case ARROW:
	case PERIOD:
	    return glist4(car(e),copy_expr(cadr(e)),caddr(e),cadddr(e));
	case LVAR: case RLVAR:case URLVAR:
	case GVAR: case RGVAR:case URGVAR:
	    return (e>gfree)?glist3(car(e),cadr(e),caddr(e)):e;
	case INDIRECT: // case RINDIRECT:
	    return glist2(car(e),copy_expr(cadr(e)));
	}
	if (LIST_ARGS(car(e))){
        /* list arguments */
	    return copy_list(caddr(e));
	} else if (UNARY_ARGS(car(e))) {
        /* unary operators */
	    e = glist2(car(e),copy_expr(cadr(e)));
	    return e;
	} else if (BINARY_ARGS(car(e))) {
        /* biary operators */
	    e = glist3(car(e),copy_expr(cadr(e)),copy_expr(caddr(e)));
	    return e;
	} else if (TERNARY_ARGS(car(e))||car(e)==STRUCT) {
        /* ternary operators */
	    e = glist4(car(e),copy_expr(cadr(e)),
		copy_expr(caddr(e)),
		copy_expr(cadddr(e)));
	    return e;
	} else if (NULLARY_ARGS(car(e))) {
        /* nullary operators */
	    if (e>gfree) { // local
		int smode = mode;
		switch(car(e)%SOP) {
		case GVAR: case RGVAR: case URGVAR:
		case LVAR: case RLVAR: case URLVAR:
		case REGISTER: case FREGISTER:
		case DREGISTER: case LREGISTER:
		case FNAME:
		case IVAR: case RIVAR:
		    e = glist3n(car(e),cadr(e),ncaddr(e)); break;
		case STRING:
		    e = glist3n(car(e),cadr(e),ncaddr(e)); break;
		case STRINGS:
		    e = glist3s(car(e),cadr(e),scaddr(e)); break;
		case CONST: 
		    switch(car(e)) {
		    case CONST:
			e = glist2(car(e),cadr(e)); break;
		    case DCONST: case FCONST:
			mode=GDECL;
			e = dlist2(car(e),dcadr(e)); mode = smode; break;
		    case LCONST: 
			mode=GDECL;
			e = llist2(car(e),lcadr(e)); mode = smode; break;
		    }
		    break;
		case LCALL:
		case LABEL:
		    e = glist2(car(e),cadr(e)); break;
		case DECL:
		case COMP:
		    // is this correct?
		    e = glist2(car(e),cadr(e)); break;
		}
	    } 
	    return e;
	} else if (IS_STATEMENT(car(e))) {
	    // already in global heap
	    return e;  // may contain anything
	} else {
	    // if (lsrc)fprintf(stderr,"Unknown Tree ID %d\n",car(e));
	    error(-1);
	    return 0;
	}
    return 0;
}

#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;
    int ty;

    e = reverse0(e);

    for(i=out;i;i=cadr(i)) {
        p = (ncaddr(car(i)))->nm;
        e1 = car(e); e = cadr(e);
        repl = code_asm_operand(p,e1,ASM_OUTPUT,repl,0,0);
        if (!chk && repl) {
	    switch(car(car(repl))) {
	    case REGISTER: ty = INT; break;
	    case LREGISTER: ty = LONGLONG; break;
	    case FREGISTER: ty = FLOAT; break;
	    case DREGISTER: ty = DOUBLE; break;
	    default: continue;
            }
	    assign = list2(assign_expr0(e1,car(repl),ty,ty),assign);
        }
    }
    repl0 = repl;
    n = length(repl0);
    for(i=in;i;i=cadr(i)) {
        p = (ncaddr(car(i)))->nm;
        e1 = car(e); e = cadr(e);
        repl = code_asm_operand(p,e1,ASM_INPUT,repl,n,repl0);
        if (!chk && repl) {
	    switch(car(car(repl))) {
	    case REGISTER: ty = INT; break;
	    case LREGISTER: ty = LONGLONG; break;
	    case FREGISTER: ty = FLOAT; break;
	    case DREGISTER: ty = DOUBLE; break;
	    default: continue;
            }
            g_expr_u(assign_expr0(car(repl),e1,ty,ty));
        }
    }

    repl = reverse0(repl);
    p = (ncaddr(asm0))->nm;
    code_asm(p,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);
}

static int 
struct_align_offset(int t,int offset)
{
    int strtype=0;
    if (t>0 && (car(t)==STRUCT||car(t)==UNION))
	strtype=1;
    int sz = size(t);
    if (lp64 && (sz%size_of_longlong==0)) {
	offset = align(offset,size_of_longlong);
    } else if (sz%size_of_int==0||strtype) {
	offset = align(offset,struct_align);
    }
    return offset;
}

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

extern NMTBL *
def(NMTBL *n,int ctmode)
{
    int sz,nsc,ndsp,align0;
    int sbit_f = bit_field_disp;
    int type0 = type_value(type);
    int attr = attribute;
    attribute = 0;

    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);
            NMTBL *fsave = fnptr;
            int save = struct_return;
            fnptr = n;
            fdecl_struct(n->ty); /* insert extra argument for struct passing */
            struct_return = save;
            fnptr = fsave;
	    set_ctmode(n,ctmode);
	    set_attributes(n,attr);
	    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 {
	    sz = size(type0);
#if STRUCT_ALIGN 
	    disp = struct_align_offset(type0,disp);
#endif
	    if ((align0=attr_value_in_list(attr,ALIGNED))) {
int hoge = disp;
		if (car(align0)!=CONST) error(-1);
		// align have to be 2^n
		align0 = caddr(align0);
                disp = align(disp,align0);
if (lsrc && hoge!=disp)
printf("# field %s %d->%d (align %d)\n",n->nm,hoge,disp,align0);
	    }
	}
	if (n!=&null_nptr)
	    fields = list4n(type,fields,disp,n);
	// don't set attribute to n
    } 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 = list4n(type,fields,0,n);
    } 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);
	set_attributes(n,attr);
	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=glist3n(GTDECL,gtypedefed,gnptr);
	set_attributes(n,attr);
	break;
    case LTDECL:                // local typedef
	nsc = TYPE;
	set_attributes(n,attr);
	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(type0>0) {
	    if (!integral(type0) && (car(type0)==FUNCTION||car(type0)==CODE)) {
		type=list2(POINTER,type); n->ty = type;
		sz = size_of_pointer;
		type0=type;
	    } else if (car(type0)==ARRAY) {
		type=list2(POINTER,cadr(type)); n->ty = type;
		sz = size_of_pointer;
		type0=type;
	    }
	}
	fnptr->dsp=list4n(type,fnptr->dsp,0,n);
	n->sc = LVAR;
	if(inmode==INLINE) {
	    n->dsp = args++;
	    n->sc = IVAR;
	} else {
	    // this is useless, because we cannot know this function is code or function now
	    // we have to fix this later in case of code segment ( in code_arg_register )
	    args = code_arg_alignment(args,n,type0,sz, is_code(fnptr));
	}

	caddr(fnptr->dsp)=sz;
	if(type0==VOID) {
	} else {
	    n->ty = type;
	}
	// don't set attribute
	set_ctmode(n,ctmode);
	return n;
    case STAT: /* return (struct hoge)f() case? */
    case LDECL:    // local variable
	set_attributes(n,attr);
	if (stmode==REGISTER && !(inmode==INLINE)) {
	    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==INLINE) {
	    nsc = IVAR;
	    ndsp = --disp;
	} else {
	    code_lvar_alignment(disp,n,type0,sz);
	    set_ctmode(n,ctmode);
	    return n;
	}
	n->sc = nsc;
	n->dsp = ndsp;
	set_ctmode(n,ctmode);
    case SFDINIT:  
	return n;
    default:
	error(DCERR);
    }
    // should be an error?
    n->sc = nsc;
    n->dsp = ndsp;
    set_ctmode(n,ctmode);
    if (stmode==EXTRN)
	n->sc = EXTRN;
    return n;
}

// standard 32bit alignment 


// for function
extern int
code_arg_alignment0(int offset,NMTBL *n, int type0,int sz, int is_code)
{
    if(type0==CHAR||type0==UCHAR) {
        if (n->dsp==0) {
            n->dsp = is_code? -offset-size_of_int:offset;
            if (endian) n->dsp += size_of_int-1;
        }
        offset += size_of_int;
    } else if(type0==SHORT||type0==USHORT) {
        if (n->dsp==0) {
            n->dsp = is_code? -offset-size_of_int: offset;
            if (endian) n->dsp += size_of_int-size_of_short;
        }
        offset += size_of_int;
    } else if(type0>0&&(car(type0)==UNION||car(type0)==STRUCT)) {
        /* alignment in struct in argument */
        /* should be GCD of member alignment */
        /* __attribute(alignment(16)) is ignored in argments */
        int asz = align(sz,size_of_int);
        n->dsp = is_code? -offset-asz:offset;
        offset += asz;
    } else {
        /* if (n->dsp==0) (argument list in ADECL is useless, type
           list can be found in type ) */
	n->dsp = is_code? -offset-size_of_int: offset;
        offset += sz; 
    }
    return offset;
}

// standard 32bit alignment for local variable

extern int
code_lvar_alignment0(int disp0,NMTBL *n,int type0,int sz) {
    int align;

    /* local variable alignment is done by new_lvar */
    if ((align=attr_value(n,ALIGNED))) {
	if (car(align)!=CONST) error(-1);
	n->sc = LVAR;
	n->dsp = new_lvar_align(sz,caddr(align));
    } else {
	n->sc = LVAR;
	n->dsp = new_lvar(sz);
    }
    return disp;
}

// for mc-parse.c
extern int
arg_alignment(int args,NMTBL *n, int type0,int sz, int is_code)
{
    return code_arg_alignment(args,n, type0,sz, is_code);
}

extern char *
nm(NMTBL *n) {
    int e;
    NMTBL *str; 
    if (n->attr) {
	if ((e=attr_value(n,ASM))) {
	    if (car(e)==STRINGS) {
		// first element only
		return scaddr(e); // may non terminated
	    }
	    if (car(e)!=STRING) error(-1);
	    str = ncaddr(e);
	    return str->nm;
	}
    }
    return n->nm;
}

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) {
	g_expr_u(car(e));
	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 int
emit_name(int e,NMTBL *n, int sz) 
{
	switch(car(e)) {
	case CONST:
	    if (lp64 && sz>4) emit_longlong(e); else emit_int(cadr(e));
	    return 1;
	case LCONST:
	    if (lp64 && sz>4) emit_longlong(e); else emit_int(lcadr(e));
	    return 1;
	case ADDRESS:
	    if (car(cadr(e))==GVAR)
		emit_address((ncaddr(cadr(e)))->nm,cadr(cadr(e)));
	    else error(INERR);
	    return 1;
	case FNAME:
	    emit_address((ncaddr(e))->nm,0);
	    return 1;
	case GVAR:
	    emit_address((ncaddr(e))->nm,0);
	    return 1;
	case STRING:
	    emit_string((ncaddr(e))->nm,n->ty);
	    return 1;
	case STRINGS:
	    emit_strings(ncaddr(e));
	    return 1;
	}
    // if (lsrc)fprintf(stderr,"## type= %d\n",t);
	return 0;
}


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 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;
    case LONGLONG: case ULONGLONG:
	if (car(e)==CONST) {
	    emit_longlong(llist2(LCONST,cadr(e))); 
	    return;
	} else if (car(e)==LCONST) {
	    emit_longlong(e); 
	    return;
	}
	if (lp64 && emit_name(e,n,size(t))) return;
	break;
    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:
	if (emit_name(e,n,size(t))) return;
    }
    error(INERR);
}


extern int
gen_delayed_decl_data(int v,int offset)
{
    int offset0=0;
    int e;
    int t,sz,offset1=0;
    int init = decl_str_init;
    NMTBL *n = ncaddr(v);

    decl_str_init = 0;
    sz = size(n->ty);
    /*
         decl_str_init
            output delayed decl data
         list4(offset,next,expression,list2(type0,type1));
     */
    while (init) {
        offset= car(init);
        e=caddr(init);
        t=car(cadddr(init));
        if (offset!=offset0) {
            // make space
            assign_data(list2(CONST,offset-offset0),EMPTY,v,offset0);
        }
        type=cadr(cadddr(init));
	offset0 = gen_decl_data0(v,t,e,offset);
        init = cadr(init);
    }
    offset = offset0;
    if ((sz=(offset1+sz-offset))>0)
        assign_data(list2(CONST,sz),EMPTY,v,offset0);
    decl_str_init = 0;
    local_nptr = 0;
    return offset;
}

static int
gen_decl_data_array(int v,int init,int target_type,int offset)
{
    int type0 = cadr(target_type);  /* array item type */
    int e;

    for(; init; init = cadr(init)) {
        // unordered data with tag or array offset
        if (car(init)!=DECL_DATA_ARRAY) {
                error(-1);
        }
        e = caddr(init);
	if (!e) continue; // {...,} case
        e = pexpr(e);
        offset = gen_decl_data0(v,type0,e,offset);
    }
    return offset;
}

static int
gen_decl_data_field(int v,int init,int target_type,int offset)
{
    int type0 = target_type;  /* list of fields */
    int e,t,type1,foffset;
    NMTBL *n;

    for(; init; init = cadr(init)) {
        // unordered data with tag or array offset
        if (car(init)!=DECL_DATA_FIELD) {
                error(-1);
        }
        n = ncadddr(init);
        type1 = search_struct_type(type0,n->nm,&foffset);
        e = caddr(init);
        if (car(e)!=DECL_DATA) error(-1);
        t = caddr(e);
        decl_str_init=insert_ascend(decl_str_init,
            glist4(offset+foffset,0,e,glist2(type1,t)),str_init_eq);
    }
    return offset;
}

static int
gen_decl_data_list(int v,int init,int target_type,int offset)
{
    int type0 = caddr(target_type);  /* list of fields */
    int e;

    for(; init; init = cadr(init)) {
	if (car(init)==DECL_DATA) {
	    // casted initilizer
	    e = cadr(init); // value
	    if (!e) continue; // {...,} case
	    offset = gen_decl_data0(v,caddr(init),e,offset);
	    continue;
	}
        // ordered data
        if (car(init)!=DECL_DATA_LIST) {
                error(-1);
        }
        e = caddr(init);
	if (!e) continue;  // {...,} case
        offset = gen_decl_data0(v,car(type0),e,offset);
	type0 = cadr(type0);
    }
    return offset;
}


static int
gen_decl_data0(int v,int target_type,int init,int offset)
{
    int e,t;
    if (car(init)==DECL_DATA) {
        switch( car(e=cadr(init))) {
        case DECL_DATA_LIST:
            offset = gen_decl_data_list(v,e,target_type,offset);
            break;
        case DECL_DATA_FIELD:
            offset = gen_decl_data_field(v,e,target_type,offset);
            break;
        case DECL_DATA_ARRAY:
            offset = gen_decl_data_array(v,e,target_type,offset);
            break;
        default:
            type = t  = caddr(init);       // type of source
	    // e = rvalue_t(e,t);
	    offset=assign_data(e,target_type,v,offset);
        }
    } else {
        error(-1);
    }
    if (decl_str_init) {
	offset = gen_delayed_decl_data(v,offset);
    }
    return offset;
}

extern int
gen_decl_data(int e,int v)
{
    NMTBL *nptr0;
    int t = caddr(e);
    int e1,sz;
    int offset = 0;
    int sinit_vars = init_vars;
    init_vars = 0;

    type = t;
    sz = size(type);

    if (v==0) {
	nptr0 = get_nptr(); 
	nptr0->nm = "";
	nptr0->sc = LVAR;
	nptr0->attr = 0;
	nptr0->ty = t;
	nptr0->dsp = new_lvar_align(sz,16); // ?!
	e1 = list3(RSTRUCT,list3n(
	    nptr0->sc,nptr0->dsp,nptr0),sz);
	v = list3n(nptr0->sc,nptr0->dsp,nptr0);
    } else {
	e1 = 0;
    }

    gen_decl_data0(v,t,e,offset);
    text_mode(0);

    if (init_vars) emit_init_vars();
    g_expr0(e1);
    init_vars = sinit_vars;
    return type;
}

//
// local variable initialization
//

extern int
assign_data(int e, int t, int v,int offset)
{
    int ass,bfd;
    int v0 = car(v);
    NMTBL *n = ncaddr(v);

    if (inmode) error(-1);
#if STRUCT_ALIGN
    if (t!=EMPTY) 
	offset = struct_align_offset(t,offset);
#endif
    if (car(e)==ADDRESS||car(e)==GVAR) {
	if (scalar(t)) {
	    int t1 = list2(POINTER,VOID); 
	    if (size(t)>=size(t1)) t = t1; // fake
	} else {
	    error(TYERR);
	}
    }
    switch (mode) {
    case GDECL:
	if (car(e)==RSTRUCT) break; // already done
	if (!is_const(e)) error(INERR);
 	emit_data(e,t,n);
	break;
    case STADECL:
	if (!local_nptr) {
	    if (!is_const(e)) error(INERR);
	    else emit_data(e,t,n);
	    break;
	}
	n = local_nptr;
    case LDECL:
    case STAT:               // inline case
	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(
    (v0==REGISTER||v0==DREGISTER||v0==FREGISTER||v0==LREGISTER)?
		v: (offset? list3(v0,cadr(v)+offset,caddr(v)) : v),
	    e,t,type);
	init_vars = list2(ass,init_vars);
	break;
    case SFDINIT:
// if (lsrc)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,
		glist4(offset,0,e,glist2(t,type)),str_init_eq);
	break;
    default:
	error(DCERR);
	return offset;
    }
    if (t>0&&car(t)==BIT_FIELD) {
	int 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(int v)
{
    int offset;
    int offset0=0;
    int e;
    int t,sz,offset1=0;
    NMTBL *n = ncaddr(v);

    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,v,offset0);
        }
        type=cadr(cadddr(decl_str_init));
// if (lsrc)printf("## %d flush   c0(e)=%d type=%d t=%d offset=%d\n",lineno,car(e),type,t,offset);
        offset0 = assign_data(e,t,v,offset);
        decl_str_init = cadr(decl_str_init);
    }
    offset = offset0;
    if ((sz=(offset1+sz-offset))>0)
        assign_data(list2(CONST,sz),EMPTY,v,offset0);
    decl_str_init = 0;
    local_nptr = 0;
}

extern void
data_closing(int e)
{
    if (!chk) {
	if (decl_str_init) flush_delayed_decl_data(e);
	emit_data_closing(ncaddr(e));
    }
}

#define ARG_REORDER_DEBUG 0

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

     arg      defined in f(a,b,c)
     new_arg  defined in   int b; short a; char c;
 */

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

    /* f(a,b,c)  int c; short a; char* b; { } case */
#if ARG_REORDER_DEBUG
 if (lsrc)fprintf(stderr,"arg_reorder old:\n");
    for(j=new_arg;j;j=cadr(j)) {
	    n=ncadddr(j);
 if (lsrc)fprintf(stderr,"dsp %d %s sz %d type %d\n",n->dsp,n->nm,cadddr(j),car(j));
    }
 if (lsrc)fprintf(stderr,"arg_reorder new:\n");
#endif
    for(j=arg;j;j=cadr(j)) {
	n=ncadddr(j);
	for(i=new_arg;i;i=cadr(i)) {
	    n1=ncadddr(i);
	    if (!neqname(n1->nm,n->nm)) break;
	    // if (n1==n) break;
	}
#if ARG_REORDER_DEBUG
 if (lsrc)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);
	    ncadddr(j)=ncadddr(i);
	    n1->dsp = n->dsp;
	    n->ty =  n1->ty;
	    n->sc =  n1->sc;
	    n->attr =  n1->attr;
	    caddr(j)=sz= caddr(i);
	    if (sz==1||sz==size_of_short) sz = size_of_int;
	    dsp += sz;
	} else if(n->sc==IVAR) {
	    n->dsp = dsp;
	    car(j)=car(i);
	    ncadddr(j)=ncadddr(i);
	    n1->dsp = n->dsp;
	    n->ty =  n1->ty;
	    n->sc =  n1->sc;
	    n->attr =  n1->attr;
	    caddr(j)=sz= caddr(i);
	    dsp ++;
	}
	arg_types = glist2(n->ty, arg_types);
    }
#if ARG_REORDER_DEBUG
 if (lsrc)fprintf(stderr,"arg_reorder end:\n");
#endif
    caddr(fnptr->ty) = reverse0(arg_types);
    return arg;
}


NMTBL str_ret;

/*
    If function has a structure return value, it has an extra
    argument for where to write the structure. It has to be
    the first argument. We add the argument here and we have 
    to fix all arguments' offset. If this 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 dummy variable is set in the 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_of_pointer;
	for(t=fnptr->dsp;t;t=cadr(t)) {
	    n=ncadddr(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_pointer);
	    struct_return = inmode
		?list3(list3n(IVAR,str_ret.dsp,0),sz,type)
		:list3(list3n(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);
    }
}

extern int
type_compatible(int t1, int t2)
{
    t1 = type_value(t1);
    t2 = type_value(t2);
    if(integral(t1)) {
	    if(t1!=t2) return 0;
    }
    else if(t1<0 || t2<0) {
	if(t1!=t2) return 0;
    } else if(car(t1)!=car(t2))
	    return 0;
    else if((car(t1)==STRUCT || car(t1)==UNION) && cadr(t1)!=cadr(t2))
	    return 0;
    else if(car(t1)==POINTER || car(t1)==ARRAY ||car(t1)==FUNCTION)
	    return type_compatible(cadr(t1),cadr(t2));
    return 1;
}

static void
compatible(int t1, int t2)
{
    if (!type_compatible(t1,t2)) error(TYERR);
}

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 || 
		(lp64&&(t==LONGLONG||t==ULONGLONG)));
}

/*
    Delayed jmp code generation. A jump code will be delayed until
    new code generation or new label. If label is equal the jumped
    label, no jump code is necesarry.
 */

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

/*
    Delayed jmp code to function leaveing part. The lastexp in
    a statement expression is also checked. It also handle the
    first case statement jump in switch statement.
 */

extern void
checkret(void)
{
    int lastexp0;
    if (!inmode) {
	code_save_stacks(); // because of statment expression
	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;
    }
}

/*
    In casading call of struct valued function, an argument for
    return position will be replaced by calling function.
 */

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;
}


/* indirect 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;
        }
    } else if (car(e2)==LADD) {
        e4=caddr(e2);
        if (car(e4)==CONST) { offset=cadr(e4); e1=e3; }
        else if (car(e4)==LCONST) { offset=lcadr(e4); e1=e3; }
    }
    return list4(t,e1,offset,type);
}

/*
    make right value from original left value.
    type will be changed.
 */
extern int
rvalue(int e)
{
    int op,c;
    NMTBL *n;    
    int type0 = type_value(type);
    int stype;

    if (e==0) error(-1);
    op = 0;
    switch(type0) {
    case INT:		
	break;
    case UNSIGNED:	op=US; type=set_type_with_attr(UNSIGNED,type); 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; /* to make idempotent */
	    if(car(e)==INDIRECT) return cadr(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:
	    if (lp64) op=LOP;
	    break;
	case BIT_FIELD:
	    if (car(e)==BIT_FIELD) {
		e =  list3(RBIT_FIELD,cadr(e),type);
            /*                         byte rvalue,   store type */
	    } else {
		e =  list3(RBIT_FIELD,e,type);
	    }
	    type=set_type_with_attr(cadr(type0),type);/* value type */
	    return e;
	default:
	    error(TYERR);
	}
    }
    switch(OP(car(e))) {
    case GVAR:
	n = ncaddr(e);
	if (cadr(e)==0 && (c=attr_value(n,KONST))) {
	    if (!has_attr(n,VOLATILE))
	    return c;
	}
	return(list3n(RGVAR+op,cadr(e),ncaddr(e)));
    case LVAR:
	n = ncaddr(e);
	if (cadr(e)==0 && n && (c=attr_value(n,KONST))) {
	    if (!has_attr(n,VOLATILE))
	    return c;
	}
	return(list3n(RLVAR+op,cadr(e),ncaddr(e)));
    case INDIRECT:
	return(indirect(RINDIRECT+op,cadr(e),type0)); // cadr(e)?
    case IVAR: case ARRAY: case PERIOD: case ARROW:
	return(indirect(RINDIRECT+op,e,type0));   // RIVAR?
    case BIT_FIELD:
	return list3(RBIT_FIELD,cadr(e),caddr(e));
    case CAST:
	stype = type;
	type = cadddr(e);
	op = rvalue(cadr(e));
	e = list4(CAST,op,caddr(e),cadddr(e));  // should be RCAST?
	type = stype;
    default:return(e); /* idempotent case? */ }
}

/*
    right value with preserving type global variable
 */
extern int
rvalue_t(int e,int type0)
{
    int op,c;
    NMTBL *n;    

    if (e==0) error(-1);
    op = 0;
    switch(type0) {
    case INT:		
	break;
    case UNSIGNED:	op=US;  break;
    case VOID:		break;
    case CHAR:		op=COP;     break;
    case UCHAR:		op=COP+US;  break;
    case SHORT:		op=SOP;     break;
    case USHORT:	op=SOP+US;  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 (type0>0) {
	    if (car(type0)==POINTER) {
		if (lp64) op=LOP;
	    } else error(-1);
	} else error(-1);
    }
    switch(OP(car(e))) {
    case GVAR:
	n = ncaddr(e);
	if (cadr(e)==0 && (c=attr_value(n,KONST))) {
	    if (!has_attr(n,VOLATILE))
	    return c;
	}
	return(list3n(RGVAR+op,cadr(e),ncaddr(e)));
    case LVAR:
	n = ncaddr(e);
	if (cadr(e)==0 && n && (c=attr_value(n,KONST))) {
	    if (!has_attr(n,VOLATILE))
	    return c;
	}
	return(list3n(RLVAR+op,cadr(e),ncaddr(e)));
    case INDIRECT:
	return(indirect(RINDIRECT+op,cadr(e),type0)); // cadr(e)?
    case IVAR: case ARRAY: case PERIOD: case ARROW:
	return(indirect(RINDIRECT+op,e,type0));   // RIVAR?
    case BIT_FIELD:
	return list3(RBIT_FIELD,cadr(e),caddr(e));
    case CAST: 
	op = rvalue_t(cadr(e),cadddr(e));
	e = list4(CAST,op,caddr(e),cadddr(e));  // should be RCAST?
    default:return(e); /* idempotent case? */
    }
}

/*
    left value check. Can we assign a value to it?
 */
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);
}

/*
    Indirect operator.
 */
extern int
indop(int e)
{
    int type0 = type_value(type);
    if(type0!=INT&&type0!=UNSIGNED&&type0!=LONGLONG&&type0!=ULONGLONG) {
	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));
}

/* struct field 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=ncadddr(type);
	if (!nptr0->ty) { error(TYERR); return 0; }
	t = caddr(type) = caddr(nptr0->ty);
    }
    for(;t;t = cadr(t)) {
	if (neqname(ncadddr(t)->nm,name)==0) {
	    *dsp = caddr(t);
	    return car(t);
	}
    }
    return 0;
}

/*
    Structure operation 
    a.b, a->b
 */

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

    if (inmode || chk) {
	e1 = list4n(ind?ARROW:PERIOD,e,type,nptr);
    }
    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 (type<=0 || (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(UFLDERR); type=INT; return e; }
    if (inmode || chk) {
	// bitfield will be checked after parse
	return e1;
    } 
    ind = 0;
    if (car(e)==INDIRECT) {
	e = cadr(e);
	ind = 1;
    }
    if(dsp) {
	switch(OP(car(e))) {
	case GVAR: 
	    // e=list2(INDIRECT,list3(ADD,e,list2(CONST,dsp)));
	    e=list3n(car(e),cadr(e)+dsp,ncaddr(e));
	    break;
	case LVAR: 
	    e=list3n(car(e),cadr(e) + dsp,ncaddr(e));  /* may have attribute */
	    break;
	case IVAR:
	    if (lp64)
		e=list3(LADD,e,llist2(LCONST,dsp));
     	    else
		e=list3(ADD,e,list2(CONST,dsp));
	    break;
	default:
	    if (lp64)
		e=list3(LADD,e,llist2(LCONST,dsp));
     	    else
		e=list3(ADD,e,list2(CONST,dsp));
	}
    }
    if (ind && !parse_mode && !inmode) {
	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;
}

/*

    Binary operator parse tree generation (and optimization)

   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; e2 = dlist2(dop+CONST,-dcadr(e2));
	    // op=ADD; dcadr(e2) = -dcadr(e2);
	} else if(op==DIV) {
	    if(dcadr(e2)==0.0) error(EXERR);
	    op=MUL; e2 = dlist2(dop+CONST,1.0/dcadr(e2));
	    // 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
lintegral(int t)
{
    return (t==LONGLONG||t==ULONGLONG);
}

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 {
	if(!(t1>0&&car(t1)==POINTER)) { type=t1; e1=longlong_value(e1); t1 = LONGLONG;}
	if(!(t2>0&&car(t2)==POINTER)) { type=t2; e2=longlong_value(e2); 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:
	    if(lintegral(t1)) {
		if(lintegral(t2)) {
			le=le1+le2;
		} else {
			if(car(t2)!=POINTER) error(TYERR);
			le=size(cadr(t2))*le1+le2;
			type=t2;
		}
	    } else {
		if(car(t1)!=POINTER) error(TYERR);
		le=le1+size(cadr(t1))*le2;
		type=t1;
	    }
	    break;
	case SUB:
	    if(lintegral(t1)) {
		le=le1-le2; type=LONGLONG;
	    } else {
		if(car(t1)!=POINTER) error(TYERR);
		if(lintegral(t2)) {
		    le=le1-size(cadr(t1))*le2;
		    type=t1;
		} else {
		    le=(le1-le2)/size(cadr(t1));
		    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 LSHIFT:
	    if (t1==ULONGLONG) le=(((unsigned long long)le1)<<le2); else le=le1<<le2;
	    break;
	case RSHIFT:
	    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==ADD) {
	if(lintegral(t1)) {
	    if(lintegral(t2)) {
		// if(t1==INT) type=t2;else type=t1;
		if (us) type=ULONGLONG; else type=LONGLONG;
		return(list3(LADD,e1,e2));
	    }
	    if(car(t2)!=POINTER) error(TYERR);
	    e=lbinop(MUL,e1,llist2(LCONST,size(cadr(t2))),t1,LONGLONG);
	    type=t2;
	    return(lvalue_opt(list3(LADD,e2,e)));
	}
	if(car(t1)!=POINTER||!lintegral(t2)) error(TYERR);
	e=lbinop(MUL,e2,llist2(LCONST,size(cadr(t1))),t2,LONGLONG);
	type=t1;
	if (car(e)==LCONST && lcadr(e)==0)
	    return(e1);
	return(llvalue_opt(list3(LADD,e1,e)));
    }
    if(op==SUB) {
	if(lintegral(t1)) {
	    if(!lintegral(t2)) error(TYERR);
	    if(t1==LONGLONG) type=t2;else type=t1;
	    if (type==ULONGLONG) type=LONGLONG;
	    return(list3(LSUB,e1,e2));
	}
	if(car(t1)!=POINTER) error(TYERR);
	if(lintegral(t2)) {
	    e=binop(MUL,e2,llist2(LCONST,size(cadr(t1))),t2,LONGLONG);
	    type=t1;
	    if (car(e)==LCONST) error(-1);
	    return(list3(LSUB,e1,e));
	}
	if(car(t2)!=POINTER)
	    error(TYERR);
	compatible(t1,t2);
	e=list3(LSUB,e1,e2);
	e=lbinop(DIV,e,llist2(LCONST,size(cadr(t1))),LONGLONG,LONGLONG);
	type= LONGLONG;
	return 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 */

extern 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 (lp64) return lbinop(op,e1,e2,t1,t2);
	if(!(op==SUB && t2>0&&car(t2)==POINTER))  {
	    type = t2; e2= int_value(e2); t2=INT; 
	}
    } else if(t2>0&&car(t2)==POINTER) { 
	if (lp64) return lbinop(op,e1,e2,t1,t2);
	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 lvalue_opt(list3(ADD,e2,e));
	}
	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);
	return(lvalue_opt(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);   // we have to execute this to fix type
    if (inmode) return  list3(ST_OP,op,list4(e1,e2,t1,t2));
    return e;
}

/*
       arugment type of binary operator
 */
extern int
type_of_bop(int op)
{
    int us;
    if (op>0) {
	switch(OP(op)) {
	    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_TAG(op) + 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
skip_cast(int e)
{
    int t,t1;
    if (car(e)==CAST) {
	t  = caddr(e);
	t1 = cadddr(e);
	if ( type_compatible(t, t1) ) {
	    return cadr(e);
	}
    } 
    return e;
}

/* coarse for function/code segments arguments */

extern int
correct_type(int e,int t0)
{
    int t = type_value(t0),t1;
    // e = rvalue(e); 
#if BIT_FIELD_CODE
    // if (type>0 && car(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;
    }
    // is this correct?
    if ((t1=type_value(type))>0 && 
	    car(type_value(type))==ARRAY && car(e)==GVAR) {
	if (!chk)
	    e=list2(ADDRESS,e);
    }
    if (t>0) {
	switch(car(t)) {
	case POINTER:
	    if (cadr(t)>0) {
		switch(car(cadr(t))) {
		case FUNCTION: 
		// type でチェックするべきだよね? 本来...
		// compatible(cadr(t),cadr(type));
		// ではあかんの?
		    if (car(e)==FNAME) {
			NMTBL *n = ncaddr(e);
			int targ0 = caddr(cadr(t));
			int targ1 = caddr(n->ty);
			if (is_function(n)) {
			    // return type
			    compatible(cadr(cadr(t)),cadr(n->ty));
			}
			// arguments
			for (;targ0;targ0=cadr(targ0),targ1=cadr(targ1)) {
			    if (car(targ0)==DOTS) break;
			    compatible(car(targ0),car(targ1));
			}
		    }
		    break;
		default:
		    if (!(t1>0&&car(t1)==ARRAY) && !scalar(t1)) error(TYERR);
		}
	    } else {
		if (!(t1>0&&car(t1)==ARRAY) && !scalar(t1)) error(TYERR);
	    }
	    break;
	case STRUCT: case UNION:
	    if (scalar(t1)) error(TYERR);
	    else 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;
}

/*
    Integer Constant expression is required
 */

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) {
//    if (lsrc)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 = list3n(LVAR,new_lvar(size_of_longlong),0);
	    code_lassign_lvar(cadr(n1),USE_CREG);
	}
	if (!code_lassop_p) {
	    n2 = list3n(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 = list3n(LVAR,new_lvar(size_of_int),0);
	    code_assign_lvar(cadr(n1),USE_CREG,size_of_int);
	}
	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, int align)
{
    disp-=sz;
    if (align) {
	disp &= ~(align-1);
    } else if (sz>=4 && (disp & (4-1))) { // alignment 4
	disp &= ~(4-1);
    }
    return disp;
}

/*
    Allocate new local variable in flat scope
 */


extern int
new_lvar_align(int size,int align)
{
    int lvar,plvar;

    /* Can we reuse previously freed local variable? */ 
    for (plvar = 0,lvar = lvar_free_list;lvar;lvar = cadr(lvar)) {
        if (caddr(lvar)==size && (~align|| car(lvar)%align==0)) {
            if (plvar) cadr(plvar) = cadr(lvar);
            else lvar_free_list = cadr(lvar);
            break;
        }
        plvar = lvar;
    }
    if (!lvar) {
        lvar_list = glist3((lvar=new_lvar0(size,align)),lvar_list,size);
    } else {
        cadr(lvar) = lvar_list; lvar_list = lvar;
        lvar = car(lvar_list);
    }
    return lvar;
}

extern int
new_lvar(int size) {  // system dependent?
    return new_lvar_align(size,0);
}

/*
    Free the allocated local variable. 	It may be reused again.
 */

extern void
free_register_var(int reg_arg_list)
{
    int arg;
    for(;reg_arg_list;reg_arg_list=cadr(reg_arg_list)) {
        arg = car(reg_arg_list);
        if (car(arg)==REGISTER||car(arg)==DREGISTER||car(arg)==FREGISTER
                ||car(arg)==LREGISTER)  {
            free_register(cadr(arg));
        } else if (car(arg)==LVAR&&cadr(arg)<0) free_lvar(cadr(arg));
    }
}


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 unsigned 
make_mask(int from,int to)
{
    unsigned mask = 0;
    unsigned bit = 1;
    int i;
    if (from<0||from>31) error(-1);
    for (i=31;from<=i;i--,bit<<=1) {
        if (i<=to) {
            mask |= bit;
        }
    }
    return mask;
}

extern unsigned long long
make_mask_64(int from,int to)
{
    unsigned long long mask = 0;
    unsigned long long bit = 1;
    int i;
    if (from<0||from>63) error(-1);
    for (i=63;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
	global pointer needs dynamic loading or table.
	it may used again, put it to a register as a cache.
 */

extern void
init_ptr_cache()
{
    int i;
    for(i=0;i<MAX_PTR_CACHE-1;i++) {
	ptr_cache=glist3n(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(ncaddr(ptcptr)&&car(ptcptr)==r) {
	    free_register(r); car(ptcptr)=-1; ncaddr(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 = car(ptcptr);
	ptcptr=cadr(ptcptr);
    }
    return r;
}


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


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

    // linear search cache 
    while(ptcptr!=ptr_cache_last) {
	if(ncaddr(ptcptr)==g) {
	    if (prev) {
		// make this top
		cadr(prev)=cadr(ptcptr);
		cadr(ptcptr) = ptr_cache;
		ptr_cache = ptcptr;
	    }
	    return car(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(car(prev)); car(ptcptr)=-1; ncaddr(ptcptr)=0;
	    ptr_cache_last = prev;
	}
	else error(-1);
    }
    r = get_register(0); // some ptr cache may remove by this
    car(p = cadr(ptr_cache_last)) = r;
    ncaddr(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 */