Refactor and simplify gfunc_call() on arm

This commit is contained in:
Thomas Preud'homme 2013-02-04 20:02:38 +01:00
parent 0650ab01c8
commit 1528a08540
1 changed files with 354 additions and 322 deletions

676
arm-gen.c
View File

@ -746,60 +746,6 @@ static void gcall_or_jmp(int is_jmp)
}
}
#ifdef TCC_ARM_HARDFLOAT
static int is_float_hgen_aggr(CType *type)
{
if ((type->t & VT_BTYPE) == VT_STRUCT) {
struct Sym *ref;
int btype, nb_fields = 0;
ref = type->ref;
btype = ref->type.t & VT_BTYPE;
if (btype == VT_FLOAT || btype == VT_DOUBLE) {
for(; ref && btype == (ref->type.t & VT_BTYPE); ref = ref->next, nb_fields++);
return !ref && nb_fields <= 4;
}
}
return 0;
}
struct avail_regs {
/* worst case: f(float, double, 3 float struct, double, 3 float struct, double) */
signed char avail[3];
int first_hole;
int last_hole;
int first_free_reg;
};
#define AVAIL_REGS_INITIALIZER (struct avail_regs) { { 0, 0, 0}, 0, 0, 0 }
/* Assign a register for a CPRC param with correct size and alignment
* size and align are in bytes, as returned by type_size */
int assign_fpreg(struct avail_regs *avregs, int align, int size)
{
int first_reg = 0;
if (avregs->first_free_reg == -1)
return -1;
if (align >> 3) { // alignment needed (base type: double)
first_reg = avregs->first_free_reg;
if (first_reg & 1)
avregs->avail[avregs->last_hole++] = first_reg++;
} else {
if (size == 4 && avregs->first_hole != avregs->last_hole)
return avregs->avail[avregs->first_hole++];
else
first_reg = avregs->first_free_reg;
}
if (first_reg + size / 4 <= 16) {
avregs->first_free_reg = first_reg + size / 4;
return first_reg;
}
avregs->first_free_reg = -1;
return -1;
}
#endif
/* 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
@ -818,61 +764,143 @@ ST_FUNC int gfunc_sret(CType *vt, CType *ret, int *ret_align) {
#endif
}
/* Generate function call. The function address is pushed first, then
all the parameters in call order. This functions pops all the
parameters and the function address. */
void gfunc_call(int nb_args)
#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
primitive float type and there is less than 4 elements.
type: the type corresponding to the structure to be tested */
static int is_hgen_float_aggr(CType *type)
{
int size, align, r, args_size, i, ncrn, ncprn, argno, vfp_argno;
signed char plan[4][2]={{-1,-1},{-1,-1},{-1,-1},{-1,-1}};
SValue *before_stack = NULL; /* SValue before first on stack argument */
SValue *before_creg = NULL; /* SValue before first argument of type struct in core register */
if ((type->t & VT_BTYPE) == VT_STRUCT) {
struct Sym *ref;
int btype, nb_fields = 0;
ref = type->ref;
btype = ref->type.t & VT_BTYPE;
if (btype == VT_FLOAT || btype == VT_DOUBLE) {
for(; ref && btype == (ref->type.t & VT_BTYPE); ref = ref->next, nb_fields++);
return !ref && nb_fields <= 4;
}
}
return 0;
}
struct avail_regs {
signed char avail[3]; /* 3 holes max with only float and double alignments */
int first_hole; /* first available hole */
int last_hole; /* last available hole (none if equal to first_hole) */
int first_free_reg; /* next free register in the sequence, hole excluded */
};
#define AVAIL_REGS_INITIALIZER (struct avail_regs) { { 0, 0, 0}, 0, 0, 0 }
/* Find suitable registers for a VFP Co-Processor Register Candidate (VFP CPRC
param) according to the rules described in the procedure call standard for
the ARM architecture (AAPCS). If found, the registers are assigned to this
VFP CPRC parameter. Registers are allocated in sequence unless a hole exists
and the parameter is a single float.
avregs: opaque structure to keep track of available VFP co-processor regs
align: alignment contraints for the param, as returned by type_size()
size: size of the parameter, as returned by type_size() */
int assign_vfpreg(struct avail_regs *avregs, int align, int size)
{
int first_reg = 0;
if (avregs->first_free_reg == -1)
return -1;
if (align >> 3) { /* double alignment */
first_reg = avregs->first_free_reg;
/* alignment contraint not respected so use next reg and record hole */
if (first_reg & 1)
avregs->avail[avregs->last_hole++] = first_reg++;
} else { /* no special alignment (float or array of float) */
/* if single float and a hole is available, assign the param to it */
if (size == 4 && avregs->first_hole != avregs->last_hole)
return avregs->avail[avregs->first_hole++];
else
first_reg = avregs->first_free_reg;
}
if (first_reg + size / 4 <= 16) {
avregs->first_free_reg = first_reg + size / 4;
return first_reg;
}
avregs->first_free_reg = -1;
return -1;
}
#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
some constraints about the order of the members of this union are respected:
- CORE_STRUCT_CLASS must come after STACK_CLASS;
- CORE_CLASS must come after STACK_CLASS, CORE_STRUCT_CLASS and
VFP_STRUCT_CLASS;
- VFP_STRUCT_CLASS must come after VFP_CLASS.
See the comment for the main loop in copy_params() for the reason. */
enum reg_class {
STACK_CLASS = 0,
CORE_STRUCT_CLASS,
VFP_CLASS,
VFP_STRUCT_CLASS,
CORE_CLASS,
NB_CLASSES
};
struct param_plan {
int start; /* first reg or addr used depending on the class */
int end; /* last reg used or next free addr depending on the class */
SValue *sval; /* pointer to SValue on the value stack */
struct param_plan *prev; /* previous element in this class */
};
struct plan {
struct param_plan *pplans; /* array of all the param plans */
struct param_plan *clsplans[NB_CLASSES]; /* per class lists of param plans */
};
#define add_param_plan(plan,pplan,class) \
do { \
pplan.prev = plan->clsplans[class]; \
plan->pplans[plan ## _nb] = pplan; \
plan->clsplans[class] = &plan->pplans[plan ## _nb++]; \
} while(0)
/* Assign parameters to registers and stack with alignment according to the
rules in the procedure call standard for the ARM architecture (AAPCS).
The overall assignment is recorded in an array of per parameter structures
called parameter plans. The parameter plans are also further organized in a
number of linked lists, one per class of parameter (see the comment for the
definition of union reg_class).
nb_args: number of parameters of the function for which a call is generated
variadic: whether the function is a variadic function or not
plan: the structure where the overall assignment is recorded
todo: a bitmap that record which core registers hold a parameter
Returns the amount of stack space needed for parameter passing
Note: this function allocated an array in plan->pplans with tcc_malloc. It
is the responsability of the caller to free this array once used (ie not
before copy_params). */
static int assign_regs(int nb_args, int variadic, struct plan *plan, int *todo)
{
int i, size, align;
int ncrn /* next core register number */, nsaa /* next stacked argument address*/;
int plan_nb = 0;
struct param_plan pplan;
#ifdef TCC_ARM_HARDFLOAT
struct avail_regs avregs = AVAIL_REGS_INITIALIZER;
signed char vfp_plan[16];
int plan2[4+16];
int variadic;
#else
int plan2[4]={0,0,0,0};
#endif
int vfp_todo=0;
int todo=0, keep;
#ifdef TCC_ARM_HARDFLOAT
memset(vfp_plan, -1, sizeof(vfp_plan));
memset(plan2, 0, sizeof(plan2));
variadic = (vtop[-nb_args].type.ref->c == FUNC_ELLIPSIS);
#endif
r = vtop->r & VT_VALMASK;
if (r == VT_CMP || (r & ~1) == VT_JMP)
gv(RC_INT);
#ifdef TCC_ARM_EABI
if((vtop[-nb_args].type.ref->type.t & VT_BTYPE) == VT_STRUCT
&& type_size(&vtop[-nb_args].type.ref->type, &align) <= 4) {
SValue tmp;
tmp=vtop[-nb_args];
vtop[-nb_args]=vtop[-nb_args+1];
vtop[-nb_args+1]=tmp;
--nb_args;
}
vpushi(0), nb_args++;
vtop->type.t = VT_LLONG;
#endif
ncrn = ncprn = argno = vfp_argno = args_size = 0;
/* Assign argument to registers and stack with alignment.
If, considering alignment constraints, enough registers of the correct type
(core or VFP) are free for the current argument, assign them to it, else
allocate on stack with correct alignment. Whenever a structure is allocated
in registers or on stack, it is always put on the stack at this stage. The
stack is divided in 3 zones. The zone are, from high addresses to low
addresses: structures to be loaded in core registers, structures to be
loaded in VFP registers, argument allocated to stack. SValue's representing
structures in the first zone are moved just after the SValue pointed by
before_stack. SValue's representing structures in the second zone are
moved just after the SValue pointer by before_creg. */
ncrn = nsaa = 0;
*todo = 0;
plan->pplans = tcc_malloc(nb_args * sizeof(*plan->pplans));
memset(plan->clsplans, 0, sizeof(plan->clsplans));
for(i = nb_args; i-- ;) {
int j, assigned_vfpreg = 0;
int j, start_vfpreg = 0;
size = type_size(&vtop[-i].type, &align);
switch(vtop[-i].type.t & VT_BTYPE) {
case VT_STRUCT:
@ -881,262 +909,266 @@ void gfunc_call(int nb_args)
case VT_LDOUBLE:
#ifdef TCC_ARM_HARDFLOAT
if (!variadic) {
int hfa = 0; /* Homogeneous float aggregate */
int is_hfa = 0; /* Homogeneous float aggregate */
if (is_float(vtop[-i].type.t)
|| (hfa = is_float_hgen_aggr(&vtop[-i].type))) {
int end_reg;
|| (is_hfa = is_hgen_float_aggr(&vtop[-i].type))) {
int end_vfpreg;
assigned_vfpreg = assign_fpreg(&avregs, align, size);
end_reg = assigned_vfpreg + (size - 1) / 4;
if (assigned_vfpreg >= 0) {
vfp_plan[vfp_argno++]=TREG_F0 + assigned_vfpreg/2;
if (hfa) {
/* if before_creg is not set, it means that no parameter has been
* allocated in core register. This implied that no argument has
* been allocated on stack neither because a VFP was available for
* this parameter. */
if (before_creg) {
/* before_creg already exists and we just update it */
vrote(&vtop[-i], &vtop[-i] - before_creg);
before_creg++;
}
for (j = assigned_vfpreg; j <= end_reg; j++)
vfp_todo|=(1<<j);
}
start_vfpreg = assign_vfpreg(&avregs, align, size);
end_vfpreg = start_vfpreg + ((size - 1) >> 2);
if (start_vfpreg >= 0) {
pplan = (struct param_plan) {start_vfpreg, end_vfpreg, &vtop[-i]};
if (is_hfa)
add_param_plan(plan, pplan, VFP_STRUCT_CLASS);
else
add_param_plan(plan, pplan, VFP_CLASS);
continue;
} else {
if (!hfa)
vfp_argno++;
if (!before_stack)
before_stack = &vtop[-i-1];
} else
break;
}
}
}
#endif
ncrn = (ncrn + (align-1)/4) & -(align/4);
size = (size + 3) & -4;
if (ncrn + size/4 <= 4 || (ncrn < 4 && assigned_vfpreg != -1)) {
if (before_stack) {
vrote(&vtop[-i], &vtop[-i] - before_stack);
before_stack++;
/* before_stack can only have been set because all VFP registers are
* assigned, so no need to care about before_creg if before_stack is
* set since no more argument will be allocated in a VFP register. */
} else if (!before_creg)
before_creg = &vtop[-i];
if (ncrn + size/4 <= 4 || (ncrn < 4 && start_vfpreg != -1)) {
/* The parameter is allocated both in core register and on stack. As
* such, it can be of either class: it would either be the last of
* CORE_STRUCT_CLASS or the first of STACK_CLASS. */
for (j = ncrn; j < 4 && j < ncrn + size / 4; j++)
todo|=(1<<j);
ncrn+=size/4;
if (ncrn > 4) {
args_size = (ncrn - 4) * 4;
if (!before_stack)
before_stack = &vtop[-i-1];
}
*todo|=(1<<j);
pplan = (struct param_plan) {ncrn, j, &vtop[-i]};
add_param_plan(plan, pplan, CORE_STRUCT_CLASS);
ncrn += size/4;
if (ncrn > 4)
nsaa = (ncrn - 4) * 4;
} else {
ncrn = 4;
/* No need to set before_creg since it has already been set when
* assigning argument to core registers */
if (!before_stack)
before_stack = &vtop[-i-1];
break;
}
continue;
default:
#ifdef TCC_ARM_EABI
if (!i) {
break;
}
#endif
if (ncrn < 4) {
int is_long = (vtop[-i].type.t & VT_BTYPE) == VT_LLONG;
if (is_long) {
ncrn = (ncrn + 1) & -2;
if (ncrn == 4) {
argno++;
if (ncrn == 4)
break;
}
}
plan[argno++][0]=ncrn++;
if (is_long) {
plan[argno-1][1]=ncrn++;
}
pplan = (struct param_plan) {ncrn, ncrn, &vtop[-i]};
ncrn++;
if (is_long)
pplan.end = ncrn++;
add_param_plan(plan, pplan, CORE_CLASS);
continue;
}
argno++;
}
#ifdef TCC_ARM_EABI
if(args_size & (align-1)) {
vpushi(0);
vtop->type.t = VT_VOID; /* padding */
vrott(i+2);
args_size += 4;
nb_args++;
argno++;
}
#endif
args_size += (size + 3) & -4;
nsaa = (nsaa + (align - 1)) & ~(align - 1);
pplan = (struct param_plan) {nsaa, nsaa + size, &vtop[-i]};
add_param_plan(plan, pplan, STACK_CLASS);
nsaa += size; /* size already rounded up before */
}
return nsaa;
}
#undef add_param_plan
/* Copy parameters to their final destination (core reg, VFP reg or stack) for
function call.
nb_args: number of parameters the function take
plan: the overall assignment plan for parameters
todo: a bitmap indicating what core reg will hold a parameter */
static void copy_params(int nb_args, struct plan *plan, int todo)
{
int size, align, r, i;
struct param_plan *pplan;
/* Put argument on stack (structure are put on stack no matter how they are
* passed via register or the stack). */
#ifdef TCC_ARM_EABI
vtop--, nb_args--;
#endif
args_size = keep = 0;
for(i = 0;i < nb_args; i++) {
vrotb(keep+1);
if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) {
size = type_size(&vtop->type, &align);
/* align to stack align size */
size = (size + 3) & -4;
/* allocate the necessary size on stack */
gadd_sp(-size);
/* generate structure store */
r = get_reg(RC_INT);
o(0xE1A0000D|(intr(r)<<12));
vset(&vtop->type, r | VT_LVAL, 0);
vswap();
vstore();
vtop--;
args_size += size;
} else if (is_float(vtop->type.t)) {
#ifdef TCC_ARM_HARDFLOAT
if (!variadic && --vfp_argno<16 && vfp_plan[vfp_argno]!=-1) {
plan2[keep++]=vfp_plan[vfp_argno];
continue;
}
if ((pplan = plan->clsplans[STACK_CLASS]) && pplan->end & 7)
o(0xE24DD004); /* sub sp, sp, #4 */
#endif
/* Several constraints require parameters to be copied in a specific order:
- structures are copied to the stack before being loaded in a reg;
- floats loaded to an odd numbered VFP reg are first copied to the
preceding even numbered VFP reg and then moved to the next VFP reg.
It is thus important that:
- structures assigned to core regs must be copied after parameters
assigned to the stack but before structures assigned to VFP regs because
a structure can lie partly in core registers and partly on the stack;
- parameters assigned to the stack and all structures be copied before
parameters assigned to a core reg since copying a parameter to the stack
require using a core reg;
- parameters assigned to VFP regs be copied before structures assigned to
VFP regs as the copy might use an even numbered VFP reg that already
holds part of a structure. */
for(i = 0; i < NB_CLASSES; i++) {
for(pplan = plan->clsplans[i]; pplan; pplan = pplan->prev) {
vpushv(pplan->sval);
pplan->sval->r = pplan->sval->r2 = VT_CONST; /* disable entry */
switch(i) {
case STACK_CLASS:
case CORE_STRUCT_CLASS:
case VFP_STRUCT_CLASS:
if ((pplan->sval->type.t & VT_BTYPE) == VT_STRUCT) {
size = type_size(&pplan->sval->type, &align);
/* align to stack align size */
size = (size + 3) & ~3;
if (i == STACK_CLASS && pplan->prev)
size += pplan->start - pplan->prev->end; /* Add padding if any */
/* allocate the necessary size on stack */
gadd_sp(-size);
/* generate structure store */
r = get_reg(RC_INT);
o(0xE1A0000D|(intr(r)<<12)); /* mov r, sp */
vset(&vtop->type, r | VT_LVAL, 0);
vswap();
vstore(); /* memcpy to current sp */
/* Homogeneous float aggregate are loaded to VFP registers
immediately since there is no way of loading data in multiple
non consecutive VFP registers as what is done for other
structures (see the use of todo). */
if (i == VFP_STRUCT_CLASS) {
int first = pplan->start, nb = pplan->end - first + 1;
/* vpop.32 {pplan->start, ..., pplan->end} */
o(0xECBD0A00|(first&1)<<22|(first>>1)<<12|nb);
/* No need to write the register used to a SValue since VFP regs
cannot be used for gcall_or_jmp */
}
} else {
if (is_float(pplan->sval->type.t)) {
#ifdef TCC_ARM_VFP
r=vfpr(gv(RC_FLOAT))<<12;
size=4;
if ((vtop->type.t & VT_BTYPE) != VT_FLOAT)
{
size=8;
r|=0x101; /* fstms -> fstmd */
}
o(0xED2D0A01+r);
r = vfpr(gv(RC_FLOAT)) << 12;
if ((pplan->sval->type.t & VT_BTYPE) == VT_FLOAT)
size = 4;
else {
size = 8;
r |= 0x101; /* vpush.32 -> vpush.64 */
}
o(0xED2D0A01 + r); /* vpush */
#else
r=fpr(gv(RC_FLOAT))<<12;
if ((vtop->type.t & VT_BTYPE) == VT_FLOAT)
size = 4;
else if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE)
size = 8;
else
size = LDOUBLE_SIZE;
r = fpr(gv(RC_FLOAT)) << 12;
if ((pplan->sval->type.t & VT_BTYPE) == VT_FLOAT)
size = 4;
else if ((pplan->sval->type.t & VT_BTYPE) == VT_DOUBLE)
size = 8;
else
size = LDOUBLE_SIZE;
if (size == 12)
r|=0x400000;
else if(size == 8)
r|=0x8000;
if (size == 12)
r |= 0x400000;
else if(size == 8)
r|=0x8000;
o(0xED2D0100|r|(size>>2));
o(0xED2D0100|r|(size>>2)); /* some kind of vpush for FPA */
#endif
} else {
/* simple type (currently always same size) */
/* XXX: implicit cast ? */
size=4;
if ((pplan->sval->type.t & VT_BTYPE) == VT_LLONG) {
lexpand_nr();
size = 8;
r = gv(RC_INT);
o(0xE52D0004|(intr(r)<<12)); /* push r */
vtop--;
}
r = gv(RC_INT);
o(0xE52D0004|(intr(r)<<12)); /* push r */
}
if (i == STACK_CLASS && pplan->prev)
gadd_sp(pplan->prev->end - pplan->start); /* Add padding if any */
}
break;
case VFP_CLASS:
gv(regmask(TREG_F0 + (pplan->start >> 1)));
if (pplan->start & 1) { /* Must be in upper part of double register */
o(0xEEF00A40|((pplan->start>>1)<<12)|(pplan->start>>1)); /* vmov.f32 s(n+1), sn */
vtop->r = VT_CONST; /* avoid being saved on stack by gv for next float */
}
break;
case CORE_CLASS:
if ((pplan->sval->type.t & VT_BTYPE) == VT_LLONG) {
lexpand_nr();
gv(regmask(pplan->end));
pplan->sval->r2 = vtop->r;
vtop--;
}
gv(regmask(pplan->start));
/* Mark register as used so that gcall_or_jmp use another one
(regs >=4 are free as never used to pass parameters) */
pplan->sval->r = vtop->r;
break;
}
vtop--;
args_size += size;
} else {
int s;
/* simple type (currently always same size) */
/* XXX: implicit cast ? */
size=4;
if ((vtop->type.t & VT_BTYPE) == VT_LLONG) {
lexpand_nr();
s=-1;
if(--argno<4 && plan[argno][1]!=-1)
s=plan[argno][1];
argno++;
size = 8;
if(s==-1) {
r = gv(RC_INT);
o(0xE52D0004|(intr(r)<<12)); /* str r,[sp,#-4]! */
vtop--;
} else {
size=0;
plan2[keep]=s;
keep++;
vswap();
}
}
s=-1;
if(--argno<4 && plan[argno][0]!=-1)
s=plan[argno][0];
#ifdef TCC_ARM_EABI
if(vtop->type.t == VT_VOID) {
if(s == -1)
o(0xE24DD004); /* sub sp,sp,#4 */
vtop--;
} else
#endif
if(s == -1) {
r = gv(RC_INT);
o(0xE52D0004|(intr(r)<<12)); /* str r,[sp,#-4]! */
vtop--;
} else {
size=0;
plan2[keep]=s;
keep++;
}
args_size += size;
}
}
for(i = 0; i < keep; i++) {
vrotb(keep);
gv(regmask(plan2[i]));
#ifdef TCC_ARM_HARDFLOAT
/* arg is in s(2d+1): plan2[i]<plan2[i+1] => alignment occured (ex f,d,f) */
if (i < keep - 1 && is_float(vtop->type.t) && (plan2[i] <= plan2[i + 1])) {
o(0xEEF00A40|(vfpr(plan2[i])<<12)|vfpr(plan2[i]));
}
#endif
}
save_regs(keep); /* save used temporary registers */
keep++;
if(vfp_todo) {
int nb_fregs=0;
for(i=0;i<16;i++)
if(vfp_todo&(1<<i)) {
o(0xED9D0A00|(i&1)<<22|(i>>1)<<12|nb_fregs);
vpushi(0);
/* There might be 2 floats in a double VFP reg but that doesn't seem
to matter */
if (!(i%2))
vtop->r=TREG_F0+i/2;
keep++;
nb_fregs++;
}
if (nb_fregs) {
gadd_sp(nb_fregs*4);
args_size-=nb_fregs*4;
/* Manually free remaining registers since next parameters are loaded
* manually, without the help of gv(int). */
save_regs(nb_args);
if(todo) {
o(0xE8BD0000|todo); /* pop {todo} */
for(pplan = plan->clsplans[CORE_STRUCT_CLASS]; pplan; pplan = pplan->prev) {
pplan->sval->r = pplan->start;
if ((pplan->sval->type.t & VT_BTYPE) == VT_LLONG)
pplan->sval->r2 = pplan->end;
}
}
if(ncrn) {
int nb_regs=0;
if (ncrn>4)
ncrn=4;
todo&=((1<<ncrn)-1);
if(todo) {
int i;
o(0xE8BD0000|todo);
for(i=0;i<4;i++)
if(todo&(1<<i)) {
vpushi(0);
vtop->r=i;
keep++;
nb_regs++;
}
}
args_size-=nb_regs*4;
}
/* Generate function call. The function address is pushed first, then
all the parameters in call order. This functions pops all the
parameters and the function address. */
void gfunc_call(int nb_args)
{
int align, r, args_size;
int variadic;
int todo;
struct plan plan;
variadic = (vtop[-nb_args].type.ref->c == FUNC_ELLIPSIS);
/* cannot let cpu flags if other instruction are generated. Also avoid leaving
VT_JMP anywhere except on the top of the stack because it would complicate
the code generator. */
r = vtop->r & VT_VALMASK;
if (r == VT_CMP || (r & ~1) == VT_JMP)
gv(RC_INT);
#ifdef TCC_ARM_EABI
/* return type is a struct so caller of gfunc_call (unary(void) in tccgen.c)
assumed it had to be passed by a pointer. Since it's less than 4 bytes, we
can actually pass it directly in a register. */
if((vtop[-nb_args].type.ref->type.t & VT_BTYPE) == VT_STRUCT
&& type_size(&vtop[-nb_args].type.ref->type, &align) <= 4) {
SValue tmp;
tmp=vtop[-nb_args];
vtop[-nb_args]=vtop[-nb_args+1];
vtop[-nb_args+1]=tmp;
--nb_args;
}
vrotb(keep);
#endif
args_size = assign_regs(nb_args, variadic, &plan, &todo);
copy_params(nb_args, &plan, todo);
tcc_free(plan.pplans);
/* Move fct SValue on top as required by gcall_or_jmp */
vrotb(nb_args + 1);
gcall_or_jmp(0);
if (args_size)
gadd_sp(args_size);
gadd_sp(args_size); /* pop all parameters passed on the stack */
#ifdef TCC_ARM_EABI
if((vtop->type.ref->type.t & VT_BTYPE) == VT_STRUCT
&& type_size(&vtop->type.ref->type, &align) <= 4)
{
store(REG_IRET,vtop-keep);
++keep;
&& type_size(&vtop->type.ref->type, &align) <= 4) {
store(REG_IRET,vtop-nb_args-1);
nb_args++;
}
#ifdef TCC_ARM_VFP
#ifdef TCC_ARM_HARDFLOAT
@ -1145,16 +1177,16 @@ save_regs(keep); /* save used temporary registers */
else if(is_float(vtop->type.ref->type.t)) {
#endif
if((vtop->type.ref->type.t & VT_BTYPE) == VT_FLOAT) {
o(0xEE000A10); /* fmsr s0,r0 */
o(0xEE000A10); /*vmov s0, r0 */
} else {
o(0xEE000B10); /* fmdlr d0,r0 */
o(0xEE201B10); /* fmdhr d0,r1 */
o(0xEE000B10); /* vmov.32 d0[0], r0 */
o(0xEE201B10); /* vmov.32 d0[1], r1 */
}
}
#endif
#endif
vtop-=keep;
leaffunc = 0;
vtop -= nb_args + 1; /* Pop all params and fct address from value stack */
leaffunc = 0; /* we are calling a function, so we aren't in a leaf function */
}
/* generate function prolog of type 't' */
@ -1182,8 +1214,8 @@ void gfunc_prolog(CType *func_type)
size = type_size(&sym2->type, &align);
#ifdef TCC_ARM_HARDFLOAT
if (!variadic && (is_float(sym2->type.t)
|| is_float_hgen_aggr(&sym2->type))) {
int tmpnf = assign_fpreg(&avregs, align, size) + 1;
|| is_hgen_float_aggr(&sym2->type))) {
int tmpnf = assign_vfpreg(&avregs, align, size) + 1;
nf = (tmpnf > nf) ? tmpnf : nf;
} else
#endif
@ -1226,8 +1258,8 @@ void gfunc_prolog(CType *func_type)
align = (align + 3) & ~3;
#ifdef TCC_ARM_HARDFLOAT
if (!variadic && (is_float(sym->type.t)
|| is_float_hgen_aggr(&sym->type))) {
int fpn = assign_fpreg(&avregs, align, size << 2);
|| is_hgen_float_aggr(&sym->type))) {
int fpn = assign_vfpreg(&avregs, align, size << 2);
if (fpn >= 0) {
addr = fpn * 4;
} else