/* * X86 code generator for TCC * * Copyright (c) 2001-2004 Fabrice Bellard * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef TARGET_DEFS_ONLY /* number of available registers */ #define NB_REGS 5 #define NB_ASM_REGS 8 #define CONFIG_TCC_ASM /* a register can belong to several classes. The classes must be sorted from more general to more precise (see gv2() code which does assumptions on it). */ #define RC_INT 0x0001 /* generic integer register */ #define RC_FLOAT 0x0002 /* generic float register */ #define RC_EAX 0x0004 #define RC_ST0 0x0008 #define RC_ECX 0x0010 #define RC_EDX 0x0020 #define RC_EBX 0x0040 #define RC_IRET RC_EAX /* function return: integer register */ #define RC_IRE2 RC_EDX /* function return: second integer register */ #define RC_FRET RC_ST0 /* function return: float register */ /* pretty names for the registers */ enum { TREG_EAX = 0, TREG_ECX, TREG_EDX, TREG_EBX, TREG_ST0, TREG_ESP = 4 }; /* return registers for function */ #define REG_IRET TREG_EAX /* single word int return register */ #define REG_IRE2 TREG_EDX /* second word return register (for long long) */ #define REG_FRET TREG_ST0 /* float return register */ /* defined if function parameters must be evaluated in reverse order */ #define INVERT_FUNC_PARAMS /* defined if structures are passed as pointers. Otherwise structures are directly pushed on stack. */ /* #define FUNC_STRUCT_PARAM_AS_PTR */ /* pointer size, in bytes */ #define PTR_SIZE 4 /* long double size and alignment, in bytes */ #define LDOUBLE_SIZE 12 #define LDOUBLE_ALIGN 4 /* maximum alignment (for aligned attribute support) */ #define MAX_ALIGN 8 /* define if return values need to be extended explicitely at caller side (for interfacing with non-TCC compilers) */ #define PROMOTE_RET /******************************************************/ #else /* ! TARGET_DEFS_ONLY */ /******************************************************/ #define USING_GLOBALS #include "tcc.h" ST_DATA const char * const target_machine_defs = "__i386__\0" "__i386\0" ; /* define to 1/0 to [not] have EBX as 4th register */ #define USE_EBX 0 ST_DATA const int reg_classes[NB_REGS] = { /* eax */ RC_INT | RC_EAX, /* ecx */ RC_INT | RC_ECX, /* edx */ RC_INT | RC_EDX, /* ebx */ (RC_INT | RC_EBX) * USE_EBX, /* st0 */ RC_FLOAT | RC_ST0, }; static unsigned long func_sub_sp_offset; static int func_ret_sub; #ifdef CONFIG_TCC_BCHECK static void gen_bounds_prolog(TCCState* S); static void gen_bounds_epilog(TCCState* S); #endif /* XXX: make it faster ? */ ST_FUNC void g(TCCState* S, int c) { int ind1; if (S->tccgen_nocode_wanted) return; ind1 = S->tccgen_ind + 1; if (ind1 > cur_text_section->data_allocated) section_realloc(S, cur_text_section, ind1); cur_text_section->data[S->tccgen_ind] = c; S->tccgen_ind = ind1; } ST_FUNC void o(TCCState* S, unsigned int c) { while (c) { g(S, c); c = c >> 8; } } ST_FUNC void gen_le16(TCCState* S, int v) { g(S, v); g(S, v >> 8); } ST_FUNC void gen_le32(TCCState* S, int c) { g(S, c); g(S, c >> 8); g(S, c >> 16); g(S, c >> 24); } /* output a symbol and patch all calls to it */ ST_FUNC void gsym_addr(TCCState* S, int t, int a) { while (t) { unsigned char *ptr = cur_text_section->data + t; uint32_t n = read32le(ptr); /* next value */ write32le(ptr, a - t - 4); t = n; } } /* instruction + 4 bytes data. Return the address of the data */ static int oad(TCCState* S, int c, int s) { int t; if (S->tccgen_nocode_wanted) return s; o(S, c); t = S->tccgen_ind; gen_le32(S, s); return t; } ST_FUNC void gen_fill_nops(TCCState* S, int bytes) { while (bytes--) g(S, 0x90); } /* generate jmp to a label */ #define gjmp2(s, instr,lbl) oad(s, instr,lbl) /* output constant with relocation if 'r & VT_SYM' is true */ ST_FUNC void gen_addr32(TCCState* S, int r, Sym *sym, int c) { if (r & VT_SYM) greloc(S, cur_text_section, sym, S->tccgen_ind, R_386_32); gen_le32(S, c); } ST_FUNC void gen_addrpc32(TCCState* S, int r, Sym *sym, int c) { if (r & VT_SYM) greloc(S, cur_text_section, sym, S->tccgen_ind, R_386_PC32); gen_le32(S, c - 4); } /* generate a modrm reference. 'op_reg' contains the additional 3 opcode bits */ static void gen_modrm(TCCState* S, int op_reg, int r, Sym *sym, int c) { op_reg = op_reg << 3; if ((r & VT_VALMASK) == VT_CONST) { /* constant memory reference */ o(S, 0x05 | op_reg); gen_addr32(S, r, sym, c); } else if ((r & VT_VALMASK) == VT_LOCAL) { /* currently, we use only ebp as base */ if (c == (char)c) { /* short reference */ o(S, 0x45 | op_reg); g(S, c); } else { oad(S, 0x85 | op_reg, c); } } else { g(S, 0x00 | op_reg | (r & VT_VALMASK)); } } /* load 'r' from value 'sv' */ ST_FUNC void load(TCCState* S, int r, SValue *sv) { int v, t, ft, fc, fr; SValue v1; #ifdef TCC_TARGET_PE SValue v2; sv = pe_getimport(S, sv, &v2); #endif fr = sv->r; ft = sv->type.t & ~VT_DEFSIGN; fc = sv->c.i; ft &= ~(VT_VOLATILE | VT_CONSTANT); v = fr & VT_VALMASK; if (fr & VT_LVAL) { if (v == VT_LLOCAL) { v1.type.t = VT_INT; v1.r = VT_LOCAL | VT_LVAL; v1.c.i = fc; v1.sym = NULL; fr = r; if (!(reg_classes[fr] & RC_INT)) fr = get_reg(S, RC_INT); load(S, fr, &v1); } if ((ft & VT_BTYPE) == VT_FLOAT) { o(S, 0xd9); /* flds */ r = 0; } else if ((ft & VT_BTYPE) == VT_DOUBLE) { o(S, 0xdd); /* fldl */ r = 0; } else if ((ft & VT_BTYPE) == VT_LDOUBLE) { o(S, 0xdb); /* fldt */ r = 5; } else if ((ft & VT_TYPE) == VT_BYTE || (ft & VT_TYPE) == VT_BOOL) { o(S, 0xbe0f); /* movsbl */ } else if ((ft & VT_TYPE) == (VT_BYTE | VT_UNSIGNED)) { o(S, 0xb60f); /* movzbl */ } else if ((ft & VT_TYPE) == VT_SHORT) { o(S, 0xbf0f); /* movswl */ } else if ((ft & VT_TYPE) == (VT_SHORT | VT_UNSIGNED)) { o(S, 0xb70f); /* movzwl */ } else { o(S, 0x8b); /* movl */ } gen_modrm(S, r, fr, sv->sym, fc); } else { if (v == VT_CONST) { o(S, 0xb8 + r); /* mov $xx, r */ gen_addr32(S, fr, sv->sym, fc); } else if (v == VT_LOCAL) { if (fc) { o(S, 0x8d); /* lea xxx(%ebp), r */ gen_modrm(S, r, VT_LOCAL, sv->sym, fc); } else { o(S, 0x89); o(S, 0xe8 + r); /* mov %ebp, r */ } } else if (v == VT_CMP) { o(S, 0x0f); /* setxx %br */ o(S, fc); o(S, 0xc0 + r); o(S, 0xc0b60f + r * 0x90000); /* movzbl %al, %eax */ } else if (v == VT_JMP || v == VT_JMPI) { t = v & 1; oad(S, 0xb8 + r, t); /* mov $1, r */ o(S, 0x05eb); /* jmp after */ gsym(S, fc); oad(S, 0xb8 + r, t ^ 1); /* mov $0, r */ } else if (v != r) { o(S, 0x89); o(S, 0xc0 + r + v * 8); /* mov v, r */ } } } /* store register 'r' in lvalue 'v' */ ST_FUNC void store(TCCState* S, int r, SValue *v) { int fr, bt, ft, fc; #ifdef TCC_TARGET_PE SValue v2; v = pe_getimport(S, v, &v2); #endif ft = v->type.t; fc = v->c.i; fr = v->r & VT_VALMASK; ft &= ~(VT_VOLATILE | VT_CONSTANT); bt = ft & VT_BTYPE; /* XXX: incorrect if float reg to reg */ if (bt == VT_FLOAT) { o(S, 0xd9); /* fsts */ r = 2; } else if (bt == VT_DOUBLE) { o(S, 0xdd); /* fstpl */ r = 2; } else if (bt == VT_LDOUBLE) { o(S, 0xc0d9); /* fld %st(0) */ o(S, 0xdb); /* fstpt */ r = 7; } else { if (bt == VT_SHORT) o(S, 0x66); if (bt == VT_BYTE || bt == VT_BOOL) o(S, 0x88); else o(S, 0x89); } if (fr == VT_CONST || fr == VT_LOCAL || (v->r & VT_LVAL)) { gen_modrm(S, r, v->r, v->sym, fc); } else if (fr != r) { o(S, 0xc0 + fr + r * 8); /* mov r, fr */ } } static void gadd_sp(TCCState* S, int val) { if (val == (char)val) { o(S, 0xc483); g(S, val); } else { oad(S, 0xc481, val); /* add $xxx, %esp */ } } #if defined CONFIG_TCC_BCHECK || defined TCC_TARGET_PE static void gen_static_call(TCCState* S, int v) { Sym *sym; sym = external_helper_sym(S, v); oad(S, 0xe8, -4); greloc(S, cur_text_section, sym, S->tccgen_ind-4, R_386_PC32); } #endif /* 'is_jmp' is '1' if it is a jump */ static void gcall_or_jmp(TCCState* S, int is_jmp) { int r; if ((S->tccgen_vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST && (S->tccgen_vtop->r & VT_SYM)) { /* constant and relocation case */ greloc(S, cur_text_section, S->tccgen_vtop->sym, S->tccgen_ind + 1, R_386_PC32); oad(S, 0xe8 + is_jmp, S->tccgen_vtop->c.i - 4); /* call/jmp im */ } else { /* otherwise, indirect call */ r = gv(S, RC_INT); o(S, 0xff); /* call/jmp *r */ o(S, 0xd0 + r + (is_jmp << 4)); } } static const uint8_t fastcall_regs[3] = { TREG_EAX, TREG_EDX, TREG_ECX }; static const uint8_t fastcallw_regs[2] = { TREG_ECX, TREG_EDX }; /* Return the number of registers needed to return the struct, or 0 if returning via struct pointer. */ ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int *regsize) { #if defined(TCC_TARGET_PE) || TARGETOS_FreeBSD || TARGETOS_OpenBSD int size, align; *ret_align = 1; // Never have to re-align return values for x86 *regsize = 4; size = type_size(vt, &align); if (size > 8 || (size & (size - 1))) return 0; if (size == 8) ret->t = VT_LLONG; else if (size == 4) ret->t = VT_INT; else if (size == 2) ret->t = VT_SHORT; else ret->t = VT_BYTE; ret->ref = NULL; return 1; #else *ret_align = 1; // Never have to re-align return values for x86 return 0; #endif } /* Generate function call. The function address is pushed first, then all the parameters in call order. This functions pops all the parameters and the function address. */ ST_FUNC void gfunc_call(TCCState* S, int nb_args) { int size, align, r, args_size, i, func_call; Sym *func_sym; #ifdef CONFIG_TCC_BCHECK if (S->do_bounds_check) gbound_args(S, nb_args); #endif args_size = 0; for(i = 0;i < nb_args; i++) { if ((S->tccgen_vtop->type.t & VT_BTYPE) == VT_STRUCT) { size = type_size(&S->tccgen_vtop->type, &align); /* align to stack align size */ size = (size + 3) & ~3; /* allocate the necessary size on stack */ #ifdef TCC_TARGET_PE if (size >= 4096) { r = get_reg(S, RC_EAX); oad(S, 0x68, size); // push size /* cannot call normal 'alloca' with bound checking */ gen_static_call(S, tok_alloc_const(S, "__alloca")); gadd_sp(S, 4); } else #endif { oad(S, 0xec81, size); /* sub $xxx, %esp */ /* generate structure store */ r = get_reg(S, RC_INT); o(S, 0xe089 + (r << 8)); /* mov %esp, r */ } vset(S, &S->tccgen_vtop->type, r | VT_LVAL, 0); vswap(S); vstore(S); args_size += size; } else if (is_float(S->tccgen_vtop->type.t)) { gv(S, RC_FLOAT); /* only one float register */ if ((S->tccgen_vtop->type.t & VT_BTYPE) == VT_FLOAT) size = 4; else if ((S->tccgen_vtop->type.t & VT_BTYPE) == VT_DOUBLE) size = 8; else size = 12; oad(S, 0xec81, size); /* sub $xxx, %esp */ if (size == 12) o(S, 0x7cdb); else o(S, 0x5cd9 + size - 4); /* fstp[s|l] 0(%esp) */ g(S, 0x24); g(S, 0x00); args_size += size; } else { /* simple type (currently always same size) */ /* XXX: implicit cast ? */ r = gv(S, RC_INT); if ((S->tccgen_vtop->type.t & VT_BTYPE) == VT_LLONG) { size = 8; o(S, 0x50 + S->tccgen_vtop->r2); /* push r */ } else { size = 4; } o(S, 0x50 + r); /* push r */ args_size += size; } S->tccgen_vtop--; } save_regs(S, 0); /* save used temporary registers */ func_sym = S->tccgen_vtop->type.ref; func_call = func_sym->f.func_call; /* fast call case */ if ((func_call >= FUNC_FASTCALL1 && func_call <= FUNC_FASTCALL3) || func_call == FUNC_FASTCALLW) { int fastcall_nb_regs; const uint8_t *fastcall_regs_ptr; if (func_call == FUNC_FASTCALLW) { fastcall_regs_ptr = fastcallw_regs; fastcall_nb_regs = 2; } else { fastcall_regs_ptr = fastcall_regs; fastcall_nb_regs = func_call - FUNC_FASTCALL1 + 1; } for(i = 0;i < fastcall_nb_regs; i++) { if (args_size <= 0) break; o(S, 0x58 + fastcall_regs_ptr[i]); /* pop r */ /* XXX: incorrect for struct/floats */ args_size -= 4; } } #if !defined(TCC_TARGET_PE) && !TARGETOS_FreeBSD || TARGETOS_OpenBSD else if ((S->tccgen_vtop->type.ref->type.t & VT_BTYPE) == VT_STRUCT) args_size -= 4; #endif gcall_or_jmp(S, 0); if (args_size && func_call != FUNC_STDCALL && func_call != FUNC_FASTCALLW) gadd_sp(S, args_size); S->tccgen_vtop--; } #ifdef TCC_TARGET_PE #define FUNC_PROLOG_SIZE (10 + USE_EBX) #else #define FUNC_PROLOG_SIZE (9 + USE_EBX) #endif /* generate function prolog of type 't' */ ST_FUNC void gfunc_prolog(TCCState* S, Sym *func_sym) { CType *func_type = &func_sym->type; int addr, align, size, func_call, fastcall_nb_regs; int param_index, param_addr; const uint8_t *fastcall_regs_ptr; Sym *sym; CType *type; sym = func_type->ref; func_call = sym->f.func_call; addr = 8; S->tccgen_loc = 0; S->tccgen_func_vc = 0; if (func_call >= FUNC_FASTCALL1 && func_call <= FUNC_FASTCALL3) { fastcall_nb_regs = func_call - FUNC_FASTCALL1 + 1; fastcall_regs_ptr = fastcall_regs; } else if (func_call == FUNC_FASTCALLW) { fastcall_nb_regs = 2; fastcall_regs_ptr = fastcallw_regs; } else { fastcall_nb_regs = 0; fastcall_regs_ptr = NULL; } param_index = 0; S->tccgen_ind += FUNC_PROLOG_SIZE; func_sub_sp_offset = S->tccgen_ind; /* if the function returns a structure, then add an implicit pointer parameter */ #if defined(TCC_TARGET_PE) || TARGETOS_FreeBSD || TARGETOS_OpenBSD size = type_size(&S->tccgen_func_vt,&align); if (((S->tccgen_func_vt.t & VT_BTYPE) == VT_STRUCT) && (size > 8 || (size & (size - 1)))) { #else if ((S->tccgen_func_vt.t & VT_BTYPE) == VT_STRUCT) { #endif /* XXX: fastcall case ? */ S->tccgen_func_vc = addr; addr += 4; param_index++; } /* define parameters */ while ((sym = sym->next) != NULL) { type = &sym->type; size = type_size(type, &align); size = (size + 3) & ~3; #ifdef FUNC_STRUCT_PARAM_AS_PTR /* structs are passed as pointer */ if ((type->t & VT_BTYPE) == VT_STRUCT) { size = 4; } #endif if (param_index < fastcall_nb_regs) { /* save FASTCALL register */ S->tccgen_loc -= 4; o(S, 0x89); /* movl */ gen_modrm(S, fastcall_regs_ptr[param_index], VT_LOCAL, NULL, S->tccgen_loc); param_addr = S->tccgen_loc; } else { param_addr = addr; addr += size; } sym_push(S, sym->v & ~SYM_FIELD, type, VT_LOCAL | VT_LVAL, param_addr); param_index++; } func_ret_sub = 0; /* pascal type call or fastcall ? */ if (func_call == FUNC_STDCALL || func_call == FUNC_FASTCALLW) func_ret_sub = addr - 8; #if !defined(TCC_TARGET_PE) && !TARGETOS_FreeBSD || TARGETOS_OpenBSD else if (S->tccgen_func_vc) func_ret_sub = 4; #endif #ifdef CONFIG_TCC_BCHECK if (S->do_bounds_check) gen_bounds_prolog(S); #endif } /* generate function epilog */ ST_FUNC void gfunc_epilog(TCCState* S) { addr_t v, saved_ind; #ifdef CONFIG_TCC_BCHECK if (S->do_bounds_check) gen_bounds_epilog(S); #endif /* align local size to word & save local variables */ v = (-S->tccgen_loc + 3) & -4; #if USE_EBX o(S, 0x8b); gen_modrm(S, TREG_EBX, VT_LOCAL, NULL, -(v+4)); #endif o(S, 0xc9); /* leave */ if (func_ret_sub == 0) { o(S, 0xc3); /* ret */ } else { o(S, 0xc2); /* ret n */ g(S, func_ret_sub); g(S, func_ret_sub >> 8); } saved_ind = S->tccgen_ind; S->tccgen_ind = func_sub_sp_offset - FUNC_PROLOG_SIZE; #ifdef TCC_TARGET_PE if (v >= 4096) { oad(S, 0xb8, v); /* mov stacksize, %eax */ gen_static_call(S, TOK___chkstk); /* call __chkstk, (does the stackframe too) */ } else #endif { o(S, 0xe58955); /* push %ebp, mov %esp, %ebp */ o(S, 0xec81); /* sub esp, stacksize */ gen_le32(S, v); #ifdef TCC_TARGET_PE o(S, 0x90); /* adjust to FUNC_PROLOG_SIZE */ #endif } o(S, 0x53 * USE_EBX); /* push ebx */ S->tccgen_ind = saved_ind; } /* generate a jump to a label */ ST_FUNC int gjmp(TCCState* S, int t) { return gjmp2(S, 0xe9, t); } /* generate a jump to a fixed address */ ST_FUNC void gjmp_addr(TCCState* S, int a) { int r; r = a - S->tccgen_ind - 2; if (r == (char)r) { g(S, 0xeb); g(S, r); } else { oad(S, 0xe9, a - S->tccgen_ind - 5); } } #if 0 /* generate a jump to a fixed address */ ST_FUNC void gjmp_cond_addr(TCCState* S, int a, int op) { int r = a - S->tccgen_ind - 2; if (r == (char)r) g(S, op - 32), g(r); else g(S, 0x0f), gjmp2(S, op - 16, r - 4); } #endif ST_FUNC int gjmp_append(TCCState* S, int n, int t) { void *p; /* insert vtop->c jump list in t */ if (n) { uint32_t n1 = n, n2; while ((n2 = read32le(p = cur_text_section->data + n1))) n1 = n2; write32le(p, t); t = n; } return t; } ST_FUNC int gjmp_cond(TCCState* S, int op, int t) { g(S, 0x0f); t = gjmp2(S, op - 16, t); return t; } ST_FUNC void gen_opi(TCCState* S, int op) { int r, fr, opc, c; switch(op) { case '+': case TOK_ADDC1: /* add with carry generation */ opc = 0; gen_op8: if ((S->tccgen_vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { /* constant case */ vswap(S); r = gv(S, RC_INT); vswap(S); c = S->tccgen_vtop->c.i; if (c == (char)c) { /* generate inc and dec for smaller code */ if ((c == 1 || c == -1) && (op == '+' || op == '-')) { opc = (c == 1) ^ (op == '+'); o(S, 0x40 | (opc << 3) | r); // inc,dec } else { o(S, 0x83); o(S, 0xc0 | (opc << 3) | r); g(S, c); } } else { o(S, 0x81); oad(S, 0xc0 | (opc << 3) | r, c); } } else { gv2(S, RC_INT, RC_INT); r = S->tccgen_vtop[-1].r; fr = S->tccgen_vtop[0].r; o(S, (opc << 3) | 0x01); o(S, 0xc0 + r + fr * 8); } S->tccgen_vtop--; if (op >= TOK_ULT && op <= TOK_GT) vset_VT_CMP(S, op); break; case '-': case TOK_SUBC1: /* sub with carry generation */ opc = 5; goto gen_op8; case TOK_ADDC2: /* add with carry use */ opc = 2; goto gen_op8; case TOK_SUBC2: /* sub with carry use */ opc = 3; goto gen_op8; case '&': opc = 4; goto gen_op8; case '^': opc = 6; goto gen_op8; case '|': opc = 1; goto gen_op8; case '*': gv2(S, RC_INT, RC_INT); r = S->tccgen_vtop[-1].r; fr = S->tccgen_vtop[0].r; S->tccgen_vtop--; o(S, 0xaf0f); /* imul fr, r */ o(S, 0xc0 + fr + r * 8); break; case TOK_SHL: opc = 4; goto gen_shift; case TOK_SHR: opc = 5; goto gen_shift; case TOK_SAR: opc = 7; gen_shift: opc = 0xc0 | (opc << 3); if ((S->tccgen_vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { /* constant case */ vswap(S); r = gv(S, RC_INT); vswap(S); c = S->tccgen_vtop->c.i & 0x1f; o(S, 0xc1); /* shl/shr/sar $xxx, r */ o(S, opc | r); g(S, c); } else { /* we generate the shift in ecx */ gv2(S, RC_INT, RC_ECX); r = S->tccgen_vtop[-1].r; o(S, 0xd3); /* shl/shr/sar %cl, r */ o(S, opc | r); } S->tccgen_vtop--; break; case '/': case TOK_UDIV: case TOK_PDIV: case '%': case TOK_UMOD: case TOK_UMULL: /* first operand must be in eax */ /* XXX: need better constraint for second operand */ gv2(S, RC_EAX, RC_ECX); r = S->tccgen_vtop[-1].r; fr = S->tccgen_vtop[0].r; S->tccgen_vtop--; save_reg(S, TREG_EDX); /* save EAX too if used otherwise */ save_reg_upstack(S, TREG_EAX, 1); if (op == TOK_UMULL) { o(S, 0xf7); /* mul fr */ o(S, 0xe0 + fr); S->tccgen_vtop->r2 = TREG_EDX; r = TREG_EAX; } else { if (op == TOK_UDIV || op == TOK_UMOD) { o(S, 0xf7d231); /* xor %edx, %edx, div fr, %eax */ o(S, 0xf0 + fr); } else { o(S, 0xf799); /* cltd, idiv fr, %eax */ o(S, 0xf8 + fr); } if (op == '%' || op == TOK_UMOD) r = TREG_EDX; else r = TREG_EAX; } S->tccgen_vtop->r = r; break; default: opc = 7; goto gen_op8; } } /* generate a floating point operation 'v = t1 op t2' instruction. The two operands are guaranteed to have the same floating point type */ /* XXX: need to use ST1 too */ ST_FUNC void gen_opf(TCCState* S, int op) { int a, ft, fc, swapped, r; if (op == TOK_NEG) { /* unary minus */ gv(S, RC_FLOAT); o(S, 0xe0d9); /* fchs */ return; } /* convert constants to memory references */ if ((S->tccgen_vtop[-1].r & (VT_VALMASK | VT_LVAL)) == VT_CONST) { vswap(S); gv(S, RC_FLOAT); vswap(S); } if ((S->tccgen_vtop[0].r & (VT_VALMASK | VT_LVAL)) == VT_CONST) gv(S, RC_FLOAT); /* must put at least one value in the floating point register */ if ((S->tccgen_vtop[-1].r & VT_LVAL) && (S->tccgen_vtop[0].r & VT_LVAL)) { vswap(S); gv(S, RC_FLOAT); vswap(S); } swapped = 0; /* swap the stack if needed so that t1 is the register and t2 is the memory reference */ if (S->tccgen_vtop[-1].r & VT_LVAL) { vswap(S); swapped = 1; } if (op >= TOK_ULT && op <= TOK_GT) { /* load on stack second operand */ load(S, TREG_ST0, S->tccgen_vtop); save_reg(S, TREG_EAX); /* eax is used by FP comparison code */ if (op == TOK_GE || op == TOK_GT) swapped = !swapped; else if (op == TOK_EQ || op == TOK_NE) swapped = 0; if (swapped) o(S, 0xc9d9); /* fxch %st(1) */ if (op == TOK_EQ || op == TOK_NE) o(S, 0xe9da); /* fucompp */ else o(S, 0xd9de); /* fcompp */ o(S, 0xe0df); /* fnstsw %ax */ if (op == TOK_EQ) { o(S, 0x45e480); /* and $0x45, %ah */ o(S, 0x40fC80); /* cmp $0x40, %ah */ } else if (op == TOK_NE) { o(S, 0x45e480); /* and $0x45, %ah */ o(S, 0x40f480); /* xor $0x40, %ah */ op = TOK_NE; } else if (op == TOK_GE || op == TOK_LE) { o(S, 0x05c4f6); /* test $0x05, %ah */ op = TOK_EQ; } else { o(S, 0x45c4f6); /* test $0x45, %ah */ op = TOK_EQ; } S->tccgen_vtop--; vset_VT_CMP(S, op); } else { /* no memory reference possible for long double operations */ if ((S->tccgen_vtop->type.t & VT_BTYPE) == VT_LDOUBLE) { load(S, TREG_ST0, S->tccgen_vtop); swapped = !swapped; } switch(op) { default: case '+': a = 0; break; case '-': a = 4; if (swapped) a++; break; case '*': a = 1; break; case '/': a = 6; if (swapped) a++; break; } ft = S->tccgen_vtop->type.t; fc = S->tccgen_vtop->c.i; if ((ft & VT_BTYPE) == VT_LDOUBLE) { o(S, 0xde); /* fxxxp %st, %st(1) */ o(S, 0xc1 + (a << 3)); } else { /* if saved lvalue, then we must reload it */ r = S->tccgen_vtop->r; if ((r & VT_VALMASK) == VT_LLOCAL) { SValue v1; r = get_reg(S, RC_INT); v1.type.t = VT_INT; v1.r = VT_LOCAL | VT_LVAL; v1.c.i = fc; v1.sym = NULL; load(S, r, &v1); fc = 0; } if ((ft & VT_BTYPE) == VT_DOUBLE) o(S, 0xdc); else o(S, 0xd8); gen_modrm(S, a, r, S->tccgen_vtop->sym, fc); } S->tccgen_vtop--; } } /* convert integers to fp 't' type. Must handle 'int', 'unsigned int' and 'long long' cases. */ ST_FUNC void gen_cvt_itof(TCCState* S, int t) { save_reg(S, TREG_ST0); gv(S, RC_INT); if ((S->tccgen_vtop->type.t & VT_BTYPE) == VT_LLONG) { /* signed long long to float/double/long double (unsigned case is handled generically) */ o(S, 0x50 + S->tccgen_vtop->r2); /* push r2 */ o(S, 0x50 + (S->tccgen_vtop->r & VT_VALMASK)); /* push r */ o(S, 0x242cdf); /* fildll (%esp) */ o(S, 0x08c483); /* add $8, %esp */ S->tccgen_vtop->r2 = VT_CONST; } else if ((S->tccgen_vtop->type.t & (VT_BTYPE | VT_UNSIGNED)) == (VT_INT | VT_UNSIGNED)) { /* unsigned int to float/double/long double */ o(S, 0x6a); /* push $0 */ g(S, 0x00); o(S, 0x50 + (S->tccgen_vtop->r & VT_VALMASK)); /* push r */ o(S, 0x242cdf); /* fildll (%esp) */ o(S, 0x08c483); /* add $8, %esp */ } else { /* int to float/double/long double */ o(S, 0x50 + (S->tccgen_vtop->r & VT_VALMASK)); /* push r */ o(S, 0x2404db); /* fildl (%esp) */ o(S, 0x04c483); /* add $4, %esp */ } S->tccgen_vtop->r2 = VT_CONST; S->tccgen_vtop->r = TREG_ST0; } /* convert fp to int 't' type */ ST_FUNC void gen_cvt_ftoi(TCCState* S, int t) { int bt = S->tccgen_vtop->type.t & VT_BTYPE; if (bt == VT_FLOAT) vpush_helper_func(S, TOK___fixsfdi); else if (bt == VT_LDOUBLE) vpush_helper_func(S, TOK___fixxfdi); else vpush_helper_func(S, TOK___fixdfdi); vswap(S); gfunc_call(S, 1); vpushi(S, 0); S->tccgen_vtop->r = REG_IRET; if ((t & VT_BTYPE) == VT_LLONG) S->tccgen_vtop->r2 = REG_IRE2; } /* convert from one floating point type to another */ ST_FUNC void gen_cvt_ftof(TCCState* S, int t) { /* all we have to do on i386 is to put the float in a register */ gv(S, RC_FLOAT); } /* char/short to int conversion */ ST_FUNC void gen_cvt_csti(TCCState* S, int t) { int r, sz, xl; r = gv(S, RC_INT); sz = !(t & VT_UNSIGNED); xl = (t & VT_BTYPE) == VT_SHORT; o(S, 0xc0b60f /* mov[sz] %a[xl], %eax */ | (sz << 3 | xl) << 8 | (r << 3 | r) << 16 ); } /* increment tcov counter */ ST_FUNC void gen_increment_tcov (TCCState* S, SValue *sv) { o(S, 0x0583); /* addl $1, xxx */ greloc(S, cur_text_section, sv->sym, S->tccgen_ind, R_386_32); gen_le32(S, 0); o(S, 1); o(S, 0x1583); /* addcl $0, xxx */ greloc(S, cur_text_section, sv->sym, S->tccgen_ind, R_386_32); gen_le32(S, 4); g(S, 0); } /* computed goto support */ ST_FUNC void ggoto(TCCState* S) { gcall_or_jmp(S, 1); S->tccgen_vtop--; } /* bound check support functions */ #ifdef CONFIG_TCC_BCHECK static void gen_bounds_prolog(TCCState* S) { /* leave some room for bound checking code */ S->func_bound_offset = lbounds_section->data_offset; S->func_bound_ind = S->tccgen_ind; S->func_bound_add_epilog = 0; oad(S, 0xb8, 0); /* lbound section pointer */ oad(S, 0xb8, 0); /* call to function */ } static void gen_bounds_epilog(TCCState* S) { addr_t saved_ind; addr_t *bounds_ptr; Sym *sym_data; int offset_modified = S->func_bound_offset != lbounds_section->data_offset; if (!offset_modified && !S->func_bound_add_epilog) return; /* add end of table info */ bounds_ptr = section_ptr_add(S, lbounds_section, sizeof(addr_t)); *bounds_ptr = 0; sym_data = get_sym_ref(S, &S->tccgen_char_pointer_type, lbounds_section, S->func_bound_offset, lbounds_section->data_offset); /* generate bound local allocation */ if (offset_modified) { saved_ind = S->tccgen_ind; S->tccgen_ind = S->func_bound_ind; greloc(S, cur_text_section, sym_data, S->tccgen_ind + 1, R_386_32); S->tccgen_ind = S->tccgen_ind + 5; gen_static_call(S, TOK___bound_local_new); S->tccgen_ind = saved_ind; } /* generate bound check local freeing */ o(S, 0x5250); /* save returned value, if any */ greloc(S, cur_text_section, sym_data, S->tccgen_ind + 1, R_386_32); oad(S, 0xb8, 0); /* mov %eax, xxx */ gen_static_call(S, TOK___bound_local_delete); o(S, 0x585a); /* restore returned value, if any */ } #endif /* Save the stack pointer onto the stack */ ST_FUNC void gen_vla_sp_save(TCCState* S, int addr) { /* mov %esp,addr(%ebp)*/ o(S, 0x89); gen_modrm(S, TREG_ESP, VT_LOCAL, NULL, addr); } /* Restore the SP from a location on the stack */ ST_FUNC void gen_vla_sp_restore(TCCState* S, int addr) { o(S, 0x8b); gen_modrm(S, TREG_ESP, VT_LOCAL, NULL, addr); } /* Subtract from the stack pointer, and push the resulting value onto the stack */ ST_FUNC void gen_vla_alloc(TCCState* S, CType *type, int align) { int use_call = 0; #if defined(CONFIG_TCC_BCHECK) use_call = S->do_bounds_check; #endif #ifdef TCC_TARGET_PE /* alloca does more than just adjust %rsp on Windows */ use_call = 1; #endif if (use_call) { vpush_helper_func(S, TOK_alloca); vswap(S); /* Move alloca ref past allocation size */ gfunc_call(S, 1); } else { int r; r = gv(S, RC_INT); /* allocation size */ /* sub r,%rsp */ o(S, 0x2b); o(S, 0xe0 | r); /* We align to 16 bytes rather than align */ /* and ~15, %esp */ o(S, 0xf0e483); vpop(S); } } /* end of X86 code generator */ /*************************************************************/ #endif /*************************************************************/