TinyC upgrade (#6593)
This commit is contained in:
parent
9c00f6decd
commit
d2c7d391c8
395 changed files with 68184 additions and 32394 deletions
544
tinyc/i386-gen.c
544
tinyc/i386-gen.c
|
|
@ -18,8 +18,12 @@
|
|||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifdef TARGET_DEFS_ONLY
|
||||
|
||||
/* number of available registers */
|
||||
#define NB_REGS 4
|
||||
#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
|
||||
|
|
@ -30,6 +34,8 @@
|
|||
#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 */
|
||||
|
|
@ -39,14 +45,9 @@ enum {
|
|||
TREG_EAX = 0,
|
||||
TREG_ECX,
|
||||
TREG_EDX,
|
||||
TREG_EBX,
|
||||
TREG_ST0,
|
||||
};
|
||||
|
||||
int reg_classes[NB_REGS] = {
|
||||
/* eax */ RC_INT | RC_EAX,
|
||||
/* ecx */ RC_INT | RC_ECX,
|
||||
/* edx */ RC_INT | RC_EDX,
|
||||
/* st0 */ RC_FLOAT | RC_ST0,
|
||||
TREG_ESP = 4
|
||||
};
|
||||
|
||||
/* return registers for function */
|
||||
|
|
@ -59,7 +60,7 @@ int reg_classes[NB_REGS] = {
|
|||
|
||||
/* defined if structures are passed as pointers. Otherwise structures
|
||||
are directly pushed on stack. */
|
||||
//#define FUNC_STRUCT_PARAM_AS_PTR
|
||||
/* #define FUNC_STRUCT_PARAM_AS_PTR */
|
||||
|
||||
/* pointer size, in bytes */
|
||||
#define PTR_SIZE 4
|
||||
|
|
@ -71,28 +72,34 @@ int reg_classes[NB_REGS] = {
|
|||
#define MAX_ALIGN 8
|
||||
|
||||
/******************************************************/
|
||||
/* ELF defines */
|
||||
|
||||
#define EM_TCC_TARGET EM_386
|
||||
|
||||
/* relocation type for 32 bit data relocation */
|
||||
#define R_DATA_32 R_386_32
|
||||
#define R_JMP_SLOT R_386_JMP_SLOT
|
||||
#define R_COPY R_386_COPY
|
||||
|
||||
#define ELF_START_ADDR 0x08048000
|
||||
#define ELF_PAGE_SIZE 0x1000
|
||||
|
||||
#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 unsigned long func_bound_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 ? */
|
||||
void g(int c)
|
||||
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);
|
||||
|
|
@ -100,7 +107,7 @@ void g(int c)
|
|||
ind = ind1;
|
||||
}
|
||||
|
||||
void o(unsigned int c)
|
||||
ST_FUNC void o(unsigned int c)
|
||||
{
|
||||
while (c) {
|
||||
g(c);
|
||||
|
|
@ -108,7 +115,13 @@ void o(unsigned int c)
|
|||
}
|
||||
}
|
||||
|
||||
void gen_le32(int c)
|
||||
ST_FUNC void gen_le16(int v)
|
||||
{
|
||||
g(v);
|
||||
g(v >> 8);
|
||||
}
|
||||
|
||||
ST_FUNC void gen_le32(int c)
|
||||
{
|
||||
g(c);
|
||||
g(c >> 8);
|
||||
|
|
@ -117,50 +130,52 @@ void gen_le32(int c)
|
|||
}
|
||||
|
||||
/* output a symbol and patch all calls to it */
|
||||
void gsym_addr(int t, int a)
|
||||
ST_FUNC void gsym_addr(int t, int a)
|
||||
{
|
||||
int n, *ptr;
|
||||
while (t) {
|
||||
ptr = (int *)(cur_text_section->data + t);
|
||||
n = *ptr; /* next value */
|
||||
*ptr = a - t - 4;
|
||||
unsigned char *ptr = cur_text_section->data + t;
|
||||
uint32_t n = read32le(ptr); /* next value */
|
||||
write32le(ptr, a - t - 4);
|
||||
t = n;
|
||||
}
|
||||
}
|
||||
|
||||
void gsym(int t)
|
||||
ST_FUNC void gsym(int t)
|
||||
{
|
||||
gsym_addr(t, ind);
|
||||
}
|
||||
|
||||
/* psym is used to put an instruction with a data field which is a
|
||||
reference to a symbol. It is in fact the same as oad ! */
|
||||
#define psym oad
|
||||
|
||||
/* instruction + 4 bytes data. Return the address of the data */
|
||||
static int oad(int c, int s)
|
||||
{
|
||||
int ind1;
|
||||
|
||||
int t;
|
||||
if (nocode_wanted)
|
||||
return s;
|
||||
o(c);
|
||||
ind1 = ind + 4;
|
||||
if (ind1 > cur_text_section->data_allocated)
|
||||
section_realloc(cur_text_section, ind1);
|
||||
*(int *)(cur_text_section->data + ind) = s;
|
||||
s = ind;
|
||||
ind = ind1;
|
||||
return s;
|
||||
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 */
|
||||
static void gen_addr32(int r, Sym *sym, int c)
|
||||
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);
|
||||
}
|
||||
|
||||
/* generate a modrm reference. 'op_reg' contains the addtionnal 3
|
||||
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)
|
||||
{
|
||||
|
|
@ -183,25 +198,33 @@ static void gen_modrm(int op_reg, int r, Sym *sym, int c)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/* load 'r' from value 'sv' */
|
||||
void load(int r, SValue *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;
|
||||
fc = sv->c.ul;
|
||||
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.ul = fc;
|
||||
load(r, &v1);
|
||||
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 */
|
||||
|
|
@ -212,7 +235,7 @@ void load(int r, SValue *sv)
|
|||
} else if ((ft & VT_BTYPE) == VT_LDOUBLE) {
|
||||
o(0xdb); /* fldt */
|
||||
r = 5;
|
||||
} else if ((ft & VT_TYPE) == VT_BYTE) {
|
||||
} 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 */
|
||||
|
|
@ -229,8 +252,13 @@ void load(int r, SValue *sv)
|
|||
o(0xb8 + r); /* mov $xx, r */
|
||||
gen_addr32(fr, sv->sym, fc);
|
||||
} else if (v == VT_LOCAL) {
|
||||
o(0x8d); /* lea xxx(%ebp), r */
|
||||
gen_modrm(r, VT_LOCAL, sv->sym, fc);
|
||||
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 */
|
||||
|
|
@ -250,13 +278,19 @@ void load(int r, SValue *sv)
|
|||
}
|
||||
|
||||
/* store register 'r' in lvalue 'v' */
|
||||
void store(int r, SValue *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.ul;
|
||||
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) {
|
||||
|
|
@ -296,37 +330,94 @@ static void gadd_sp(int val)
|
|||
}
|
||||
}
|
||||
|
||||
#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) {
|
||||
/* constant case */
|
||||
if (vtop->r & VT_SYM) {
|
||||
/* relocation case */
|
||||
greloc(cur_text_section, vtop->sym,
|
||||
ind + 1, R_386_PC32);
|
||||
} else {
|
||||
/* put an empty PC32 relocation */
|
||||
put_elf_reloc(symtab_section, cur_text_section,
|
||||
ind + 1, R_386_PC32, 0);
|
||||
}
|
||||
oad(0xe8 + is_jmp, vtop->c.ul - 4); /* call/jmp im */
|
||||
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. */
|
||||
void gfunc_call(int nb_args)
|
||||
ST_FUNC void gfunc_call(int nb_args)
|
||||
{
|
||||
int size, align, r, args_size, i, func_call;
|
||||
Sym *func_sym;
|
||||
|
|
@ -380,7 +471,7 @@ void gfunc_call(int nb_args)
|
|||
}
|
||||
save_regs(0); /* save used temporary registers */
|
||||
func_sym = vtop->type.ref;
|
||||
func_call = FUNC_CALL(func_sym->r);
|
||||
func_call = func_sym->f.func_call;
|
||||
/* fast call case */
|
||||
if ((func_call >= FUNC_FASTCALL1 && func_call <= FUNC_FASTCALL3) ||
|
||||
func_call == FUNC_FASTCALLW) {
|
||||
|
|
@ -401,20 +492,25 @@ void gfunc_call(int nb_args)
|
|||
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)
|
||||
|
||||
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
|
||||
#define FUNC_PROLOG_SIZE (10 + USE_EBX)
|
||||
#else
|
||||
#define FUNC_PROLOG_SIZE 9
|
||||
#define FUNC_PROLOG_SIZE (9 + USE_EBX)
|
||||
#endif
|
||||
|
||||
/* generate function prolog of type 't' */
|
||||
void gfunc_prolog(CType *func_type)
|
||||
ST_FUNC void gfunc_prolog(CType *func_type)
|
||||
{
|
||||
int addr, align, size, func_call, fastcall_nb_regs;
|
||||
int param_index, param_addr;
|
||||
|
|
@ -423,9 +519,11 @@ void gfunc_prolog(CType *func_type)
|
|||
CType *type;
|
||||
|
||||
sym = func_type->ref;
|
||||
func_call = FUNC_CALL(sym->r);
|
||||
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;
|
||||
|
|
@ -443,7 +541,14 @@ void gfunc_prolog(CType *func_type)
|
|||
/* 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;
|
||||
|
|
@ -475,57 +580,69 @@ void gfunc_prolog(CType *func_type)
|
|||
param_index++;
|
||||
}
|
||||
func_ret_sub = 0;
|
||||
/* pascal type call ? */
|
||||
if (func_call == FUNC_STDCALL)
|
||||
/* 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 */
|
||||
func_bound_offset = lbounds_section->data_offset;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* generate function epilog */
|
||||
void gfunc_epilog(void)
|
||||
ST_FUNC void gfunc_epilog(void)
|
||||
{
|
||||
int v, saved_ind;
|
||||
addr_t v, saved_ind;
|
||||
|
||||
#ifdef CONFIG_TCC_BCHECK
|
||||
if (tcc_state->do_bounds_check
|
||||
&& func_bound_offset != lbounds_section->data_offset) {
|
||||
int saved_ind;
|
||||
int *bounds_ptr;
|
||||
Sym *sym, *sym_data;
|
||||
addr_t saved_ind;
|
||||
addr_t *bounds_ptr;
|
||||
Sym *sym_data;
|
||||
|
||||
/* add end of table info */
|
||||
bounds_ptr = section_ptr_add(lbounds_section, sizeof(int));
|
||||
bounds_ptr = section_ptr_add(lbounds_section, sizeof(addr_t));
|
||||
*bounds_ptr = 0;
|
||||
|
||||
/* generate bound local allocation */
|
||||
saved_ind = ind;
|
||||
ind = func_sub_sp_offset;
|
||||
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 */
|
||||
sym = external_global_sym(TOK___bound_local_new, &func_old_type, 0);
|
||||
greloc(cur_text_section, sym,
|
||||
ind + 1, R_386_PC32);
|
||||
oad(0xe8, -4);
|
||||
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);
|
||||
greloc(cur_text_section, sym_data, ind + 1, R_386_32);
|
||||
oad(0xb8, 0); /* mov %eax, xxx */
|
||||
sym = external_global_sym(TOK___bound_local_delete, &func_old_type, 0);
|
||||
greloc(cur_text_section, sym,
|
||||
ind + 1, R_386_PC32);
|
||||
oad(0xe8, -4);
|
||||
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 */
|
||||
|
|
@ -534,38 +651,34 @@ void gfunc_epilog(void)
|
|||
g(func_ret_sub);
|
||||
g(func_ret_sub >> 8);
|
||||
}
|
||||
/* align local size to word & save local variables */
|
||||
|
||||
v = (-loc + 3) & -4;
|
||||
saved_ind = ind;
|
||||
ind = func_sub_sp_offset - FUNC_PROLOG_SIZE;
|
||||
#ifdef TCC_TARGET_PE
|
||||
if (v >= 4096) {
|
||||
Sym *sym = external_global_sym(TOK___chkstk, &func_old_type, 0);
|
||||
oad(0xb8, v); /* mov stacksize, %eax */
|
||||
oad(0xe8, -4); /* call __chkstk, (does the stackframe too) */
|
||||
greloc(cur_text_section, sym, ind-4, R_386_PC32);
|
||||
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);
|
||||
#if FUNC_PROLOG_SIZE == 10
|
||||
#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 */
|
||||
int gjmp(int t)
|
||||
ST_FUNC int gjmp(int t)
|
||||
{
|
||||
return psym(0xe9, t);
|
||||
return gjmp2(0xe9, t);
|
||||
}
|
||||
|
||||
/* generate a jump to a fixed address */
|
||||
void gjmp_addr(int a)
|
||||
ST_FUNC void gjmp_addr(int a)
|
||||
{
|
||||
int r;
|
||||
r = a - ind - 2;
|
||||
|
|
@ -577,53 +690,64 @@ void gjmp_addr(int a)
|
|||
}
|
||||
}
|
||||
|
||||
/* generate a test. set 'inv' to invert test. Stack entry is popped */
|
||||
int gtst(int inv, int t)
|
||||
ST_FUNC void gtst_addr(int inv, int a)
|
||||
{
|
||||
int v, *p;
|
||||
|
||||
v = vtop->r & VT_VALMASK;
|
||||
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 = psym((vtop->c.i - 16) ^ inv, t);
|
||||
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 */
|
||||
p = &vtop->c.i;
|
||||
while (*p != 0)
|
||||
p = (int *)(cur_text_section->data + *p);
|
||||
*p = t;
|
||||
t = vtop->c.i;
|
||||
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);
|
||||
}
|
||||
} else {
|
||||
if (is_float(vtop->type.t) ||
|
||||
(vtop->type.t & VT_BTYPE) == VT_LLONG) {
|
||||
vpushi(0);
|
||||
gen_op(TOK_NE);
|
||||
}
|
||||
if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == 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;
|
||||
}
|
||||
|
||||
/* generate an integer binary operation */
|
||||
void gen_opi(int op)
|
||||
ST_FUNC void gen_opi(int op)
|
||||
{
|
||||
int r, fr, opc, c;
|
||||
|
||||
|
|
@ -639,10 +763,16 @@ void gen_opi(int op)
|
|||
vswap();
|
||||
c = vtop->c.i;
|
||||
if (c == (char)c) {
|
||||
/* XXX: generate inc and dec for smaller code ? */
|
||||
o(0x83);
|
||||
o(0xc0 | (opc << 3) | r);
|
||||
g(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);
|
||||
|
|
@ -728,6 +858,8 @@ void gen_opi(int op)
|
|||
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);
|
||||
|
|
@ -755,9 +887,9 @@ void gen_opi(int op)
|
|||
}
|
||||
|
||||
/* generate a floating point operation 'v = t1 op t2' instruction. The
|
||||
two operands are guaranted to have the same floating point type */
|
||||
two operands are guaranteed to have the same floating point type */
|
||||
/* XXX: need to use ST1 too */
|
||||
void gen_opf(int op)
|
||||
ST_FUNC void gen_opf(int op)
|
||||
{
|
||||
int a, ft, fc, swapped, r;
|
||||
|
||||
|
|
@ -794,7 +926,10 @@ void gen_opf(int op)
|
|||
swapped = 0;
|
||||
if (swapped)
|
||||
o(0xc9d9); /* fxch %st(1) */
|
||||
o(0xe9da); /* fucompp */
|
||||
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 */
|
||||
|
|
@ -840,7 +975,7 @@ void gen_opf(int op)
|
|||
break;
|
||||
}
|
||||
ft = vtop->type.t;
|
||||
fc = vtop->c.ul;
|
||||
fc = vtop->c.i;
|
||||
if ((ft & VT_BTYPE) == VT_LDOUBLE) {
|
||||
o(0xde); /* fxxxp %st, %st(1) */
|
||||
o(0xc1 + (a << 3));
|
||||
|
|
@ -852,7 +987,7 @@ void gen_opf(int op)
|
|||
r = get_reg(RC_INT);
|
||||
v1.type.t = VT_INT;
|
||||
v1.r = VT_LOCAL | VT_LVAL;
|
||||
v1.c.ul = fc;
|
||||
v1.c.i = fc;
|
||||
load(r, &v1);
|
||||
fc = 0;
|
||||
}
|
||||
|
|
@ -869,7 +1004,7 @@ void gen_opf(int op)
|
|||
|
||||
/* convert integers to fp 't' type. Must handle 'int', 'unsigned int'
|
||||
and 'long long' cases. */
|
||||
void gen_cvt_itof(int t)
|
||||
ST_FUNC void gen_cvt_itof(int t)
|
||||
{
|
||||
save_reg(TREG_ST0);
|
||||
gv(RC_INT);
|
||||
|
|
@ -898,65 +1033,31 @@ void gen_cvt_itof(int t)
|
|||
}
|
||||
|
||||
/* convert fp to int 't' type */
|
||||
/* XXX: handle long long case */
|
||||
void gen_cvt_ftoi(int t)
|
||||
ST_FUNC void gen_cvt_ftoi(int t)
|
||||
{
|
||||
int r, r2, size;
|
||||
Sym *sym;
|
||||
CType ushort_type;
|
||||
|
||||
ushort_type.t = VT_SHORT | VT_UNSIGNED;
|
||||
|
||||
gv(RC_FLOAT);
|
||||
if (t != VT_INT)
|
||||
size = 8;
|
||||
else
|
||||
size = 4;
|
||||
|
||||
o(0x2dd9); /* ldcw xxx */
|
||||
sym = external_global_sym(TOK___tcc_int_fpu_control,
|
||||
&ushort_type, VT_LVAL);
|
||||
greloc(cur_text_section, sym,
|
||||
ind, R_386_32);
|
||||
gen_le32(0);
|
||||
|
||||
oad(0xec81, size); /* sub $xxx, %esp */
|
||||
if (size == 4)
|
||||
o(0x1cdb); /* fistpl */
|
||||
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
|
||||
o(0x3cdf); /* fistpll */
|
||||
o(0x24);
|
||||
o(0x2dd9); /* ldcw xxx */
|
||||
sym = external_global_sym(TOK___tcc_fpu_control,
|
||||
&ushort_type, VT_LVAL);
|
||||
greloc(cur_text_section, sym,
|
||||
ind, R_386_32);
|
||||
gen_le32(0);
|
||||
|
||||
r = get_reg(RC_INT);
|
||||
o(0x58 + r); /* pop r */
|
||||
if (size == 8) {
|
||||
if (t == VT_LLONG) {
|
||||
vtop->r = r; /* mark reg as used */
|
||||
r2 = get_reg(RC_INT);
|
||||
o(0x58 + r2); /* pop r2 */
|
||||
vtop->r2 = r2;
|
||||
} else {
|
||||
o(0x04c483); /* add $4, %esp */
|
||||
}
|
||||
}
|
||||
vtop->r = r;
|
||||
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 */
|
||||
void gen_cvt_ftof(int t)
|
||||
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 */
|
||||
void ggoto(void)
|
||||
ST_FUNC void ggoto(void)
|
||||
{
|
||||
gcall_or_jmp(1);
|
||||
vtop--;
|
||||
|
|
@ -966,33 +1067,28 @@ void ggoto(void)
|
|||
#ifdef CONFIG_TCC_BCHECK
|
||||
|
||||
/* generate a bounded pointer addition */
|
||||
void gen_bounded_ptr_add(void)
|
||||
ST_FUNC void gen_bounded_ptr_add(void)
|
||||
{
|
||||
Sym *sym;
|
||||
|
||||
/* 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 */
|
||||
sym = external_global_sym(TOK___bound_ptr_add, &func_old_type, 0);
|
||||
greloc(cur_text_section, sym,
|
||||
ind + 1, R_386_PC32);
|
||||
oad(0xe8, -4);
|
||||
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.ul = (cur_text_section->reloc->data_offset - sizeof(Elf32_Rel));
|
||||
vtop->c.i = (cur_text_section->reloc->data_offset - sizeof(Elf32_Rel));
|
||||
}
|
||||
|
||||
/* patch pointer addition in vtop so that pointer dereferencing is
|
||||
also tested */
|
||||
void gen_bounded_ptr_deref(void)
|
||||
ST_FUNC void gen_bounded_ptr_deref(void)
|
||||
{
|
||||
int func;
|
||||
int size, align;
|
||||
addr_t func;
|
||||
int size, align;
|
||||
Elf32_Rel *rel;
|
||||
Sym *sym;
|
||||
|
||||
|
|
@ -1014,14 +1110,14 @@ void gen_bounded_ptr_deref(void)
|
|||
case 12: func = TOK___bound_ptr_indir12; break;
|
||||
case 16: func = TOK___bound_ptr_indir16; break;
|
||||
default:
|
||||
error("unhandled size when derefencing bounded pointer");
|
||||
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.ul);
|
||||
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);
|
||||
|
|
@ -1029,6 +1125,40 @@ void gen_bounded_ptr_deref(void)
|
|||
}
|
||||
#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
|
||||
/*************************************************************/
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue