Fix struct ret in variadic fct with ARM hardfloat

The procedure calling standard for ARM architecture mandate the use of
the base standard for variadic function. Therefore, hgen float aggregate
must be returned via stack when greater than 4 bytes and via core
registers else in case of variadic function.

This patch improve gfunc_sret() to take into account whether the
function is variadic or not and make use of gfunc_sret() return value to
determine whether to pass a structure via stack in gfunc_prolog(). It
also take advantage of knowing if a function is variadic or not move
float result value from VFP register to core register in gfunc_epilog().
This commit is contained in:
Thomas Preud'homme 2014-01-06 22:27:39 +08:00
parent bcc1904f9c
commit 8efaa71190
7 changed files with 35 additions and 23 deletions

View File

@ -839,12 +839,12 @@ 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, CType *ret, int *ret_align) {
ST_FUNC int gfunc_sret(CType *vt, int variadic, 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)) {
if (!variadic && (is_float(vt->t) || is_hgen_float_aggr(vt))) {
*ret_align = 8;
ret->ref = NULL;
ret->t = VT_DOUBLE;
@ -1221,21 +1221,19 @@ void gfunc_call(int nb_args)
void gfunc_prolog(CType *func_type)
{
Sym *sym,*sym2;
int n,nf,size,align, variadic, struct_ret = 0;
int n, nf, size, align, struct_ret = 0;
#ifdef TCC_ARM_HARDFLOAT
struct avail_regs avregs = AVAIL_REGS_INITIALIZER;
#endif
CType ret_type;
sym = func_type->ref;
func_vt = sym->type;
func_var = (func_type->ref->c == FUNC_ELLIPSIS);
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)
if ((func_vt.t & VT_BTYPE) == VT_STRUCT &&
!gfunc_sret(&func_vt, func_var, &ret_type, &align))
{
n++;
struct_ret = 1;
@ -1244,7 +1242,7 @@ void gfunc_prolog(CType *func_type)
for(sym2=sym->next;sym2 && (n<4 || nf<16);sym2=sym2->next) {
size = type_size(&sym2->type, &align);
#ifdef TCC_ARM_HARDFLOAT
if (!variadic && (is_float(sym2->type.t)
if (!func_var && (is_float(sym2->type.t)
|| is_hgen_float_aggr(&sym2->type))) {
int tmpnf = assign_vfpreg(&avregs, align, size);
tmpnf += (size + 3) / 4;
@ -1255,9 +1253,9 @@ void gfunc_prolog(CType *func_type)
n += (size + 3) / 4;
}
o(0xE1A0C00D); /* mov ip,sp */
if(variadic)
if (func_var)
n=4;
if(n) {
if (n) {
if(n>4)
n=4;
#ifdef TCC_ARM_EABI
@ -1289,7 +1287,7 @@ void gfunc_prolog(CType *func_type)
size = (size + 3) >> 2;
align = (align + 3) & ~3;
#ifdef TCC_ARM_HARDFLOAT
if (!variadic && (is_float(sym->type.t)
if (!func_var && (is_float(sym->type.t)
|| is_hgen_float_aggr(&sym->type))) {
int fpn = assign_vfpreg(&avregs, align, size << 2);
if (fpn >= 0) {
@ -1329,10 +1327,14 @@ void gfunc_epilog(void)
{
uint32_t x;
int diff;
/* Copy float return value to core register if base standard is used and
float computation is made with VFP */
#ifdef TCC_ARM_EABI
/* Useless but harmless copy of the float result into main register(s) in case
of variadic function in the hardfloat variant */
if(is_float(func_vt.t)) {
if (
#ifdef TCC_ARM_HARDFLOAT
func_var &&
#endif
is_float(func_vt.t)) {
if((func_vt.t & VT_BTYPE) == VT_FLOAT)
o(0xEE100A10); /* fmrs r0, s0 */
else {

View File

@ -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, CType *ret, int *ret_align) {
ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align) {
*ret_align = 1; // Never have to re-align return values for x86-64
return 0;
}
@ -1971,6 +1971,7 @@ void gfunc_prolog(CType * func_type)
/* if the function returns a structure, then add an
implicit pointer parameter */
func_vt = sym->type;
func_var = (sym->c == FUNC_ELLIPSIS);
if ((func_vt.t & VT_BTYPE) == VT_STRUCT) {
func_vc = addr;
addr += 4;

View File

@ -376,7 +376,7 @@ 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, CType *ret, int *ret_align)
ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align)
{
#ifdef TCC_TARGET_PE
int size, align;
@ -527,6 +527,7 @@ ST_FUNC void gfunc_prolog(CType *func_type)
/* if the function returns a structure, then add an
implicit pointer parameter */
func_vt = sym->type;
func_var = (sym->c == FUNC_ELLIPSIS);
#ifdef TCC_TARGET_PE
size = type_size(&func_vt,&align);
if (((func_vt.t & VT_BTYPE) == VT_STRUCT) && (size > 8)) {

View File

@ -441,6 +441,7 @@ void gfunc_prolog(int t)
/* if the function returns a structure, then add an
implicit pointer parameter */
func_vt = sym->t;
func_var = (sym->c == FUNC_ELLIPSIS);
if ((func_vt & VT_BTYPE) == VT_STRUCT) {
func_vc = addr;
addr++;

3
tcc.h
View File

@ -1176,6 +1176,7 @@ ST_DATA int const_wanted; /* true if constant wanted */
ST_DATA int nocode_wanted; /* true if no code generation wanted for an expression */
ST_DATA int global_expr; /* true if compound literals must be allocated globally (used during initializers parsing */
ST_DATA CType func_vt; /* current function return type (used by return instruction) */
ST_DATA int func_var; /* true if current function is variadic */
ST_DATA int func_vc;
ST_DATA int last_line_num, last_ind, func_ind; /* debug last line number and pc */
ST_DATA char *funcname;
@ -1288,7 +1289,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, CType *ret, int *align);
ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *align);
ST_FUNC void gfunc_call(int nb_args);
ST_FUNC void gfunc_prolog(CType *func_type);
ST_FUNC void gfunc_epilog(void);

View File

@ -66,6 +66,7 @@ ST_DATA int const_wanted; /* true if constant wanted */
ST_DATA int nocode_wanted; /* true if no code generation wanted for an expression */
ST_DATA int global_expr; /* true if compound literals must be allocated globally (used during initializers parsing */
ST_DATA CType func_vt; /* current function return type (used by return instruction) */
ST_DATA int func_var; /* true if current function is variadic (used by return instruction) */
ST_DATA int func_vc;
ST_DATA int last_line_num, last_ind, func_ind; /* debug last line number and pc */
ST_DATA char *funcname;
@ -3960,7 +3961,7 @@ ST_FUNC void unary(void)
} else if (tok == '(') {
SValue ret;
Sym *sa;
int nb_args, ret_nregs, ret_align;
int nb_args, ret_nregs, ret_align, variadic;
/* function call */
if ((vtop->type.t & VT_BTYPE) != VT_FUNC) {
@ -3984,7 +3985,9 @@ 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) {
ret_nregs = gfunc_sret(&s->type, &ret.type, &ret_align);
variadic = (s->c == FUNC_ELLIPSIS);
ret_nregs = gfunc_sret(&s->type, variadic, &ret.type,
&ret_align);
if (!ret_nregs) {
/* get some space for the returned structure */
size = type_size(&s->type, &align);
@ -4637,7 +4640,8 @@ 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, ret_nregs;
ret_nregs = gfunc_sret(&func_vt, &ret_type, &ret_align);
ret_nregs = gfunc_sret(&func_vt, func_var, &ret_type,
&ret_align);
if (0 == ret_nregs) {
/* if returning structure, must copy it to implicit
first pointer arg location */
@ -5747,6 +5751,7 @@ static void gen_function(Sym *sym)
cur_text_section = NULL;
funcname = ""; /* for safety */
func_vt.t = VT_VOID; /* for safety */
func_var = 0; /* for safety */
ind = 0; /* for safety */
nocode_wanted = saved_nocode_wanted;
}

View File

@ -658,7 +658,7 @@ 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, CType *ret, int *ret_align)
ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align)
{
int size, align;
*ret_align = 1; // Never have to re-align return values for x86-64
@ -833,6 +833,7 @@ void gfunc_prolog(CType *func_type)
/* if the function returns a structure, then add an
implicit pointer parameter */
func_vt = sym->type;
func_var = (sym->c == FUNC_ELLIPSIS);
size = gfunc_arg_size(&func_vt);
if (size > 8) {
gen_modrm64(0x89, arg_regs[reg_param_index], VT_LOCAL, NULL, addr);