diff --git a/arm-gen.c b/arm-gen.c index 3bb5326..0201cd2 100644 --- a/arm-gen.c +++ b/arm-gen.c @@ -869,18 +869,20 @@ int floats_in_core_regs(SValue *sval) /* 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) { +ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int *regsize) { #ifdef TCC_ARM_EABI int size, align; size = type_size(vt, &align); if (float_abi == ARM_HARD_FLOAT && !variadic && (is_float(vt->t) || is_hgen_float_aggr(vt))) { *ret_align = 8; + *regsize = 8; ret->ref = NULL; ret->t = VT_DOUBLE; return (size + 7) >> 3; } else if (size <= 4) { *ret_align = 4; + *regsize = 4; ret->ref = NULL; ret->t = VT_INT; return 1; @@ -1255,7 +1257,7 @@ void gfunc_call(int nb_args) void gfunc_prolog(CType *func_type) { Sym *sym,*sym2; - int n, nf, size, align, struct_ret = 0; + int n, nf, size, align, rs, struct_ret = 0; int addr, pn, sn; /* pn=core, sn=stack */ CType ret_type; @@ -1269,7 +1271,7 @@ void gfunc_prolog(CType *func_type) n = nf = 0; if ((func_vt.t & VT_BTYPE) == VT_STRUCT && - !gfunc_sret(&func_vt, func_var, &ret_type, &align)) + !gfunc_sret(&func_vt, func_var, &ret_type, &align, &rs)) { n++; struct_ret = 1; diff --git a/arm64-gen.c b/arm64-gen.c index 3581e5d..0c435d9 100644 --- a/arm64-gen.c +++ b/arm64-gen.c @@ -1196,7 +1196,7 @@ ST_FUNC void gen_va_arg(CType *t) } } -ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *align) +ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *align, int *regsize) { return 0; } diff --git a/c67-gen.c b/c67-gen.c index 5a6fd56..2182518 100644 --- a/c67-gen.c +++ b/c67-gen.c @@ -1881,7 +1881,7 @@ static void gcall_or_jmp(int is_jmp) /* 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) { +ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int *regsize) { *ret_align = 1; // Never have to re-align return values for x86-64 return 0; } diff --git a/i386-gen.c b/i386-gen.c index 9bdf998..c2db3d0 100644 --- a/i386-gen.c +++ b/i386-gen.c @@ -376,12 +376,13 @@ 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) +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) { return 0; diff --git a/tcc.h b/tcc.h index 7d53b9b..759d3f4 100644 --- a/tcc.h +++ b/tcc.h @@ -1327,7 +1327,7 @@ ST_FUNC void gsym_addr(int t, int a); ST_FUNC void gsym(int t); ST_FUNC void load(int r, SValue *sv); ST_FUNC void store(int r, SValue *v); -ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *align); +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(CType *func_type); ST_FUNC void gfunc_epilog(void); diff --git a/tccgen.c b/tccgen.c index 52541e2..58c8b13 100644 --- a/tccgen.c +++ b/tccgen.c @@ -4153,7 +4153,7 @@ ST_FUNC void unary(void) } else if (tok == '(') { SValue ret; Sym *sa; - int nb_args, ret_nregs, ret_align, variadic; + int nb_args, ret_nregs, ret_align, regsize, variadic; /* function call */ if ((vtop->type.t & VT_BTYPE) != VT_FUNC) { @@ -4179,7 +4179,7 @@ ST_FUNC void unary(void) if ((s->type.t & VT_BTYPE) == VT_STRUCT) { variadic = (s->c == FUNC_ELLIPSIS); ret_nregs = gfunc_sret(&s->type, variadic, &ret.type, - &ret_align); + &ret_align, ®size); if (!ret_nregs) { /* get some space for the returned structure */ size = type_size(&s->type, &align); @@ -4259,6 +4259,10 @@ ST_FUNC void unary(void) int addr, offset; size = type_size(&s->type, &align); + /* We're writing whole regs often, make sure there's enough + space. Assume register size is power of 2. */ + if (regsize > align) + align = regsize; loc = (loc - size) & -align; addr = loc; offset = 0; @@ -4269,8 +4273,7 @@ ST_FUNC void unary(void) vtop--; if (--ret_nregs == 0) break; - /* XXX: compatible with arm only: ret_align == register_size */ - offset += ret_align; + offset += regsize; } vset(&s->type, VT_LOCAL | VT_LVAL, addr); } @@ -4850,9 +4853,9 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym, #else if ((func_vt.t & VT_BTYPE) == VT_STRUCT) { CType type, ret_type; - int ret_align, ret_nregs; + int ret_align, ret_nregs, regsize; ret_nregs = gfunc_sret(&func_vt, func_var, &ret_type, - &ret_align); + &ret_align, ®size); if (0 == ret_nregs) { /* if returning structure, must copy it to implicit first pointer arg location */ @@ -4890,9 +4893,10 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym, /* We assume that when a structure is returned in multiple registers, their classes are consecutive values of the suite s(n) = 2^n */ + /* XXX This seems confused, if r == 0, this never + changes r. */ r <<= 1; - /* XXX: compatible with arm only: ret_align == register_size */ - vtop->c.i += ret_align; + vtop->c.i += regsize; vtop->r = VT_LOCAL | VT_LVAL; } } diff --git a/x86_64-gen.c b/x86_64-gen.c index 67aaadc..d5ed4f6 100644 --- a/x86_64-gen.c +++ b/x86_64-gen.c @@ -662,9 +662,10 @@ void gen_offs_sp(int b, int r, int d) /* 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) +ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int *regsize) { int size, align; + *regsize = 8; *ret_align = 1; // Never have to re-align return values for x86-64 size = type_size(vt, &align); ret->ref = NULL; @@ -1069,10 +1070,11 @@ ST_FUNC int classify_x86_64_va_arg(CType *ty) /* 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) +ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int *regsize) { int size, align, reg_count; *ret_align = 1; // Never have to re-align return values for x86-64 + *regsize = 8; return (classify_x86_64_arg(vt, ret, &size, &align, ®_count) != x86_64_mode_memory); }