From b6a16e3be4aff2d6b6e4870a1fde5e83707e9be4 Mon Sep 17 00:00:00 2001 From: herman ten brugge Date: Thu, 22 May 2025 16:58:12 +0200 Subject: [PATCH] Save registers around attribute cleanup This makes attribute cleanup code work the same as gcc and also makes bound checking a very little bit faster. tcc.h: Add save_return_reg(CType *) and restore_return_reg(CType *) Change gfunc_epilog() to gfunc_epilog(Sym *) arm-gen.c: arm64-gen.c: c67-gen.c: i386-gen.c: il-gen.c: riscv64-gen.c: x86_64-gen.c: Move save and restore register around bound_local_delete call to save_return_reg and restore_return_reg. Pass func_type from gfunc_epilog to gen_bounds_epilog. tccgen.c: Call save_return_reg/restore_return_reg in try_call_scope_cleanup when RETURN is found. tccrun.c: Fix warning when bound checking not used. tests/tests2/101_cleanup.c tests/tests2/101_cleanup.expect Extra checks attribute cleanup save/restore registers. tests/tests2/Makefile: Fix when bound checking not used. --- arm-gen.c | 43 ++++++++++++++--- arm64-gen.c | 42 ++++++++++++++--- c67-gen.c | 17 ++++++- i386-gen.c | 32 ++++++++++--- il-gen.c | 15 +++++- riscv64-gen.c | 57 +++++++++++++++++++---- tcc.h | 4 +- tccgen.c | 28 ++++++----- tccrun.c | 2 + tests/tests2/101_cleanup.c | 82 +++++++++++++++++++++++++++++++++ tests/tests2/101_cleanup.expect | 10 ++++ tests/tests2/Makefile | 1 + x86_64-gen.c | 72 +++++++++++++++++++++++------ 13 files changed, 346 insertions(+), 59 deletions(-) diff --git a/arm-gen.c b/arm-gen.c index ec4a2e25..9aa6e7ae 100644 --- a/arm-gen.c +++ b/arm-gen.c @@ -837,6 +837,32 @@ static void gcall_or_jmp(int is_jmp) } } +ST_FUNC void save_return_reg(CType *func_type) +{ + int freg = is_float(func_type->t & VT_BTYPE); + int ireg = !freg; + + if ((func_type->t & VT_BTYPE) == VT_STRUCT) + ireg = freg = 1; + if (ireg) + o(0xe92d0003); /* push {r0,r1} */ + if (freg) + o(0xed2d0b04); /* vpush {d0,d1} */ +} + +ST_FUNC void restore_return_reg(CType *func_type) +{ + int freg = is_float(func_type->t & VT_BTYPE); + int ireg = !freg; + + if ((func_type->t & VT_BTYPE) == VT_STRUCT) + ireg = freg = 1; + if (freg) + o(0xecbd0b04); /* vpop {d0,d1} */ + if (ireg) + o(0xe8bd0003); /* pop {r0,r1} */ +} + #if defined(CONFIG_TCC_BCHECK) static void gen_bounds_call(int v) @@ -860,12 +886,13 @@ static void gen_bounds_prolog(void) o(0xe1a00000); /* call __bound_local_new */ } -static void gen_bounds_epilog(void) +static void gen_bounds_epilog(Sym *func_sym) { addr_t saved_ind; addr_t *bounds_ptr; Sym *sym_data; int offset_modified = func_bound_offset != lbounds_section->data_offset; + CType *func_type = &func_sym->type.ref->type; if (!offset_modified && !func_bound_add_epilog) return; @@ -891,16 +918,16 @@ static void gen_bounds_epilog(void) } /* generate bound check local freeing */ - o(0xe92d0003); /* push {r0,r1} */ - o(0xed2d0b04); /* vpush {d0,d1} */ + if (func_type->t != VT_VOID) + save_return_reg(func_type); o(0xe59f0000); /* ldr r0, [pc] */ o(0xea000000); /* b $+4 */ greloc(cur_text_section, sym_data, ind, R_ARM_REL32); o(-12); /* lbounds_section->data_offset */ o(0xe080000f); /* add r0,r0,pc */ gen_bounds_call(TOK___bound_local_delete); - o(0xecbd0b04); /* vpop {d0,d1} */ - o(0xe8bd0003); /* pop {r0,r1} */ + if (func_type->t != VT_VOID) + restore_return_reg(func_type); } #endif @@ -1508,15 +1535,17 @@ from_stack: } /* generate function epilog */ -void gfunc_epilog(void) +void gfunc_epilog(Sym *func_sym) { uint32_t x; int diff; #ifdef CONFIG_TCC_BCHECK if (tcc_state->do_bounds_check) - gen_bounds_epilog(); + gen_bounds_epilog(func_sym); #endif + func_sym = NULL; + /* Copy float return value to core register if base standard is used and float computation is made with VFP */ #if defined(TCC_ARM_EABI) && defined(TCC_ARM_VFP) diff --git a/arm64-gen.c b/arm64-gen.c index d15446d3..a10a3d72 100644 --- a/arm64-gen.c +++ b/arm64-gen.c @@ -674,6 +674,32 @@ static void arm64_gen_bl_or_b(int b) } } +ST_FUNC void save_return_reg(CType *func_type) +{ + int freg = is_float(func_type->t & VT_BTYPE); + int ireg = !freg; + + if ((func_type->t & VT_BTYPE) == VT_STRUCT) + ireg = freg = 1; + if (ireg) + o(0xa9bf07e0); /* stp x0, x1, [sp, #-16]! */ + if (freg) + o(0xadbf07e0); /* stp q0, q1, [sp, #-32]! */ +} + +ST_FUNC void restore_return_reg(CType *func_type) +{ + int freg = is_float(func_type->t & VT_BTYPE); + int ireg = !freg; + + if ((func_type->t & VT_BTYPE) == VT_STRUCT) + ireg = freg = 1; + if (freg) + o(0xacc107e0); /* ldp q0, q1, [sp], #32 */ + if (ireg) + o(0xa8c107e0); /* ldp x0, x1, [sp], #16 */ +} + #if defined(CONFIG_TCC_BCHECK) static void gen_bounds_call(int v) @@ -696,12 +722,13 @@ static void gen_bounds_prolog(void) o(0xd503201f); /* nop -> call __bound_local_new */ } -static void gen_bounds_epilog(void) +static void gen_bounds_epilog(Sym *func_sym) { addr_t saved_ind; addr_t *bounds_ptr; Sym *sym_data; int offset_modified = func_bound_offset != lbounds_section->data_offset; + CType *func_type = &func_sym->type.ref->type; if (!offset_modified && !func_bound_add_epilog) return; @@ -726,15 +753,15 @@ static void gen_bounds_epilog(void) } /* generate bound check local freeing */ - o(0xa9bf07e0); /* stp x0, x1, [sp, #-16]! */ - o(0x3c9f0fe0); /* str q0, [sp, #-16]! */ + if (func_type->t != VT_VOID) + save_return_reg(func_type); greloca(cur_text_section, sym_data, ind, R_AARCH64_ADR_GOT_PAGE, 0); o(0x90000000 | 0); // adrp x0, #sym_data greloca(cur_text_section, sym_data, ind, R_AARCH64_LD64_GOT_LO12_NC, 0); o(0xf9400000 | 0 | (0 << 5)); // ld x0,[x0, #sym_data] gen_bounds_call(TOK___bound_local_delete); - o(0x3cc107e0); /* ldr q0, [sp], #16 */ - o(0xa8c107e0); /* ldp x0, x1, [sp], #16 */ + if (func_type->t != VT_VOID) + restore_return_reg(func_type); } #endif @@ -1459,12 +1486,13 @@ ST_FUNC void gfunc_return(CType *func_type) vtop--; } -ST_FUNC void gfunc_epilog(void) +ST_FUNC void gfunc_epilog(Sym *func_sym) { #ifdef CONFIG_TCC_BCHECK if (tcc_state->do_bounds_check) - gen_bounds_epilog(); + gen_bounds_epilog(func_sym); #endif + func_sym = NULL; if (loc) { // Insert instructions to subtract size of stack frame from SP. diff --git a/c67-gen.c b/c67-gen.c index 9490a27f..bafedc01 100644 --- a/c67-gen.c +++ b/c67-gen.c @@ -2019,9 +2019,22 @@ void gfunc_prolog(Sym *func_sym) C67_PUSH(C67_B3); } -/* generate function epilog */ -void gfunc_epilog(void) +ST_FUNC void save_return_reg(CType *func_type) { + func_type = NULL; + // TODO +} + +ST_FUNC void restore_return_reg(CType *func_type) +{ + func_type = NULL; + // TODO +} + +/* generate function epilog */ +void gfunc_epilog(Sym *func_sym) +{ + func_sym = NULL; { int local = (-loc + 7) & -8; // stack must stay aligned to 8 bytes for LDDW instr C67_POP(C67_B3); diff --git a/i386-gen.c b/i386-gen.c index 5814a398..5e9a9e5c 100644 --- a/i386-gen.c +++ b/i386-gen.c @@ -104,7 +104,7 @@ static 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); +static void gen_bounds_epilog(Sym *func_sym); #endif /* XXX: make it faster ? */ @@ -595,14 +595,15 @@ ST_FUNC void gfunc_prolog(Sym *func_sym) } /* generate function epilog */ -ST_FUNC void gfunc_epilog(void) +ST_FUNC void gfunc_epilog(Sym *func_sym) { addr_t v, saved_ind; #ifdef CONFIG_TCC_BCHECK if (tcc_state->do_bounds_check) - gen_bounds_epilog(); + gen_bounds_epilog(func_sym); #endif + func_sym = NULL; /* align local size to word & save local variables */ v = (-loc + 3) & -4; @@ -1040,6 +1041,22 @@ ST_FUNC void ggoto(void) vtop--; } +ST_FUNC void save_return_reg(CType *func_type) +{ + int ireg = !is_float(func_type->t & VT_BTYPE); + + if (ireg) + o(0x5250); /* push %rax; %push %rdx */ +} + +ST_FUNC void restore_return_reg(CType *func_type) +{ + int ireg = !is_float(func_type->t & VT_BTYPE); + + if (ireg) + o(0x585a); /* pop %rdx; pop %rax */ +} + /* bound check support functions */ #ifdef CONFIG_TCC_BCHECK @@ -1053,12 +1070,13 @@ static void gen_bounds_prolog(void) oad(0xb8, 0); /* call to function */ } -static void gen_bounds_epilog(void) +static void gen_bounds_epilog(Sym *func_sym) { addr_t saved_ind; addr_t *bounds_ptr; Sym *sym_data; int offset_modified = func_bound_offset != lbounds_section->data_offset; + CType *func_type = &func_sym->type.ref->type; if (!offset_modified && !func_bound_add_epilog) return; @@ -1081,11 +1099,13 @@ static void gen_bounds_epilog(void) } /* generate bound check local freeing */ - o(0x5250); /* save returned value, if any */ + if (func_type->t != VT_VOID) + save_return_reg(func_type); 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 */ + if (func_type->t != VT_VOID) + restore_return_reg(func_type); } #endif diff --git a/il-gen.c b/il-gen.c index bb670ccb..a00d6786 100644 --- a/il-gen.c +++ b/il-gen.c @@ -457,9 +457,20 @@ void gfunc_prolog(int t) } } -/* generate function epilog */ -void gfunc_epilog(void) +void save_return_reg(CType *func_type) { + func_type = NULL; +} + +void restore_return_reg(CType *func_type) +{ + func_type = NULL; +} + +/* generate function epilog */ +void gfunc_epilog(Sym *func_sym) +{ + func_sym = NULL; out_op(IL_OP_RET); fprintf(il_outfile, "}\n\n"); } diff --git a/riscv64-gen.c b/riscv64-gen.c index 99c94dc3..c38c996f 100644 --- a/riscv64-gen.c +++ b/riscv64-gen.c @@ -436,6 +436,46 @@ static void gcall_or_jmp(int docall) } } +ST_FUNC void save_return_reg(CType *func_type) +{ + int freg = is_float(func_type->t & VT_BTYPE) && + (func_type->t & VT_BTYPE) != VT_LDOUBLE; + int ireg = !freg; + + if ((func_type->t & VT_BTYPE) == VT_STRUCT) + ireg = freg = 1; + if (ireg && freg) { + o(0xe02a1101); /* addi sp,sp,-32 sd a0,0(sp) */ + o(0xa82ae42e); /* sd a1,8(sp) fsd fa0,16(sp) */ + } + else if (ireg) { + o(0xe02a1141); /* addi sp,sp,-16 sd a0,0(sp) */ + o(0x0001e42e); /* sd a1,8(sp) nop */ + } + else if (freg) + o(0xa02a1141); /* addi sp,sp,-16 fsd fa0,0(sp) */ +} + +ST_FUNC void restore_return_reg(CType *func_type) +{ + int freg = is_float(func_type->t & VT_BTYPE) && + (func_type->t & VT_BTYPE) != VT_LDOUBLE; + int ireg = !freg; + + if ((func_type->t & VT_BTYPE) == VT_STRUCT) + ireg = freg = 1; + if (ireg && freg) { + o(0x65a26502); /* ld a0,0(sp) ld a1,8(sp) */ + o(0x61052542); /* fld fa0,16(sp) addi sp,sp,32 */ + } + else if (ireg) { + o(0x65a26502); /* ld a0,0(sp) ld a1,8(sp) */ + o(0x00010141); /* addi sp,sp,16 nop */ + } + else if (freg) + o(0x01412502); /* fld fa0,0(sp) addi sp,sp,16 */ +} + #if defined(CONFIG_TCC_BCHECK) static void gen_bounds_call(int v) @@ -459,14 +499,14 @@ static void gen_bounds_prolog(void) o(0x00000013); } -static void gen_bounds_epilog(void) +static void gen_bounds_epilog(Sym *func_sym) { addr_t saved_ind; addr_t *bounds_ptr; Sym *sym_data; Sym label = {0}; - int offset_modified = func_bound_offset != lbounds_section->data_offset; + CType *func_type = &func_sym->type.ref->type; if (!offset_modified && !func_bound_add_epilog) return; @@ -494,16 +534,16 @@ static void gen_bounds_epilog(void) } /* generate bound check local freeing */ - o(0xe02a1101); /* addi sp,sp,-32 sd a0,0(sp) */ - o(0xa82ae42e); /* sd a1,8(sp) fsd fa0,16(sp) */ + if (func_type->t != VT_VOID) + save_return_reg(func_type); put_extern_sym(&label, cur_text_section, ind, 0); greloca(cur_text_section, sym_data, ind, R_RISCV_GOT_HI20, 0); o(0x17 | (10 << 7)); // auipc a0, 0 %pcrel_hi(sym)+addend greloca(cur_text_section, &label, ind, R_RISCV_PCREL_LO12_I, 0); EI(0x03, 3, 10, 10, 0); // ld a0, 0(a0) gen_bounds_call(TOK___bound_local_delete); - o(0x65a26502); /* ld a0,0(sp) ld a1,8(sp) */ - o(0x61052542); /* fld fa0,16(sp) addi sp,sp,32 */ + if (func_type->t != VT_VOID) + restore_return_reg(func_type); } #endif @@ -893,14 +933,15 @@ ST_FUNC void arch_transfer_ret_regs(int aftercall) vtop--; } -ST_FUNC void gfunc_epilog(void) +ST_FUNC void gfunc_epilog(Sym *func_sym) { int v, saved_ind, d, large_ofs_ind; #ifdef CONFIG_TCC_BCHECK if (tcc_state->do_bounds_check) - gen_bounds_epilog(); + gen_bounds_epilog(func_sym); #endif + func_sym = NULL; loc = (loc - num_va_regs * 8); d = v = (-loc + 15) & -16; diff --git a/tcc.h b/tcc.h index 452a723a..debc96d7 100644 --- a/tcc.h +++ b/tcc.h @@ -1607,7 +1607,9 @@ ST_FUNC void store(int r, SValue *v); ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *align, int *regsize); ST_FUNC void gfunc_call(int nb_args); ST_FUNC void gfunc_prolog(Sym *func_sym); -ST_FUNC void gfunc_epilog(void); +ST_FUNC void gfunc_epilog(Sym *func_sym); +ST_FUNC void save_return_reg(CType *func_type); +ST_FUNC void restore_return_reg(CType *func_type); ST_FUNC void gen_fill_nops(int); ST_FUNC int gjmp(int t); ST_FUNC void gjmp_addr(int a); diff --git a/tccgen.c b/tccgen.c index d4106ac2..a1b1b80c 100644 --- a/tccgen.c +++ b/tccgen.c @@ -6839,10 +6839,14 @@ static void end_switch(void) /* ------------------------------------------------------------------------- */ /* __attribute__((cleanup(fn))) */ -static void try_call_scope_cleanup(Sym *stop) +static void try_call_scope_cleanup(Sym *stop, CType *func_type) { Sym *cls = cur_scope->cl.s; + if (cls == stop) + func_type = NULL; + if (func_type && func_type->t != VT_VOID) + save_return_reg(func_type); for (; cls != stop; cls = cls->next) { Sym *fs = cls->cleanup_func; Sym *vs = cls->prev_tok; @@ -6854,6 +6858,8 @@ static void try_call_scope_cleanup(Sym *stop) gaddrof(); gfunc_call(1); } + if (func_type && func_type->t != VT_VOID) + restore_return_reg(func_type); } static void try_call_cleanup_goto(Sym *cleanupstate) @@ -6873,7 +6879,7 @@ static void try_call_cleanup_goto(Sym *cleanupstate) for (; cc != oc; cc = cc->next, oc = oc->next, --ccd) ; - try_call_scope_cleanup(cc); + try_call_scope_cleanup(cc, NULL); } /* call 'func' for each __attribute__((cleanup(func))) */ @@ -6887,7 +6893,7 @@ static void block_cleanup(struct scope *o) if (!jmp) jmp = gjmp(0); gsym(pcl->jnext); - try_call_scope_cleanup(o->cl.s); + try_call_scope_cleanup(o->cl.s, NULL); pcl->jnext = gjmp(0); if (!o->cl.n) goto remove_pending; @@ -6900,7 +6906,7 @@ static void block_cleanup(struct scope *o) } } gsym(jmp); - try_call_scope_cleanup(o->cl.s); + try_call_scope_cleanup(o->cl.s, NULL); } /* ------------------------------------------------------------------------- */ @@ -6964,11 +6970,11 @@ static void prev_scope(struct scope *o, int is_expr) } /* leave a scope via break/continue(/goto) */ -static void leave_scope(struct scope *o) +static void leave_scope(struct scope *o, CType *func_type) { if (!o) return; - try_call_scope_cleanup(o->cl.s); + try_call_scope_cleanup(o->cl.s, func_type); vla_leave(o); } @@ -7110,9 +7116,9 @@ again: tcc_warning("'return' with no value"); b = 0; } - leave_scope(root_scope); if (b) gfunc_return(&func_vt); + leave_scope(root_scope, &func_vt); skip(';'); /* jump unless last stmt in top-level block */ if (tok != '}' || local_scope != 1) @@ -7126,9 +7132,9 @@ again: if (!cur_scope->bsym) tcc_error("cannot break"); if (cur_switch && cur_scope->bsym == cur_switch->bsym) - leave_scope(cur_switch->scope); + leave_scope(cur_switch->scope, NULL); else - leave_scope(loop_scope); + leave_scope(loop_scope, NULL); *cur_scope->bsym = gjmp(*cur_scope->bsym); skip(';'); @@ -7136,7 +7142,7 @@ again: /* compute jump */ if (!cur_scope->csym) tcc_error("cannot continue"); - leave_scope(loop_scope); + leave_scope(loop_scope, NULL); *cur_scope->csym = gjmp(*cur_scope->csym); skip(';'); @@ -8400,7 +8406,7 @@ static void gen_function(Sym *sym) /* reset local stack */ pop_local_syms(NULL, 0); tcc_debug_prolog_epilog(tcc_state, 1); - gfunc_epilog(); + gfunc_epilog(sym); /* end of function */ tcc_debug_funcend(tcc_state, ind - func_ind); diff --git a/tccrun.c b/tccrun.c index 6c0dab6d..37e6c9bf 100644 --- a/tccrun.c +++ b/tccrun.c @@ -495,7 +495,9 @@ static void bt_link(TCCState *s1) { #ifdef CONFIG_TCC_BACKTRACE rt_context *rc; +#ifdef CONFIG_TCC_BCHECK void *p; +#endif if (!s1->do_backtrace) return; diff --git a/tests/tests2/101_cleanup.c b/tests/tests2/101_cleanup.c index de5dca27..125b768a 100644 --- a/tests/tests2/101_cleanup.c +++ b/tests/tests2/101_cleanup.c @@ -1,9 +1,15 @@ extern int printf(const char*, ...); static int glob_i = 0; +typedef struct { int a; int b; int c; int d; int e; int f; int g; int h; } tstl; +typedef struct { int a; int b; int c; int d; } tsti; +typedef struct { double a; double b; } tstd; +typedef struct { long double a; } tstld; + void incr_glob_i(int *i) { glob_i += *i; + *i = -1; } #define INCR_GI { \ @@ -71,17 +77,20 @@ void check(int *j) goto out; } } + *j = -1; return; } void check_oh_i(char *oh_i) { printf("c: %c\n", *oh_i); + *oh_i = '0'; } void goto_hell(double *f) { printf("oo: %f\n", *f); + *f = -1.0; } char *test() @@ -152,6 +161,7 @@ int test3(void) { void cl(int *ip) { printf("%d\n", *ip); + *ip = -1; } void loop_cleanups(void) @@ -191,11 +201,72 @@ void loop_cleanups(void) printf("after break\n"); } +void my_cleanup1(int *p) { + printf("%d\n", *p); + *p = 0x90; +} + +int test_cleanup1(void) { + int __attribute__((cleanup(my_cleanup1))) n = 42; + return n; +} + +void my_cleanup2(tstl *p) { + printf("%d %d %d %d %d %d %d %d\n", p->a, p->b, p->c, p->d, + p->e, p->f, p->g, p->h); + p->a = 0x90; p->b = 0x91; p->c = 0x92; p->d = 0x93; + p->e = 0x94; p->f = 0x95; p->g = 0x96; p->h = 0x97; +} + +tstl test_cleanup2(void) { + tstl __attribute__((cleanup(my_cleanup2))) n; + n.a = 42; n.b = 43; n.c = 44; n.d = 45; + n.e = 46; n.f = 47; n.g = 48; n.h = 49; + return n; +} + +void my_cleanup3(tsti *p) { + printf("%d %d %d %d\n", p->a, p->b, p->c, p->d); + p->a = 0x90; p->b = 0x91; p->c = 0x92; p->d = 0x93; +} + +tsti test_cleanup3(void) { + tsti __attribute__((cleanup(my_cleanup3))) n; + n.a = 42; n.b = 43; n.c = 44; n.d = 45; + return n; +} + +void my_cleanup4(tstd *p) { + printf("%g %g\n", p->a, p->b); + p->a = 90.0; p->b = 91.0; +} + +tstd test_cleanup4(void) { + tstd __attribute__((cleanup(my_cleanup4))) n; + n.a = 42.0; n.b = 43.0; + return n; +} + +void my_cleanup5(tstld *p) { + printf("%Lf\n", p->a); + p->a = 90.0; +} + +tstld test_cleanup5(void) { + tstld __attribute__((cleanup(my_cleanup5))) n; + n.a = 42.0; + return n; +} + int main() { int i __attribute__ ((__cleanup__(check))) = 0, not_i; int chk = 0; (void)not_i; + tstl tl; + tsti ti; + tstd td; + tstld tld; { __attribute__ ((__cleanup__(check_oh_i))) char oh_i = 'o', o = 'a'; @@ -218,10 +289,21 @@ int main() test2(); test3(); loop_cleanups(); + printf("%d\n", test_cleanup1()); + tl = test_cleanup2(); + printf("%d %d %d %d %d %d %d %d\n", tl.a, tl.b, tl.c, tl.d, + tl.e, tl.f, tl.g, tl.h); + ti = test_cleanup3(); + printf("%d %d %d %d\n", ti.a, ti.b, ti.c, ti.d); + td = test_cleanup4(); + printf("%g %g\n", td.a, td.b); + tld = test_cleanup5(); + printf("%Lf\n", tld.a); return i; } void check2(char **hum) { printf("str: %s\n", *hum); + *hum = "fail"; } diff --git a/tests/tests2/101_cleanup.expect b/tests/tests2/101_cleanup.expect index 84960cd5..7c3d2f88 100644 --- a/tests/tests2/101_cleanup.expect +++ b/tests/tests2/101_cleanup.expect @@ -48,6 +48,16 @@ str: three 400 after break 1000 +42 +42 +42 43 44 45 46 47 48 49 +42 43 44 45 46 47 48 49 +42 43 44 45 +42 43 44 45 +42 43 +42 43 +42.000000 +42.000000 ---- 0 ---- 1 str: plop diff --git a/tests/tests2/Makefile b/tests/tests2/Makefile index f485b254..0726e99d 100644 --- a/tests/tests2/Makefile +++ b/tests/tests2/Makefile @@ -31,6 +31,7 @@ ifeq ($(CONFIG_bcheck),no) SKIP += 116_bound_setjmp2.test SKIP += 117_builtins.test SKIP += 126_bound_global.test + SKIP += 132_bound_test.test endif ifeq ($(CONFIG_dll),no) SKIP += 113_btdll.test # no shared lib support yet diff --git a/x86_64-gen.c b/x86_64-gen.c index 3a430585..d4c4c650 100644 --- a/x86_64-gen.c +++ b/x86_64-gen.c @@ -655,6 +655,50 @@ static void gcall_or_jmp(int is_jmp) } } +ST_FUNC void save_return_reg(CType *func_type) +{ + int freg = is_float(func_type->t & VT_BTYPE); + int ireg = !freg; + + if ((func_type->t & VT_BTYPE) == VT_STRUCT) + ireg = freg = 1; + if (ireg) + o(0x5250); /* push %rax; %push %rdx */ + if (freg) { + if ((func_type->t & VT_BTYPE) == VT_LDOUBLE || + (func_type->t & VT_BTYPE) == VT_STRUCT) { + o(0x10ec8348); /* sub $16,%rsp */ + o(0x243cdb); /* fstpt (%rsp) */ + } + o(0x20ec8348); /* sub $32,%rsp */ + o(0x290f); /* movaps %xmm0,0x10(%rsp) */ + o(0x102444); + o(0x240c290f); /* movaps %xmm1,(%rsp) */ + } +} + +ST_FUNC void restore_return_reg(CType *func_type) +{ + int freg = is_float(func_type->t & VT_BTYPE); + int ireg = !freg; + + if ((func_type->t & VT_BTYPE) == VT_STRUCT) + ireg = freg = 1; + if (freg) { + o(0x280f); /* movaps 0x10(%rsp),%xmm0 */ + o(0x102444); + o(0x240c280f); /* movaps (%rsp),%xmm1 */ + o(0x20c48348); /* add $32,%rsp */ + if ((func_type->t & VT_BTYPE) == VT_LDOUBLE || + (func_type->t & VT_BTYPE) == VT_STRUCT) { + o(0x242cdb); /* fldt (%rsp) */ + o(0x10c48348); /* add $16,%rsp */ + } + } + if (ireg) + o(0x585a); /* pop %rdx; pop %rax */ +} + #if defined(CONFIG_TCC_BCHECK) static void gen_bounds_call(int v) @@ -681,12 +725,13 @@ static void gen_bounds_prolog(void) oad(0xb8, 0); /* call to function */ } -static void gen_bounds_epilog(void) +static void gen_bounds_epilog(Sym *func_sym) { addr_t saved_ind; addr_t *bounds_ptr; Sym *sym_data; int offset_modified = func_bound_offset != lbounds_section->data_offset; + CType *func_type = &func_sym->type.ref->type; if (!offset_modified && !func_bound_add_epilog) return; @@ -709,20 +754,14 @@ static void gen_bounds_epilog(void) } /* generate bound check local freeing */ - o(0x5250); /* save returned value, if any */ - o(0x20ec8348); /* sub $32,%rsp */ - o(0x290f); /* movaps %xmm0,0x10(%rsp) */ - o(0x102444); - o(0x240c290f); /* movaps %xmm1,(%rsp) */ + if (func_type->t != VT_VOID) + save_return_reg(func_type); greloca(cur_text_section, sym_data, ind + 3, R_X86_64_PC32, -4); o(0x0d8d48 + ((TREG_FASTCALL_1 == TREG_RDI) * 0x300000)); /* lea xxx(%rip), %rcx/rdi */ gen_le32 (0); gen_bounds_call(TOK___bound_local_delete); - o(0x280f); /* movaps 0x10(%rsp),%xmm0 */ - o(0x102444); - o(0x240c280f); /* movaps (%rsp),%xmm1 */ - o(0x20c48348); /* add $32,%rsp */ - o(0x585a); /* restore returned value, if any */ + if (func_type->t != VT_VOID) + restore_return_reg(func_type); } #endif @@ -1005,7 +1044,7 @@ void gfunc_prolog(Sym *func_sym) } /* generate function epilog */ -void gfunc_epilog(void) +void gfunc_epilog(Sym *func_sym) { int v, start; @@ -1015,8 +1054,9 @@ void gfunc_epilog(void) #ifdef CONFIG_TCC_BCHECK if (tcc_state->do_bounds_check) - gen_bounds_epilog(); + gen_bounds_epilog(func_sym); #endif + func_sym = NULL; o(0xc9); /* leave */ if (func_ret_sub == 0) { @@ -1600,14 +1640,16 @@ void gfunc_prolog(Sym *func_sym) } /* generate function epilog */ -void gfunc_epilog(void) +void gfunc_epilog(Sym *func_sym) { int v, saved_ind; #ifdef CONFIG_TCC_BCHECK if (tcc_state->do_bounds_check) - gen_bounds_epilog(); + gen_bounds_epilog(func_sym); #endif + func_sym = NULL; + o(0xc9); /* leave */ if (func_ret_sub == 0) { o(0xc3); /* ret */