1122 lines · 30.0 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_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" /* 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_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);} /* 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;            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) {            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) {            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, 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_PEstatic void gen_static_call(int v){    Sym *sym;     sym = external_global_sym(v, &func_old_type);    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));    }} 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;    #ifdef CONFIG_TCC_BCHECK    if (tcc_state->do_bounds_check)        gbound_args(nb_args);#endif     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 */#ifdef TCC_TARGET_PE            if (size >= 0x4096) {                /* cannot call alloca with bound checking. Do stack probing. */                o(0x50);               // push %eax                oad(0xb8, size - 4);   // mov size-4,%eax                oad(0x3d, 4096);       // p1: cmp $4096,%eax                o(0x1476);             // jbe <p2>                oad(0x248485,-4096);   // test %eax,-4096(%esp)                oad(0xec81, 4096);     // sub $4096,%esp                oad(0x2d, 4096);       // sub $4096,%eax                o(0xe5eb);             // jmp <p1>                o(0xc429);             // p2: sub %eax,%esp                oad(0xc481, size - 4); // add size-4,%esp                o(0x58);               // pop %eax            }#endif            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)

Lines 1–500 of 1122.