mirror of
https://github.com/mirror/tinycc.git
synced 2025-03-28 12:10:05 +08:00
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:
parent
d9d60a1ebd
commit
dcec8673f2
48
arm-gen.c
48
arm-gen.c
@ -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
|
#ifdef TCC_ARM_HARDFLOAT
|
||||||
/* Return whether a structure is an homogeneous float aggregate or not.
|
/* 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
|
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
|
#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
|
/* Parameters are classified according to how they are copied to their final
|
||||||
destination for the function call. Because the copying is performed class
|
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
|
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;
|
n = nf = 0;
|
||||||
variadic = (func_type->ref->c == FUNC_ELLIPSIS);
|
variadic = (func_type->ref->c == FUNC_ELLIPSIS);
|
||||||
if((func_vt.t & VT_BTYPE) == VT_STRUCT
|
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)
|
&& type_size(&func_vt,&align) > 4)
|
||||||
{
|
{
|
||||||
n++;
|
n++;
|
||||||
|
@ -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) {
|
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
|
*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
|
/* generate function call with address in (vtop->t, vtop->c) and free function
|
||||||
|
@ -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 fastcall_regs[3] = { TREG_EAX, TREG_EDX, TREG_ECX };
|
||||||
static uint8_t fastcallw_regs[2] = { TREG_ECX, TREG_EDX };
|
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)
|
ST_FUNC int gfunc_sret(CType *vt, CType *ret, int *ret_align)
|
||||||
{
|
{
|
||||||
#ifdef TCC_TARGET_PE
|
#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
|
*ret_align = 1; // Never have to re-align return values for x86
|
||||||
size = type_size(vt, &align);
|
size = type_size(vt, &align);
|
||||||
if (size > 8) {
|
if (size > 8) {
|
||||||
return 1;
|
return 0;
|
||||||
} else if (size > 4) {
|
} else if (size > 4) {
|
||||||
ret->ref = NULL;
|
ret->ref = NULL;
|
||||||
ret->t = VT_LLONG;
|
ret->t = VT_LLONG;
|
||||||
return 0;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
ret->ref = NULL;
|
ret->ref = NULL;
|
||||||
ret->t = VT_INT;
|
ret->t = VT_INT;
|
||||||
@ -395,7 +396,7 @@ ST_FUNC int gfunc_sret(CType *vt, CType *ret, int *ret_align)
|
|||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
*ret_align = 1; // Never have to re-align return values for x86
|
*ret_align = 1; // Never have to re-align return values for x86
|
||||||
return 1;
|
return 0;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
46
tccgen.c
46
tccgen.c
@ -3927,7 +3927,7 @@ ST_FUNC void unary(void)
|
|||||||
} else if (tok == '(') {
|
} else if (tok == '(') {
|
||||||
SValue ret;
|
SValue ret;
|
||||||
Sym *sa;
|
Sym *sa;
|
||||||
int nb_args, sret;
|
int nb_args, sret, ret_align;
|
||||||
|
|
||||||
/* function call */
|
/* function call */
|
||||||
if ((vtop->type.t & VT_BTYPE) != VT_FUNC) {
|
if ((vtop->type.t & VT_BTYPE) != VT_FUNC) {
|
||||||
@ -3951,9 +3951,8 @@ ST_FUNC void unary(void)
|
|||||||
ret.r2 = VT_CONST;
|
ret.r2 = VT_CONST;
|
||||||
/* compute first implicit argument if a structure is returned */
|
/* compute first implicit argument if a structure is returned */
|
||||||
if ((s->type.t & VT_BTYPE) == VT_STRUCT) {
|
if ((s->type.t & VT_BTYPE) == VT_STRUCT) {
|
||||||
int ret_align;
|
|
||||||
sret = gfunc_sret(&s->type, &ret.type, &ret_align);
|
sret = gfunc_sret(&s->type, &ret.type, &ret_align);
|
||||||
if (sret) {
|
if (!sret) {
|
||||||
/* get some space for the returned structure */
|
/* get some space for the returned structure */
|
||||||
size = type_size(&s->type, &align);
|
size = type_size(&s->type, &align);
|
||||||
loc = (loc - size) & -align;
|
loc = (loc - size) & -align;
|
||||||
@ -3966,11 +3965,11 @@ ST_FUNC void unary(void)
|
|||||||
nb_args++;
|
nb_args++;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sret = 0;
|
sret = 1;
|
||||||
ret.type = s->type;
|
ret.type = s->type;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!sret) {
|
if (sret) {
|
||||||
/* return in register */
|
/* return in register */
|
||||||
if (is_float(ret.type.t)) {
|
if (is_float(ret.type.t)) {
|
||||||
ret.r = reg_fret(ret.type.t);
|
ret.r = reg_fret(ret.type.t);
|
||||||
@ -4010,18 +4009,23 @@ ST_FUNC void unary(void)
|
|||||||
vtop -= (nb_args + 1);
|
vtop -= (nb_args + 1);
|
||||||
}
|
}
|
||||||
/* return value */
|
/* return value */
|
||||||
vsetc(&ret.type, ret.r, &ret.c);
|
for (r = ret.r + sret + !sret; r-- > ret.r;) {
|
||||||
vtop->r2 = ret.r2;
|
vsetc(&ret.type, r, &ret.c);
|
||||||
|
vtop->r2 = ret.r2; /* Loop only happens when r2 is VT_CONST */
|
||||||
|
}
|
||||||
/* handle packed struct return */
|
/* handle packed struct return */
|
||||||
if (((s->type.t & VT_BTYPE) == VT_STRUCT) && !sret) {
|
if (((s->type.t & VT_BTYPE) == VT_STRUCT) && sret) {
|
||||||
int addr;
|
int addr, offset;
|
||||||
|
|
||||||
size = type_size(&s->type, &align);
|
size = type_size(&s->type, &align);
|
||||||
loc = (loc - size) & -align;
|
loc = (loc - size) & -align;
|
||||||
addr = loc;
|
addr = loc;
|
||||||
vset(&ret.type, VT_LOCAL | VT_LVAL, addr);
|
for(offset = 0; offset < size; offset += ret_align) {
|
||||||
vswap();
|
vset(&ret.type, VT_LOCAL | VT_LVAL, addr + offset);
|
||||||
vstore();
|
vswap();
|
||||||
vtop--;
|
vstore();
|
||||||
|
vtop--;
|
||||||
|
}
|
||||||
vset(&s->type, VT_LOCAL | VT_LVAL, addr);
|
vset(&s->type, VT_LOCAL | VT_LVAL, addr);
|
||||||
}
|
}
|
||||||
} else {
|
} 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) {
|
if ((func_vt.t & VT_BTYPE) == VT_STRUCT) {
|
||||||
CType type, ret_type;
|
CType type, ret_type;
|
||||||
int ret_align;
|
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
|
/* if returning structure, must copy it to implicit
|
||||||
first pointer arg location */
|
first pointer arg location */
|
||||||
type = func_vt;
|
type = func_vt;
|
||||||
@ -4605,7 +4609,7 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym,
|
|||||||
vstore();
|
vstore();
|
||||||
} else {
|
} else {
|
||||||
/* returning structure packed into registers */
|
/* returning structure packed into registers */
|
||||||
int size, addr, align;
|
int r, size, addr, offset, align;
|
||||||
size = type_size(&func_vt,&align);
|
size = type_size(&func_vt,&align);
|
||||||
if ((vtop->r != (VT_LOCAL | VT_LVAL) || (vtop->c.i & (ret_align-1)))
|
if ((vtop->r != (VT_LOCAL | VT_LVAL) || (vtop->c.i & (ret_align-1)))
|
||||||
&& (align & (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;
|
vtop->type = ret_type;
|
||||||
if (is_float(ret_type.t))
|
if (is_float(ret_type.t))
|
||||||
gv(rc_fret(ret_type.t));
|
r = rc_fret(ret_type.t);
|
||||||
else
|
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)) {
|
} else if (is_float(func_vt.t)) {
|
||||||
gv(rc_fret(func_vt.t));
|
gv(rc_fret(func_vt.t));
|
||||||
|
18
x86_64-gen.c
18
x86_64-gen.c
@ -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)
|
ST_FUNC int gfunc_sret(CType *vt, CType *ret, int *ret_align)
|
||||||
{
|
{
|
||||||
int size, 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);
|
size = type_size(vt, &align);
|
||||||
ret->ref = NULL;
|
ret->ref = NULL;
|
||||||
if (size > 8) {
|
if (size > 8) {
|
||||||
return 1;
|
return 0;
|
||||||
} else if (size > 4) {
|
} else if (size > 4) {
|
||||||
ret->t = VT_LLONG;
|
ret->t = VT_LLONG;
|
||||||
return 0;
|
return 1;
|
||||||
} else if (size > 2) {
|
} else if (size > 2) {
|
||||||
ret->t = VT_INT;
|
ret->t = VT_INT;
|
||||||
return 0;
|
return 1;
|
||||||
} else if (size > 1) {
|
} else if (size > 1) {
|
||||||
ret->t = VT_SHORT;
|
ret->t = VT_SHORT;
|
||||||
return 0;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
ret->t = VT_BYTE;
|
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 gfunc_sret(CType *vt, CType *ret, int *ret_align) {
|
||||||
int size, align, reg_count;
|
int size, align, reg_count;
|
||||||
*ret_align = 1; // Never have to re-align return values for x86-64
|
*ret_align = 1; // Never have to re-align return values for x86-64
|
||||||
return (classify_x86_64_arg(vt, ret, &size, &align, ®_count) == x86_64_mode_memory);
|
return (classify_x86_64_arg(vt, ret, &size, &align, ®_count) != x86_64_mode_memory);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define REGN 6
|
#define REGN 6
|
||||||
|
Loading…
Reference in New Issue
Block a user