From 21c35b94437178b4a9ee50e6688f259a6bcc26da Mon Sep 17 00:00:00 2001 From: bellard Date: Sun, 23 Dec 2001 00:34:26 +0000 Subject: [PATCH] use register classes (will allow better and simpler code gen - fixed long double handling --- i386-gen.c | 212 ++++++++++++++++++++++++++++++------------------------------- tcc.c | 113 ++++++++++++++++++++++---------- 2 files changed, 183 insertions(+), 142 deletions(-) diff --git a/i386-gen.c b/i386-gen.c index 8e7c2f46..11f48984 100644 --- a/i386-gen.c +++ b/i386-gen.c @@ -21,11 +21,11 @@ /* number of available registers */ #define NB_REGS 4 -#define NB_REG_CLASSES 2 - /* a register can belong to several classes */ -#define REG_CLASS_INT 0x0001 -#define REG_CLASS_FLOAT 0x0002 +#define RC_INT 0x0001 /* generic integer register */ +#define RC_FLOAT 0x0002 /* generic float register */ +#define RC_IRET 0x0004 /* function returned integer register */ +#define RC_FRET 0x0008 /* function returned float register */ /* pretty names for the registers */ enum { @@ -36,16 +36,15 @@ enum { }; int reg_classes[NB_REGS] = { - REG_CLASS_INT, /* eax */ - REG_CLASS_INT, /* ecx */ - REG_CLASS_INT, /* edx */ - REG_CLASS_FLOAT, /* st0 */ + /* eax */ RC_INT | RC_IRET, + /* ecx */ RC_INT, + /* edx */ RC_INT, + /* st0 */ RC_FLOAT | RC_FRET, }; -/* integer return register for functions */ -#define FUNC_RET_REG 0 -/* float return register for functions */ -#define FUNC_RET_FREG 3 +/* return registers for function */ +#define REG_IRET REG_EAX +#define REG_FRET REG_ST0 /* defined if function parameters must be evaluated in reverse order */ #define INVERT_FUNC_PARAMS @@ -89,40 +88,17 @@ void gen_le32(int c) g(c >> 24); } -/* add a new relocation entry to symbol 's' */ -void greloc(Sym *s, int addr, int type) -{ - Reloc *p; - p = malloc(sizeof(Reloc)); - if (!p) - error("memory full"); - p->type = type; - p->addr = addr; - p->next = (Reloc *)s->c; - s->c = (int)p; -} - -/* patch each relocation entry with value 'val' */ -void greloc_patch(Sym *s, int val) +/* patch relocation entry with value 'val' */ +void greloc_patch1(Reloc *p, int val) { - Reloc *p, *p1; - - p = (Reloc *)s->c; - while (p != NULL) { - p1 = p->next; - switch(p->type) { - case RELOC_ADDR32: - *(int *)p->addr = val; - break; - case RELOC_REL32: - *(int *)p->addr = val - p->addr - 4; - break; - } - free(p); - p = p1; + switch(p->type) { + case RELOC_ADDR32: + *(int *)p->addr = val; + break; + case RELOC_REL32: + *(int *)p->addr = val - p->addr - 4; + break; } - s->c = val; - s->r &= ~VT_FORWARD; } /* output a symbol and patch all calls to it */ @@ -166,6 +142,30 @@ void gen_addr32(int r, int c) } } +/* generate a modrm reference. 'op_reg' contains the addtionnal 3 + opcode bits */ +void gen_modrm(int op_reg, int r, int c) +{ + op_reg = op_reg << 3; + if ((r & VT_VALMASK) == VT_CONST) { + /* constant memory reference */ + o(0x05 | op_reg); + gen_addr32(r, c); + } else if ((r & VT_VALMASK) == VT_LOCAL) { + /* currently, we use only ebp as base */ + if (c == (char)c) { + /* short reference */ + o(0x45 | op_reg); + g(c); + } else { + oad(0x85 | op_reg, c); + } + } else { + g(0x00 | op_reg | (r & VT_VALMASK)); + } +} + + /* load 'r' from value 'sv' */ void load(int r, SValue *sv) { @@ -183,7 +183,7 @@ void load(int r, SValue *sv) v1.r = VT_LOCAL | VT_LVAL; v1.c.ul = fc; load(r, &v1); - v = r; + fr = r; } if ((ft & VT_BTYPE) == VT_FLOAT) { o(0xd9); /* flds */ @@ -204,22 +204,14 @@ void load(int r, SValue *sv) o(0xb70f); /* movzwl */ else o(0x8b); /* movl */ - - if (v == VT_CONST) { - o(0x05 + r * 8); /* 0xXX, r */ - gen_addr32(fr, fc); - } else if (v == VT_LOCAL) { - oad(0x85 + r * 8, fc); /* xx(%ebp), r */ - } else { - g(0x00 + r * 8 + v); /* (v), r */ - } + gen_modrm(r, fr, fc); } else { if (v == VT_CONST) { o(0xb8 + r); /* mov $xx, r */ gen_addr32(fr, fc); } else if (v == VT_LOCAL) { - o(0x8d); - oad(0x85 + r * 8, fc); /* lea xxx(%ebp), r */ + o(0x8d); /* lea xxx(%ebp), r */ + gen_modrm(r, VT_LOCAL, fc); } else if (v == VT_CMP) { oad(0xb8 + r, 0); /* mov $0, r */ o(0x0f); /* setxx %br */ @@ -247,8 +239,7 @@ void store(int r, SValue *v) fc = v->c.ul; fr = v->r & VT_VALMASK; bt = ft & VT_BTYPE; - /* XXX: incorrect if reg to reg */ - /* XXX: should not flush float stack */ + /* XXX: incorrect if float reg to reg */ if (bt == VT_FLOAT) { o(0xd9); /* fsts */ r = 2; @@ -267,13 +258,10 @@ void store(int r, SValue *v) else o(0x89); } - if (fr == VT_CONST) { - o(0x05 + r * 8); /* mov r,xxx */ - gen_addr32(v->r, fc); - } else if (fr == VT_LOCAL) { - oad(0x85 + r * 8, fc); /* mov r,xxx(%ebp) */ - } else if (v->r & VT_LVAL) { - g(fr + r * 8); /* mov r, (fr) */ + if (fr == VT_CONST || + fr == VT_LOCAL || + (v->r & VT_LVAL)) { + gen_modrm(r, v->r, fc); } else if (fr != r) { o(0xc0 + fr + r * 8); /* mov r, fr */ } @@ -298,7 +286,7 @@ void gfunc_param(GFuncContext *c) /* allocate the necessary size on stack */ oad(0xec81, size); /* sub $xxx, %esp */ /* generate structure store */ - r = get_reg(REG_CLASS_INT); + r = get_reg(RC_INT); o(0x89); /* mov %esp, r */ o(0xe0 + r); vset(VT_INT, r, 0); @@ -306,7 +294,7 @@ void gfunc_param(GFuncContext *c) vstore(); c->args_size += size; } else if (is_float(vtop->t)) { - gv(); /* only one float register */ + gv(RC_FLOAT); /* only one float register */ if ((vtop->t & VT_BTYPE) == VT_FLOAT) size = 4; else if ((vtop->t & VT_BTYPE) == VT_DOUBLE) @@ -324,7 +312,7 @@ void gfunc_param(GFuncContext *c) } else { /* simple type (currently always same size) */ /* XXX: implicit cast ? */ - r = gv(); + r = gv(RC_INT); o(0x50 + r); /* push r */ c->args_size += 4; } @@ -347,7 +335,7 @@ void gfunc_call(GFuncContext *c) } } else { /* otherwise, indirect call */ - r = gv(); + r = gv(RC_INT); o(0xff); /* call *r */ o(0xd0 + r); } @@ -383,17 +371,22 @@ int gtst(int inv, int t) t = gjmp(t); gsym(vtop->c.i); } - } else if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) { - /* constant jmp optimization */ - if ((vtop->c.i != 0) != inv) - t = gjmp(t); } else { - /* XXX: floats */ - v = gv(); - o(0x85); - o(0xc0 + v * 9); - g(0x0f); - t = psym(0x85 ^ inv, t); + if (is_float(vtop->t)) { + vpushi(0); + gen_op(TOK_NE); + } + if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_FORWARD)) == VT_CONST) { + /* constant jmp optimization */ + if ((vtop->c.i != 0) != inv) + t = gjmp(t); + } else { + v = gv(RC_INT); + o(0x85); + o(0xc0 + v * 9); + g(0x0f); + t = psym(0x85 ^ inv, t); + } } vtop--; return t; @@ -405,9 +398,9 @@ void gen_opi(int op) int t, r, fr; vswap(); - r = gv(); + r = gv(RC_INT); vswap(); - fr = gv(); + fr = gv(RC_INT); vtop--; if (op == '+') { @@ -460,9 +453,9 @@ void gen_opi(int op) oad(0xbd, t); } if (op == '%' | op == TOK_UMOD) - r = 2; + r = REG_EDX; else - r = 0; + r = REG_EAX; vtop->r = r; } else { vtop--; @@ -477,22 +470,22 @@ void gen_opi(int op) /* NOTE: currently floats can only be lvalues */ void gen_opf(int op) { - int a, ft, fc, swapped, r; + int a, ft, fc, swapped; /* convert constants to memory references */ if ((vtop[-1].r & (VT_CONST | VT_LVAL)) == VT_CONST) { vswap(); - gv(); + gv(RC_FLOAT); vswap(); } if ((vtop[0].r & (VT_CONST | VT_LVAL)) == VT_CONST) - gv(); + gv(RC_FLOAT); /* must put at least one value in the floating point register */ if ((vtop[-1].r & VT_LVAL) && (vtop[0].r & VT_LVAL)) { vswap(); - gv(); + gv(RC_FLOAT); vswap(); } if (op >= TOK_EQ && op <= TOK_GT) { @@ -520,13 +513,18 @@ void gen_opf(int op) vtop->r = VT_CMP; vtop->c.i = op; } else { + swapped = 0; /* swap the stack if needed so that t1 is the register and t2 is the memory reference */ - swapped = 0; if (vtop[-1].r & VT_LVAL) { vswap(); swapped = 1; } + /* no memory reference possible for long double operations */ + if ((vtop->t & VT_BTYPE) == VT_LDOUBLE) { + load(REG_ST0, vtop); + swapped = !swapped; + } switch(op) { default: @@ -534,34 +532,30 @@ void gen_opf(int op) a = 0; break; case '-': - a = 0x20; + a = 4; if (swapped) - a += 8; + a++; break; case '*': - a = 0x08; + a = 1; break; case '/': - a = 0x30; + a = 6; if (swapped) - a += 8; + a++; break; } ft = vtop->t; fc = vtop->c.ul; - if ((ft & VT_BTYPE) == VT_DOUBLE) - o(0xdc); - else - o(0xd8); - - r = vtop->r & VT_VALMASK; - if (r == VT_CONST) { - o(0x05 + a); - gen_addr32(vtop->r, fc); - } else if (r == VT_LOCAL) { - oad(0x85 + a, fc); + if ((ft & VT_BTYPE) == VT_LDOUBLE) { + o(0xde); /* fxxxp %st, %st(1) */ + o(0xc1 + (a << 3)); } else { - g(0x00 + a + r); + if ((ft & VT_BTYPE) == VT_DOUBLE) + o(0xdc); + else + o(0xd8); + gen_modrm(a, vtop->r, fc); } vtop--; } @@ -570,7 +564,7 @@ void gen_opf(int op) /* convert integers to fp 't' type */ void gen_cvt_itof(int t) { - gv(); + gv(RC_INT); if ((vtop->t & (VT_BTYPE | VT_UNSIGNED)) == (VT_INT | VT_UNSIGNED)) { /* unsigned int to float/double/long double */ o(0x6a); /* push $0 */ @@ -599,7 +593,7 @@ void gen_cvt_ftoi(int t) { int r, size; - gv(); + gv(RC_FLOAT); if (t == VT_INT | VT_UNSIGNED && t == VT_LLONG | VT_UNSIGNED && t == VT_LLONG) @@ -607,7 +601,7 @@ void gen_cvt_ftoi(int t) else size = 4; - r = get_reg(REG_CLASS_INT); + r = get_reg(RC_INT); oad(0x2dd9, (int)&__tcc_int_fpu_control); /* ldcw xxx */ oad(0xec81, size); /* sub $xxx, %esp */ if (size == 4) @@ -626,7 +620,7 @@ void gen_cvt_ftoi(int t) void gen_cvt_ftof(int t) { /* all we have to do on i386 is to put the float in a register */ - gv(); + gv(RC_FLOAT); } /* pop stack value */ diff --git a/tcc.c b/tcc.c index 855e8e9e..23e34f33 100644 --- a/tcc.c +++ b/tcc.c @@ -340,7 +340,7 @@ void gexpr(void); void decl(int l); void decl_initializer(int t, int r, int c, int first, int size_only); int decl_initializer_alloc(int t, int sec, int has_init); -int gv(void); +int gv(int rc); void move_reg(int r, int s); void save_reg(int r); void vpop(void); @@ -360,7 +360,9 @@ int pointed_size(int t); int parse_btype(int *type_ptr); int type_decl(int *v, int t, int td); void error(const char *fmt, ...); +void vpushi(int v); void vset(int t, int r, int v); +void greloc(Sym *s, int addr, int type); /* true if float/double/long double type */ static inline int is_float(int t) @@ -419,6 +421,36 @@ void *dlsym(void *handle, char *symbol) #endif +/* add a new relocation entry to symbol 's' */ +void greloc(Sym *s, int addr, int type) +{ + Reloc *p; + p = malloc(sizeof(Reloc)); + if (!p) + error("memory full"); + p->type = type; + p->addr = addr; + p->next = (Reloc *)s->c; + s->c = (int)p; +} + +/* patch each relocation entry with value 'val' */ +void greloc_patch(Sym *s, int val) +{ + Reloc *p, *p1; + + p = (Reloc *)s->c; + while (p != NULL) { + p1 = p->next; + greloc_patch1(p, val); + free(p); + p = p1; + } + s->c = val; + s->r &= ~VT_FORWARD; +} + + static inline int isid(int c) { return (c >= 'a' && c <= 'z') || @@ -1884,7 +1916,7 @@ void vsetc(int t, int r, CValue *vc) /* cannot let cpu flags if other instruction are generated */ /* XXX: VT_JMP test too ? */ if ((vtop->r & VT_VALMASK) == VT_CMP) - gv(); + gv(RC_INT); vtop++; vtop->t = t; vtop->r = r; @@ -2028,12 +2060,12 @@ void move_reg(int r, int s) } } -/* convert a (vtop->t, vtop->c) in register. lvalues are converted as - values. Cannot be used if cannot be converted to register value - (such as structures). */ -int gv(void) +/* store vtop a register belonging to class 'rc'. lvalues are + converted as values. Cannot be used if cannot be converted to + register value (such as structures). */ +int gv(int rc) { - int r, bit_pos, bit_size, rc, size, align, i; + int r, bit_pos, bit_size, size, align, i; /* NOTE: get_reg can modify vstack[] */ if (vtop->t & VT_BITFIELD) { @@ -2047,7 +2079,7 @@ int gv(void) vpushi(32 - bit_size); /* NOTE: transformed to SHR if unsigned */ gen_op(TOK_SAR); - r = gv(); + r = gv(rc); } else { if (is_float(vtop->t) && (vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) { @@ -2064,14 +2096,16 @@ int gv(void) glo += size << 2; } r = vtop->r & VT_VALMASK; - if (r >= VT_CONST || (vtop->r & VT_LVAL)) { - if (is_float(vtop->t)) - rc = REG_CLASS_FLOAT; - else - rc = REG_CLASS_INT; + /* need to reload if: + - constant + - lvalue (need to dereference pointer) + - already a register, but not in the right class */ + if (r >= VT_CONST || + (vtop->r & VT_LVAL) || + !(reg_classes[r] & rc)) { r = get_reg(rc); + load(r, vtop); } - load(r, vtop); vtop->r = r; } return r; @@ -2620,7 +2654,7 @@ void gen_assign_cast(int dt) /* store vtop in lvalue pushed on stack */ void vstore(void) { - int ft, r, t, size, align, bit_size, bit_pos; + int ft, r, t, size, align, bit_size, bit_pos, rc; GFuncContext gf; ft = vtop[-1].t; @@ -2675,11 +2709,14 @@ void vstore(void) /* store result */ vstore(); } else { - r = gv(); /* generate value */ + rc = RC_INT; + if (is_float(ft)) + rc = RC_FLOAT; + r = gv(rc); /* generate value (XXX: move that to store code) */ /* if lvalue was saved on stack, must read it */ if ((vtop[-1].r & VT_VALMASK) == VT_LLOCAL) { SValue sv; - t = get_reg(REG_CLASS_INT); + t = get_reg(RC_INT); sv.t = VT_INT; sv.r = VT_LOCAL | VT_LVAL; sv.c.ul = vtop[-1].c.ul; @@ -2696,19 +2733,24 @@ void vstore(void) /* post defines POST/PRE add. c is the token ++ or -- */ void inc(int post, int c) { - int r, r1; + int r, r1, rc, t; SValue sv; test_lvalue(); if (post) vdup(); /* room for returned value */ vdup(); /* save lvalue */ - r = gv(); if (post) { /* duplicate value */ - /* XXX: handle floats */ - r1 = get_reg(REG_CLASS_INT); + rc = RC_INT; sv.t = VT_INT; + t = vtop->t & VT_TYPE; + if (is_float(t)) { + rc = RC_FLOAT; + sv.t = t; + } + r = gv(rc); + r1 = get_reg(rc); sv.r = r; sv.c.ul = 0; load(r1, &sv); /* move r to r1 */ @@ -3137,10 +3179,10 @@ Sym *external_sym(int v, int u, int r) void indir(void) { - if (vtop->r & VT_LVAL) - gv(); if ((vtop->t & VT_BTYPE) != VT_PTR) expect("pointer"); + if (vtop->r & VT_LVAL) + gv(RC_INT); vtop->t = pointed_type(vtop->t); if (!(vtop->t & VT_ARRAY)) /* an array is never an lvalue */ vtop->r |= VT_LVAL; @@ -3442,7 +3484,11 @@ void unary(void) gfunc_param(&gf); } else { ret.t = s->t; - ret.r = FUNC_RET_REG; /* return in register */ + /* return in register */ + if (is_float(ret.t)) + ret.r = REG_FRET; + else + ret.r = REG_IRET; ret.c.i = 0; } #ifndef INVERT_FUNC_PARAMS @@ -3561,7 +3607,7 @@ void eor(void) /* XXX: better constant handling */ void expr_eq(void) { - int t, u, c, r1, r2; + int t, u, c, r1, r2, rc; if (const_wanted) { sum(10); @@ -3582,16 +3628,19 @@ void expr_eq(void) if (tok == '?') { next(); t = gtst(1, 0); - gexpr(); - r1 = gv(); + /* XXX: float handling ? */ + rc = RC_INT; + if (is_float(vtop->t)) + rc = RC_FLOAT; + r1 = gv(rc); vpop(); skip(':'); u = gjmp(0); gsym(t); expr_eq(); - r2 = gv(); + r2 = gv(rc); move_reg(r1, r2); vtop->r = r1; gsym(u); @@ -3718,11 +3767,9 @@ void block(int *bsym, int *csym, int *case_sym, int *def_sym, int case_reg) /* copy structure value to pointer */ vstore(); } else if (is_float(func_vt)) { - /* move return value to float return register */ - move_reg(FUNC_RET_FREG, gv()); + gv(RC_FRET); } else { - /* move return value to standard return register */ - move_reg(FUNC_RET_REG, gv()); + gv(RC_IRET); } vpop(); } @@ -3794,7 +3841,7 @@ void block(int *bsym, int *csym, int *case_sym, int *def_sym, int case_reg) next(); skip('('); gexpr(); - case_reg = gv(); + case_reg = gv(RC_INT); vpop(); skip(')'); a = 0; -- 2.11.4.GIT