Add support for struct > 4B returned via registers

On ARM with hardfloat calling convention, structure containing 4 fields
or less of the same float type are returned via float registers. This
means that a structure can be returned in up to 4 double registers in a
structure is composed of 4 doubles. This commit adds support for return
of structures in several registers.
This commit is contained in:
Thomas Preud'homme 2013-11-22 09:27:15 +08:00
parent d9d60a1ebd
commit dcec8673f2
5 changed files with 77 additions and 49 deletions

View File

@ -746,24 +746,6 @@ static void gcall_or_jmp(int is_jmp)
}
}
/* Return 1 if this function returns via an sret pointer, 0 otherwise */
ST_FUNC int gfunc_sret(CType *vt, CType *ret, int *ret_align) {
#ifdef TCC_ARM_EABI
int size, align;
size = type_size(vt, &align);
if (size > 4) {
return 1;
} else {
*ret_align = 4;
ret->ref = NULL;
ret->t = VT_INT;
return 0;
}
#else
return 1;
#endif
}
#ifdef TCC_ARM_HARDFLOAT
/* Return whether a structure is an homogeneous float aggregate or not.
The answer is true if all the elements of the structure are of the same
@ -831,6 +813,33 @@ int assign_vfpreg(struct avail_regs *avregs, int align, int size)
}
#endif
/* Return the number of registers needed to return the struct, or 0 if
returning via struct pointer. */
ST_FUNC int gfunc_sret(CType *vt, CType *ret, int *ret_align) {
#ifdef TCC_ARM_EABI
int size, align;
size = type_size(vt, &align);
#ifdef TCC_ARM_HARDFLOAT
if (is_float(vt->t) || is_hgen_float_aggr(vt)) {
*ret_align = 8;
ret->ref = NULL;
ret->t = VT_DOUBLE;
return (size + 7) >> 3;
} else
#endif
if (size > 4) {
return 0;
} else {
*ret_align = 4;
ret->ref = NULL;
ret->t = VT_INT;
return 1;
}
#else
return 0;
#endif
}
/* Parameters are classified according to how they are copied to their final
destination for the function call. Because the copying is performed class
after class according to the order in the union below, it is important that
@ -1198,6 +1207,9 @@ void gfunc_prolog(CType *func_type)
n = nf = 0;
variadic = (func_type->ref->c == FUNC_ELLIPSIS);
if((func_vt.t & VT_BTYPE) == VT_STRUCT
#ifdef TCC_ARM_HARDFLOAT
&& (variadic || !is_hgen_float_aggr(&func_vt))
#endif
&& type_size(&func_vt,&align) > 4)
{
n++;

View File

@ -1879,10 +1879,11 @@ static void gcall_or_jmp(int is_jmp)
}
}
/* Return 1 if this function returns via an sret pointer, 0 otherwise */
/* Return the number of registers needed to return the struct, or 0 if
returning via struct pointer. */
ST_FUNC int gfunc_sret(CType *vt, CType *ret, int *ret_align) {
*ret_align = 1; // Never have to re-align return values for x86-64
return 1;
return 0;
}
/* generate function call with address in (vtop->t, vtop->c) and free function

View File

@ -374,7 +374,8 @@ static void gcall_or_jmp(int is_jmp)
static uint8_t fastcall_regs[3] = { TREG_EAX, TREG_EDX, TREG_ECX };
static uint8_t fastcallw_regs[2] = { TREG_ECX, TREG_EDX };
/* Return 1 if this function returns via an sret pointer, 0 otherwise */
/* Return the number of registers needed to return the struct, or 0 if
returning via struct pointer. */
ST_FUNC int gfunc_sret(CType *vt, CType *ret, int *ret_align)
{
#ifdef TCC_TARGET_PE
@ -383,11 +384,11 @@ ST_FUNC int gfunc_sret(CType *vt, CType *ret, int *ret_align)
*ret_align = 1; // Never have to re-align return values for x86
size = type_size(vt, &align);
if (size > 8) {
return 1;
return 0;
} else if (size > 4) {
ret->ref = NULL;
ret->t = VT_LLONG;
return 0;
return 1;
} else {
ret->ref = NULL;
ret->t = VT_INT;
@ -395,7 +396,7 @@ ST_FUNC int gfunc_sret(CType *vt, CType *ret, int *ret_align)
}
#else
*ret_align = 1; // Never have to re-align return values for x86
return 1;
return 0;
#endif
}

View File

@ -3927,7 +3927,7 @@ ST_FUNC void unary(void)
} else if (tok == '(') {
SValue ret;
Sym *sa;
int nb_args, sret;
int nb_args, sret, ret_align;
/* function call */
if ((vtop->type.t & VT_BTYPE) != VT_FUNC) {
@ -3951,9 +3951,8 @@ ST_FUNC void unary(void)
ret.r2 = VT_CONST;
/* compute first implicit argument if a structure is returned */
if ((s->type.t & VT_BTYPE) == VT_STRUCT) {
int ret_align;
sret = gfunc_sret(&s->type, &ret.type, &ret_align);
if (sret) {
if (!sret) {
/* get some space for the returned structure */
size = type_size(&s->type, &align);
loc = (loc - size) & -align;
@ -3966,11 +3965,11 @@ ST_FUNC void unary(void)
nb_args++;
}
} else {
sret = 0;
sret = 1;
ret.type = s->type;
}
if (!sret) {
if (sret) {
/* return in register */
if (is_float(ret.type.t)) {
ret.r = reg_fret(ret.type.t);
@ -4010,18 +4009,23 @@ ST_FUNC void unary(void)
vtop -= (nb_args + 1);
}
/* return value */
vsetc(&ret.type, ret.r, &ret.c);
vtop->r2 = ret.r2;
for (r = ret.r + sret + !sret; r-- > ret.r;) {
vsetc(&ret.type, r, &ret.c);
vtop->r2 = ret.r2; /* Loop only happens when r2 is VT_CONST */
}
/* handle packed struct return */
if (((s->type.t & VT_BTYPE) == VT_STRUCT) && !sret) {
int addr;
if (((s->type.t & VT_BTYPE) == VT_STRUCT) && sret) {
int addr, offset;
size = type_size(&s->type, &align);
loc = (loc - size) & -align;
addr = loc;
vset(&ret.type, VT_LOCAL | VT_LVAL, addr);
vswap();
vstore();
vtop--;
for(offset = 0; offset < size; offset += ret_align) {
vset(&ret.type, VT_LOCAL | VT_LVAL, addr + offset);
vswap();
vstore();
vtop--;
}
vset(&s->type, VT_LOCAL | VT_LVAL, addr);
}
} else {
@ -4593,7 +4597,7 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym,
if ((func_vt.t & VT_BTYPE) == VT_STRUCT) {
CType type, ret_type;
int ret_align;
if (gfunc_sret(&func_vt, &ret_type, &ret_align)) {
if (!gfunc_sret(&func_vt, &ret_type, &ret_align)) {
/* if returning structure, must copy it to implicit
first pointer arg location */
type = func_vt;
@ -4605,7 +4609,7 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym,
vstore();
} else {
/* returning structure packed into registers */
int size, addr, align;
int r, size, addr, offset, align;
size = type_size(&func_vt,&align);
if ((vtop->r != (VT_LOCAL | VT_LVAL) || (vtop->c.i & (ret_align-1)))
&& (align & (ret_align-1))) {
@ -4619,9 +4623,17 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym,
}
vtop->type = ret_type;
if (is_float(ret_type.t))
gv(rc_fret(ret_type.t));
r = rc_fret(ret_type.t);
else
gv(RC_IRET);
r = RC_IRET;
/* We assume that when a structure is returned in multiple
registers, their classes are consecutive values of the
suite s(n) = 2^n */
for (offset = 0; offset < size; offset += ret_align, r<<=1) {
gv(r);
vtop->c.i += ret_align;
vtop->r = VT_LOCAL | VT_LVAL;
}
}
} else if (is_float(func_vt.t)) {
gv(rc_fret(func_vt.t));

View File

@ -656,7 +656,8 @@ void gen_offs_sp(int b, int r, int d)
}
}
/* Return 1 if this function returns via an sret pointer, 0 otherwise */
/* Return the number of registers needed to return the struct, or 0 if
returning via struct pointer. */
ST_FUNC int gfunc_sret(CType *vt, CType *ret, int *ret_align)
{
int size, align;
@ -664,19 +665,19 @@ ST_FUNC int gfunc_sret(CType *vt, CType *ret, int *ret_align)
size = type_size(vt, &align);
ret->ref = NULL;
if (size > 8) {
return 1;
return 0;
} else if (size > 4) {
ret->t = VT_LLONG;
return 0;
return 1;
} else if (size > 2) {
ret->t = VT_INT;
return 0;
return 1;
} else if (size > 1) {
ret->t = VT_SHORT;
return 0;
return 1;
} else {
ret->t = VT_BYTE;
return 0;
return 1;
}
}
@ -1056,11 +1057,12 @@ ST_FUNC int classify_x86_64_va_arg(CType *ty) {
}
}
/* Return 1 if this function returns via an sret pointer, 0 otherwise */
/* Return the number of registers needed to return the struct, or 0 if
returning via struct pointer. */
int gfunc_sret(CType *vt, CType *ret, int *ret_align) {
int size, align, reg_count;
*ret_align = 1; // Never have to re-align return values for x86-64
return (classify_x86_64_arg(vt, ret, &size, &align, &reg_count) == x86_64_mode_memory);
return (classify_x86_64_arg(vt, ret, &size, &align, &reg_count) != x86_64_mode_memory);
}
#define REGN 6