On 5/29/25 02:35, Lino Mastrodomenico wrote:
Thanks to both of you.
The semantics of __attribute__((cleanup)) require that the entire
return expression is evaluated before the cleanup functions are
called. So saving the cleanup symbol solves most common cases but not
all of them.
E.g. this slightly modified test case returns 42 on GCC and Clang but
0x90 on the current TCC mob:
static void my_cleanup(int **p) {
**p = 0x90;
}
int test_cleanup(void) {
int n = 42;
int __attribute__((cleanup(my_cleanup))) *p = &n;
return n;
}
When looking at the source code I'm tempted to swap leave_scope and
gfunc_return as Herman did.
Il giorno sab 24 mag 2025 alle ore 23:28 grischka via Tinycc-devel
<tinycc-devel@nongnu.org> ha scritto:
On 22.05.2025 17:29, Herman ten Brugge via Tinycc-devel wrote:
> On 5/11/25 23:11, Lino Mastrodomenico wrote:
>> Hi all,
>>
>> TCC semantics for the __attribute__((cleanup)) implementation are
>> different from GCC and Clang.
>> [...]
> I pushed a fix for this.
>
> Herman
I'd think we better don't switch those statements
leave_scope(root_scope);
if (b)
gfunc_return(&func_vt);
and instead save only the cleanup symbol, specifically if it is
a SValue on vstack. See
https://repo.or.cz/tinycc.git/commitdiff/83de532563c6d922c6262dea757a22cb90d06101
I implemented it in a simpler change. This is no finished yet. See
attachment.
The REG_FRE2 define in arm-gen.c and REG_IRE2 in arm64-gen.c should be
removed
once the VT_STRUCT code in save_return_reg is implemented correctly.
In fact a lot more registers should be saved for arm-gen.c and
arm64-gen.c but
this currently works for now.
Only lightly tested.
I am going on a holiday so have no time to implement this at the moment.
Herman
diff --git a/arm-gen.c b/arm-gen.c
index ec4a2e2..afa23f9 100644
--- a/arm-gen.c
+++ b/arm-gen.c
@@ -91,6 +91,7 @@ enum {
#define REG_IRET TREG_R0 /* single word int return register */
#define REG_IRE2 TREG_R1 /* second word return register (for long long) */
#define REG_FRET TREG_F0 /* float return register */
+#define REG_FRE2 TREG_F1 /* float return register (struct return) */
#ifdef TCC_ARM_EABI
#define TOK___divdi3 TOK___aeabi_ldivmod
@@ -891,16 +892,12 @@ static void gen_bounds_epilog(void)
}
/* generate bound check local freeing */
- o(0xe92d0003); /* push {r0,r1} */
- o(0xed2d0b04); /* vpush {d0,d1} */
- o(0xe59f0000); /* ldr r0, [pc] */
- o(0xea000000); /* b $+4 */
+ save_return_reg();
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} */
+ restore_return_reg();
}
#endif
diff --git a/arm64-gen.c b/arm64-gen.c
index d15446d..a4e8266 100644
--- a/arm64-gen.c
+++ b/arm64-gen.c
@@ -29,6 +29,7 @@
#define RC_FRET (RC_F(0)) // float return register class
#define REG_IRET (TREG_R(0)) // int return register number
+#define REG_IRE2 (TREG_R(1)) // int return register number (struct return)
#define REG_FRET (TREG_F(0)) // float return register number
#define PTR_SIZE 8
@@ -726,15 +727,13 @@ static void gen_bounds_epilog(void)
}
/* generate bound check local freeing */
- o(0xa9bf07e0); /* stp x0, x1, [sp, #-16]! */
- o(0x3c9f0fe0); /* str q0, [sp, #-16]! */
+ save_return_reg();
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 */
+ restore_return_reg();
}
#endif
diff --git a/i386-gen.c b/i386-gen.c
index 5814a39..4cae558 100644
--- a/i386-gen.c
+++ b/i386-gen.c
@@ -1081,11 +1081,11 @@ static void gen_bounds_epilog(void)
}
/* generate bound check local freeing */
- o(0x5250); /* save returned value, if any */
+ save_return_reg();
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 */
+ restore_return_reg();
}
#endif
diff --git a/riscv64-gen.c b/riscv64-gen.c
index 99c94dc..6cc904e 100644
--- a/riscv64-gen.c
+++ b/riscv64-gen.c
@@ -494,16 +494,14 @@ 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) */
+ save_return_reg();
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 */
+ restore_return_reg();
}
#endif
diff --git a/tcc.h b/tcc.h
index 452a723..4b1b6bf 100644
--- a/tcc.h
+++ b/tcc.h
@@ -1608,6 +1608,8 @@ ST_FUNC int gfunc_sret(CType *vt, int variadic, CType
*ret, int *align, int *reg
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 save_return_reg(void);
+ST_FUNC void restore_return_reg();
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 5829037..038aafe 100644
--- a/tccgen.c
+++ b/tccgen.c
@@ -312,6 +312,56 @@ static int RC2_TYPE(int t, int rc)
return RC_INT;
}
+static void save_one_reg(int reg, CType type)
+{
+ int size, align, r2;
+ SValue sv;
+
+ if (reg == VT_CONST)
+ return;
+ sv.type = type;
+ size = type_size(&sv.type, &align);
+ sv.c.i = get_temp_local_var(size, align, &r2);
+ sv.r = VT_LOCAL | VT_LVAL;
+ sv.r2 = r2;
+ sv.cmp_op = reg;
+ vpushv(&sv);
+ store(reg, &sv);
+}
+
+ST_FUNC void save_return_reg(void)
+{
+ if (func_vt.t == VT_VOID)
+ return;
+ if ((func_vt.t & VT_BTYPE) == VT_STRUCT) {
+ /* TODO: optimize this. See gfunc_return. */
+ CType type;
+
+ type.t = VT_LLONG;
+ save_one_reg(REG_IRET, type);
+ save_one_reg(REG_IRE2, type);
+ type.t = VT_DOUBLE;
+ save_one_reg(REG_FRET, type);
+#ifdef REG_FRE2
+ save_one_reg(REG_FRE2, type);
+#endif
+#if defined TCC_TARGET_X86_64
+ type.t = VT_LDOUBLE;
+ save_one_reg(RC_ST0, type);
+#endif
+ }
+ else {
+ save_one_reg(R_RET(func_vt.t), func_vt);
+ save_one_reg(R2_RET(func_vt.t), func_vt);
+ }
+}
+
+ST_FUNC void restore_return_reg(void)
+{
+ for (;vtop != vstack - 1; vtop--)
+ load(vtop->cmp_op, vtop);
+}
+
/* we use our own 'finite' function to avoid potential problems with
non standard math libs */
/* XXX: endianness dependent */
@@ -6839,34 +6889,20 @@ static void end_switch(void)
/* ------------------------------------------------------------------------- */
/* __attribute__((cleanup(fn))) */
-/* save SValue of symbol to local stack */
-static void save_cleanup_sym(Sym *s)
-{
- SValue *sv = vtop;
- while (sv >= vstack) {
- if (sv->sym == s) {
- int align, size = type_size(&sv->type, &align);
- loc = (loc - size) & -align;
- vset(&sv->type, VT_LOCAL | VT_LVAL, loc);
- vpushv(sv), *sv = vtop[-1], vstore(), --vtop;
- }
- --sv;
- }
-}
-
static void try_call_scope_cleanup(Sym *stop)
{
Sym *cls = cur_scope->cl.s;
for (; cls != stop; cls = cls->next) {
Sym *fs = cls->cleanup_func;
Sym *vs = cls->prev_tok;
- save_cleanup_sym(vs);
+ save_return_reg();
vpushsym(&fs->type, fs);
vset(&vs->type, vs->r, vs->c);
vtop->sym = vs;
mk_pointer(&vtop->type);
gaddrof();
gfunc_call(1);
+ restore_return_reg();
}
}
@@ -7124,9 +7160,9 @@ again:
tcc_warning("'return' with no value");
b = 0;
}
- leave_scope(root_scope);
if (b)
gfunc_return(&func_vt);
+ leave_scope(root_scope);
skip(';');
/* jump unless last stmt in top-level block */
if (tok != '}' || local_scope != 1)
diff --git a/x86_64-gen.c b/x86_64-gen.c
index 3a43058..4bc8ed9 100644
--- a/x86_64-gen.c
+++ b/x86_64-gen.c
@@ -709,20 +709,12 @@ 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) */
+ save_return_reg();
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 */
+ restore_return_reg();
}
#endif
_______________________________________________
Tinycc-devel mailing list
Tinycc-devel@nongnu.org
https://lists.nongnu.org/mailman/listinfo/tinycc-devel