diff options
Diffstat (limited to 'tinyc/i386-gen.c')
-rw-r--r-- | tinyc/i386-gen.c | 1164 |
1 files changed, 0 insertions, 1164 deletions
diff --git a/tinyc/i386-gen.c b/tinyc/i386-gen.c deleted file mode 100644 index b9c3599fd..000000000 --- a/tinyc/i386-gen.c +++ /dev/null @@ -1,1164 +0,0 @@ -/* - * 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_LRET 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_LRET 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 - -/******************************************************/ -#else /* ! TARGET_DEFS_ONLY */ -/******************************************************/ -#include "tcc.h" - -/* 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 addr_t func_bound_offset; -static unsigned long func_bound_ind; -#endif - -/* XXX: make it faster ? */ -ST_FUNC void g(int c) -{ - int ind1; - if (nocode_wanted) - return; - ind1 = ind + 1; - if (ind1 > cur_text_section->data_allocated) - section_realloc(cur_text_section, ind1); - cur_text_section->data[ind] = c; - ind = ind1; -} - -ST_FUNC void o(unsigned int c) -{ - while (c) { - g(c); - c = c >> 8; - } -} - -ST_FUNC void gen_le16(int v) -{ - g(v); - g(v >> 8); -} - -ST_FUNC void gen_le32(int c) -{ - g(c); - g(c >> 8); - g(c >> 16); - g(c >> 24); -} - -/* output a symbol and patch all calls to it */ -ST_FUNC void gsym_addr(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; - } -} - -ST_FUNC void gsym(int t) -{ - gsym_addr(t, ind); -} - -/* instruction + 4 bytes data. Return the address of the data */ -static int oad(int c, int s) -{ - int t; - if (nocode_wanted) - return s; - o(c); - t = ind; - gen_le32(s); - return t; -} - -/* generate jmp to a label */ -#define gjmp2(instr,lbl) oad(instr,lbl) - -/* output constant with relocation if 'r & VT_SYM' is true */ -ST_FUNC void gen_addr32(int r, Sym *sym, int c) -{ - if (r & VT_SYM) - greloc(cur_text_section, sym, ind, R_386_32); - gen_le32(c); -} - -ST_FUNC void gen_addrpc32(int r, Sym *sym, int c) -{ - if (r & VT_SYM) - greloc(cur_text_section, sym, ind, R_386_PC32); - gen_le32(c - 4); -} - -/* generate a modrm reference. 'op_reg' contains the additional 3 - opcode bits */ -static void gen_modrm(int op_reg, int r, Sym *sym, int c) -{ - op_reg = op_reg << 3; - if ((r & VT_VALMASK) == VT_CONST) { - /* constant memory reference */ - o(0x05 | op_reg); - gen_addr32(r, sym, 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' */ -ST_FUNC void load(int r, SValue *sv) -{ - int v, t, ft, fc, fr; - SValue v1; - -#ifdef TCC_TARGET_PE - SValue v2; - sv = pe_getimport(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; - fr = r; - if (!(reg_classes[fr] & RC_INT)) - fr = get_reg(RC_INT); - load(fr, &v1); - } - if ((ft & VT_BTYPE) == VT_FLOAT) { - o(0xd9); /* flds */ - r = 0; - } else if ((ft & VT_BTYPE) == VT_DOUBLE) { - o(0xdd); /* fldl */ - r = 0; - } else if ((ft & VT_BTYPE) == VT_LDOUBLE) { - o(0xdb); /* fldt */ - r = 5; - } else if ((ft & VT_TYPE) == VT_BYTE || (ft & VT_TYPE) == VT_BOOL) { - o(0xbe0f); /* movsbl */ - } else if ((ft & VT_TYPE) == (VT_BYTE | VT_UNSIGNED)) { - o(0xb60f); /* movzbl */ - } else if ((ft & VT_TYPE) == VT_SHORT) { - o(0xbf0f); /* movswl */ - } else if ((ft & VT_TYPE) == (VT_SHORT | VT_UNSIGNED)) { - o(0xb70f); /* movzwl */ - } else { - o(0x8b); /* movl */ - } - gen_modrm(r, fr, sv->sym, fc); - } else { - if (v == VT_CONST) { - o(0xb8 + r); /* mov $xx, r */ - gen_addr32(fr, sv->sym, fc); - } else if (v == VT_LOCAL) { - if (fc) { - o(0x8d); /* lea xxx(%ebp), r */ - gen_modrm(r, VT_LOCAL, sv->sym, fc); - } else { - o(0x89); - o(0xe8 + r); /* mov %ebp, r */ - } - } else if (v == VT_CMP) { - oad(0xb8 + r, 0); /* mov $0, r */ - o(0x0f); /* setxx %br */ - o(fc); - o(0xc0 + r); - } else if (v == VT_JMP || v == VT_JMPI) { - t = v & 1; - oad(0xb8 + r, t); /* mov $1, r */ - o(0x05eb); /* jmp after */ - gsym(fc); - oad(0xb8 + r, t ^ 1); /* mov $0, r */ - } else if (v != r) { - o(0x89); - o(0xc0 + r + v * 8); /* mov v, r */ - } - } -} - -/* store register 'r' in lvalue 'v' */ -ST_FUNC void store(int r, SValue *v) -{ - int fr, bt, ft, fc; - -#ifdef TCC_TARGET_PE - SValue v2; - v = pe_getimport(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(0xd9); /* fsts */ - r = 2; - } else if (bt == VT_DOUBLE) { - o(0xdd); /* fstpl */ - r = 2; - } else if (bt == VT_LDOUBLE) { - o(0xc0d9); /* fld %st(0) */ - o(0xdb); /* fstpt */ - r = 7; - } else { - if (bt == VT_SHORT) - o(0x66); - if (bt == VT_BYTE || bt == VT_BOOL) - o(0x88); - else - o(0x89); - } - if (fr == VT_CONST || - fr == VT_LOCAL || - (v->r & VT_LVAL)) { - gen_modrm(r, v->r, v->sym, fc); - } else if (fr != r) { - o(0xc0 + fr + r * 8); /* mov r, fr */ - } -} - -static void gadd_sp(int val) -{ - if (val == (char)val) { - o(0xc483); - g(val); - } else { - oad(0xc481, val); /* add $xxx, %esp */ - } -} - -#if defined CONFIG_TCC_BCHECK || defined TCC_TARGET_PE -static void gen_static_call(int v) -{ - Sym *sym; - - sym = external_global_sym(v, &func_old_type, 0); - oad(0xe8, -4); - greloc(cur_text_section, sym, ind-4, R_386_PC32); -} -#endif - -/* 'is_jmp' is '1' if it is a jump */ -static void gcall_or_jmp(int is_jmp) -{ - int r; - if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST && (vtop->r & VT_SYM)) { - /* constant and relocation case */ - greloc(cur_text_section, vtop->sym, ind + 1, R_386_PC32); - oad(0xe8 + is_jmp, vtop->c.i - 4); /* call/jmp im */ - } else { - /* otherwise, indirect call */ - r = gv(RC_INT); - o(0xff); /* call/jmp *r */ - o(0xd0 + r + (is_jmp << 4)); - } - if (!is_jmp) { - int rt; - /* extend the return value to the whole register if necessary - visual studio and gcc do not always set the whole eax register - when assigning the return value of a function */ - rt = vtop->type.ref->type.t; - switch (rt & VT_BTYPE) { - case VT_BYTE: - if (rt & VT_UNSIGNED) { - o(0xc0b60f); /* movzx %al, %eax */ - } - else { - o(0xc0be0f); /* movsx %al, %eax */ - } - break; - case VT_SHORT: - if (rt & VT_UNSIGNED) { - o(0xc0b70f); /* movzx %ax, %eax */ - } - else { - o(0xc0bf0f); /* movsx %ax, %eax */ - } - break; - default: - break; - } - } -} - -static uint8_t fastcall_regs[3] = { TREG_EAX, TREG_EDX, TREG_ECX }; -static 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) -{ -#ifdef TCC_TARGET_PE - 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(int nb_args) -{ - int size, align, r, args_size, i, func_call; - Sym *func_sym; - - args_size = 0; - for(i = 0;i < nb_args; i++) { - if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) { - size = type_size(&vtop->type, &align); - /* align to stack align size */ - size = (size + 3) & ~3; - /* allocate the necessary size on stack */ - oad(0xec81, size); /* sub $xxx, %esp */ - /* generate structure store */ - r = get_reg(RC_INT); - o(0x89); /* mov %esp, r */ - o(0xe0 + r); - vset(&vtop->type, r | VT_LVAL, 0); - vswap(); - vstore(); - args_size += size; - } else if (is_float(vtop->type.t)) { - gv(RC_FLOAT); /* only one float register */ - if ((vtop->type.t & VT_BTYPE) == VT_FLOAT) - size = 4; - else if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE) - size = 8; - else - size = 12; - oad(0xec81, size); /* sub $xxx, %esp */ - if (size == 12) - o(0x7cdb); - else - o(0x5cd9 + size - 4); /* fstp[s|l] 0(%esp) */ - g(0x24); - g(0x00); - args_size += size; - } else { - /* simple type (currently always same size) */ - /* XXX: implicit cast ? */ - r = gv(RC_INT); - if ((vtop->type.t & VT_BTYPE) == VT_LLONG) { - size = 8; - o(0x50 + vtop->r2); /* push r */ - } else { - size = 4; - } - o(0x50 + r); /* push r */ - args_size += size; - } - vtop--; - } - save_regs(0); /* save used temporary registers */ - func_sym = 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; - 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(0x58 + fastcall_regs_ptr[i]); /* pop r */ - /* XXX: incorrect for struct/floats */ - args_size -= 4; - } - } -#ifndef TCC_TARGET_PE - else if ((vtop->type.ref->type.t & VT_BTYPE) == VT_STRUCT) - args_size -= 4; -#endif - gcall_or_jmp(0); - - if (args_size && func_call != FUNC_STDCALL && func_call != FUNC_FASTCALLW) - gadd_sp(args_size); - 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(CType *func_type) -{ - int addr, align, size, func_call, fastcall_nb_regs; - int param_index, param_addr; - uint8_t *fastcall_regs_ptr; - Sym *sym; - CType *type; - - sym = func_type->ref; - func_call = sym->f.func_call; - addr = 8; - loc = 0; - 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; - - ind += FUNC_PROLOG_SIZE; - func_sub_sp_offset = ind; - /* if the function returns a structure, then add an - implicit pointer parameter */ - func_vt = sym->type; - func_var = (sym->f.func_type == FUNC_ELLIPSIS); -#ifdef TCC_TARGET_PE - size = type_size(&func_vt,&align); - if (((func_vt.t & VT_BTYPE) == VT_STRUCT) - && (size > 8 || (size & (size - 1)))) { -#else - if ((func_vt.t & VT_BTYPE) == VT_STRUCT) { -#endif - /* XXX: fastcall case ? */ - 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 */ - loc -= 4; - o(0x89); /* movl */ - gen_modrm(fastcall_regs_ptr[param_index], VT_LOCAL, NULL, loc); - param_addr = loc; - } else { - param_addr = addr; - addr += size; - } - sym_push(sym->v & ~SYM_FIELD, type, - VT_LOCAL | lvalue_type(type->t), 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; -#ifndef TCC_TARGET_PE - else if (func_vc) - func_ret_sub = 4; -#endif - -#ifdef CONFIG_TCC_BCHECK - /* leave some room for bound checking code */ - if (tcc_state->do_bounds_check) { - func_bound_offset = lbounds_section->data_offset; - func_bound_ind = ind; - oad(0xb8, 0); /* lbound section pointer */ - oad(0xb8, 0); /* call to function */ - } -#endif -} - -/* generate function epilog */ -ST_FUNC void gfunc_epilog(void) -{ - addr_t v, saved_ind; - -#ifdef CONFIG_TCC_BCHECK - if (tcc_state->do_bounds_check - && func_bound_offset != lbounds_section->data_offset) { - addr_t saved_ind; - addr_t *bounds_ptr; - Sym *sym_data; - - /* add end of table info */ - bounds_ptr = section_ptr_add(lbounds_section, sizeof(addr_t)); - *bounds_ptr = 0; - - /* generate bound local allocation */ - saved_ind = ind; - ind = func_bound_ind; - sym_data = get_sym_ref(&char_pointer_type, lbounds_section, - func_bound_offset, lbounds_section->data_offset); - greloc(cur_text_section, sym_data, - ind + 1, R_386_32); - oad(0xb8, 0); /* mov %eax, xxx */ - gen_static_call(TOK___bound_local_new); - ind = saved_ind; - - /* generate bound check local freeing */ - o(0x5250); /* save returned value, if any */ - greloc(cur_text_section, sym_data, ind + 1, R_386_32); - oad(0xb8, 0); /* mov %eax, xxx */ - gen_static_call(TOK___bound_local_delete); - o(0x585a); /* restore returned value, if any */ - } -#endif - - /* align local size to word & save local variables */ - v = (-loc + 3) & -4; - -#if USE_EBX - o(0x8b); - gen_modrm(TREG_EBX, VT_LOCAL, NULL, -(v+4)); -#endif - - o(0xc9); /* leave */ - if (func_ret_sub == 0) { - o(0xc3); /* ret */ - } else { - o(0xc2); /* ret n */ - g(func_ret_sub); - g(func_ret_sub >> 8); - } - saved_ind = ind; - ind = func_sub_sp_offset - FUNC_PROLOG_SIZE; -#ifdef TCC_TARGET_PE - if (v >= 4096) { - oad(0xb8, v); /* mov stacksize, %eax */ - gen_static_call(TOK___chkstk); /* call __chkstk, (does the stackframe too) */ - } else -#endif - { - o(0xe58955); /* push %ebp, mov %esp, %ebp */ - o(0xec81); /* sub esp, stacksize */ - gen_le32(v); -#ifdef TCC_TARGET_PE - o(0x90); /* adjust to FUNC_PROLOG_SIZE */ -#endif - } - o(0x53 * USE_EBX); /* push ebx */ - ind = saved_ind; -} - -/* generate a jump to a label */ -ST_FUNC int gjmp(int t) -{ - return gjmp2(0xe9, t); -} - -/* generate a jump to a fixed address */ -ST_FUNC void gjmp_addr(int a) -{ - int r; - r = a - ind - 2; - if (r == (char)r) { - g(0xeb); - g(r); - } else { - oad(0xe9, a - ind - 5); - } -} - -ST_FUNC void gtst_addr(int inv, int a) -{ - int v = vtop->r & VT_VALMASK; - if (v == VT_CMP) { - inv ^= (vtop--)->c.i; - a -= ind + 2; - if (a == (char)a) { - g(inv - 32); - g(a); - } else { - g(0x0f); - oad(inv - 16, a - 4); - } - } else if ((v & ~1) == VT_JMP) { - if ((v & 1) != inv) { - gjmp_addr(a); - gsym(vtop->c.i); - } else { - gsym(vtop->c.i); - o(0x05eb); - gjmp_addr(a); - } - vtop--; - } -} - -/* generate a test. set 'inv' to invert test. Stack entry is popped */ -ST_FUNC int gtst(int inv, int t) -{ - int v = vtop->r & VT_VALMASK; - if (nocode_wanted) { - ; - } else if (v == VT_CMP) { - /* fast case : can jump directly since flags are set */ - g(0x0f); - t = gjmp2((vtop->c.i - 16) ^ inv, t); - } else if (v == VT_JMP || v == VT_JMPI) { - /* && or || optimization */ - if ((v & 1) == inv) { - /* insert vtop->c jump list in t */ - uint32_t n1, n = vtop->c.i; - if (n) { - while ((n1 = read32le(cur_text_section->data + n))) - n = n1; - write32le(cur_text_section->data + n, t); - t = vtop->c.i; - } - } else { - t = gjmp(t); - gsym(vtop->c.i); - } - } - vtop--; - return t; -} - -/* generate an integer binary operation */ -ST_FUNC void gen_opi(int op) -{ - int r, fr, opc, c; - - switch(op) { - case '+': - case TOK_ADDC1: /* add with carry generation */ - opc = 0; - gen_op8: - if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { - /* constant case */ - vswap(); - r = gv(RC_INT); - vswap(); - c = vtop->c.i; - if (c == (char)c) { - /* generate inc and dec for smaller code */ - if (c==1 && opc==0 && op != TOK_ADDC1) { - o (0x40 | r); // inc - } else if (c==1 && opc==5 && op != TOK_SUBC1) { - o (0x48 | r); // dec - } else { - o(0x83); - o(0xc0 | (opc << 3) | r); - g(c); - } - } else { - o(0x81); - oad(0xc0 | (opc << 3) | r, c); - } - } else { - gv2(RC_INT, RC_INT); - r = vtop[-1].r; - fr = vtop[0].r; - o((opc << 3) | 0x01); - o(0xc0 + r + fr * 8); - } - vtop--; - if (op >= TOK_ULT && op <= TOK_GT) { - vtop->r = VT_CMP; - vtop->c.i = 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(RC_INT, RC_INT); - r = vtop[-1].r; - fr = vtop[0].r; - vtop--; - o(0xaf0f); /* imul fr, r */ - o(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 ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { - /* constant case */ - vswap(); - r = gv(RC_INT); - vswap(); - c = vtop->c.i & 0x1f; - o(0xc1); /* shl/shr/sar $xxx, r */ - o(opc | r); - g(c); - } else { - /* we generate the shift in ecx */ - gv2(RC_INT, RC_ECX); - r = vtop[-1].r; - o(0xd3); /* shl/shr/sar %cl, r */ - o(opc | r); - } - 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(RC_EAX, RC_ECX); - r = vtop[-1].r; - fr = vtop[0].r; - vtop--; - save_reg(TREG_EDX); - /* save EAX too if used otherwise */ - save_reg_upstack(TREG_EAX, 1); - if (op == TOK_UMULL) { - o(0xf7); /* mul fr */ - o(0xe0 + fr); - vtop->r2 = TREG_EDX; - r = TREG_EAX; - } else { - if (op == TOK_UDIV || op == TOK_UMOD) { - o(0xf7d231); /* xor %edx, %edx, div fr, %eax */ - o(0xf0 + fr); - } else { - o(0xf799); /* cltd, idiv fr, %eax */ - o(0xf8 + fr); - } - if (op == '%' || op == TOK_UMOD) - r = TREG_EDX; - else - r = TREG_EAX; - } - 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(int op) -{ - int a, ft, fc, swapped, r; - - /* convert constants to memory references */ - if ((vtop[-1].r & (VT_VALMASK | VT_LVAL)) == VT_CONST) { - vswap(); - gv(RC_FLOAT); - vswap(); - } - if ((vtop[0].r & (VT_VALMASK | VT_LVAL)) == VT_CONST) - 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(RC_FLOAT); - vswap(); - } - swapped = 0; - /* swap the stack if needed so that t1 is the register and t2 is - the memory reference */ - if (vtop[-1].r & VT_LVAL) { - vswap(); - swapped = 1; - } - if (op >= TOK_ULT && op <= TOK_GT) { - /* load on stack second operand */ - load(TREG_ST0, vtop); - save_reg(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(0xc9d9); /* fxch %st(1) */ - if (op == TOK_EQ || op == TOK_NE) - o(0xe9da); /* fucompp */ - else - o(0xd9de); /* fcompp */ - o(0xe0df); /* fnstsw %ax */ - if (op == TOK_EQ) { - o(0x45e480); /* and $0x45, %ah */ - o(0x40fC80); /* cmp $0x40, %ah */ - } else if (op == TOK_NE) { - o(0x45e480); /* and $0x45, %ah */ - o(0x40f480); /* xor $0x40, %ah */ - op = TOK_NE; - } else if (op == TOK_GE || op == TOK_LE) { - o(0x05c4f6); /* test $0x05, %ah */ - op = TOK_EQ; - } else { - o(0x45c4f6); /* test $0x45, %ah */ - op = TOK_EQ; - } - vtop--; - vtop->r = VT_CMP; - vtop->c.i = op; - } else { - /* no memory reference possible for long double operations */ - if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) { - load(TREG_ST0, 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 = vtop->type.t; - fc = vtop->c.i; - if ((ft & VT_BTYPE) == VT_LDOUBLE) { - o(0xde); /* fxxxp %st, %st(1) */ - o(0xc1 + (a << 3)); - } else { - /* if saved lvalue, then we must reload it */ - r = vtop->r; - if ((r & VT_VALMASK) == VT_LLOCAL) { - SValue v1; - r = get_reg(RC_INT); - v1.type.t = VT_INT; - v1.r = VT_LOCAL | VT_LVAL; - v1.c.i = fc; - load(r, &v1); - fc = 0; - } - - if ((ft & VT_BTYPE) == VT_DOUBLE) - o(0xdc); - else - o(0xd8); - gen_modrm(a, r, vtop->sym, fc); - } - vtop--; - } -} - -/* convert integers to fp 't' type. Must handle 'int', 'unsigned int' - and 'long long' cases. */ -ST_FUNC void gen_cvt_itof(int t) -{ - save_reg(TREG_ST0); - gv(RC_INT); - if ((vtop->type.t & VT_BTYPE) == VT_LLONG) { - /* signed long long to float/double/long double (unsigned case - is handled generically) */ - o(0x50 + vtop->r2); /* push r2 */ - o(0x50 + (vtop->r & VT_VALMASK)); /* push r */ - o(0x242cdf); /* fildll (%esp) */ - o(0x08c483); /* add $8, %esp */ - } else if ((vtop->type.t & (VT_BTYPE | VT_UNSIGNED)) == - (VT_INT | VT_UNSIGNED)) { - /* unsigned int to float/double/long double */ - o(0x6a); /* push $0 */ - g(0x00); - o(0x50 + (vtop->r & VT_VALMASK)); /* push r */ - o(0x242cdf); /* fildll (%esp) */ - o(0x08c483); /* add $8, %esp */ - } else { - /* int to float/double/long double */ - o(0x50 + (vtop->r & VT_VALMASK)); /* push r */ - o(0x2404db); /* fildl (%esp) */ - o(0x04c483); /* add $4, %esp */ - } - vtop->r = TREG_ST0; -} - -/* convert fp to int 't' type */ -ST_FUNC void gen_cvt_ftoi(int t) -{ - int bt = vtop->type.t & VT_BTYPE; - if (bt == VT_FLOAT) - vpush_global_sym(&func_old_type, TOK___fixsfdi); - else if (bt == VT_LDOUBLE) - vpush_global_sym(&func_old_type, TOK___fixxfdi); - else - vpush_global_sym(&func_old_type, TOK___fixdfdi); - vswap(); - gfunc_call(1); - vpushi(0); - vtop->r = REG_IRET; - vtop->r2 = REG_LRET; -} - -/* convert from one floating point type to another */ -ST_FUNC void gen_cvt_ftof(int t) -{ - /* all we have to do on i386 is to put the float in a register */ - gv(RC_FLOAT); -} - -/* computed goto support */ -ST_FUNC void ggoto(void) -{ - gcall_or_jmp(1); - vtop--; -} - -/* bound check support functions */ -#ifdef CONFIG_TCC_BCHECK - -/* generate a bounded pointer addition */ -ST_FUNC void gen_bounded_ptr_add(void) -{ - /* prepare fast i386 function call (args in eax and edx) */ - gv2(RC_EAX, RC_EDX); - /* save all temporary registers */ - vtop -= 2; - save_regs(0); - /* do a fast function call */ - gen_static_call(TOK___bound_ptr_add); - /* returned pointer is in eax */ - vtop++; - vtop->r = TREG_EAX | VT_BOUNDED; - /* address of bounding function call point */ - vtop->c.i = (cur_text_section->reloc->data_offset - sizeof(Elf32_Rel)); -} - -/* patch pointer addition in vtop so that pointer dereferencing is - also tested */ -ST_FUNC void gen_bounded_ptr_deref(void) -{ - addr_t func; - int size, align; - Elf32_Rel *rel; - Sym *sym; - - size = 0; - /* XXX: put that code in generic part of tcc */ - if (!is_float(vtop->type.t)) { - if (vtop->r & VT_LVAL_BYTE) - size = 1; - else if (vtop->r & VT_LVAL_SHORT) - size = 2; - } - if (!size) - size = type_size(&vtop->type, &align); - switch(size) { - case 1: func = TOK___bound_ptr_indir1; break; - case 2: func = TOK___bound_ptr_indir2; break; - case 4: func = TOK___bound_ptr_indir4; break; - case 8: func = TOK___bound_ptr_indir8; break; - case 12: func = TOK___bound_ptr_indir12; break; - case 16: func = TOK___bound_ptr_indir16; break; - default: - tcc_error("unhandled size when dereferencing bounded pointer"); - func = 0; - break; - } - - /* patch relocation */ - /* XXX: find a better solution ? */ - rel = (Elf32_Rel *)(cur_text_section->reloc->data + vtop->c.i); - sym = external_global_sym(func, &func_old_type, 0); - if (!sym->c) - put_extern_sym(sym, NULL, 0, 0); - rel->r_info = ELF32_R_INFO(sym->c, ELF32_R_TYPE(rel->r_info)); -} -#endif - -/* Save the stack pointer onto the stack */ -ST_FUNC void gen_vla_sp_save(int addr) { - /* mov %esp,addr(%ebp)*/ - o(0x89); - gen_modrm(TREG_ESP, VT_LOCAL, NULL, addr); -} - -/* Restore the SP from a location on the stack */ -ST_FUNC void gen_vla_sp_restore(int addr) { - o(0x8b); - gen_modrm(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(CType *type, int align) { -#ifdef TCC_TARGET_PE - /* alloca does more than just adjust %rsp on Windows */ - vpush_global_sym(&func_old_type, TOK_alloca); - vswap(); /* Move alloca ref past allocation size */ - gfunc_call(1); -#else - int r; - r = gv(RC_INT); /* allocation size */ - /* sub r,%rsp */ - o(0x2b); - o(0xe0 | r); - /* We align to 16 bytes rather than align */ - /* and ~15, %esp */ - o(0xf0e483); - vpop(); -#endif -} - -/* end of X86 code generator */ -/*************************************************************/ -#endif -/*************************************************************/ |