diff options
Diffstat (limited to 'x86_64-gen.c')
| -rw-r--r-- | x86_64-gen.c | 35 |
1 files changed, 27 insertions, 8 deletions
diff --git a/x86_64-gen.c b/x86_64-gen.c index 19d5dd7..1873a9d 100644 --- a/x86_64-gen.c +++ b/x86_64-gen.c @@ -728,13 +728,13 @@ static int arg_prepare_reg(int idx) { return arg_regs[idx]; } -static int func_scratch; +static int func_scratch, func_alloca; /* 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 gen_offs_sp(int b, int r, int d) +static void gen_offs_sp(int b, int r, int d) { orex(1,0,r & 0x100 ? 0 : r, b); if (d == (char)d) { @@ -746,6 +746,11 @@ void gen_offs_sp(int b, int r, int d) } } +static int using_regs(int size) +{ + return !(size > 8 || (size & (size - 1))); +} + /* 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) @@ -754,7 +759,7 @@ ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int *ret_align = 1; // Never have to re-align return values for x86-64 *regsize = 8; size = type_size(vt, &align); - if (size > 8 || (size & (size - 1))) + if (!using_regs(size)) return 0; if (size == 8) ret->t = VT_LLONG; @@ -774,7 +779,7 @@ static int is_sse_float(int t) { return bt == VT_DOUBLE || bt == VT_FLOAT; } -int gfunc_arg_size(CType *type) { +static int gfunc_arg_size(CType *type) { int align; if (type->t & (VT_ARRAY|VT_BITFIELD)) return 8; @@ -801,7 +806,7 @@ void gfunc_call(int nb_args) bt = (sv->type.t & VT_BTYPE); size = gfunc_arg_size(&sv->type); - if (size <= 8) + if (using_regs(size)) continue; /* arguments smaller than 8 bytes passed in registers or on stack */ if (bt == VT_STRUCT) { @@ -835,7 +840,7 @@ void gfunc_call(int nb_args) bt = (vtop->type.t & VT_BTYPE); size = gfunc_arg_size(&vtop->type); - if (size > 8) { + if (!using_regs(size)) { /* align to stack align size */ size = (size + 15) & ~15; if (arg >= REGN) { @@ -895,6 +900,12 @@ void gfunc_call(int nb_args) } gcall_or_jmp(0); + + if ((vtop->r & VT_SYM) && vtop->sym->v == TOK_alloca) { + /* need to add the "func_scratch" area after alloca */ + o(0x0548), gen_le32(func_alloca), func_alloca = ind - 4; + } + /* other compilers don't clear the upper bits when returning char/short */ bt = vtop->type.ref->type.t & (VT_BTYPE | VT_UNSIGNED); if (bt == (VT_BYTE | VT_UNSIGNED)) @@ -926,6 +937,7 @@ void gfunc_prolog(CType *func_type) func_ret_sub = 0; func_scratch = 0; + func_alloca = 0; loc = 0; addr = PTR_SIZE * 2; @@ -940,7 +952,7 @@ void gfunc_prolog(CType *func_type) func_vt = sym->type; func_var = (sym->c == FUNC_ELLIPSIS); size = gfunc_arg_size(&func_vt); - if (size > 8) { + if (!using_regs(size)) { gen_modrm64(0x89, arg_regs[reg_param_index], VT_LOCAL, NULL, addr); func_vc = addr; reg_param_index++; @@ -952,7 +964,7 @@ void gfunc_prolog(CType *func_type) type = &sym->type; bt = type->t & VT_BTYPE; size = gfunc_arg_size(type); - if (size > 8) { + if (!using_regs(size)) { if (reg_param_index < REGN) { gen_modrm64(0x89, arg_regs[reg_param_index], VT_LOCAL, NULL, addr); } @@ -1015,6 +1027,13 @@ void gfunc_epilog(void) gen_le32(v); } + /* add the "func_scratch" area after each alloca seen */ + while (func_alloca) { + unsigned char *ptr = cur_text_section->data + func_alloca; + func_alloca = read32le(ptr); + write32le(ptr, func_scratch); + } + cur_text_section->data_offset = saved_ind; pe_add_unwind_data(ind, saved_ind, v); ind = cur_text_section->data_offset; |
