The common code to move a returned structure packed into registers into memory on the caller side didn't take the register size into account when allocating local storage, so sometimes that lead to stack overwrites (e.g. in 73_arm64.c), on x86_64. This fixes it by generally making gfunc_sret also return the register size.
Unified · Split
| @@ -869,18 +869,20 @@ int floats_in_core_regs(SValue *sval) · 868 unmodified lines | ||
| 869 | 869 | |
| 870 | 870 | /* Return the number of registers needed to return the struct, or 0 if |
| 871 | 871 | returning via struct pointer. */ |
| 872 | −ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align) { | |
| 872 | +ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int *regsize) { | |
| 873 | 873 | #ifdef TCC_ARM_EABI |
| 874 | 874 | int size, align; |
| 875 | 875 | size = type_size(vt, &align); |
| 876 | 876 | if (float_abi == ARM_HARD_FLOAT && !variadic && |
| 877 | 877 | (is_float(vt->t) || is_hgen_float_aggr(vt))) { |
| 878 | 878 | *ret_align = 8; |
| 879 | + *regsize = 8; | |
| 879 | 880 | ret->ref = NULL; |
| 880 | 881 | ret->t = VT_DOUBLE; |
| 881 | 882 | return (size + 7) >> 3; |
| 882 | 883 | } else if (size <= 4) { |
| 883 | 884 | *ret_align = 4; |
| 885 | + *regsize = 4; | |
| 884 | 886 | ret->ref = NULL; |
| 885 | 887 | ret->t = VT_INT; |
| 886 | 888 | return 1; |
| @@ -1255,7 +1257,7 @@ void gfunc_call(int nb_args) · 368 unmodified lines | ||
| 1255 | 1257 | void gfunc_prolog(CType *func_type) |
| 1256 | 1258 | { |
| 1257 | 1259 | Sym *sym,*sym2; |
| 1258 | − int n, nf, size, align, struct_ret = 0; | |
| 1260 | + int n, nf, size, align, rs, struct_ret = 0; | |
| 1259 | 1261 | int addr, pn, sn; /* pn=core, sn=stack */ |
| 1260 | 1262 | CType ret_type; |
| 1261 | 1263 | |
| @@ -1269,7 +1271,7 @@ void gfunc_prolog(CType *func_type) · 7 unmodified lines | ||
| 1269 | 1271 | |
| 1270 | 1272 | n = nf = 0; |
| 1271 | 1273 | if ((func_vt.t & VT_BTYPE) == VT_STRUCT && |
| 1272 | − !gfunc_sret(&func_vt, func_var, &ret_type, &align)) | |
| 1274 | + !gfunc_sret(&func_vt, func_var, &ret_type, &align, &rs)) | |
| 1273 | 1275 | { |
| 1274 | 1276 | n++; |
| 1275 | 1277 | struct_ret = 1; |
| @@ -1196,7 +1196,7 @@ ST_FUNC void gen_va_arg(CType *t) · 1195 unmodified lines | ||
| 1196 | 1196 | } |
| 1197 | 1197 | } |
| 1198 | 1198 | |
| 1199 | −ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *align) | |
| 1199 | +ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *align, int *regsize) | |
| 1200 | 1200 | { |
| 1201 | 1201 | return 0; |
| 1202 | 1202 | } |
| @@ -1881,7 +1881,7 @@ static void gcall_or_jmp(int is_jmp) · 1880 unmodified lines | ||
| 1881 | 1881 | |
| 1882 | 1882 | /* Return the number of registers needed to return the struct, or 0 if |
| 1883 | 1883 | returning via struct pointer. */ |
| 1884 | −ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align) { | |
| 1884 | +ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int *regsize) { | |
| 1885 | 1885 | *ret_align = 1; // Never have to re-align return values for x86-64 |
| 1886 | 1886 | return 0; |
| 1887 | 1887 | } |
| @@ -376,12 +376,13 @@ static uint8_t fastcallw_regs[2] = { TREG_ECX, TREG_EDX }; · 375 unmodified lines | ||
| 376 | 376 | |
| 377 | 377 | /* Return the number of registers needed to return the struct, or 0 if |
| 378 | 378 | returning via struct pointer. */ |
| 379 | −ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align) | |
| 379 | +ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int *regsize) | |
| 380 | 380 | { |
| 381 | 381 | #ifdef TCC_TARGET_PE |
| 382 | 382 | int size, align; |
| 383 | 383 | |
| 384 | 384 | *ret_align = 1; // Never have to re-align return values for x86 |
| 385 | + *regsize = 4; | |
| 385 | 386 | size = type_size(vt, &align); |
| 386 | 387 | if (size > 8) { |
| 387 | 388 | return 0; |
| @@ -1327,7 +1327,7 @@ ST_FUNC void gsym_addr(int t, int a); · 1326 unmodified lines | ||
| 1327 | 1327 | ST_FUNC void gsym(int t); |
| 1328 | 1328 | ST_FUNC void load(int r, SValue *sv); |
| 1329 | 1329 | ST_FUNC void store(int r, SValue *v); |
| 1330 | −ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *align); | |
| 1330 | +ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *align, int *regsize); | |
| 1331 | 1331 | ST_FUNC void gfunc_call(int nb_args); |
| 1332 | 1332 | ST_FUNC void gfunc_prolog(CType *func_type); |
| 1333 | 1333 | ST_FUNC void gfunc_epilog(void); |
| @@ -4153,7 +4153,7 @@ ST_FUNC void unary(void) · 4152 unmodified lines | ||
| 4153 | 4153 | } else if (tok == '(') { |
| 4154 | 4154 | SValue ret; |
| 4155 | 4155 | Sym *sa; |
| 4156 | − int nb_args, ret_nregs, ret_align, variadic; | |
| 4156 | + int nb_args, ret_nregs, ret_align, regsize, variadic; | |
| 4157 | 4157 | |
| 4158 | 4158 | /* function call */ |
| 4159 | 4159 | if ((vtop->type.t & VT_BTYPE) != VT_FUNC) { |
| @@ -4179,7 +4179,7 @@ ST_FUNC void unary(void) · 19 unmodified lines | ||
| 4179 | 4179 | if ((s->type.t & VT_BTYPE) == VT_STRUCT) { |
| 4180 | 4180 | variadic = (s->c == FUNC_ELLIPSIS); |
| 4181 | 4181 | ret_nregs = gfunc_sret(&s->type, variadic, &ret.type, |
| 4182 | − &ret_align); | |
| 4182 | + &ret_align, ®size); | |
| 4183 | 4183 | if (!ret_nregs) { |
| 4184 | 4184 | /* get some space for the returned structure */ |
| 4185 | 4185 | size = type_size(&s->type, &align); |
| @@ -4259,6 +4259,10 @@ ST_FUNC void unary(void) · 73 unmodified lines | ||
| 4259 | 4259 | int addr, offset; |
| 4260 | 4260 | |
| 4261 | 4261 | size = type_size(&s->type, &align); |
| 4262 | + /* We're writing whole regs often, make sure there's enough | |
| 4263 | + space. Assume register size is power of 2. */ | |
| 4264 | + if (regsize > align) | |
| 4265 | + align = regsize; | |
| 4262 | 4266 | loc = (loc - size) & -align; |
| 4263 | 4267 | addr = loc; |
| 4264 | 4268 | offset = 0; |
| @@ -4269,8 +4273,7 @@ ST_FUNC void unary(void) · 4 unmodified lines | ||
| 4269 | 4273 | vtop--; |
| 4270 | 4274 | if (--ret_nregs == 0) |
| 4271 | 4275 | break; |
| 4272 | − /* XXX: compatible with arm only: ret_align == register_size */ | |
| 4273 | − offset += ret_align; | |
| 4276 | + offset += regsize; | |
| 4274 | 4277 | } |
| 4275 | 4278 | vset(&s->type, VT_LOCAL | VT_LVAL, addr); |
| 4276 | 4279 | } |
| @@ -4850,9 +4853,9 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym, · 573 unmodified lines | ||
| 4850 | 4853 | #else |
| 4851 | 4854 | if ((func_vt.t & VT_BTYPE) == VT_STRUCT) { |
| 4852 | 4855 | CType type, ret_type; |
| 4853 | − int ret_align, ret_nregs; | |
| 4856 | + int ret_align, ret_nregs, regsize; | |
| 4854 | 4857 | ret_nregs = gfunc_sret(&func_vt, func_var, &ret_type, |
| 4855 | − &ret_align); | |
| 4858 | + &ret_align, ®size); | |
| 4856 | 4859 | if (0 == ret_nregs) { |
| 4857 | 4860 | /* if returning structure, must copy it to implicit |
| 4858 | 4861 | first pointer arg location */ |
| @@ -4890,9 +4893,10 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym, · 31 unmodified lines | ||
| 4890 | 4893 | /* We assume that when a structure is returned in multiple |
| 4891 | 4894 | registers, their classes are consecutive values of the |
| 4892 | 4895 | suite s(n) = 2^n */ |
| 4896 | + /* XXX This seems confused, if r == 0, this never | |
| 4897 | + changes r. */ | |
| 4893 | 4898 | r <<= 1; |
| 4894 | − /* XXX: compatible with arm only: ret_align == register_size */ | |
| 4895 | − vtop->c.i += ret_align; | |
| 4899 | + vtop->c.i += regsize; | |
| 4896 | 4900 | vtop->r = VT_LOCAL | VT_LVAL; |
| 4897 | 4901 | } |
| 4898 | 4902 | } |
| @@ -662,9 +662,10 @@ void gen_offs_sp(int b, int r, int d) · 661 unmodified lines | ||
| 662 | 662 | |
| 663 | 663 | /* Return the number of registers needed to return the struct, or 0 if |
| 664 | 664 | returning via struct pointer. */ |
| 665 | −ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align) | |
| 665 | +ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int *regsize) | |
| 666 | 666 | { |
| 667 | 667 | int size, align; |
| 668 | + *regsize = 8; | |
| 668 | 669 | *ret_align = 1; // Never have to re-align return values for x86-64 |
| 669 | 670 | size = type_size(vt, &align); |
| 670 | 671 | ret->ref = NULL; |
| @@ -1069,10 +1070,11 @@ ST_FUNC int classify_x86_64_va_arg(CType *ty) · 398 unmodified lines | ||
| 1069 | 1070 | |
| 1070 | 1071 | /* Return the number of registers needed to return the struct, or 0 if |
| 1071 | 1072 | returning via struct pointer. */ |
| 1072 | −ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align) | |
| 1073 | +ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int *regsize) | |
| 1073 | 1074 | { |
| 1074 | 1075 | int size, align, reg_count; |
| 1075 | 1076 | *ret_align = 1; // Never have to re-align return values for x86-64 |
| 1077 | + *regsize = 8; | |
| 1076 | 1078 | return (classify_x86_64_arg(vt, ret, &size, &align, ®_count) != x86_64_mode_memory); |
| 1077 | 1079 | } |
| 1078 | 1080 | |