Sigrid Solveig Haflínudóttirget rid of trailing whitespacebbe192110 months agoHistory
1314 lines · 35.6 KB · C
RawDownload
/* *  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_EDX     0x0008#define RC_ECX     0x0010#define RC_EBX     0x0020#define RC_ST0     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,    TREG_MEM = 0x20}; #define REG_VALUE(reg) ((reg) & 7) /* 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"    ; #if defined CONFIG_TCC_PIC# define USE_EBX 2#else# define USE_EBX 0 /* define to 1 to have EBX as 4th register */#endif 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 == 1),    /* st0 */ RC_FLOAT | RC_ST0,}; static unsigned long func_sub_sp_offset;static int func_ret_sub;#ifdef CONFIG_TCC_BCHECKstatic addr_t func_bound_offset;static unsigned long func_bound_ind;ST_DATA int func_bound_add_epilog;static void gen_bounds_prolog(void);static void gen_bounds_epilog(void);#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;    }} /* 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;} ST_FUNC void gen_fill_nops(int bytes){    while (bytes--)      g(0x90);} #if defined CONFIG_TCC_PICstatic void gen_static_call(int v);static void get_pc_thunk(int r, int add){    static const char * const pc_thunk_name[] = {	"__x86.get_pc_thunk.ax",	"__x86.get_pc_thunk.cx",	"__x86.get_pc_thunk.dx",	"__x86.get_pc_thunk.bx"    };    if (nocode_wanted)        return;    gen_static_call(tok_alloc_const(pc_thunk_name[r]));    if (add) {        Sym label = {0};        label.type.t = VT_VOID|VT_STATIC;        put_extern_sym(&label, cur_text_section, ind, 0);        r = REG_VALUE(r);	if (r == 0)	    oad(0x05, 1); /* add _GLOBAL_OFFSET_TABLE_, %eax */	else            oad(0xc081 + r * 0x100, 2); /* add _GLOBAL_OFFSET_TABLE_, %erx */        greloc(cur_text_section, &label, ind - 4, R_386_GOTPC);    }} ST_FUNC void gen_gotpcrel(int r, Sym *sym, int c){    greloc(cur_text_section, sym, ind, R_386_GOT32X);    gen_le32(0);    if (c) {	r = REG_VALUE(r);	if (r == 0)            oad(0x05, c);	else            oad(0xc081 + r * 0x100, c);    }}#endif /* 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 opc, int op_r2, int r, Sym *sym, int c){    int op_reg = REG_VALUE(op_r2) << 3;     if ((r & VT_SYM) && (sym->type.t & VT_TLS)) {	if (opc == 0xdbc0d9) // VT_LDOUBLE	    o(0xc0d9), opc = 0xdb;        o(0x65); /* gs segment prefix */        o(opc);        oad(0x05 | op_reg, c);        greloc(cur_text_section, sym, ind - 4, R_386_TLS_LE);#if defined CONFIG_TCC_PIC    } else if ((r & (VT_VALMASK|VT_SYM)) == (VT_CONST|VT_SYM)) {        int is_got = (op_r2 & TREG_MEM) && !(sym->type.t & VT_STATIC);        int here = ind;        get_pc_thunk(TREG_EBX, is_got);        o(opc);        o(0x83 | op_reg);        if (is_got) {            gen_gotpcrel(r, sym, c);        } else {            gen_addrpc32(r, sym, c + (ind - here - 1));        }    } else if ((r & VT_VALMASK) < VT_CONST && (r & TREG_MEM)) {        o(opc);        if (c) {            g(0x80 | op_reg | REG_VALUE(r));            gen_le32(c);        } else {            g(0x00 | op_reg | REG_VALUE(r));        }#endif    } else if ((r & VT_VALMASK) == VT_CONST) {        /* constant memory reference */        o(opc);        o(0x05 | op_reg);        gen_addr32(r, sym, c);    } else if ((r & VT_VALMASK) == VT_LOCAL) {	o(opc);        /* currently, we use only ebp as base */        if (c == (signed char)c) {            /* short reference */            o(0x45 | op_reg);            g(c);        } else {            oad(0x85 | op_reg, c);        }    } else {	o(opc);        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, opc;    SValue v1;     fr = sv->r;    ft = sv->type.t & ~VT_DEFSIGN;    fc = sv->c.i;    ft &= ~(VT_VOLATILE | VT_CONSTANT);    v = fr & VT_VALMASK; #if defined CONFIG_TCC_PIC    /* we use indirect access via got */    if ((fr & (VT_VALMASK|VT_SYM|VT_LVAL)) == (VT_CONST|VT_SYM|VT_LVAL)        && !(sv->sym->type.t & (VT_STATIC|VT_TLS))) {        /* use the result register as a temporal register */        int tr = r | TREG_MEM;        if (is_float(ft)) {            /* we cannot use float registers as a temporal register */            tr = get_reg(RC_INT) | TREG_MEM;        }        gen_modrm(0x8b, tr, fr, sv->sym, 0);        /* load from the temporal register */        fr = tr | VT_LVAL;    }#endif     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(RC_INT);            load(fr, &v1);        }        if ((ft & VT_BTYPE) == VT_FLOAT) {            opc = 0xd9; /* flds */            r = 0;        } else if ((ft & VT_BTYPE) == VT_DOUBLE) {            opc = 0xdd; /* fldl */            r = 0;        } else if ((ft & VT_BTYPE) == VT_LDOUBLE) {            opc = 0xdb; /* fldt */            r = 5;        } else if ((ft & VT_TYPE) == VT_BYTE || (ft & VT_TYPE) == VT_BOOL) {            opc = 0xbe0f;   /* movsbl */        } else if ((ft & VT_TYPE) == (VT_BYTE | VT_UNSIGNED) ||		   (ft & VT_TYPE) == (VT_BOOL | VT_UNSIGNED)) {            opc = 0xb60f;   /* movzbl */        } else if ((ft & VT_TYPE) == VT_SHORT) {            opc = 0xbf0f;   /* movswl */        } else if ((ft & VT_TYPE) == (VT_SHORT | VT_UNSIGNED)) {            opc = 0xb70f;   /* movzwl */        } else {            opc = 0x8b;     /* movl */        }        gen_modrm(opc, r, fr, sv->sym, fc);    } else {        if ((fr & VT_SYM) && (sv->sym->type.t & VT_TLS)) {            oad(0x058b65 | REG_VALUE(r) << 19, 0); /* mov gs:0,r */            oad(0xC081 | REG_VALUE(r) << 8, fc); /* add tpoffs,r */            greloc(cur_text_section, sv->sym, ind - 4, R_386_TLS_LE);#if defined CONFIG_TCC_PIC        } else if ((fr & (VT_VALMASK|VT_SYM)) == (VT_CONST|VT_SYM)) {            if (sv->sym->type.t & VT_STATIC) {                get_pc_thunk(r, 0);                o(0x808d | REG_VALUE(r) * 0x900); /* lea $xx(r), r */                gen_addrpc32(fr, sv->sym, fc + 6);            } else {                get_pc_thunk(r, 1);                o(0x808b | REG_VALUE(r) * 0x900); /* mov $xx(r), r */                gen_gotpcrel(r, sv->sym, fc);            }#endif        } else if (v == VT_CONST) {            o(0xb8 + r); /* mov $xx, r */            gen_addr32(fr, sv->sym, fc);        } else if (v == VT_LOCAL) {            if (fc) {                /* lea xxx(%ebp), r */                gen_modrm(0x8d, r, VT_LOCAL, sv->sym, fc);            } else {                o(0x89);                o(0xe8 + r); /* mov %ebp, r */            }        } else if (v == VT_CMP) {            o(0x0f); /* setxx %br */            o(fc);            o(0xc0 + r);            o(0xc0b60f + r * 0x90000); /* movzbl %al, %eax */        } 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, fc, opc;     bt = v->type.t & VT_BTYPE;     /* XXX: incorrect if float reg to reg */    if (bt == VT_FLOAT) {        opc = 0xd9; /* fsts */        r = 2;    } else if (bt == VT_DOUBLE) {        opc = 0xdd; /* fstpl */        r = 2;    } else if (bt == VT_LDOUBLE) {        opc = 0xdbc0d9; /* fld %st(0), fstpt */        r = 7;    } else if (bt == VT_SHORT) {        opc = 0x8966;    } else if (bt == VT_BYTE || bt == VT_BOOL) {        opc = 0x88;    } else {        opc = 0x89;    }     fc = v->c.i;    fr = v->r & VT_VALMASK; #if defined CONFIG_TCC_PIC    /* we need to access the variable via got */    if ((v->r & (VT_VALMASK|VT_SYM)) == (VT_CONST|VT_SYM)        && !(v->sym->type.t & (VT_STATIC|VT_TLS))) {	get_pc_thunk(TREG_EBX, 1);	o(0x9b8b); /* mov xx(%ebx),%ebx */	gen_gotpcrel(TREG_EBX, v->sym, v->c.i);	o(opc);	o(3 + (r << 3));    } else#endif     if (fr == VT_CONST || fr == VT_LOCAL || (v->r & VT_LVAL)) {        gen_modrm(opc, r, v->r, v->sym, fc);    } else if (fr != r) {	o(opc);        o(0xc0 + fr + r * 8); /* mov r, fr */    }} static void gadd_sp(int val){    if (val == (signed char)val) {        o(0xc483);        g(val);    } else {        oad(0xc481, val); /* add $xxx, %esp */    }} #if defined TCC_TARGET_PE || defined CONFIG_TCC_PICstatic void gen_static_call(int v){    Sym *sym;     sym = external_helper_sym(v);    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_SYM)) == (VT_CONST|VT_SYM)) {        /* constant and relocation case */#if defined CONFIG_TCC_PIC	if (!(vtop->sym->type.t & VT_STATIC)) {	    get_pc_thunk(TREG_EBX, 1);            oad(0xe8 + is_jmp, vtop->c.i - 4); /* call/jmp im */            greloc(cur_text_section, vtop->sym, ind - 4, R_386_PLT32);            return;	}#endif        oad(0xe8 + is_jmp, vtop->c.i - 4); /* call/jmp im */        greloc(cur_text_section, vtop->sym, ind - 4, R_386_PC32);    } else {        /* otherwise, indirect call */        r = gv(RC_INT);        o(0xff); /* call/jmp *r */        o(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

Lines 1–500 of 1314.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t