Mercurial > hg > CbC > old > device
view mc-codegen.c @ 577:7e0076617c76
env switch continue..
author | kono |
---|---|
date | Sun, 15 Jan 2006 18:28:39 +0900 |
parents | 00e5ce0e341a |
children | dbde3b869a0f |
line wrap: on
line source
/* Micro-C Generic Code Generation Part */ /* $Id$ */ #include <stdio.h> #include "mc.h" #include "mc-parse.h" #include "mc-codegen.h" #include "mc-code.h" #include "mc-switch.h" #include "mc-inline.h" int use; /* generated value will be used */ char *init_src; int size_of_int; int size_of_short; int size_of_float; int size_of_double; int size_of_longlong; int bit_of_byte; int endian; #define STRUCT_ALIGN 1 static void assign(int e1); #if ASM_CODE static void gen_asm(int asm0,int in,int out,int opt,int e); #endif static void compatible(int t1, int t2); static int contains(int e,int type); static int contains_in_list(int e,int type); static int contains_in_list_p(int e,int (*p)(int)); static void iassop(int e1); static int is_same_type(int e1,int e2); static void jump(int e1, int env); static void machinop(int e1); static int register_to_lvar(int e); static void remove0(int *parent,int e) ; static void sassign(int e1); #if FLOAT_CODE /* floating point */ static void dassop(int e1); static void dmachinop(int e1,int d); static void dassign(int e1); #endif #if LONGLONG_CODE static void lassop(int e1); static void lmachinop(int e1); static void lassign(int e1); #endif #if BIT_FIELD_CODE static int bit_field_repl(int e1,int e2,int t); static int bit_field(int e1,int t); static int bassign(int e1,int e2,int t); static int bassop(int e1,int e2,int op,int t,int post); #endif extern void codegen_init() { /* called only once */ code_init(); } extern void codegen_reinit() { /* called for each file */ emit_reinit(); } extern void codegen_decl_init() { /* called before each declaration */ emit_init(); init_free_lvar_list(); } extern void arg_register(NMTBL *fnptr) { code_arg_register(fnptr); } extern int gexpr(int e1,int use0) { if (chk) return INT; gexpr_init(); use = use0; return g_expr0(e1); } /* gexpr for value unused */ extern int g_expr_u(int e1) { int t; int suse = use; use=0; t=g_expr0(e1); code_gexpr(e1); use=suse; return t; } /* gexpr for value used */ extern int g_expr(int e1) { int t; int suse = use; use=1; t=g_expr0(e1); code_gexpr(e1); use=suse; return t; } /* gexpr for used flag untouched */ extern int g_expr0(int e1) { int e2,e3,t,d,t1; NMTBL *n; if (inmode) { error(-1); // return (parse = list3(ST_COMP,parse,e1)); } if (!control && !IS_STATEMENT(car(e1))) return VOID; for(;e1;e1=e2) { code_gexpr(e1); e2 = cadr(e1); switch (car(e1)){ case GVAR: code_gvar(e1,USE_CREG); return ADDRESS; case RGVAR: code_rgvar(e1,USE_CREG); return INT; case CRGVAR: code_crgvar(e1,USE_CREG,1,1); return CHAR; case CURGVAR: code_crgvar(e1,USE_CREG,0,1); return UCHAR; case SRGVAR: code_crgvar(e1,USE_CREG,1,size_of_short); return CHAR; case SURGVAR: code_crgvar(e1,USE_CREG,0,size_of_short); return UCHAR; case LVAR: code_lvar(e2,USE_CREG); return ADDRESS; case REGISTER: code_register(e2,USE_CREG); return INT; #if FLOAT_CODE case DREGISTER: code_dregister(e2,USE_CREG,1); return DOUBLE; case FREGISTER: code_dregister(e2,USE_CREG,0); return FLOAT; #endif #if LONGLONG_CODE case LREGISTER: code_lregister(e2,USE_CREG); return LONGLONG; #endif case RLVAR: code_rlvar(e2,USE_CREG); return INT; case CRLVAR: code_crlvar(e2,USE_CREG,1,1); return CHAR; case CURLVAR: code_crlvar(e2,USE_CREG,0,1); return UCHAR; case SRLVAR: code_crlvar(e2,USE_CREG,1,size_of_short); return CHAR; case SURLVAR: code_crlvar(e2,USE_CREG,0,size_of_short); return UCHAR; #if FLOAT_CODE case FRLVAR: code_drlvar(e2,0,USE_CREG); return FLOAT; case FRGVAR: code_drgvar(e1,0,USE_CREG); return FLOAT; case DRLVAR: code_drlvar(e2,1,USE_CREG); return DOUBLE; case DRGVAR: code_drgvar(e1,1,USE_CREG); return DOUBLE; #endif #if LONGLONG_CODE case LRLVAR: code_lrlvar(e2,USE_CREG); return LONGLONG; case LRGVAR: code_lrgvar(e1,USE_CREG); return LONGLONG; case LURLVAR: code_lrlvar(e2,USE_CREG); return ULONGLONG; case LURGVAR: code_lrgvar(e1,USE_CREG); return ULONGLONG; #endif case FNAME: code_fname((NMTBL *)(e2),USE_CREG); return ADDRESS; case LABEL: if (car(e2)!=LVAR) error(-1); code_label_value(cadr(e2),USE_CREG); return ADDRESS; case CONST: /* 代入する値が0でも特別な処理はしない */ code_const(e2,USE_CREG); return INT; #if FLOAT_CODE case DCONST: code_dconst(e1,USE_CREG,1); return DOUBLE; case FCONST: code_dconst(e1,USE_CREG,0); return FLOAT; #endif #if LONGLONG_CODE case LCONST: code_lconst(e1,USE_CREG); return LONGLONG; #endif case STRING: code_string(e1,USE_CREG); return ADDRESS; case FUNCTION: if (car(e2)==FNAME&&is_code((NMTBL*)cadr(e2))) { // error(FNERR); jump(e1,0); return VOID; } t = function(e1); return t; case CODE: if (car(e2)==FNAME&&is_function((NMTBL*)cadr(e2))) { // error(GTERR); return function(e1); } jump(e2,caddr(e1)); return VOID; case INLINE: return gen_inline(e1); case INDIRECT: return g_expr0(e2); case RINDIRECT: code_rindirect(e2,USE_CREG,caddr(e1),1,0); return INT; case URINDIRECT: code_rindirect(e2,USE_CREG,caddr(e1),0,0); return UNSIGNED; case CRINDIRECT: code_rindirect(e2,USE_CREG,caddr(e1),1,1); return CHAR; case CURINDIRECT: code_rindirect(e2,USE_CREG,caddr(e1),0,1); return UCHAR; case SRINDIRECT: code_rindirect(e2,USE_CREG,caddr(e1),1,size_of_short); return SHORT; case SURINDIRECT: code_rindirect(e2,USE_CREG,caddr(e1),0,size_of_short); return USHORT; #if FLOAT_CODE case FRINDIRECT: return code_drindirect(e2,USE_CREG,caddr(e1),0); case DRINDIRECT: return code_drindirect(e2,USE_CREG,caddr(e1),1); #endif #if LONGLONG_CODE case LRINDIRECT: return code_lrindirect(e2,USE_CREG,caddr(e1),0); case LURINDIRECT: return code_lrindirect(e2,USE_CREG,caddr(e1),1); #endif case ADDRESS: if (car(e2)==REGISTER||car(e2)==DREGISTER||car(e2)==FREGISTER) return register_to_lvar(e2); /* too late? */ else return g_expr0(e2); case MINUS: /* レジスタに対し、neglを実行すれば実現可能 */ g_expr0(e2); code_neg(USE_CREG); return INT; #if LONGLONG_CODE case LMINUS: g_expr0(e2); code_lneg(USE_CREG); return LONGLONG; #endif #if FLOAT_CODE case DMINUS: g_expr0(e2); code_dneg(USE_CREG,1); return DOUBLE; case FMINUS: g_expr0(e2); code_dneg(USE_CREG,0); return FLOAT; #endif case CONV: g_expr0(e2); switch(caddr(e1)) { case I2C: code_i2c(USE_CREG); return INT; case I2S: code_i2s(USE_CREG); return INT; case U2UC: code_u2uc(USE_CREG); return UNSIGNED; case U2US: code_u2us(USE_CREG); return UNSIGNED; #if FLOAT_CODE case I2D: code_i2d(USE_CREG); return DOUBLE; case D2I: code_d2i(USE_CREG); return INT; case U2D: code_u2d(USE_CREG); return DOUBLE; case F2U: code_f2u(USE_CREG); return UNSIGNED; case I2F: code_i2f(USE_CREG); return FLOAT; case F2I: code_f2i(USE_CREG); return INT; case U2F: code_u2f(USE_CREG); return FLOAT; case D2U: code_d2u(USE_CREG); return UNSIGNED; case D2F: code_d2f(USE_CREG); return FLOAT; case F2D: code_f2d(USE_CREG); return DOUBLE; #endif #if LONGLONG_CODE case I2LL: code_i2ll(USE_CREG); return LONGLONG; case I2ULL: code_i2ull(USE_CREG); return ULONGLONG; case U2LL: code_u2ll(USE_CREG); return LONGLONG; case U2ULL: code_u2ull(USE_CREG); return ULONGLONG; case LL2I: code_ll2i(USE_CREG); return INT; case LL2U: code_ll2u(USE_CREG); return UNSIGNED; case ULL2I: code_ull2i(USE_CREG); return INT; case ULL2U: code_ull2u(USE_CREG); return UNSIGNED; #if FLOAT_CODE case D2LL: code_d2ll(USE_CREG); return LONGLONG; case D2ULL: code_d2ull(USE_CREG); return ULONGLONG; case F2LL: code_f2ll(USE_CREG); return LONGLONG; case F2ULL: code_f2ull(USE_CREG); return ULONGLONG; case LL2D: code_ll2d(USE_CREG); return DOUBLE; case LL2F: code_ll2f(USE_CREG); return FLOAT; case ULL2D: code_ull2d(USE_CREG); return DOUBLE; case ULL2F: code_ull2f(USE_CREG); return FLOAT; #endif #endif default: error(-1); return INT; } case BNOT: /* ~ */ g_expr0(e2); code_not(USE_CREG); return INT; case LNOT: /* ! */ g_expr0(e2); code_lnot(USE_CREG); return INT; case PREINC: code_preinc(e1,e2,caddr(e1),1,cadddr(e1),USE_CREG); return INT; case UPREINC: code_preinc(e1,e2,caddr(e1),0,cadddr(e1),USE_CREG); return INT; case POSTINC: code_postinc(e1,e2,caddr(e1),1,cadddr(e1),USE_CREG); return INT; case UPOSTINC: code_postinc(e1,e2,caddr(e1),0,cadddr(e1),USE_CREG); return INT; #if FLOAT_CODE case DPREINC: /* ++d */ code_dpreinc(e1,e2,1,USE_CREG); return DOUBLE; case DPOSTINC: /* d++ */ code_dpostinc(e1,e2,1,USE_CREG); return DOUBLE; case FPREINC: /* ++f */ code_dpreinc(e1,e2,0,USE_CREG); return FLOAT; case FPOSTINC: /* f++ */ code_dpostinc(e1,e2,0,USE_CREG); return FLOAT; #endif #if LONGLONG_CODE case LPREINC: /* ++d */ code_lpreinc(e1,e2,USE_CREG); return LONGLONG; case LPOSTINC: /* d++ */ code_lpostinc(e1,e2,USE_CREG); return LONGLONG; case LUPREINC: /* ++d */ code_lpreinc(e1,e2,USE_CREG); return ULONGLONG; case LUPOSTINC: /* d++ */ code_lpostinc(e1,e2,USE_CREG); return ULONGLONG; #endif case MUL: case UMUL: case DIV: case UDIV: case MOD: case UMOD: case LSHIFT: case ULSHIFT: case RSHIFT: case URSHIFT: case ADD: case SUB: case BAND: case EOR: case BOR: case CMP: case CMPGE: case UCMP: case CMPEQ: case CMPNEQ: case UCMPGE: machinop(e1); return INT; #if FLOAT_CODE case DMUL: case DDIV: case DADD: case DSUB: case DCMP: case DCMPGE: case DCMPEQ: case DCMPNEQ: dmachinop(e1,1); return DOUBLE; case FMUL: case FDIV: case FADD: case FSUB: case FCMP: case FCMPGE: case FCMPEQ: case FCMPNEQ: dmachinop(e1,0); return FLOAT; #endif #if LONGLONG_CODE case LMUL: case LUMUL: case LDIV: case LUDIV: case LMOD: case LUMOD: case LLSHIFT: case LULSHIFT: case LRSHIFT: case LURSHIFT: case LADD: case LSUB: case LBAND: case LEOR: case LBOR: case LCMP: lmachinop(e1); return INT; #endif case COND: /* a?0:1 should consider non-brach instruction */ case UCOND: d = INT; goto cond_case; case LUCOND: case LCOND: d = LONGLONG; goto cond_case; case DCOND: d = DOUBLE; goto cond_case; case FCOND: d = FLOAT; cond_case: e2=fwdlabel(); if (caddr(e1)) { b_expr(cadr(e1),0,e2,0); g_expr0(caddr(e1)); } else { // gcc extenstion a?:DEF bexpr(cadr(e1),0,e2); // value used } t = code_get_fixed_creg(USE_CREG,d); gen_jmp(e3=fwdlabel()); fwddef(e2); t1=g_expr0(cadddr(e1)); code_set_fixed_creg(t,1,d); fwddef(e3); return t1; case STASS: sassign(e1); return RSTRUCT; case ASS: case CASS: case SASS: assign(e1); return INT; case SASSOP: case SUASSOP: case ASSOP: case CASSOP: case CUASSOP: iassop(e1); return INT; #if FLOAT_CODE case FASS: dassign(e1); return FLOAT; case DASS: dassign(e1); return DOUBLE; case FASSOP: dassop(e1); return FLOAT; case DASSOP: dassop(e1); return DOUBLE; #endif #if LONGLONG_CODE case LASS: lassign(e1); return LONGLONG; case LASSOP: case LUASSOP: lassop(e1); return LONGLONG ; #endif case RSTRUCT: g_expr0(e2); return RSTRUCT; case ALLOCA: code_alloca(e2,USE_CREG); return list2(POINTER,CHAR); case BUILTINP: /* Too late. Should be evaluated in pexpr. */ code_const(is_const(e2),USE_CREG); return INT; case COMMA: g_expr_u(e2); return g_expr0(caddr(e1)); case RETURN: n = (NMTBL *)e2; if (retcont==0) retcont=fwdlabel(); code_return(USE_CREG); return VOID; case ENVIRONMENT: code_environment(USE_CREG); return ADDRESS; case LCALL: code_save_stacks(); gen_jmp(e2); fwddef(caddr(e1)); return VOID; #if BIT_FIELD_CODE case RBIT_FIELD: return bit_field(e2,caddr(e1) /* type */); case BASS: return bassign(e2,caddr(e1),cadr(cadddr(e1))/* type */); case BPREINC: return bassop(e2,list2(CONST,caddr(e1)),ADD, cadddr(e1)/* type */,0); case BPOSTINC: return bassop(e2,list2(CONST,caddr(e1)),ADD, cadddr(e1)/* type */,1); case BASSOP: return bassop(e2,caddr(e1),car(cadddr(e1)),/* op */ cadr(cadddr(e1))/* type */,0); #endif #if ASM_CODE case ASM: gen_asm(car(e2),cadr(e2),caddr(e2),cadddr(e2),caddr(e1)); /* asm in (str) out (str) opt(str) expr */ return VOID; #endif case ST_DECL: st_decl(e1); break; case ST_IF: st_if(e1); break; case ST_DO: st_do(e1); break; case ST_WHILE: st_while(e1); break; case ST_FOR: st_for(e1); break; case ST_SWITCH: st_switch(e1); break; case ST_COMP: st_comp(e1); break; case ST_BREAK: st_break(e1); break; case ST_CONTINUE: st_continue(e1); break; case ST_CASE: st_case(e1); break; case ST_DEFAULT: st_default(e1); break; case ST_RETURN: st_return(e1); break; case ST_GOTO: st_goto(e1); break; case ST_ASM: st_asm(e1); break; case ST_LABEL: st_label(e1); break; case ST_COMMENT: st_comment(e1); break; case IVAR: error(-1); break; case 0: break; // empty case default: code_bool(e1,USE_CREG); /* type? */ return INT; } } return VOID; } static int rop_dual(int op) { // x op y => y dual(op) x switch(op) { case GT: return LT; case UGT: return ULT; case GE: return LE; case UGE: return ULE; case LT: return GT; case ULT: return UGT; case LE: return GE; case ULE: return UGE; case DOP+GT: return DOP+LT; case DOP+GE: return DOP+LE; case DOP+LT: return DOP+GT; case DOP+LE: return DOP+GE; case FOP+GT: return FOP+LT; case FOP+GE: return FOP+LE; case FOP+LT: return FOP+GT; case FOP+LE: return FOP+GE; case LOP+GT: return LOP+LT; case LOP+GE: return LOP+LE; case LOP+LT: return LOP+GT; case LOP+LE: return LOP+GE; case LOP+UGT: return FOP+ULT; case LOP+UGE: return FOP+ULE; case LOP+ULT: return FOP+UGT; case LOP+ULE: return FOP+UGE; } return op; } /* bexpr for value unused */ /* l1 ... label for branch */ /* return 0 if l1 is not used, otherwise return l1 */ extern int bexpr_u(int e1, char cond, int l1) { int op = car(e1); if (chk) return l1; // gexpr_init(); switch(op) { case GT: case UGT: case GE: case UGE: case LT: case ULT: case LE: case ULE: case DOP+GT: case DOP+GE: case DOP+LT: case DOP+LE: case FOP+GT: case FOP+GE: case FOP+LT: case FOP+LE: case FOP+EQ: case FOP+NEQ: case EQ: case NEQ: case DOP+EQ: case DOP+NEQ: switch(car(cadr(e1))) { case CONST: case DCONST: case FCONST: case LCONST: return b_expr(list3(rop_dual(op),caddr(e1),cadr(e1)),cond,l1,0); } } return b_expr(e1,cond,l1,0); } /* bexpr for value used */ extern int bexpr(int e1, char cond, int l1) { int uses = use; use=1; l1 = bexpr_u(e1, cond, l1); use = uses; return l1; } /* branch expression generator */ /* if (cond?e1:!e1) goto l1 */ /* 1 or 0 is return for code_bool */ extern int b_expr(int e1, char cond, int l1,int err) { int e2,l2,t; if (!control) return l1; l2 = 0; e2=cadr(e1); switch(car(e1)) { case LNOT: return b_expr(e2,!cond,l1,0); case GT: case GE: case LT: case LE: case EQ: case NEQ: return rexpr(e1,l1,cond,INT); return l1; case UGT: case UGE: case ULT: case ULE: return rexpr(e1,l1,cond,UNSIGNED); #if FLOAT_CODE case DOP+GT: case DOP+GE: case DOP+EQ: case DOP+NEQ: case FOP+GT: case FOP+GE: case FOP+EQ: case FOP+NEQ: return drexpr(cadr(e1),caddr(e1),l1,car(e1),cond); case FOP+LT: case FOP+LE: case DOP+LT: case DOP+LE: return drexpr(caddr(e1),cadr(e1),l1,rop_dual(car(e1)),cond); #endif #if LONGLONG_CODE case LOP+GT: case LOP+GE: case LOP+EQ: case LOP+NEQ: case LOP+UGT: case LOP+UGE: return lrexpr(cadr(e1),caddr(e1),l1,car(e1),cond); case LOP+LT: case LOP+LE: case LOP+ULT: case LOP+ULE: return lrexpr(caddr(e1),cadr(e1),l1,rop_dual(car(e1)),cond); #endif case LAND: l2=bexpr(e2,0,cond?(l2=fwdlabel()):l1); l1=bexpr_u(caddr(e1),cond,l1); if(cond) fwddef(l2); return l1; case LOR: l2=bexpr(e2,1,cond?l1:(l2=fwdlabel())); l1=bexpr_u(caddr(e1),cond,l1); if(!cond) fwddef(l2); return l1; case CRGVAR: case CURGVAR: code_cmp_crgvar(e1,USE_CREG,1,l1,cond); return l1; case SRGVAR: case SURGVAR: code_cmp_crgvar(e1,USE_CREG,size_of_short,l1,cond); return l1; case CRLVAR: case CURLVAR: code_cmp_crlvar(e2,USE_CREG,1,l1,cond); return l1; case SRLVAR: case SURLVAR: code_cmp_crlvar(e2,USE_CREG,size_of_short,l1,cond); return l1; case RGVAR: code_cmp_rgvar(e1,USE_CREG,l1,cond); return l1; case RLVAR: code_cmp_rlvar(e2,USE_CREG,l1,cond); return l1; #if 0 && FLOAT_CODE case DRLVAR: code_cmp_drlvar(e2,USE_CREG,1,l1,cond); return l1; case FRLVAR: code_cmp_drlvar(e2,USE_CREG,0,l1,cond); return l1; case DRGVAR: code_cmp_drgvar(e2,USE_CREG,1,l1,cond); return l1; case FRGVAR: code_cmp_drgvar(e2,USE_CREG,0,l1,cond); return l1; case FREGISTER: code_cmp_dregister(e2,0,l1,cond); return l1; case DREGISTER: code_cmp_dregister(e2,1,l1,cond); return l1; case DCONST: case FCONST: if(control&&((dcadr(e2)!=0.0)^cond)) { gen_jmp(l1); return l1; } else return 0; #endif #if 0 && LONGLONG_CODE case LRLVAR: code_cmp_lrlvar(e2,USE_CREG,l1,cond); return l1; case LRGVAR: code_cmp_lrgvar(e2,USE_CREG,l1,cond); return l1; case LREGISTER: code_cmp_lregister(e2,l1,cond); return l1; case LCONST: if(control&&((lcadr(e2)!=0)^cond)) { gen_jmp(l1); return l1; } else return 0; #endif case REGISTER: code_cmp_register(e2,l1,cond); return l1; case CONST: if(control&&((cond&&e2)||(!cond&&!e2))) { gen_jmp(l1); return l1; } else return 0; default: if(err) { error(-1); return l1; /* recursive g_expr/b_expr */ } t=g_expr(e1); if (!use) return l1; if (0) ; #if FLOAT_CODE else if(t==FLOAT) code_cmp_dregister(USE_CREG,0,l1,cond); else if(t==DOUBLE) code_cmp_dregister(USE_CREG,1,l1,cond); #endif #if LONGLONG_CODE else if(t==LONGLONG||t==ULONGLONG) code_cmp_lregister(USE_CREG,l1,cond); #endif else code_cmp_register(USE_CREG,l1,cond); return l1; } } extern int is_const(int e) { switch(car(e)) { case ADDRESS: e = cadr(e); return (car(e)==GVAR||car(e)==FNAME||car(e)==LVAR); case STRING: case GVAR: // case FUNCTION: case CODE: case EXTRN: case EXTRN1: case FNAME: case CONST: case LCONST: case FCONST: case DCONST: return 1; default: return 0; } } extern int is_code(NMTBL *fnptr) { int type = type_value(fnptr->ty); return (type==CODE|| (type>0 && car(type)==CODE)); } extern int is_function(NMTBL *fnptr) { int type = type_value(fnptr->ty); return (type==FUNCTION || (type>0 && car(type)==FUNCTION)); } extern int is_inline(NMTBL *f) { return (f && attr_value(f,INLINE)); } extern int function_type(int e1,int *dots) { int ret_type,t; ret_type = type_value(cadr(e1)); if (ret_type==CHAR) ret_type=INT; /* check argments type is DOTS? */ t = type_value(caddr(e1)); if (/* t==0 || */ t==DOTS) *dots = 1; else { *dots = 0; for(;t;t = type_value(cadr(t))) { if (car(t)==DOTS) *dots = 1; } } return ret_type; } static int register_to_lvar(int e) { error(REG_ERR); return 0; #if 0 途中でレジスタからLVARに変更しても、間に合わない。 NMTBL *n = (NMTBL*)caddr(e); int reg = cadr(e); int tag = car(e); int lvar; int t; if (!n||n==&null_nptr) error(REG_ERR); switch(tag) { case REGISTER: n->dsp = new_lvar(size_of_int); t = INT; break; case DREGISTER: n->dsp = new_lvar(size_of_double); t = DOUBLE; break; case FREGISTER: n->dsp = new_lvar(size_of_float); t = DOUBLE; break; case LREGISTER: n->dsp = new_lvar(size_of_longlong); t = LONGLONG; break; default: error(-1); } n->sc = LVAR; lvar = list3(LVAR,n->dsp,(int)n); g_expr_u(assign_expr0(list3(LVAR,n->dsp,(int)n),list3(tag,reg,(int)n),t,t)); if (tag==REGISTER||tag==DREGISTER||tag==FREGISTER||tag==LREGISTER) { free_register(reg); return g_expr0(lvar); #endif } // parallel assignment of registers. // // target = list3(target_regnum,next,source_regnum); extern void parallel_rassign(int assigns) { int free,tmp,remains,t0=0,t2,src; tmp = 0; for(;;) { remains = 0; // find free target for(free=assigns;free;free=cadr(free)) { if (!caddr(free)) continue; // already done if (car(free)==caddr(free)) { caddr(free)=0; continue; } remains++; t0 = car(free); // target register // check target is free for(src=assigns;src;src=cadr(src)) { if ((t2=caddr(src)) && t0==t2) break; // target is in source } if (src==0) { break; // free is a free target } } if (remains==0) { if (tmp) free_lvar(tmp); return; } if (free) { // free target if (t0!=caddr(free)) { if (caddr(free)>=0) { code_assign_register(t0,0,caddr(free)); } else { code_rlvar(caddr(free),t0); } } caddr(free)=0; // mark it done } else { // no free target for(free=assigns;free;free=cadr(free)) { if (caddr(free)) break; // not yet done } if (!free) error(-1); tmp = new_lvar(size_of_int); if (tmp>0) error(-1); code_assign_lvar(tmp,caddr(free),0); caddr(free) = tmp; } } } /* goto arguments list */ /* target list4(list2(tag,disp),cdr,ty,source_expr) */ /* source expr=listn(tag,...) */ /* source (after) list2(tag,disp) */ /* source list list3(e,cdr,sz) */ #define DEBUG_PARALLEL_ASSIGN 0 static int is_writable(int); /* overlap return list of overlapped target */ static int overlap(int t,int sz,int target) { int s,s0,s1; int t0=cadr(t); int t1=t0+sz; int source; int result=0; if (!is_writable(t)) error(-1); for(;target;target=cadr(target)) { for(source=caddddr(target);source;source=cadr(source)) { s=car(source); s0=cadr(s); switch(car(s)) { case REGISTER: case DREGISTER: case FREGISTER: case LREGISTER: if (code_register_overlap(s,t)) { result = list2(target,result); } break; default: if (is_same_type(s,t)) { s1=s0+caddr(source); #if DEBUG_PARALLEL_ASSIGN>1 printf("## overlap source %d t0 %d t1 %d\n",car(car(t)),t0,t1); printf("## overlap target %d s0 %d s1 %d\n",car(car(source)),s0,s1); printf("## overlap equal = %d\n",((t0<=s0&&s0<t1)||(t0<s1&&s1<=t1))); #endif if((t0<=s0&&s0<t1)||(t0<s1&&s1<=t1)) result = list2(target,result); } } } } return result; } static void remove_target(int *target,int t,int *use) { int use0=*use; int reg; while(use0) { if (car(use0)==t) { reg = car(caddr(use0)); if (reg==REGISTER||reg==FREGISTER||reg==DREGISTER||reg==LREGISTER) free_register(cadr(caddr(use0))); break; } use0 = cadr(use0); } remove0(target,t); } static void save_target(int t,int s,int *target,int *use,int sz,int ty) { int e1; /*新しいレジスタ(or スタック)を取得する*/ if (scalar(ty) && sz==size_of_int && (e1=get_register_var(0))!=-1) { // e1=list3(REGISTER,e1,0); if (code_register_overlap(s,e1)) goto use_lvar; *use=list3(t,*use,e1); g_expr_u(assign_expr0(e1,s,ty,ty)); *target = append5(*target,t,ty,e1,list3(e1,0,sz)); #if FLOAT_CODE } else if (ty==DOUBLE && sz==size_of_double && (e1=get_dregister_var(0,1))!=-1) { // e1=list3(DREGISTER,e1,0); if (code_register_overlap(s,e1)) goto use_lvar; *use=list3(t,*use,e1); g_expr_u(assign_expr0(e1,s,ty,ty)); *target = append5(*target,t,ty,e1,list3(e1,0,sz)); } else if (ty==FLOAT && sz==size_of_float && (e1=get_dregister_var(0,0))!=-1) { // e1=list3(FREGISTER,e1,0); if (code_register_overlap(s,e1)) goto use_lvar; *use=list3(t,*use,e1); g_expr_u(assign_expr0(e1,s,ty,ty)); *target = append5(*target,t,ty,e1,list3(e1,0,sz)); #endif #if LONGLONG_CODE } else if ((ty==LONGLONG||ty==ULONGLONG)&&(e1=get_lregister_var(0))!=-1) { // e1=list3(LREGISTER,e1,0); if (code_register_overlap(s,e1)) goto use_lvar; *use=list3(t,*use,e1); g_expr_u(assign_expr0(e1,s,ty,ty)); *target = append5(*target,t,ty,e1,list3(e1,0,sz)); #endif } else { if (0) { use_lvar:; #if DEBUG_PARALLEL_ASSIGN>1 printf("## register overrap in save_target\n"); #endif } g_expr_u(assign_expr0((e1=list3(LVAR,new_lvar(sz),0)),s,ty,ty)); *target = append5(*target,t,ty,e1,list3(e1,0,sz)); *use=list3(t,*use,e1); } } static int circular_dependency(int t,int clist,int target,int history) { int t1,h,sz,s,clist1,t2; for(;clist;clist=cadr(clist)) { /* conflict list */ loop: t1 = car(clist); for(h=history;h;h=cadr(h)) { if (t1==car(h)) { #if DEBUG_PARALLEL_ASSIGN printf("## circular dependency %d ty %d\n",car(t1),cadr(t1)); #endif return t1; } } for(s=caddddr(t1);s;s=cadr(s)) { /* dependent memory sources */ sz=caddr(s); if ((clist1=overlap(car(s),sz,target))) { if (!cadr(t1)&&!cadr(s)) { history = list2(t,history); t = t1; clist = clist1; goto loop; // tail recursion } else { if ((t2=circular_dependency(t1, clist1,target,list2(t,history)))) { return t2; } } } } } return 0; } // static void remove_a(int source,int s); // remove all child static void parallel_assign(int *target,int *processing,int *use) { int t,s,sz,ty,target0,s1,progress; while(*target) { progress = 0; for(target0=*target;target0; target0=cadr(target0)) { t=car(target0); s=cadddr(target0); sz=size(ty=caddr(target0)); if(car(t)==car(s) && cadr(t)==cadr(s)) { /*書き込み先が自分自身*/ #if DEBUG_PARALLEL_ASSIGN printf("## remove same %d ty %d+%d sz %d\n",car(t),ty,cadr(t),sz); #endif remove_target(target,t,use); progress = 1; } else if (!(s1=overlap(t,sz,*target)) || (cadr(s1)==0 && car(car(s1))==t)) { /* 重なってないので安心して書き込める */ #if DEBUG_PARALLEL_ASSIGN if (s1 && cadr(s1)==0) printf("## singleton %d ty %d+%d sz %d\n",car(t),ty,cadr(t),sz); else printf("## normal assign %d ty %d+%d sz %d\n",car(t),ty,cadr(t),sz); #endif g_expr_u(assign_expr0(t,s,ty,ty)); remove_target(target,t,use); progress = 1; } else if((t=circular_dependency(target0,s1,*target,0))) { remove_target(target,car(t),use); sz=size(ty=caddr(t)); save_target(car(t),cadddr(t),target,use,sz,ty); progress = 1; #if DEBUG_PARALLEL_ASSIGN printf("## saving %d ty %d+%d sz %d\n",car(car(t)),ty,cadr(car(t)),sz); #endif break; } } if (!progress) { // can't performe parallel assign // error(-1); target0 = *target; t=car(target0); s=cadddr(target0); sz=size(ty=caddr(target0)); #if DEBUG_PARALLEL_ASSIGN printf("## can't progress save any %d ty %d+%d sz %d\n",car(s),ty,cadr(t),sz); #endif remove_target(target,t,use); save_target(t,s,target,use,sz,ty); } } } static void remove0(int *parent,int e) { int list; while ((list=*parent)) { if (car(list)==e) { *parent= cadr(list); return; } else { parent=&cadr(list); } } } /* static int remove_1(int source,int e) { int sz; if ((sz=is_memory(e))) { remove0((int*)source,e); } return source; } static void remove_a(int source,int s) { contains_p1(source,s,remove_1); } */ // #define SAVE_ALL_NON_MEMORY #ifdef SAVE_ALL_NON_MEMORY static int is_simple(int e1) { switch(e1) { case CONST: case FNAME: case LVAR: case REGISTER: case DREGISTER: case FREGISTER: case LREGISTER: case GVAR: case RGVAR: case RLVAR: case CRLVAR: case CRGVAR: case DRLVAR: case FRLVAR: case LRLVAR: case CURLVAR: case SURLVAR: case CURGVAR: case SURGVAR: return 1; } return 0; } #endif static int is_same_type(int e1,int e2) { int ce1=car(e1); int ce2=car(e2); if (ce1==LVAR) { switch(ce2) { case RLVAR: case CRLVAR: case FRLVAR: case DRLVAR: case SRLVAR: case SURLVAR: case CURLVAR: case LVAR: return 1; } } else if (ce2==LVAR) { switch(ce1) { case RLVAR: case CRLVAR: case FRLVAR: case DRLVAR: case SRLVAR: case SURLVAR: case CURLVAR: case LRLVAR: case LVAR: return 1; } } else if (ce1==GVAR) { return 0; #if 0 switch(ce2) { case RGVAR: case CRGVAR: case FRGVAR: case DRGVAR: case SRGVAR: case SURGVAR: case CURGVAR: return 1; } #endif } else if (ce2==GVAR) { return 0; #if 0 switch(ce1) { case RGVAR: case CRGVAR: case FRGVAR: case DRGVAR: case SRGVAR: case SURGVAR: case CURGVAR: case LRGVAR: return 1; } #endif } return 0; } static int is_writable(int e1) { switch(car(e1)) { case GVAR : case LVAR : case RLVAR : // this is wrong, but ia32 generates this. case REGISTER : return size_of_int; case FREGISTER : return size_of_float; case DREGISTER : return size_of_double; case LREGISTER: return size_of_longlong; } return 0; } extern int reference(int e1) { switch(car(e1)) { case GVAR : case CRGVAR : case CURGVAR : case DRGVAR : case FRGVAR : case LRGVAR : case LURGVAR : case RGVAR: case SRGVAR : case SURGVAR: return list3(GVAR,cadr(e1),caddr(e1)); case LVAR : case CRLVAR : case CURLVAR : case DRLVAR : case FRLVAR : case LRLVAR: case LURLVAR: case RLVAR: case SRLVAR : case SURLVAR : return list3(LVAR,cadr(e1),caddr(e1)); case FREGISTER : case REGISTER: case LREGISTER: case DREGISTER: return e1; break; default: error(-1); } return e1; } static int is_memory0(int e1,int *global) { *global=0; switch(car(e1)) { case CRGVAR : case CURGVAR : *global=1; case CRLVAR : case CURLVAR : return 1; case SRGVAR : case SURGVAR: *global=1; case SRLVAR : case SURLVAR : return size_of_short; // case GVAR : case RGVAR: *global=1; // case LVAR : case RLVAR: case REGISTER : return size_of_int; case FRGVAR : *global=1; case FRLVAR : case FREGISTER : return size_of_float; case DRGVAR : *global=1; case DRLVAR : case DREGISTER : return size_of_double; case LRGVAR : case LURGVAR : *global=1; case LRLVAR : case LURLVAR : case LREGISTER: return size_of_longlong; } return 0; } extern int is_memory(int e1) { int global; return is_memory0(e1,&global); } extern int is_local_memory(int e1) { int global; return is_memory0(e1,&global) && !global; } static int check_source(int source,int e) { int sz; if ((sz=is_memory(e))) { source = list3(e,source,sz); } return source; } // // CbC goto statement with environment // // maximum size of struct divide (don't make it large) #define ASSIGN_STRUCT_DIVIDE 40 static void jump(int e1, int env) { int e2,e3,e4,sz,arg_size,ty,regs,fregs; int t0,s0,r,reg; NMTBL *code0 = 0; int target = 0; int processing = 0; int use = 0; int envreg = 0; /* e1 = list4(FUNCTION,code_segment,arglist,ftype); */ if (env) { envreg = get_register_var(0); g_expr_u(assign_expr0(envreg,env,INT,INT)); } /* まず、サイズを計算しながら、target を決まった形に落す。 */ /* list5(target,next,ty,source,source_dependency) */ arg_size = 0; regs = 0; fregs = 0; for (e3 = reverse0(caddr(e1)); e3; e3 = cadr(e3)) { e2 = car(e3); sz = size(ty=caddr(e3)); if (scalar(ty) && (r = get_input_register_var(regs,0,1))) { target=list5(r,target,ty,e2,0); regs++; } else if (ty==FLOAT && (r = get_input_dregister_var(fregs,0,1,0))) { target=list5(r, target,ty,e2,0); fregs++; } else if (ty==DOUBLE && (r = get_input_dregister_var(fregs,0,1,1))) { target=list5(r, target,ty,e2,0); fregs++; } else if ((ty==LONGLONG||ty==ULONGLONG) && (r = get_input_lregister_var(fregs,0,1))) { target=list5(r, target,ty,e2,0); regs+=2; } else if (env) { while(car(e2)==RSTRUCT) e2=cadr(e2); g_expr_u(assign_expr0( list2(INDIRECT, list3(ADD,rvalue_t(envreg,INT),list2(CONST,-arg_size-sz)) ), e2,ty,ty)); } else { while(car(e2)==RSTRUCT) e2=cadr(e2); target=list5(list3(LVAR,0,0), target,ty,e2,0); } /* keep arg space for register variables */ arg_size += sz; #if DEBUG_PARALLEL_ASSIGN printf("## target %d ty %d+%d sz %d\n",car(car(target)),ty,cadr(car(target)),sz); #endif } if (env) { /* change the frame pointer during parallel assignment */ target=list5(code_frame_pointer_register(), target,INT,rvalue_t(envreg,INT),0); } /* disp を飛び先似合わせて修正 */ if (is_code(fnptr)) { if (-arg_size<disp) disp = -arg_size; } else { if (disp_offset-arg_size<disp) disp = disp_offset-arg_size; } /* 複雑な式を前もって計算しておく */ /* 必要なら局所変数を用いる。 */ /* 局所変数へのオフセットを覚えておく */ for (e2 = target; e2; e2 = cadr(e2)) { t0=car(e2); s0=cadddr(e2); sz=size(ty=caddr(e2)); if(car(t0)==LVAR) { /* ここで、書込先アドレスを決める */ if (envreg) error(-1); cadr(t0)=-arg_size; } arg_size-=sz; #ifdef SAVE_ALL_NON_MEMORY if (!is_simple(car(s0))) { #else if (contains_p(s0,not_simple_p)) { #endif /* complex case */ g_expr_u(assign_expr0((e4=list3(LVAR,new_lvar(sz),0)),s0,ty,ty)); use=list3(ty,use,e1); cadddr(e2)=e4; caddddr(e2)=list3(e4,0,sz); s0=e4; } else if (is_same_type(t0,s0)) { if(cadr(t0)==cadr(s0)) { if(is_writable(s0)) { caddddr(e2)=list3(s0,0,sz); continue; } else error(-1); } } if(is_writable(s0)) { if (sz>8 && car(s0)==LVAR && car(t0)==LVAR &&sz<ASSIGN_STRUCT_DIVIDE) { /* large struct generate large save/restore */ /* divide it to avoid large copy */ #if DEBUG_PARALLEL_ASSIGN printf("## division sz %d\n",sz); #endif caddr(e2) = UNSIGNED; caddddr(e2) = list3( cadddr(e2)=list3(LVAR,cadr(s0),0), 0, size_of_int); #if DEBUG_PARALLEL_ASSIGN printf("## div 0 source %d ty %d+%d sz %d\n",car(s0),ty,cadr(s0),size_of_int); #endif for(e4=size_of_int;e4<sz;) { cadr(e2) = list5(car(e2),cadr(e2), caddr(e2),cadddr(e2),caddddr(e2)); switch(sz-e4) { case 1: caddr(e2) = UCHAR; r = 1; break; case 2: case 3: caddr(e2) = USHORT; r = size_of_short; break; default: caddr(e2) = UNSIGNED; r = size_of_int; } if (e4==size_of_int) e3=cadr(e2); car(e2) = list3(LVAR,cadr(t0)+e4,0); caddddr(e2) = list3( cadddr(e2) = list3(LVAR,cadr(s0)+e4,0),0, r); e4 += r; #if DEBUG_PARALLEL_ASSIGN printf("## div 1 source %d ty %d+%d sz %d\n",car(s0),ty,cadr(s0),r); #endif } e2 = e3; continue; } caddddr(e2)=list3(s0,0,sz); #if DEBUG_PARALLEL_ASSIGN printf("## source %d ty %d+%d sz %d\n",car(s0),ty,cadr(s0),sz); #endif } else { /* check used sources in rather complex source */ /* more complex sources are compiled before */ caddddr(e2)=contains_p1(0,s0,check_source); } } /* compute jump address */ e2 = cadr(e1); if (car(e2) == FNAME) { code0=(NMTBL *)cadr(e2); // if (!is_code(code0)) { error(TYERR); return; } } else { /* indirect */ g_expr(e2); emit_push(); } /* 並列代入を実行 */ parallel_assign(&target,&processing,&use); while (use) { reg = car(caddr(use)); if (reg==REGISTER||reg==FREGISTER||reg==DREGISTER||reg==LREGISTER) free_register(cadr(caddr(use))); else if (car(caddr(use))==LVAR) free_lvar(cadr(caddr(use))); use=cadr(use); } if(target) error(-1); if(env) { if (car(envreg)==REGISTER) free_register(cadr(envreg)); else free_lvar(cadr(envreg)); } if (car(e2) == FNAME) { if (is_function(fnptr)) code_fix_frame_pointer(disp_offset); code_jmp(code0->nm); } else { e2 = emit_pop(0); if (is_function(fnptr)) code_fix_frame_pointer(disp_offset); code_indirect_jmp(e2); emit_pop_free(e2); } } static void machinop(int e1) { int e2,e3,op; e2 = cadr(e1); op = car(e1); e3 = caddr(e1); if (code_const_op_p(op,e3)) { g_expr(e2); oprtc(op,USE_CREG,e3); return; } g_expr(e3); emit_push(); g_expr(e2); tosop(op,USE_CREG,(e2=pop_register())); emit_pop_free(e2); return; } #if FLOAT_CODE static void dmachinop(int e1,int d) { int e2,e3,op; e2 = cadr(e1); op = car(e1); e3 = caddr(e1); g_expr(e3); emit_dpush(d); g_expr(e2); dtosop(car(e1),USE_CREG,(e2=emit_dpop(d))); emit_dpop_free(e2,d); return; } #endif #if LONGLONG_CODE static void lmachinop(int e1) { int e2,e3,op; e2 = cadr(e1); op = car(e1); e3 = caddr(e1); if (code_lconst_op_p(op,e3)) { g_expr(e2); loprtc(op,USE_CREG,e3); return; } g_expr(e3); emit_lpush(); g_expr(e2); ltosop(car(e1),USE_CREG,(e2=emit_lpop())); emit_lpop_free(e2); return; } #endif static void sassign(int e1) { int e2,e3,e4,sz,xreg,det,offset; /* structure assignment */ e2 = cadr(e1); /* pointer variable to the struct */ e3 = cadr(e2); /* offset of the variable (distination) */ e4 = caddr(e1); /* right value (source) */ sz = cadddr(e1); /* size of struct or union */ if (car(e4)==RSTRUCT) { e4 = cadr(e4); } if (is_same_type(e2,e4)) { if (cadr(e2)==cadr(e4)) { if (use) g_expr(e4); return; } } g_expr(e4); emit_push(); g_expr(e2); xreg = emit_pop(0); /* 一般的にはコピーのオーバラップの状況は実行時にしかわからない */ /* しかし、わかる場合もある */ if (is_same_type(e2,e4)) { if(cadr(e2)<cadr(e4)) { offset=sz; sz=-sz;} else offset=0; det=1; } else { det = 0; offset=0; } emit_copy(xreg,USE_CREG,sz,offset,1,det); emit_pop_free(xreg); return; } static void assign_opt(int e5,int e2,int e4,int byte) { int reg; /* e2=e4 */ if (e5==REGISTER) { reg = cadr(e4); switch(car(e2)) { case GVAR: code_assign_gvar(e2,reg,byte); return; case LVAR: code_assign_lvar(cadr(e2),reg,byte); return; case REGISTER: code_assign_register(cadr(e2),byte,reg); return; } g_expr(e2); code_assign(USE_CREG,byte,reg); return; } /* e2 is register now */ if (car(e2)!=REGISTER) error(-1); reg = cadr(e2); switch(e5) { case CRGVAR: case CURGVAR: code_crgvar(e4,reg,e5==CRGVAR,1); return; case SRGVAR: case SURGVAR: code_crgvar(e4,reg,e5==SRGVAR,size_of_short); return; case RGVAR: case URGVAR: code_rgvar(e4,reg); return; case CRLVAR: case CURLVAR: code_crlvar(cadr(e4),reg,e5==CRLVAR,1); return; case SRLVAR: case SURLVAR: code_crlvar(cadr(e4),reg,e5==SRLVAR,size_of_short); return; case RLVAR: case URLVAR: code_rlvar(cadr(e4),reg); return; case GVAR: code_gvar(e4,reg); return; case LVAR: code_lvar(cadr(e4),reg); return; case CONST: code_const(cadr(e4),reg); return; case ADDRESS: if (car(cadr(e4))==STRING) code_string(cadr(e4),reg); else code_gvar(cadr(e4),reg); return; case FNAME: code_fname((NMTBL*)cadr(e4),reg); return; case STRING: code_string(e4,reg); return; default: error(-1); } } static void assign(int e1) { int e2,e4,byte,e5; byte=(car(e1) == CASS)?1:(car(e1) == SASS)?size_of_short:0; /* e2=e4 */ e2 = cadr(e1); e4 = caddr(e1);e5=car(e4); if (is_same_type(e2,e4)&&cadr(e2)==cadr(e4)) { if (use) g_expr(e4); return; } if (!use) { if (e5==REGISTER) { assign_opt(e5,e2,e4,byte); return; } else if (car(e2)==REGISTER) { switch(e5) { case ADDRESS: if (!((car(cadr(e4))==STRING) || car(cadr(e4))==GVAR)) break; case CRGVAR : case CRLVAR : case RGVAR : case RLVAR : case URGVAR : case URLVAR : case CURGVAR : case CURLVAR : case SURGVAR : case SURLVAR : case GVAR : case LVAR : case CONST : case FNAME : case STRING : assign_opt(e5,e2,e4,byte); return; } } } switch(car(e2)) { case GVAR: /* i=3 */ g_expr(e4); code_assign_gvar(e2,USE_CREG,byte); return; case LVAR: g_expr(e4); code_assign_lvar(cadr(e2),USE_CREG,byte); return; case REGISTER: g_expr(e4); code_assign_register(cadr(e2),byte,USE_CREG); return; } g_expr(e2); emit_push(); g_expr(e4); e2 = emit_pop(0); code_assign(e2,byte,USE_CREG); emit_pop_free(e2); return; } #if FLOAT_CODE static void dassign_opt(int e5,int e2,int e4,int d) { int reg; /* e2=e4 */ if (e5==DREGISTER||e5==FREGISTER) { reg = cadr(e4); switch(car(e2)) { case GVAR: /* i=3 */ code_dassign_gvar(e2,reg,d); return; case LVAR: code_dassign_lvar(cadr(e2),reg,d); return; case DREGISTER: case FREGISTER: if (reg!=cadr(e2)) code_dassign_dregister(cadr(e2),d,reg); return; default: error(-1); } } /* e2 is register now */ if (car(e2)!=DREGISTER && car(e2)!=FREGISTER) error(-1); reg = cadr(e2); switch(e5) { case FRGVAR: case DRGVAR: code_drgvar(e4,d,reg); return; case FRLVAR: case DRLVAR: code_drlvar(cadr(e4),d,reg); return; case FCONST: case DCONST: code_dconst(e4,reg,d); return; default: error(-1); } } static void dassign(int e1) { int e2,e3,e4,d=0,e5; /* e2=e4 */ e2 = cadr(e1); e3 = cadr(e2); e4 = caddr(e1); e5=car(e4); if (is_same_type(e2,e4)&&cadr(e2)==cadr(e4)) { if (use) g_expr(e4); return; } if (car(e1)==DASS) d=1; else if (car(e1)==FASS) d=0; else error(-1); if (!use) { switch(e5) { case DRGVAR: case DRLVAR: case DCONST: if (car(e2)!=DREGISTER) break; dassign_opt(e5,e2,e4,d); return; case FRGVAR: case FRLVAR: case FCONST: if (car(e2)!=FREGISTER) break; case DREGISTER: case FREGISTER: dassign_opt(e5,e2,e4,d); return; } } switch(car(e2)) { case GVAR: g_expr(e4); code_dassign_gvar(e2,USE_CREG,d); return; case LVAR: g_expr(e4); code_dassign_lvar(cadr(e2),USE_CREG,d); return; case DREGISTER: case FREGISTER: g_expr(e4); code_dassign_dregister(cadr(e2),d,USE_CREG); return; } g_expr(e2); emit_push(); g_expr(e4); e2 = emit_pop(0); code_dassign(e2,USE_CREG,d); emit_pop_free(e2); return; } #endif #if LONGLONG_CODE static void lassign_opt(int e5,int e2,int e4) { int reg; /* e2=e4 */ if (e5==LREGISTER) { reg = cadr(e4); switch(car(e2)) { case GVAR: /* i=3 */ code_lassign_gvar(e2,reg); return; case LVAR: code_lassign_lvar(cadr(e2),reg); return; case LREGISTER: if (reg!=cadr(e2)) code_lassign_lregister(cadr(e2),reg); return; default: error(-1); } } /* e2 is register now */ if (car(e2)!=LREGISTER) error(-1); reg = cadr(e2); switch(e5) { case LRGVAR: case LURGVAR: code_lrgvar(e4,reg); return; case LRLVAR: case LURLVAR: code_lrlvar(cadr(e4),reg); return; case LCONST: code_lconst(e4,reg); return; default: error(-1); } } static void lassign(int e1) { int e2,e3,e4,e5; /* e2=e4 */ e2 = cadr(e1); e3 = cadr(e2); e4 = caddr(e1); e5=car(e4); if (is_same_type(e2,e4)&&cadr(e2)==cadr(e4)) { if (use) g_expr(e4); return; } if (!use && ( (e5==LREGISTER &&(car(e2)==GVAR||car(e2)==LVAR||car(e2)==LREGISTER)) || (car(e2)==LREGISTER&& (e5==LRGVAR||e5==LRLVAR||e5==LURLVAR||e5==LURGVAR||e5==LCONST)) )) { lassign_opt(e5,e2,e4); return; } switch(car(e2)) { case GVAR: g_expr(e4); code_lassign_gvar(e2,USE_CREG); return; case LVAR: g_expr(e4); code_lassign_lvar(cadr(e2),USE_CREG); return; case LREGISTER: g_expr(e4); code_lassign_lregister(cadr(e2),USE_CREG); return; } g_expr(e2); emit_push(); g_expr(e4); e2 = emit_pop(0); code_lassign(e2,USE_CREG); emit_pop_free(e2); return; } #endif /* numerical type conversion */ #if FLOAT_CODE static int double_value(int e2) { int t = type_value(type); if (t>0&&car(t)==BIT_FIELD) e2=rvalue(e2); switch(car(e2)) { case LCONST: #if LONGLONG_CODE e2 = dlist2(DCONST,(double)lcadr(e2)); break; #endif case CONST: e2 = dlist2(DCONST,(double)cadr(e2)); break; case FCONST: e2 = dlist2(DCONST,dcadr(e2)); break; default: switch(type_value(type)) { case DOUBLE: break; case FLOAT: e2 = list3(CONV,rvalue(e2),F2D); break; case UNSIGNED: e2 = list3(CONV,rvalue(e2),U2D); break; case LONGLONG: e2 = list3(CONV,rvalue(e2),LL2D); break; case ULONGLONG: e2 = list3(CONV,rvalue(e2),ULL2D); break; default: if(integral(type)) e2 = list3(CONV,rvalue(e2),I2D); else { error(TYERR); e2 = dlist2(DCONST,1.0); } } } type = set_type_with_attr(DOUBLE,type); return e2; } static int float_value(int e2) { int t = type_value(type); if (t>0&&car(t)==BIT_FIELD) e2=rvalue(e2); if (0) ; #if LONGLONG_CODE else if (car(e2)==LCONST) e2 = dlist2(FCONST,(double)lcadr(e2)); #endif else if (car(e2)==CONST) e2 = dlist2(FCONST,(double)cadr(e2)); else if (car(e2)==DCONST) e2 = dlist2(FCONST,dcadr(e2)); else { switch(type_value(type)) { case LONGLONG: e2 = list3(CONV,rvalue(e2),LL2F); break; case ULONGLONG: e2 = list3(CONV,rvalue(e2),ULL2F); break; case FLOAT: break; case DOUBLE: e2 = list3(CONV,rvalue(e2),D2F); break; case UNSIGNED: e2 = list3(CONV,rvalue(e2),U2F); break; default: if(integral(type)) e2 = list3(CONV,rvalue(e2),I2F); else { error(TYERR); e2 = dlist2(DCONST,1.0); } } } type = set_type_with_attr(FLOAT,type); return e2; } #endif #if LONGLONG_CODE static int longlong_value(int e2) { int t = type_value(type); if (t>0&&car(t)==BIT_FIELD) e2=rvalue(e2); if (0) ; else if (car(e2)==CONST) e2 = llist2(LCONST,(long long)cadr(e2)); else if (car(e2)==LCONST) ; #if FLOAT_CODE else if (car(e2)==DCONST||car(e2)==FCONST) e2 = llist2(LCONST,(long long)dcadr(e2)); #endif else { switch(type_value(type)) { case FLOAT: e2 = list3(CONV,rvalue(e2),F2LL); break; case DOUBLE: e2 = list3(CONV,rvalue(e2),D2LL); break; case UNSIGNED: e2 = list3(CONV,rvalue(e2),U2LL); break; case LONGLONG: break; case ULONGLONG: break; default: if(integral(type)) e2 = list3(CONV,rvalue(e2),I2LL); else { error(TYERR); e2 = llist2(LCONST,0LL); } } } type = set_type_with_attr(LONGLONG,type); return e2; } static int ulonglong_value(int e2) { int t = type_value(type); if (t>0&&car(t)==BIT_FIELD) e2=rvalue(e2); if (0); else if (car(e2)==CONST) e2 = llist2(LCONST,(unsigned long long)cadr(e2)); else if (car(e2)==LCONST) ; #if FLOAT_CODE else if (car(e2)==DCONST||car(e2)==FCONST) e2 = llist2(LCONST,(unsigned long long)dcadr(e2)); #endif else { switch(type_value(type)) { case FLOAT: e2 = list3(CONV,rvalue(e2),F2ULL); break; case DOUBLE: e2 = list3(CONV,rvalue(e2),D2ULL); break; case UNSIGNED: e2 = list3(CONV,rvalue(e2),U2ULL); break; case LONGLONG: break; case ULONGLONG: break; default: if(integral(type)) e2 = list3(CONV,rvalue(e2),I2ULL); else { error(TYERR); e2 = llist2(LCONST,0LL); } } } type = set_type_with_attr(ULONGLONG,type); return e2; } #endif static int int_value(int e2) { int t = type_value(type); if (t>0&&car(t)==BIT_FIELD) { e2=rvalue(e2); t=type_value(type); } if (0); else if(t>0&&car(t)==ARRAY) return e2; else if(scalar(t)) { type = set_type_with_attr(INT,type); return e2; } #if FLOAT_CODE else if (car(e2)==DCONST||car(e2)==FCONST) e2 = list2(CONST,(int)dcadr(e2)); #endif #if LONGLONG_CODE else if (car(e2)==LCONST) e2 = list2(CONST,(int)lcadr(e2)); #endif else { switch(t) { case FLOAT: e2 = list3(CONV,rvalue(e2),F2I); break; case DOUBLE: e2 = list3(CONV,rvalue(e2),D2I); break; case LONGLONG: e2 = list3(CONV,rvalue(e2),LL2I); break; case ULONGLONG: e2 = list3(CONV,rvalue(e2),ULL2I); break; default: error(TYERR); e2 = list2(CONST,1); } } type = set_type_with_attr(INT,type); return e2; } static int char_value(int e2) { int t = type_value(type); if (t!=CHAR&&t!=INT) { e2 = list3(CONV,int_value(rvalue(e2)),I2C); type = set_type_with_attr(INT,type); } return e2; } static int short_value(int e2) { int t = type_value(type); if (t!=SHORT&&t!=INT) { e2 = list3(CONV,int_value(rvalue(e2)),I2S); type = set_type_with_attr(INT,type); } return e2; } static int unsigned_value(int e2) { int t = type_value(type); if (t>0&&car(t)==BIT_FIELD) { e2=rvalue(e2); t=type_value(type); } else if(t>0&&car(t)==ARRAY) return e2; if (0); else if(scalar(t)) { type = set_type_with_attr(UNSIGNED,type); return e2; } #if FLOAT_CODE else if (car(e2)==DCONST||car(e2)==FCONST) e2 = list2(CONST,(int)dcadr(e2)); #endif #if LONGLONG_CODE else if (car(e2)==LCONST) e2 = list2(CONST,(unsigned)lcadr(e2)); #endif else { switch(t) { case LONGLONG: e2 = list3(CONV,rvalue(e2),LL2U); break; case ULONGLONG: e2 = list3(CONV,rvalue(e2),ULL2U); break; case FLOAT: e2 = list3(CONV,rvalue(e2),F2U); break; case DOUBLE: e2 = list3(CONV,rvalue(e2),D2U); break; default: error(TYERR); } } type = set_type_with_attr(UNSIGNED,type); return e2; } static int uchar_value(int e2) { int t = type_value(type); if (t!=UCHAR&&t!=UNSIGNED) { e2 = list3(CONV,unsigned_value(rvalue(e2)),U2UC); type = set_type_with_attr(UNSIGNED,type); } return e2; } static int ushort_value(int e2) { int t = type_value(type); if (t!=USHORT&&t!=UNSIGNED) { e2 = list3(CONV,unsigned_value(rvalue(e2)),U2US); type = set_type_with_attr(UNSIGNED,type); } return e2; } /* assign statement */ /* keep type */ extern int assign_expr0(int e1,int e2,int t,int type0) { int stype; stype=type; type = type0; e2 = rvalue(e2); e1=assign_expr(e1,e2,t); type=stype; return e1; } /* with conversion (will destroy type global variable) */ extern int assign_expr(int e1,int e2,int t) { /* we should check const / assign violation here */ t = type_value(t); if (t>0) { switch(car(type_value(t))) { case BIT_FIELD: // type = list4(BIT_FIELD,type, // list3(type /*store type*/,0 /*bit offset*/,bitsize)); e2 = correct_type(e2,cadr(t)); /* value type */ return(list4(BASS,e1,e2,list2(BASS,t))); case STRUCT:case UNION: if (size(t)!=size(type)) error(TYERR); type=t; // dispose attr if(car(e2)==RSTRUCT && car(cadr(e2))==FUNCTION) { replace_return_struct(cadr(e2),e1); return cadr(e2); } else { return (list4(STASS,e1,e2,size(t))); } default: if(scalar(t)) { if (car(t)!=POINTER) { e2=(type_value(t)==UNSIGNED)? unsigned_value(e2):int_value(e2); } return(list3(ASS,e1,e2)); } error(TYERR); return list3(ASS,e1,e2); } } switch(t) { case VOID: break; case CHAR:case UCHAR: e2=(t==UCHAR)?unsigned_value(e2):int_value(e2); return(list3(CASS,e1,e2)); case SHORT:case USHORT: e2=(t==USHORT)?unsigned_value(e2):int_value(e2); return(list3(SASS,e1,e2)); case INT:case UNSIGNED: case ENUM: e2=(t==UNSIGNED)?unsigned_value(e2):int_value(e2); return(list3(ASS,e1,e2)); #if FLOAT_CODE case DOUBLE: e2=double_value(e2); return(list3(DASS,e1,e2)); case FLOAT: e2=float_value(e2); return(list3(FASS,e1,e2)); #endif #if LONGLONG_CODE case LONGLONG: e2=longlong_value(e2); return(list3(LASS,e1,e2)); case ULONGLONG: e2=ulonglong_value(e2); return(list3(LASS,e1,e2)); #endif } error(TYERR); return list3(ASS,e1,e2); } extern int cond(int t,int e1,int e2,int e3) { int t0 = type_value(t); int t1 = type_value(type); if(car(e1)==CONST) { if(cadr(e1)) {type=set_type_with_attr(t,type);return e2?e2:e1;} else return e3; } #if FLOAT_CODE if(car(e1)==DCONST) { if(dcadr(e1)) {type=t;return e2?e2:e1;} else return e3; } if(t1==DOUBLE||t0==DOUBLE) { e3=double_value(e3); type = t; if (e2) e2=double_value(e2); return(list4(DCOND,e1,e2,e3)); } if(t1==FLOAT||t0==FLOAT) { e3=float_value(e3); type = t; if (e2) e2=float_value(e2); return(list4(FCOND,e1,e2,e3)); } #endif #if LONGLONG_CODE if(car(e1)==LCONST) { if(lcadr(e1)) {type=set_type_with_attr(t,type);return e2?e2:e1;} else return e3; } if(t1==LONGLONG||t0==LONGLONG) { e3=longlong_value(e3); type = t; if (e2) e2=longlong_value(e2); return(list4(LCOND,e1,e2,e3)); } if(t1==ULONGLONG||t0==ULONGLONG) { e3=ulonglong_value(e3); type = t; if (e2) e2=ulonglong_value(e2); return(list4(LUCOND,e1,e2,e3)); } #endif if(t1==INT||t0==INT) { e3=int_value(e3); type = t; if (e2) e2=int_value(e2); return(list4(COND,e1,e2,e3)); } e3=unsigned_value(e3); type = t; if (e2) e2=unsigned_value(e2); /* if (t!=type) error(TYERR); */ return(list4(UCOND,e1,e2,e3)); } /* assop parse tree generation */ extern int assop(int e1,int e2,int op,int t,int no_float) { int ass=0,u = 0; int t0 = type_value(type); if(!(integral(t0)||t0==FLOAT||t0==DOUBLE|| t0==LONGLONG||t0==ULONGLONG )) error(TYERR); switch(t) { #if FLOAT_CODE case FLOAT: if (no_float) error(TYERR); e2=float_value(e2); return(list4(FASSOP,e1,e2,op+FOP)); case DOUBLE: if (no_float) error(TYERR); e2=double_value(e2); return(list4(DASSOP,e1,e2,op+DOP)); #endif #if LONGLONG_CODE case LONGLONG: e2=longlong_value(e2); return(list4(LASSOP,e1,e2,op+LOP)); case ULONGLONG: e2=ulonglong_value(e2); return(list4(LASSOP,e1,e2,op+LOP+((op==MUL+AS||op==DIV+AS)?US:0))); #endif case CHAR: e2=correct_type(e2,INT); ass = CASSOP; break; case SHORT: e2=correct_type(e2,INT); ass = SASSOP; break; case INT: e2=correct_type(e2,INT); ass = ASSOP; break; case UCHAR: e2=correct_type(e2,UNSIGNED); ass = CUASSOP; u=1; break; case USHORT: e2=correct_type(e2,UNSIGNED); ass = SUASSOP; u=1; break; case UNSIGNED: e2=correct_type(e2,UNSIGNED); ass = ASSOP; u=1; break; default: if (t>0 && car(t)==BIT_FIELD) { // type = list4(BIT_FIELD,type, // list3(type /*store type*/,0 /*bit offset*/,symval)); e2 = correct_type(e2,car(caddr(t))); /* store type */ type = set_type_with_attr(cadr(t),type); /* value type */ return(list4(BASSOP,e1,e2,list2(op,t))); } } if (u) { if (op==RSHIFT||op==LSHIFT) op=op+US; else { switch(type) { case UCHAR: case USHORT: case UNSIGNED: if (op==MUL||op==DIV||op==MOD) op=op+US; } } } type = set_type_with_attr(t,type); if(integral(t)) return(list4(ass,e1,e2,op)); /* pointer += ... */ if((op!=ADD&&op!=SUB)||car(t)!=POINTER) error(TYERR); e2=binop(MUL,e2,list2(CONST,size(cadr(t))),INT,UNSIGNED); type = set_type_with_attr(t,type); return list4(ASSOP,e1,e2,op); } /* assop code generation */ static void iassop(int e1) { int e2,e3,byte,op,sign,size; int n,t; /* e2 op= e3 */ switch(car(e1)) { case CUASSOP: byte = 1; sign = 0; size = 1; break; case CASSOP: byte = 1; sign = 1; size = 1; break; case SUASSOP: byte = size_of_short; sign = 0; size = size_of_short; break; case SASSOP: byte = size_of_short; sign = 1; size = size_of_short; break; default: byte = 0; sign = 1; size = size_of_int; } e2 = cadr(e1); if (car(e2)==INDIRECT) e2=cadr(e2); e3 = caddr(e1); op = cadddr(e1); if (car(e2)==REGISTER) { if (code_const_op_p(op,e3)) { oprtc(op,cadr(e2),e3); } else { g_expr(e3); code_register_assop(cadr(e2),USE_CREG,op,byte); } if (use) { code_register(cadr(e2),USE_CREG); } return; } if (car(e3)==CONST) { /* e2 = e2 op e3; */ t = sign?INT:UNSIGNED; // oprtc expected if (car(e2)==LVAR||car(e2)==GVAR) { g_expr(assign_expr0(e2,list3(op,rvalue_t(e2,t),e3),t,t)); return; } /* new = &e2 */ /* *new = *new op e3 */ n = list3(LVAR,new_lvar(size_of_int),0); g_expr_u(assign_expr0(n,list2(ADDRESS,e2),INT,INT)); g_expr(assign_expr0(rvalue_t(n,INT), list3(op,rvalue_t(list2(INDIRECT,rvalue_t(n,INT)),t),e3),t,t)); free_lvar(cadr(n)); return; } g_expr(e3); emit_push(); g_expr(e2); code_assop(op,USE_CREG,byte,sign); return; } #if FLOAT_CODE static void dassop(int e1) { int e2,e3,op,d; /* e2 op= e3 */ d = (car(e1) == DASSOP); e2 = cadr(e1); if (car(e2)==INDIRECT) e2=cadr(e2); e3 = caddr(e1); op = cadddr(e1); g_expr(e3); if (car(e2)==DREGISTER||car(e2)==FREGISTER) { emit_dpush(d); code_register_dassop(cadr(e2),op,d); if (use) code_dregister(cadr(e2),USE_CREG,d); return; } emit_dpush(d); g_expr(e2); code_dassop(op,USE_CREG,d); return; } #endif #if LONGLONG_CODE static int long_sign(int op) { return (op==LUDIV||op==LUMOD||op==LULSHIFT||op==LURSHIFT)?ULONGLONG:LONGLONG; } static void lassop(int e1) { int e2,e3,op; int n,t; /* e2 op= e3 */ e2 = cadr(e1); if (car(e2)==INDIRECT) e2=cadr(e2); e3 = caddr(e1); op = cadddr(e1); if (car(e2)==LREGISTER) { if (code_lconst_op_p(op,e3)) { loprtc(op,cadr(e2),e3); if (use) { code_lregister(cadr(e2),USE_CREG); } return; } if (code_lassop_p) { g_expr(e3); emit_lpush(); code_register_lassop(cadr(e2),op); if (use) { code_lregister(cadr(e2),USE_CREG); } return; } } if (!code_lassop_p||car(e3)==LCONST) { /* e2 = e2 op e3; */ t = long_sign(op); if (car(e2)==LREGISTER||car(e2)==LVAR||car(e2)==GVAR) { g_expr(assign_expr0(e2,list3(op,rvalue_t(e2,t),e3),t,t)); return; } /* new = &e2 */ /* *new = *new op e3 */ n = list3(LVAR,new_lvar(size_of_int),0); g_expr_u(assign_expr0(n,list2(ADDRESS,e2),INT,INT)); g_expr(assign_expr0(rvalue_t(n,INT), list3(op,rvalue_t(list2(INDIRECT,rvalue_t(n,INT)),t),e3),t,t)); free_lvar(cadr(n)); return; } g_expr(e3); if (car(e2)==LREGISTER) { emit_lpush(); code_register_lassop(cadr(e2),op); if (use) code_lregister(cadr(e2),USE_CREG); return; } emit_lpush(); g_expr(e2); code_lassop(op,USE_CREG); return; } #endif extern void cmpdimm(int e, int csreg,int label,int cond) { if (!chk) { if (car(csreg)==CONST) { switch(cond) { case 1: case 0: if (cond ^ (cadr(csreg)==e)) gen_jmp(label); break; case LT: if ((cadr(csreg)>e)) gen_jmp(label); break; } } else if (car(csreg)==REGISTER) { code_cmpdimm(e, cadr(csreg),label,cond); } else error(-1); } } extern int csvalue() { return code_csvalue(); } extern void gen_ret() { if (chk) return; code_ret(); } extern void gen_label_call(int l) { if (chk) return; code_label_call(l); } extern int fwdlabel(void) { return labelno++; } extern void fwddef(int l) { if (l==0) return; checkjmp(l); control=1; if (!chk) code_label(l); } extern int backdef(void) { checkjmp(0); control=1; if (!chk) code_label(labelno); return labelno++; } // define case label with default label for switch statement extern void def_label(int cslabel, int dlabel) { int fl; checkjmp(0); fl = 0; if (control) { gen_jmp(fl=fwdlabel()); } fwddef(cslabel); if (dlabel) gen_jmp(dlabel); if (fl) { fwddef(fl); } } extern void ret(void) { if (!is_inline(fnptr)) { if (!chk) code_set_return_register(1); } gen_jmp(retlabel); } extern void opening(char *filename) { emit_init(); if (!chk) code_opening(filename); } extern void closing() { int e; NMTBL *n; for(e=inline_funcs;e;e=cadr(e)) { n = (NMTBL*)car(e); if (is_code(n)||is_function(n)) { if (n->sc!=STATIC || has_attr(n,FNAME)) { // global or used as pointer // generate possibly called inline function pfdecl(n); } } } if (!chk) code_closing(); } static int contains_in_list(int e,int type) { while(e) { if(contains(car(e),type)) return 1; e = cadr(e); } return 0; } static int contains(int e,int type) { while(e) { if (car(e)==type) return 1; if (!car(e)) return 0; if (LIST_ARGS(car(e))){ /* list arguments */ return contains_in_list(caddr(e),type); } else if (UNARY_ARGS(car(e))) { /* unary operators */ e = cadr(e); continue; } else if (BINARY_ARGS(car(e))) { /* biary operators */ if (contains(cadr(e),type)) return 1; e = caddr(e); continue; } else if (TARNARY_ARGS(car(e))) { /* tarary operators */ if (contains(cadr(e), type)) return 1; if (contains(caddr(e),type)) return 1; e = cadddr(e); continue; } else if (NULLARY_ARGS(car(e))) { /* nullary operators */ return 0; } else if (IS_STATEMENT(car(e))) { return 1; // may contain anything } else { // fprintf(stderr,"Unknown Tree ID %d\n",car(e)); error(-1); return 0; } } return 0; } static int contains_in_list_p(int e,int (*p)(int)) { while(e) { if(contains_p(car(e),p)) return 1; e = cadr(e); } return 0; } extern int contains_p(int e,int (*p)(int)) { while(e) { if (!car(e)) return 0; if (p(car(e))) return 1; if (LIST_ARGS(car(e))){ /* list arguments */ return contains_in_list_p(caddr(e),p); } else if (UNARY_ARGS(car(e))) { /* unary operators */ e = cadr(e); continue; } else if (BINARY_ARGS(car(e))) { /* biary operators */ if (contains_p(cadr(e),p)) return 1; e = caddr(e); continue; } else if (TARNARY_ARGS(car(e))) { /* tarary operators */ if (contains_p(cadr(e), p)) return 1; if (contains_p(caddr(e),p)) return 1; e = cadddr(e); continue; } else if (NULLARY_ARGS(car(e))) { /* nullary operators */ return 0; } else if (IS_STATEMENT(car(e))) { return 1; // may contain anything } else { // fprintf(stderr,"Unknown Tree ID %d\n",car(e)); error(-1); return 0; } } return 0; } /* gahter accumurated list in all parse tree */ static int contains_in_list_p1(int arg,int e,int (*p)(int,int)) { while(e) { arg=contains_p1(arg,car(e),p); e = cadr(e); } return arg; } extern int contains_p1(int arg,int e,int (*p)(int,int)) { while(e) { if (!car(e)) return arg; if (LIST_ARGS(car(e))){ /* list arguments */ return contains_in_list_p1(arg,caddr(e),p); } else if (UNARY_ARGS(car(e))) { /* unary operators */ e = cadr(e); continue; } else if (BINARY_ARGS(car(e))) { /* biary operators */ arg=contains_p1(arg,cadr(e),p); e = caddr(e); continue; } else if (TARNARY_ARGS(car(e))) { /* tarary operators */ arg=contains_p1(arg,cadr(e), p); arg=contains_p1(arg,caddr(e),p); e = cadddr(e); continue; } else if (NULLARY_ARGS(car(e))) { /* nullary operators */ arg=p(arg,e); return arg; } else if (IS_STATEMENT(car(e))) { return arg; } else { // fprintf(stderr,"Unknown Tree ID %d\n",car(e)); // error(-1); return arg; } } return arg; } #if ASM_CODE /* __asm__ __volatile__ ("sthbrx %1,0,%2" : "=m" (*addr) : "r" (val), "r" (a ddr)); asm string : output constraint parameter : input constraint parameter : opt 1: asm string %1,%2 will be replaced by register or value 2: constraint gcc constraint sting prefix = overwrite by this asm for output & overwrite by this asm and can't be used as input register ignored in this compiler constraints m value expression is modified (no corresponding register) information for compiler r register for input or output input register, output register can be shared 0-9 same operands as outout register in input constraints 3: opt "cc", "memory" ignored in this compiler */ static void gen_asm(int asm0,int in,int out,int opt,int e) { int i,e1,n; int repl = 0; int repl0; int assign = 0; char *p; // printf("## asm\n"); e = reverse0(e); for(i=out;i;i=cadr(i)) { p = (char*)cadr(car(i)); e1 = car(e); e = cadr(e); repl = code_asm_operand(p,e1,ASM_OUTPUT,repl,0,0); if (car(car(repl))==REGISTER) { assign = list2(assign_expr0(e1,car(repl),INT,INT),assign); } } repl0 = repl; n = length(repl0); for(i=in;i;i=cadr(i)) { p = (char*)cadr(car(i)); e1 = car(e); e = cadr(e); repl = code_asm_operand(p,e1,ASM_INPUT,repl,n,repl0); if (car(car(repl))==REGISTER) { g_expr_u(assign_expr0(car(repl),e1,INT,INT)); } } repl = reverse0(repl); code_asm((char*)cadr(asm0),repl); for(i=assign;i;i=cadr(i)) { g_expr_u(car(i)); } code_free_asm_operand(repl); // no check for opt } #endif static void set_ctmode(NMTBL *n,int ctmode) { if (ctmode & KONST_BIT) set_attr(n,KONST,0); if (ctmode & VOLATILE_BIT) set_attr(n,VOLATILE,0); if (ctmode & RESTRICT_BIT) set_attr(n,RESTRICT,0); } /* define symbol name contents depending on stmode, mode define displacement */ extern NMTBL * def(NMTBL *n,int ctmode) { int sz,nsc,ndsp; int sbit_f = bit_field_disp; int type0 = type_value(type); bit_field_disp = 0; // default is 0, recover only in bit-field if (n==0) { n=anonymous_nptr(); n->nm = "_"; } nsc=ndsp=0; if (stmode==EXTRN||mode==GDECL) n->ty = type; /* must be in global table/heap */ if(type0>0&&(car(type0)==FUNCTION || car(type0)==CODE)) { if ((mode==GDECL)) { fcheck(n); set_ctmode(n,ctmode); return n; /* function and code segment are defined using fdecl/code_decl */ /* in decl() */ } } // compute size if (mode==GSDECL||mode==LSDECL) { // struct /* Struct fields name lists are in the struct type or tag. */ /* Only name in the table is used. Do not set n->ty! */ /* Struct field may volatile... where do I put? list2(VOLATILE,type)? */ /* disp is pushded and reset in sdecl */ if (type0>0 && car(type0)==BIT_FIELD) { bit_field_disp=sbit_f; // default is 0, recover only here. // type = list4(BIT_FIELD,value type, // list3(store type,bit offset,bit_width)); #if BIT_FIELD_CODE cadr(caddr(type0)) = code_bit_field_disp( type,&disp,&bit_field_disp,&sz); /* bit_field_disp is next bit posision */ #else error(-1); #endif } else { #if STRUCT_ALIGN sz = size(type0); if (sz%size_of_int==0) { disp = ((disp+(size_of_int-1))&~(size_of_int-1)); } #endif } if (n!=&null_nptr) fields = list4(type,fields,(int)(n->nm),disp); } else if (mode==GUDECL||mode==LUDECL) { // union /* disp is pushded and reset in sdecl */ if (type0>0 && car(type0)==BIT_FIELD) { cadr(caddr(type0)) = 0; sz = size(cadr(type0)); } else { sz = size(type0); } fields = list4(type,fields,(int)(n->nm),0); } else { if (n->sc!=EMPTY && !(n->sc==EXTRN||n->sc==EXTRN1||n->sc==STATIC)) { /* redefined case */ if (mode==ADECL) { /* K&R arguments case */ if (n->sc==LVAR && n->ty==INT); else if ( n->sc==REGISTER && n->ty==INT); else if ( n->sc==TYPE) { n = lsearch(n->nm,0); } else error(RDERR); } else { if (mode==GDECL) compatible(n->ty,type); else error(RDERR); // on different type } } sz = size(n->ty = type); } switch(mode) { case GDECL: // global variable gen_gdecl(n->nm,gpc); case STADECL: // static variable nsc = GVAR; ndsp = gpc; if (n->dsp!=-1) /* don't set dsp if initialized static */ n->dsp = ndsp; /* emit_data will override this */ if (stmode==EXTRN) nsc = EXTRN; else if (stmode==STATIC||stmode==LDECL) nsc = STATIC; n->sc = nsc; if (stmode==LDECL) { // this means local static variable n = new_static_name(n->nm,'.'); if (!n->next) { n->next = local_static_list; local_static_list = n; } } else { if (!n->next) { n->next = global_list; global_list = n; } } gpc +=sz; set_ctmode(n,ctmode); return n; case GSDECL: case LSDECL: // struct disp += sz; return n; case GUDECL: case LUDECL: // union /* In union case, disp contains max size of the member */ if (disp < sz) disp = sz; return n; case GTDECL: // typedef nsc = TYPE; gtypedefed=glist2((int)gnptr,gtypedefed); break; case LTDECL: // local typedef nsc = TYPE; break; case LLDECL: // label def (gcc extension) nsc = FLABEL; if (!inmode) ndsp = fwdlabel(); else ndsp = --disp; // inmode の時は pvartable のoffset を確保する。st_label で // nptr が 0 ならば backdef、st_label よりも先に使われたら // fwdlabel すれば良い。 // scope は parse 時に解決される。 break; case ADECL: // funcion arguments if(!integral(type0)&&type0>0&&(car(type0)==FUNCTION||car(type0)==CODE)) { type=list2(POINTER,type); n->ty = type; type0=type; } fnptr->dsp=list4(type,fnptr->dsp,(int)n,0); n->sc = LVAR; if(inmode==INLINE) { n->dsp = args++; n->sc = IVAR; } else if(type0==CHAR||type0==UCHAR) { if (n->dsp==0) { n->dsp = args; if (endian) n->dsp += size_of_int-1; } args += size_of_int; } else if(type0==SHORT||type0==USHORT) { if (n->dsp==0) { n->dsp = args; if (endian) n->dsp += size_of_int-size_of_short; } args += size_of_int; #if 1 } else if(type0>0&&(car(type0)==UNION||car(type0)==STRUCT)) { /* alignment in struct in argument */ n->dsp = args; args += ((sz+(size_of_int-1))&~(size_of_int-1)); #endif } else { /* if (n->dsp==0) (argument list in ADECL is useless, type list can be found in type ) */ n->dsp = args; args += sz; } cadddr(fnptr->dsp)=sz; if(type0==VOID) { } else { n->ty = type; } set_ctmode(n,ctmode); return n; case STAT: /* return (struct hoge)f() case? */ case LDECL: // local variable if (stmode==REGISTER && !inmode) { if(scalar(type0)) { ndsp = get_register_var(n); #if FLOAT_CODE } else if (type0==FLOAT) { ndsp = get_dregister_var(n,0); } else if (type0==DOUBLE) { ndsp = get_dregister_var(n,1); #endif #if LONGLONG_CODE } else if (type0==LONGLONG||type0==ULONGLONG) { ndsp = get_lregister_var(n); #endif } else error(DCERR); nsc = car(ndsp); ndsp = cadr(ndsp); } else if (inmode) { nsc = IVAR; ndsp = --disp; } else { /* local variable alignment is done by new_lvar */ nsc = LVAR; ndsp = new_lvar(sz); } n->sc = nsc; n->dsp = ndsp; set_ctmode(n,ctmode); return n; default: error(DCERR); } n->sc = nsc; n->dsp = ndsp; set_ctmode(n,ctmode); if (stmode==EXTRN) n->sc = EXTRN; return n; } extern void emit_init_vars(void) { int e; if (!init_vars) return; e = reverse0(init_vars); init_vars = 0; if (inmode) { while(e) { parse = list3(ST_COMP,parse,car(e)); e = cadr(e); } return; } while(e) { gexpr(car(e),0); e = cadr(e); } } static int str_init_eq() { // error(-1); // duplicate struct field value return 2; // allow override keep unique } // // generate constant on global memory // static void emit_data(int e, int t, NMTBL *n) { t = type_value(t); if(mode!=GDECL && mode!=STADECL) { error(-1); return; } if (chk) return; if (n->dsp != -1) { n->dsp = -1; /* initialized flag */ emit_global(n,t,e); } switch(t) { case EMPTY: if(car(e)!=CONST) error(-1); emit_space(cadr(e)); return; case CHAR: case UCHAR: if (car(e)!=CONST) error(INERR); emit_char(cadr(e)); data_alignment++; return; case SHORT: case USHORT: if (car(e)!=CONST) error(INERR); emit_short(cadr(e)); data_alignment++; return; case LONGLONG: case ULONGLONG: if (car(e)!=LCONST&&car(e)!=CONST) error(INERR); emit_longlong(e); return; case DOUBLE: if (car(e)!=DCONST&&car(e)!=FCONST) error(INERR); emit_double(e); return; case FLOAT: if (car(e)!=DCONST&&car(e)!=FCONST) error(INERR); emit_float(e); data_alignment++; return; default: if (t<0) error(-1); if (car(t)==BIT_FIELD) { /* not yet supported */ error(-1); return; } if (car(t)!=POINTER&&car(t)!=ARRAY) error(-1); case INT: case UNSIGNED: /* including address case */ case ENUM: switch(car(e)) { case CONST: emit_int(cadr(e)); return; case ADDRESS: if (car(cadr(e))==GVAR) emit_address(((NMTBL *)caddr(cadr(e)))->nm,cadr(cadr(e))); else error(INERR); return; case FNAME: emit_address(((NMTBL *)cadr(e))->nm,0); return; case GVAR: emit_address(((NMTBL *)caddr(e))->nm,0); return; case STRING: emit_string(((NMTBL *)cadr(e))->nm,n->ty); return; } // fprintf(stderr,"## type= %d\n",t); } error(INERR); } // // local variable initialization // extern int assign_data(int e, int t, NMTBL *n,int offset) { int ass,sz,bfd; #if STRUCT_ALIGN if (t!=-99) { sz = size(t); if (sz%size_of_int==0) { offset = ((offset+(size_of_int-1))&~(size_of_int-1)); } } #endif if (car(e)==ADDRESS||car(e)==GVAR) { if (scalar(t)) { t = list2(POINTER,VOID); // fake } else { error(TYERR); } } if(mode==GDECL) { if (!is_const(e)) error(INERR); emit_data(e,t,n); } else if(mode==LDECL || (mode==STADECL&&local_nptr&&(n=local_nptr))) { if (t==EMPTY) { /* empty space in partial initialization */ return offset+cadr(e); } /* If this is a local declared constant, we don't have to assign. But some one may take it's address. We have to generate assign. */ ass = assign_expr0( inmode?( offset? list3(ADD,list3(n->sc,n->dsp,(int)n),list2(CONST,offset)): list3(n->sc,n->dsp,(int)n)): (n->sc==REGISTER||n->sc==DREGISTER||n->sc==FREGISTER||n->sc==LREGISTER)? list3(n->sc,n->dsp,(int)n): list3(n->sc,n->dsp+offset,(int)n), e,t,type); init_vars = list2(ass,init_vars); } else if(mode==STADECL) { if (!is_const(e)) error(INERR); else emit_data(e,t,n); } else if(mode==SFDINIT) { // printf("## %d sfdinit c0(e)=%d type=%d t=%d offset=%d\n",lineno,car(e),type,t,offset); decl_str_init=insert_ascend(decl_str_init, list4(offset,0,e,list2(t,type)),str_init_eq); } else { error(DCERR); return offset; } if (t>0&&car(t)==BIT_FIELD) { sz = 0; bfd = cadr(caddr(t)); /* bit_field_disp */ #if BIT_FIELD_CODE code_bit_field_disp(t,&offset,&bfd,&sz); #endif return offset+sz; } /* constant value field */ if (offset==0 && (has_attr(n,KONST))) { if (is_const(e)) set_attr(n,KONST,e); } return offset+((t==EMPTY)?cadr(e):size(t)); } extern void flush_delayed_decl_data(NMTBL *n) { int offset; int offset0=0; int e; int t,sz,offset1=0; int smode=mode; mode = STADECL; sz = size(n->ty); /* decl_str_init output delayed decl data list4(offset,next,expression,list2(type0,type1)); */ while (decl_str_init) { offset= car(decl_str_init); e=caddr(decl_str_init); t=car(cadddr(decl_str_init)); if (offset!=offset0) { // make space assign_data(list2(CONST,offset-offset0),EMPTY,n,offset0); } type=cadr(cadddr(decl_str_init)); // printf("## %d flush c0(e)=%d type=%d t=%d offset=%d\n",lineno,car(e),type,t,offset); offset0 = assign_data(e,t,n,offset); decl_str_init = cadr(decl_str_init); } offset = offset0; if ((sz=(offset1+sz-offset))>0) assign_data(list2(CONST,sz),EMPTY,n,offset0); decl_str_init = 0; local_nptr = 0; mode=smode; } extern void data_closing(NMTBL *n) { if (!chk) { if (decl_str_init) flush_delayed_decl_data(n); emit_data_closing(n); } } #define ARG_REORDER_DEBUG 0 /* In K&R style, order of argment list and order of argment type decl are differnt. Fix them. */ extern int arg_reorder(int arg,int new_arg) { /* list4(type,fnptr->dsp,(int)n,size); */ int i,j,sz; int dsp = 0; NMTBL *n,*n1; /* f(a,b,c) int c; short a; char* b; { } case */ #if ARG_REORDER_DEBUG fprintf(stderr,"arg_reorder old:\n"); for(j=new_arg;j;j=cadr(j)) { n=(NMTBL *)caddr(j); fprintf(stderr,"dsp %d %s sz %d type %d\n",n->dsp,n->nm,cadddr(j),car(j)); } fprintf(stderr,"arg_reorder new:\n"); #endif for(j=arg;j;j=cadr(j)) { n=(NMTBL *)caddr(j); for(i=new_arg;i;i=cadr(i)) { n1=(NMTBL *)caddr(i); if (!neqname(n1->nm,n->nm)) break; // if (n1==n) break; } #if ARG_REORDER_DEBUG fprintf(stderr,"dsp %d %s %s sz %d type %d\n",dsp,n->nm,n1->nm,cadddr(i),car(i)); #endif if (!i) { /* f(a,b,c) int c; { } case (what?!) */ i = j; n1 = n; } if(n->sc==LVAR) { n->dsp = dsp; car(j)=car(i); caddr(j)=caddr(i); n1->dsp = n->dsp; n->ty = n1->ty; n->sc = n1->sc; n->attr = n1->attr; cadddr(j)=sz= cadddr(i); if (sz==1||sz==size_of_short) sz = size_of_int; dsp += sz; } } #if ARG_REORDER_DEBUG fprintf(stderr,"arg_reorder end:\n"); #endif return arg; } static NMTBL str_ret; /* If function has structure return value, it has an extra argument for where to write the structure. It have to be a first argument. We add it here and we have to fix all arguments' offset. If it is the last value, we don't have to fix, but gcc has a first argument convention. */ extern void fdecl_struct(int fntype) { int type_save,mode_save,t,sz; NMTBL *n; int sargs = args; t = cadr(fntype); if (t>0 && (car(t)==STRUCT||car(t)==UNION)) { mode_save = mode; mode=ADECL; type_save = type; /* extra argument for struct return */ /* this extra dummy arguments are set at calling sequence */ str_ret.nm = "str_ret"; str_ret.sc = EMPTY; str_ret.dsp = 0; str_ret.ty = 0; type=list2(POINTER,t); /* fix all argument's offset */ sz = inmode?1:size(type); for(t=fnptr->dsp;t;t=cadr(t)) { n=(NMTBL *)caddr(t); n->dsp += inmode?1:sz; } fnptr->dsp = reverse0(fnptr->dsp); if ((sz=size(cadr(fntype)))==-1) error(TYERR); else { args=0; // set struct var dsp = 0 def(&str_ret,0); args = sargs + (inmode?1:size_of_int); struct_return = inmode ?list3(list3(IVAR,str_ret.dsp,0),sz,type) :list3(list3(LVAR,str_ret.dsp,0),sz,type); caddr(fnptr->ty) = glist2(POINTER,caddr(fnptr->ty)); } type = type_save; mode = mode_save; } else { struct_return = 0; fnptr->dsp = reverse0(fnptr->dsp); } } extern void fcheck(NMTBL *n) { int type0 = type_value(type); if(!(mode==GDECL||mode==ADECL)|| (car(type0)!=FUNCTION&&car(type0)!=CODE)) error(DCERR); if (n->sc==EMPTY) { n->sc=EXTRN; n->ty=type; } else if(is_code(n)) { // if (car(type0)!=CODE) error(TYERR); compatible(cadr(n->ty),cadr(type)); } else if(is_function(n)) { // if (car(type0)!=FUNCTION) error(TYERR); compatible(cadr(n->ty),cadr(type)); } else { error(DCERR); } } static void compatible(int t1, int t2) { t1 = type_value(t1); t2 = type_value(t2); if(integral(t1)) { if(t1!=t2) error(TYERR); } else if(t1<0 || t2<0) { if(t1!=t2) error(TYERR); } else if(car(t1)!=car(t2)) error(TYERR); else if((car(t1)==STRUCT || car(t1)==UNION) && cadr(t1)!=cadr(t2)) error(TYERR); else if(car(t1)==POINTER || car(t1)==ARRAY ||car(t1)==FUNCTION) compatible(cadr(t1),cadr(t2)); } extern int scalar(int t) { t = type_value(t); return(integral(t)||(t>0 && car(t)==POINTER)); } extern int integral(int t) { t = type_value(t); return(t==INT||t==SIGNED||t==CHAR||t==UNSIGNED|| t==UCHAR||t==SHORT||t==USHORT||t==ENUM); } extern void checkjmp(int l) { int p = pending_jmp; pending_jmp = 0; if (p) { if (p!=l) { control=0; if (!chk) jmp(p); } } } extern void checkret(void) { int lastexp0; if (!inmode) { if (cslabel==0) { if (!control) error(CSERR); // no execute code in switch checkjmp(0); control=0; jmp(cslabel=fwdlabel()); } else if (retpending) { ret(); control=0; retpending=0; } if (lastexp) { if(!control) error(-1); lastexp0 = lastexp; lastexp = 0; // checkret can be nest? gexpr(lastexp0,0); } } else if (lastexp) { parse = list3(ST_COMP,parse,lastexp); lastexp = 0; } } extern void replace_return_struct(int func,int left) { int e = caddr(func); /* arg lists */ while(cadr(e)) e=cadr(e); /* find first arg */ e = car(e); /* return_struct arg */ cadr(e) = left; } /* right value , get the value of the variable */ static int indirect(int t,int e1,int type) { int e2,e3,e4,offset; e2 = e1; offset = 0; e3 = cadr(e2); if (car(e2)==ADD) { e4=caddr(e2); if (car(e4)==CONST) { offset=cadr(e4); e1=e3; } } return list4(t,e1,offset,type); } extern int rvalue(int e) { int op,c; NMTBL *n; int type0 = type_value(type); if (e==0) error(-1); op = 0; switch(type0) { case INT: break; case UNSIGNED: break; case VOID: break; case CHAR: op=COP; type=set_type_with_attr(INT,type); break; case UCHAR: op=COP+US; type=set_type_with_attr(UNSIGNED,type); break; case SHORT: op=SOP; type=set_type_with_attr(SHORT,type); break; case USHORT: op=SOP+US; type=set_type_with_attr(UNSIGNED,type); break; case LONGLONG: op=LOP; break; case ULONGLONG: op=LOP+US; break; case FLOAT: op=FOP; break; case DOUBLE: op=DOP; break; case CODE: return e; case 0: error(-1); return e; default: if (integral(type0)) break; switch(car(type0)) { case ARRAY: type=set_type_with_attr(list2(POINTER,cadr(type)),type); if(car(e)==INDIRECT) return cadr(e); return list2(ADDRESS,e); case STRUCT: case UNION: if(car(e)==RSTRUCT) return e; /* ??? */ return list3(RSTRUCT,e,cadr(type) /* size */); case FUNCTION: type=set_type_with_attr(cadr(type0),type); return e; case CODE: return e; case POINTER: break; case BIT_FIELD: e = list3(RBIT_FIELD,cadr(e),type); /* byte rvalue, store type */ type=set_type_with_attr(cadr(type0),type);/* value type */ return e; break; default: error(TYERR); } } switch(car(e)) { case GVAR: n = (NMTBL*)caddr(e); if (cadr(e)==0 && (c=attr_value(n,KONST))) { if (!has_attr(n,VOLATILE)) return c; } return(list3(RGVAR+op,cadr(e),caddr(e))); case LVAR: n = (NMTBL*)caddr(e); if (cadr(e)==0 && n && (c=attr_value(n,KONST))) { if (!has_attr(n,VOLATILE)) return c; } return(list3(RLVAR+op,cadr(e),caddr(e))); case INDIRECT: return(indirect(RINDIRECT+op,cadr(e),type0)); case IVAR: return(indirect(RINDIRECT+op,e,type0)); default:return(e); /* idempotent case? */ } } extern int rvalue_t(int e,int t) { int stype = type; type = t; e = rvalue(e); type = stype; return e; } extern void lcheck(int e) { int t; int type0 = type_value(type); if(scalar(type0)) return; switch(type0) { case DOUBLE: case FLOAT : case LONGLONG: case ULONGLONG: return; default: switch(car(e)) { case GVAR: case LVAR: case INDIRECT : case REGISTER : case DREGISTER : case FREGISTER : case LREGISTER: return; } } if ((t=car(type0))<0 && t!=STRUCT && t!=UNION) error(LVERR); } extern int indop(int e) { int type0 = type_value(type); if(type0!=INT&&type0!=UNSIGNED) { if(car(type0)==POINTER) type=set_type_with_attr(cadr(type),type); else if(car(type0)==CODE || car(type0)==FUNCTION) { // type=type; } else error(TYERR); } else type= set_type_with_attr(CHAR,type); // ?! if(car(e)==ADDRESS) return(cadr(e)); return(list2(INDIRECT,e)); } /* filed name search */ /* type = list4(s,disp,fields,tag_nptr); */ extern int search_struct_type(int type,char *name,int *dsp) { int t; NMTBL *nptr0; t = caddr(type_value(type)); if (t==0) { nptr0=(NMTBL*)cadddr(type); t = caddr(type) = caddr(nptr0->ty); } for(;t;t = cadr(t)) { if (neqname((char *)caddr(t),name)==0) { *dsp = cadddr(t); return car(t); } } return 0; } extern int strop(int e,int ind) { int dsp = 0; int type0; if (ind) e = indop(rvalue(e)); type0 = type_value(type); if (integral(type0)||(car(type0)!=STRUCT && car(type0)!=UNION)) { e=rvalue(e); type0 = type_value(type); } if ((car(type0)!=STRUCT && car(type0)!=UNION)) { error(TYERR); type=INT; return e; } /* type = list4(s,disp,fields,tag_nptr); */ /* print_fields(caddr(type),"strop"); */ type = search_struct_type(type,nptr->nm,&dsp); if (!type) { error(TYERR); type=INT; return e; } if(dsp) { switch(car(e)) { case GVAR: // e=list2(INDIRECT,list3(ADD,e,list2(CONST,dsp))); e=list3(GVAR,cadr(e)+dsp,caddr(e)); break; case LVAR: e=list3(LVAR,cadr(e) + dsp,0); /* may have attribute */ break; case INDIRECT: e=list2(INDIRECT,list3(ADD,cadr(e),list2(CONST,dsp))); break; default: e=list2(INDIRECT,list3(ADD,e,list2(CONST,dsp))); } } else { switch(car(e)) { case GVAR: case LVAR: case INDIRECT: break; default: e=list2(INDIRECT,e); } } type0 = type_value(type); if (type>0&&car(type)==BIT_FIELD) { // n->ty = list4(BIT_FIELD,type,bit_offset, bit_size); e=list3(BIT_FIELD,e,type); // ??? } return e; } /* I don't know how to handle type attribute (const/volatie) in binop */ #if FLOAT_CODE /* binary floating computation */ #define DTYPE(dop) (dop==DOP?DOUBLE:FLOAT) static int fdbinop(int op, int e1, int e2, int t1, int t2, int dop) { double d1,d2,d; int b=0,t; if (dop==DOP) { type=t1; e1=double_value(e1); type=t2; e2=double_value(e2); } else { type=t1; e1=float_value(e1); type=t2; e2=float_value(e2); } if(car(e1)==dop+CONST&&car(e2)==dop+CONST) { d1=dcadr(e1); d2=dcadr(e2); switch(op) { case ADD: d=d1+d2; break; case SUB: d=d1-d2; break; case MUL: d=d1*d2;break; case DIV: if(!d2) error(EXERR);d=d1/d2;break; default: switch(op) { case GT: b=(d1>d2);break; case GE: b=(d1>=d2);break; case LT: b=(d1<d2);break; case LE: b=(d1<=d2);break; case EQ: b=(d1==d2);break; case NEQ: b=(d1!=d2);break; default: error(EXERR); } type = INT; return list2(CONST,b); } return dlist2(dop+CONST,d); } if(car(e1)==dop+CONST) { if ((op==SUB||op==ADD)&&dcadr(e1)==0.0) { return e2; } else if (op==MUL&&dcadr(e1)==1.0) { return e2; } else if (op==MUL&&-dcadr(e1)==1.0) { return list2(dop+MINUS,e2); } } if(car(e2)==dop+CONST) { if ((op==SUB||op==ADD)&&dcadr(e2)==0.0) { return e1; } if ((op==DIV||op==MUL)&&dcadr(e2)==1.0) { return e1; } if ((op==DIV||op==MUL)&&-dcadr(e2)==1.0) { return list2(DMINUS,e1); } if (op==SUB) { op=ADD; dcadr(e2) = -dcadr(e2); } else if(op==DIV) { if(dcadr(e2)==0.0) error(EXERR); op=MUL; dcadr(e2)=1/dcadr(e2); } } if ((op==ADD||op==MUL) && ( car(e1)==dop+CONST || car(e2)==DRLVAR || car(e2)==DRGVAR || car(e2)==FRLVAR || car(e2)==FRGVAR )) { return(list3(op+dop,e2,e1)); } if(op==ADD||op==SUB||op==MUL||op==DIV) { return(list3(op+dop,e1,e2)); } t = type; type=INT; if(op==LT) { return(list3(GT+dop,e2,e1)); } else if(op==LE) { return(list3(GE+dop,e2,e1)); } else if(op==GT||op==GE||op==EQ||op==NEQ) { return(list3(op+dop,e1,e2)); } else { error(-1); return e1; } } static int dbinop(int op, int e1, int e2, int t1, int t2) { return fdbinop(op, e1, e2, t1, t2,DOP); } static int fbinop(int op, int e1, int e2, int t1, int t2) { return fdbinop(op, e1, e2, t1, t2,FOP); } #endif #if LONGLONG_CODE static int lbinop(int op, int e1, int e2, int t1, int t2) { int e=0; long long le1, le2; long long le = 0; int us = ((t1==ULONGLONG||t1==UNSIGNED)&&(t2==ULONGLONG||t2==UNSIGNED)); if (us||(t1==ULONGLONG&&(op==LSHIFT||op==RSHIFT))) { type=t1; e1=ulonglong_value(e1); type=t2; e2=ulonglong_value(e2); t1=t2=ULONGLONG; } else { type=t1; e1=longlong_value(e1); type=t2; e2=longlong_value(e2); t1=t2=LONGLONG; } if(car(e1)==LCONST&&car(e2)==LCONST) { le1=lcadr(e1); le2=lcadr(e2); switch(op) { case BOR: le=le1|le2;break; case EOR: le=le1^le2;break; case BAND: le=le1&le2;break; case ADD: le=le1+le2;break; case SUB: le=le1-le2; type=LONGLONG; break; case MUL: le=le1*le2;break; case DIV: if(!le2) error(EXERR); if (us) le=(((unsigned long long )le1)/((unsigned long long )le2)); else e=(le1/le2); case MOD: if(!le2) error(EXERR); if (us) le=(((unsigned long long )le1)%((unsigned long long )le2)); else e=(le1%le2); case RSHIFT: if (t1==ULONGLONG) le=(((unsigned long long)le1)<<le2); else le=le1<<le2; break; case LSHIFT: if (t1==ULONGLONG) le=(((unsigned long long)le1)>>le2); else le=le1>>le2; break; default: switch(op) { case EQ: e=(le1==le2);break; case NEQ: e=(le1!=le2);break; case LT: le=le1;le1=le2;le2=le; case GT: if (us) e=((unsigned long long)le1>(unsigned long long)le2); else e=(le1>le2); break; case LE: le=le1;le1=le2;le2=le; case GE: if (us) e=((unsigned long long)le1>=(unsigned long long)le2); else e=(le1>=le2); break; default: error(-1); return list2(CONST,0); } type = INT; return list2(CONST,e); } return llist2(LCONST,le); } if(op==SUB) { us = 0; type=LONGLONG; } if(op==SUB&&car(e2)==LCONST) { op=ADD; e2=llist2(LCONST,-lcadr(e2)); } if((op==ADD||op==MUL||op==BOR||op==EOR||op==BAND)&& (car(e1)!=LCONST) && ( car(e2)==LRGVAR||car(e2)==LRLVAR|| car(e2)==LURGVAR||car(e2)==LURLVAR )) { e=e1;e1=e2;e2=e;e=t1;t1=t2;t2=e; } if((op==MUL||op==DIV)&&car(e2)==LCONST&&lcadr(e2)==1) return e1; if(op==BOR||op==EOR||op==BAND||op==ADD||op==SUB) { return(list3(op+LOP,e1,e2)); } if(op==LSHIFT && car(e2)==LCONST) { if (lcadr(e2)==0) return e1; else if (lcadr(e2)>63) return llist2(LCONST,0); } if(op==RSHIFT && car(e2)==LCONST) { if (lcadr(e2)==0) return e1; } if(op==LSHIFT||op==RSHIFT) { return(list3(op+LOP+(t1==ULONGLONG),e1,e2)); } if (op==DIV||op==MUL||op==ADD||op==SUB||op==MOD) { return(list3(op+LOP,e1,e2)); } type = INT; if(op==LT) { return(list3(GT+LOP+us,e2,e1)); } else if(op==LE) { return(list3(GE+LOP+us,e2,e1)); } else if(op==EQ||op==NEQ) { // EQ/NEQ has no unsign return(list3(op+LOP,e1,e2)); } else if(op==GT||op==GE||op==LT||op==LE) { return(list3(op+LOP+us,e1,e2)); } else error(-1); return 0; /* not reached */ } #endif /* binary integer computation */ static int binop0(int op, int e1, int e2, int t1, int t2) { int e=0; int us = 0; // for inmode, destructive modification e1,e2,t1,t2 is not allowed if(t1>0&&car(t1)==POINTER) { if(!(op==SUB && t2>0&&car(t2)==POINTER)) { type = t2; e2= int_value(e2); t2=INT; } } else if(t2>0&&car(t2)==POINTER) { type = t1; e1= int_value(e1); t1=INT; } #if FLOAT_CODE else if(t1==DOUBLE||t2==DOUBLE) return dbinop(op,e1,e2,t1,t2); else if(t1==FLOAT||t2==FLOAT) return fbinop(op,e1,e2,t1,t2); #endif #if LONGLONG_CODE else if(t1==LONGLONG||t2==LONGLONG||t1==ULONGLONG||t2==ULONGLONG) return lbinop(op,e1,e2,t1,t2); #endif if (t1==UNSIGNED) { if (t2==UNSIGNED || (car(e2)==CONST && cadr(e2)>0)) us = 1; } if (t2==UNSIGNED) { if (t1==UNSIGNED || (car(e1)==CONST && cadr(e1)>0)) us = 1; } if(car(e1)==CONST&&car(e2)==CONST) { e1=cadr(e1); e2=cadr(e2); type= INT; switch(op) { case BOR: e=e1|e2;break; case EOR: e=e1^e2;break; case BAND: e=e1&e2;break; case ADD: if(integral(t1)) { if(integral(t2)) { e=e1+e2; } else { if(car(t2)!=POINTER) error(TYERR); e=size(cadr(t2))*e1+e2; type=t2; } } else { if(car(t1)!=POINTER) error(TYERR); e=e1+size(cadr(t1))*e2; type=t1; } break; case SUB: if(integral(t1)) { e=e1-e2; type=INT; } else { if(car(t1)!=POINTER) error(TYERR); if(integral(t2)) { e=e1-size(cadr(t1))*e2; type=t1; } else { e=(e1-e2)/size(cadr(t1)); type=INT; } } break; case MUL: e=e1*e2;break; case DIV: if(!e2) error(EXERR); if (us) e=(((unsigned)e1)/((unsigned)e2)); else e=e1/e2; break; case MOD: if(!e2) error(EXERR); if (us) e=(((unsigned)e1)%((unsigned)e2)); else e=e1%e2; break; case RSHIFT: if (t1==UNSIGNED) e=(((unsigned)e1)>>((unsigned)e2)); else e=e1>>e2; break; case LSHIFT: if (t1==UNSIGNED) e=(((unsigned)e1)<<((unsigned)e2)); else e=e1<<e2; break; case EQ: e=(e1==e2);break; case NEQ: e=(e1!=e2);break; case LT: e=e1;e1=e2;e2=e; case GT: if (us) e=(((unsigned)e1)>((unsigned)e2)); else e=(e1>e2); break; case LE: e=e1;e1=e2;e2=e; case GE: if (us) e=(((unsigned)e1)>=((unsigned)e2)); else e=(e1>=e2); break; default: error(-1); return list2(CONST,0); } return list2(CONST,e); } if(op==GT||op==GE||op==LT||op==LE) { return(car(e1)==CONST?list3(rop_dual(op)+us,e2,e1):list3(op+us,e1,e2)); } else if(op==EQ||op==NEQ) { return(car(e1)==CONST?list3(op,e2,e1):list3(op,e1,e2)); } if(op==SUB&&car(e2)==CONST) { op=ADD; e2=list2(CONST,-cadr(e2)); } if((op==ADD||op==MUL||op==BOR||op==EOR||op==BAND)&& (car(e1)!=CONST)) { switch(car(e2)) { case RGVAR: case RLVAR: case URGVAR: case URLVAR: case SRGVAR: case SRLVAR: case SURGVAR: case SURLVAR: case CRGVAR: case CRLVAR: case CURGVAR: case CURLVAR: e=e1;e1=e2;e2=e;e=t1;t1=t2;t2=e; } } if(op==ADD) { if(integral(t1)) { if(integral(t2)) { // if(t1==INT) type=t2;else type=t1; if (us) type=UNSIGNED; else type=INT; return(list3(ADD,e1,e2)); } if(car(t2)!=POINTER) error(TYERR); e=binop0(MUL,e1,list2(CONST,size(cadr(t2))),t1,INT); type=t2; return(list3(ADD,e,e2)); } if(car(t1)!=POINTER||!integral(t2)) error(TYERR); e=binop0(MUL,e2,list2(CONST,size(cadr(t1))),t2,INT); type=t1; if (car(e)==CONST && cadr(e)==0) return(e1); if (car(e)==CONST) { if(car(e1)==ADDRESS) { if (car(cadr(e1))==IVAR) { } else if (car(cadr(e1))!=GVAR) { // must be lvar if (car(cadr(e1))!=LVAR) error(-1); return(list2(ADDRESS, list3(car(cadr(e1)),cadr(cadr(e1))+cadr(e), caddr(cadr(e1))))); } else { return(list2(ADDRESS, list3(GVAR,cadr(cadr(e1))+cadr(e),caddr(cadr(e1))))); } } else if(car(e1)==GVAR) { return(list3(GVAR,cadr(e1)+cadr(e),caddr(e1))); } else if(car(e1)==LVAR) { return(list3(LVAR,cadr(e1)+cadr(e),0)); } } return(list3(ADD,e1,e)); } if(op==SUB) { if(integral(t1)) { if(!integral(t2)) error(TYERR); if(t1==INT) type=t2;else type=t1; if (type==UNSIGNED) type=INT; return(list3(SUB,e1,e2)); } if(car(t1)!=POINTER) error(TYERR); if(integral(t2)) { e=binop0(MUL,e2,list2(CONST,size(cadr(t1))),t2,INT); type=t1; if (car(e)==CONST) error(-1); return(list3(SUB,e1,e)); } if(car(t2)!=POINTER) error(TYERR); compatible(t1,t2); e=list3(SUB,e1,e2); e=binop0(DIV,e,list2(CONST,size(cadr(t1))),INT,INT); type= INT; return e; } if(!integral(t1)||!integral(t2)) error(TYERR); // if(t1==INT) type=t2; else type=t1; /* ??? */ if (us) type=UNSIGNED; else type=INT; if((op==MUL||op==DIV)&&car(e2)==CONST&&cadr(e2)==1) return e1; if(op==BOR||op==EOR||op==BAND) return(list3(op,e1,e2)); if(op==LSHIFT||op==RSHIFT) return(list3(op+(t1==UNSIGNED?US:0),e1,e2)); // which ops remain? return(list3(op+us,e1,e2)); } extern int binop(int op, int e1, int e2, int t1, int t2) { int e = binop0(op,e1,e2,t1,t2); if (inmode) return list3(ST_OP,op,list4(e1,e2,t1,t2)); else return e; } /* arugment type of binary operator */ extern int type_of_bop(int op) { int us; if (op>0) { switch(op%200) { case UMUL: case UDIV: case UMOD: case URSHIFT: case ULSHIFT: case ULT: case UCMP: case UCMPGE: case UGE: case UGT: case ULE: us = 1; break; case MUL: case DIV: case MOD: case ADD: case SUB: case CMP: case RSHIFT: case LSHIFT: case GT: case GE: case LT: case LE: case EQ: case NEQ: case BAND: case EOR: case BOR: case CMPGE: case CMPEQ: case CMPNEQ: us = 0; break; default: return 0; } switch((op/200)*200 + us) { case SOP: return SHORT; case SOP+US: return USHORT; case COP: return CHAR; case COP+US: return UCHAR; case DOP: return DOUBLE; case FOP: return FLOAT; case LOP: return LONGLONG; case LOP+US: return ULONGLONG; case US: return UNSIGNED; case 0: return INT; } } return 0; } /* type of result of conversion operator */ extern int type_of_conv(int op) { switch(op) { case LL2D: case ULL2D: case F2D: case I2D: case U2D: return DOUBLE; case LL2F: case ULL2F: case D2F: case I2F: case U2F: return FLOAT; case LL2I: case ULL2I: case D2I: case F2I: case I2C: case I2S: return INT; case D2LL: case F2LL: case I2LL: case U2LL: return LONGLONG; case D2ULL: case F2ULL: case I2ULL: case U2ULL: return ULONGLONG; case LL2U: case ULL2U: case D2U: case F2U: case U2UC: case U2US: return UNSIGNED; } return 0; } extern int op_of(int op) { return (op%200); } /* coarse for function/code segments arguments */ extern int correct_type(int e,int t0) { int t = type_value(t0); /* e = rvalue(e); */ #if BIT_FIELD_CODE if (type==BIT_FIELD) e = rvalue(e); #endif if (t==DOTS) { if (type==FLOAT) t=DOUBLE; else if (type==CHAR) t=INT; else if (type==UCHAR) t=UNSIGNED; else if (type==SHORT) t=INT; else if (type==USHORT) t=UNSIGNED; } if (type_value(type)>0 && car(type_value(type))==ARRAY && car(e)==GVAR) { e=list2(ADDRESS,e); } if (t>0) { switch(car(t)) { case STRUCT: case UNION: if(size(t)!=size(type)) error(TYERR); break; } } else { switch(t) { case DOTS: return e; case UNSIGNED: e = unsigned_value(e); t = UNSIGNED; break; case CHAR: e = char_value(e); t = INT; break; case UCHAR: e = uchar_value(e); t = UNSIGNED; break; case SHORT: e = short_value(e); t = INT; break; case USHORT: e = ushort_value(e); t = UNSIGNED; break; #if FLOAT_CODE case FLOAT: e = float_value(e); break; case DOUBLE: e = double_value(e); break; #endif #if LONGLONG_CODE case LONGLONG: e = longlong_value(e); break; case ULONGLONG: e = ulonglong_value(e); break; #endif default: if (integral(t)) e = int_value(e); } } type = set_type_with_attr(t,t0); return e; } extern int cexpr(int e) { if (car(e) == CONV) { switch(caddr(e)) { case I2C: case I2S: case U2UC: case U2US: e=cadr(e); break; default: error(-1); // illeagal int constant } } if (car(e) != CONST) error(CNERR); return (cadr(e)); } #define is_long_type(type) (type==LONGLONG||type==ULONGLONG) #if BIT_FIELD_CODE /* bitfield struct { char a:1; unsigned int b:10; } System dependent bit alignment is defined by code_bit_field_disp. type of bitfield represents type of the value these values are stored in a stored type which can be different from value type. */ static int bit_field(int e1,int t) { int reg; if (car(e1)==BIT_FIELD) { // printf("## bit_field_bug\n"); e1 = cadr(e1); } g_expr(e1); emit_push(); code_bit_field(t, reg = emit_pop(0), USE_CREG); emit_pop_free(reg); return cadr(t); /* value type */ } static int bit_field_repl(int e1,int e2,int t) { /* e1 = e2 */ if ((car(e2)==CONST||car(e2)==LCONST)) { g_expr(e1); code_bit_replace_const(e2,USE_CREG,t); return t; } g_expr(e1); emit_push(); g_expr(e2); code_bit_replace(e2=emit_pop(0),USE_CREG,t); emit_pop_free(e2); return t; } static int bassign(int e2,int e3,int t) { int type = cadr(t); /* e2 = e3 */ if (car(e2)==BIT_FIELD) { e2 = cadr(e2); } bit_field_repl(e2,e3,t); return type; } static int bassop(int e2,int e3,int op,int t,int post) { /* n = bit_field address code_bit_field if (post) n1 = value value op= operand n bit-repl value if (post) n1; */ /* e2 = e2 op e3; */ /* new = &e2 */ /* *new = *new op e3 */ int suse = use; int lo = (type==LONGLONG||type==ULONGLONG); int n1=0,n2=0; int reg; g_expr(list2(ADDRESS,cadr(e2))); reg = emit_push(); code_bit_field(t, reg, USE_CREG); use=1; if (lo) { #if LONGLONG_CODE if (post) { n1 = list3(LVAR,new_lvar(size_of_longlong),0); code_lassign_lvar(cadr(n1),USE_CREG); } if (!code_lassop_p) { n2 = list3(LVAR,new_lvar(size_of_longlong),0); code_lassign_lvar(cadr(n2),USE_CREG); lassign(list4(LASSOP,n2,e3,op+LOP)); } else { emit_lpush(); g_expr(e3); code_register_lassop(USE_CREG,op+LOP); } #else error(TYERR); #endif } else { if (post) { n1 = list3(LVAR,new_lvar(size_of_int),0); code_assign_lvar(cadr(n1),USE_CREG,0); } emit_push(); g_expr(e3); code_register_assop((e2=emit_pop(0)),USE_CREG,op,size(type)); if (use) { code_register(e2,USE_CREG); } emit_pop_free(e2); } use=post?0:suse; code_bit_replace(e2=emit_pop(0),USE_CREG,t); emit_pop_free(e2); use = suse; if (post&&use) { g_expr(rvalue_t(n1,type));} if (n1) free_lvar(cadr(n1)); if (n2) free_lvar(cadr(n2)); return type; } #endif /* temporal local variable free list */ static int lvar_list,lvar_free_list; extern int new_lvar0(int sz) { disp-=sz; #if 1 if (sz>=4 && (disp & (4-1))) { disp &= ~(4-1); } #endif return disp; } extern int new_lvar(int size) { int lvar,plvar; for (plvar = 0,lvar = lvar_free_list;lvar;lvar = cadr(lvar)) { if (caddr(lvar)==size) { if (plvar) cadr(plvar) = cadr(lvar); else lvar_free_list = cadr(lvar); break; } plvar = lvar; } if (!lvar) { lvar_list = glist3((lvar=new_lvar0(size)),lvar_list,size); } else { cadr(lvar) = lvar_list; lvar_list = lvar; lvar = car(lvar_list); } return lvar; } extern void free_lvar(int disp) { int lvar,plvar; for (plvar = 0,lvar = lvar_list;lvar;lvar = cadr(lvar)) { if (car(lvar)==disp) { if (plvar) cadr(plvar) = cadr(lvar); else lvar_list = cadr(lvar); break; } plvar = lvar; } if (!lvar) error(-1); cadr(lvar) = lvar_free_list; lvar_free_list = lvar; } extern void init_free_lvar_list() { int lvar; while((lvar=lvar_list)) { lvar_list=cadr(lvar_list); free_glist3(lvar); } while((lvar=lvar_free_list)) { lvar_free_list=cadr(lvar_free_list); free_glist3(lvar); } } extern void gen_comment(char *s) { if (!chk) code_comment(s); } extern void gen_code_enter(char *name) { code_enter(name); } extern void gen_code_enter1(int args) { code_enter1(args); } extern void gen_code_leave(char *name) { code_leave(name); } extern void gen_enter(char *name) { enter(name); } extern void gen_enter1() { enter1(); } extern void gen_leave(int control, char *name) { leave(control,name); } extern void gen_jmp(int l) { control=0; if (!pending_jmp) { pending_jmp = l; } } extern void gen_indirect_goto(int e1) { g_expr(e1); code_indirect_jmp(USE_CREG); } /* make bit mask MSB 1 2 3 4 .... 29 30 31 LSB */ extern int make_mask(int from,int to) { int mask = 0; int bit = 1; int i; if (from<0||from>32) error(-1); for (i=31;from<=i;i--,bit<<=1) { if (i<=to) { mask |= bit; } } return mask; } #define MAX_PTR_CACHE 10 static int ptr_cache=0; static int ptr_cache_last=0; /* global name pointer cache */ extern void init_ptr_cache() { int i; for(i=0;i<MAX_PTR_CACHE-1;i++) { ptr_cache=glist3(0,ptr_cache,0); } ptr_cache_last=ptr_cache; } extern void clear_ptr_cache_reg(int r) { int ptcptr=ptr_cache; int prev = 0; while(ptcptr!=ptr_cache_last) { if(car(ptcptr)&&caddr(ptcptr)==r) { free_register(r); caddr(ptcptr)=-1; car(ptcptr)=0; // remove me if (prev) cadr(prev) = cadr(ptcptr); else ptr_cache = cadr(ptcptr); // add me to the next of ptr_cache_last cadr(ptcptr) = cadr(ptr_cache_last); cadr(ptr_cache_last) = ptcptr; return; } ptcptr=cadr(prev=ptcptr); } } extern int last_ptr_cache() { int ptcptr=ptr_cache; int r = 0; while(ptcptr!=ptr_cache_last) { r = caddr(ptcptr); ptcptr=cadr(ptcptr); } return r; } extern void clear_ptr_cache() { int ptcptr=ptr_cache; while(ptcptr!=ptr_cache_last) { free_register(caddr(ptcptr)); caddr(ptcptr)=-1; car(ptcptr)=0; ptcptr=cadr(ptcptr); } ptr_cache_last = ptr_cache; } extern int get_ptr_cache(NMTBL *nptr) { int r; int ptcptr=ptr_cache; int g = (int)nptr; int prev=0,p; // linear search cache while(ptcptr!=ptr_cache_last) { if(car(ptcptr)==g) { if (prev) { // make this top cadr(prev)=cadr(ptcptr); cadr(ptcptr) = ptr_cache; ptr_cache = ptcptr; } return caddr(ptcptr); } ptcptr=cadr(prev=ptcptr); } if (!cadr(ptr_cache_last)) { // cache is full if (prev) { // remove oldest cache and it becomes the last free_register(caddr(prev)); caddr(ptcptr)=-1; car(ptcptr)=0; ptr_cache_last = prev; } else error(-1); } r = get_register(0); // some ptr cache may remove by this caddr(p = cadr(ptr_cache_last)) = r; car(p) = g; use_ptr_cache(r); cadr(ptr_cache_last) = cadr(p); cadr(p) = ptr_cache; ptr_cache = p; code_ptr_cache_def(r,nptr); return r; } extern int ilog(int i) { /* number of bit (i-1) is better? */ switch(i) { case 2: return 1; case 4: return 2; case 8: return 3; case 16: return 4; case 32: return 5; case 64: return 6; case 128: return 7; case 256: return 8; case 512: return 9; case 1024: return 10; case 2048: return 11; case 4096: return 12; case 8192: return 13; case 16384: return 14; case 32768: return 15; case 65536: return 16; case 131072: return 17; case 262144: return 18; case 524288: return 19; } return 0; } /* end */