diff options
| author | jiang <30155751@qq.com> | 2014-04-29 16:05:16 +0800 |
|---|---|---|
| committer | jiang <30155751@qq.com> | 2014-04-29 16:05:16 +0800 |
| commit | 2a8905c93b4f67a21e3dbf297c3e93c598831528 (patch) | |
| tree | 6f7491150cfe01700c9863946906203adad0de13 /arm-gen.c | |
| parent | 02e2fe3c260415f4cb1cbcaca9b2403a17f7ff2e (diff) | |
| parent | e20c1eb99e1003c1e59522c136dbb15c52d7cc7c (diff) | |
| download | tinycc-2a8905c93b4f67a21e3dbf297c3e93c598831528.tar.gz tinycc-2a8905c93b4f67a21e3dbf297c3e93c598831528.tar.bz2 | |
So that the generated code, and more short
Diffstat (limited to 'arm-gen.c')
| -rw-r--r-- | arm-gen.c | 865 |
1 files changed, 466 insertions, 399 deletions
@@ -1,6 +1,6 @@ /* * ARMv4 code generator for TCC - * + * * Copyright (c) 2003 Daniel Glöckner * Copyright (c) 2012 Thomas Preud'homme * @@ -23,10 +23,8 @@ #ifdef TARGET_DEFS_ONLY -#ifdef TCC_ARM_EABI -#ifndef TCC_ARM_VFP /* Avoid useless warning */ -#define TCC_ARM_VFP -#endif +#if defined(TCC_ARM_EABI) && !defined(TCC_ARM_VFP) +#error "Currently TinyCC only supports float computation with VFP instructions" #endif /* number of available registers */ @@ -46,7 +44,7 @@ #define RC_INT 0x0001 /* generic integer register */ #define RC_FLOAT 0x0002 /* generic float register */ #define RC_R0 0x0004 -#define RC_R1 0x0008 +#define RC_R1 0x0008 #define RC_R2 0x0010 #define RC_R3 0x0020 #define RC_R12 0x0040 @@ -143,11 +141,18 @@ enum { #define ELF_START_ADDR 0x00008000 #define ELF_PAGE_SIZE 0x1000 +enum float_abi { + ARM_SOFTFP_FLOAT, + ARM_HARD_FLOAT, +}; + /******************************************************/ #else /* ! TARGET_DEFS_ONLY */ /******************************************************/ #include "tcc.h" +enum float_abi float_abi; + ST_DATA const int reg_classes[NB_REGS] = { /* r0 */ RC_INT | RC_R0, /* r1 */ RC_INT | RC_R1, @@ -171,7 +176,7 @@ static int leaffunc; #if defined(TCC_ARM_EABI) && defined(TCC_ARM_VFP) static CType float_type, double_type, func_float_type, func_double_type; -ST_FUNC void arm_init_types(void) +ST_FUNC void arm_init(struct TCCState *s) { float_type.t = VT_FLOAT; double_type.t = VT_DOUBLE; @@ -179,12 +184,27 @@ ST_FUNC void arm_init_types(void) func_float_type.ref = sym_push(SYM_FIELD, &float_type, FUNC_CDECL, FUNC_OLD); func_double_type.t = VT_FUNC; func_double_type.ref = sym_push(SYM_FIELD, &double_type, FUNC_CDECL, FUNC_OLD); + + float_abi = s->float_abi; +#ifndef TCC_ARM_HARDFLOAT + tcc_warning("soft float ABI currently not supported: default to softfp"); +#endif } #else #define func_float_type func_old_type #define func_double_type func_old_type #define func_ldouble_type func_old_type -ST_FUNC void arm_init_types(void) {} +ST_FUNC void arm_init(struct TCCState *s) +{ +#if !defined (TCC_ARM_VFP) + tcc_warning("Support for FPA is deprecated and will be removed in next" + " release"); +#endif +#if !defined (TCC_ARM_EABI) + tcc_warning("Support for OABI is deprecated and will be removed in next" + " release"); +#endif +} #endif static int two2mask(int a,int b) { @@ -197,6 +217,16 @@ static int regmask(int r) { /******************************************************/ +#ifdef TCC_ARM_EABI +char *default_elfinterp(struct TCCState *s) +{ + if (s->float_abi == ARM_HARD_FLOAT) + return "/lib/ld-linux-armhf.so.3"; + else + return "/lib/ld-linux.so.3"; +} +#endif + void o(uint32_t i) { /* this is a good place to start adding big-endian support*/ @@ -211,7 +241,7 @@ void o(uint32_t i) cur_text_section->data[ind++] = i&255; i>>=8; cur_text_section->data[ind++] = i&255; - i>>=8; + i>>=8; cur_text_section->data[ind++] = i&255; i>>=8; cur_text_section->data[ind++] = i; @@ -506,7 +536,7 @@ void load(int r, SValue *sv) sign=1; fc=-fc; } - + v = fr & VT_VALMASK; if (fr & VT_LVAL) { uint32_t base = 0xB; // fp @@ -645,8 +675,8 @@ void store(int r, SValue *sv) sign=1; fc=-fc; } - - v = fr & VT_VALMASK; + + v = fr & VT_VALMASK; if (fr & VT_LVAL || fr == VT_LOCAL) { uint32_t base = 0xb; if(v < VT_CONST) { @@ -660,16 +690,16 @@ void store(int r, SValue *sv) v1.sym=sv->sym; load(base=14, &v1); fc=sign=0; - v=VT_LOCAL; + v=VT_LOCAL; } if(v == VT_LOCAL) { if(is_float(ft)) { calcaddr(&base,&fc,&sign,1020,2); #ifdef TCC_ARM_VFP op=0xED000A00; /* fsts */ - if(!sign) - op|=0x800000; - if ((ft & VT_BTYPE) != VT_FLOAT) + if(!sign) + op|=0x800000; + if ((ft & VT_BTYPE) != VT_FLOAT) op|=0x100; /* fsts -> fstd */ o(op|(vfpr(r)<<12)|(fc>>2)|(base<<16)); #else @@ -746,14 +776,18 @@ static void gcall_or_jmp(int is_jmp) } } -#ifdef TCC_ARM_HARDFLOAT -static int is_float_hgen_aggr(CType *type) +/* 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) { if ((type->t & VT_BTYPE) == VT_STRUCT) { struct Sym *ref; int btype, nb_fields = 0; - ref = type->ref; + ref = type->ref->next; 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++); @@ -764,28 +798,36 @@ static int is_float_hgen_aggr(CType *type) } 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; + 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 } -/* 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) +/* 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) { // alignment needed (base type: double) + 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 { + } 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 @@ -798,394 +840,443 @@ int assign_fpreg(struct avail_regs *avregs, int align, int size) avregs->first_free_reg = -1; return -1; } + +/* Returns whether all params need to be passed in core registers or not. + This is the case for function part of the runtime ABI. */ +int floats_in_core_regs(SValue *sval) +{ + if (!sval->sym) + return 0; + + switch (sval->sym->v) { + case TOK___floatundisf: + case TOK___floatundidf: + case TOK___fixunssfdi: + case TOK___fixunsdfdi: +#ifndef TCC_ARM_VFP + case TOK___fixunsxfdi: #endif + case TOK___floatdisf: + case TOK___floatdidf: + case TOK___fixsfdi: + case TOK___fixdfdi: + return 1; -/* Return 1 if this function returns via an sret pointer, 0 otherwise */ -ST_FUNC int gfunc_sret(CType *vt, CType *ret, int *ret_align) { -#if TCC_ARM_EABI + default: + return 0; + } +} + +/* 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) { +#ifdef TCC_ARM_EABI int size, align; size = type_size(vt, &align); - if (size > 4) { - return 1; - } else { + if (float_abi == ARM_HARD_FLOAT && !variadic && + (is_float(vt->t) || is_hgen_float_aggr(vt))) { + *ret_align = 8; + ret->ref = NULL; + ret->t = VT_DOUBLE; + return (size + 7) >> 3; + } else if (size <= 4) { *ret_align = 4; ret->ref = NULL; ret->t = VT_INT; + return 1; + } else return 0; - } #else - return 1; + return 0; #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) +/* 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 + float_abi: float ABI in use for this function call + 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 responsibility of the caller to free this array once used (ie not + before copy_params). */ +static int assign_regs(int nb_args, int float_abi, struct plan *plan, int *todo) { - 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_vfpreg_hfa = NULL; /* SValue before first in VFP reg hfa argument */ -#ifdef TCC_ARM_HARDFLOAT + int i, size, align; + int ncrn /* next core register number */, nsaa /* next stacked argument address*/; + int plan_nb = 0; + struct param_plan pplan; 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 low addresses to high - 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_vfpreg_hfa. SValue's representing structures in the second zone are - moved just after the SValue pointer by before_stack. */ + 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; - size = type_size(&vtop[-i].type, &align); + int j, start_vfpreg = 0; + CType type = vtop[-i].type; + type.t &= ~VT_ARRAY; + size = type_size(&type, &align); + size = (size + 3) & ~3; + align = (align + 3) & ~3; switch(vtop[-i].type.t & VT_BTYPE) { case VT_STRUCT: case VT_FLOAT: case VT_DOUBLE: case VT_LDOUBLE: -#ifdef TCC_ARM_HARDFLOAT - if (!variadic) { - int hfa = 0; /* Homogeneous float aggregate */ + if (float_abi == ARM_HARD_FLOAT) { + 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) { - /* before_stack can only have been set because all core registers - are assigned, so no need to care about before_vfpreg_hfa if - before_stack is set */ - if (before_stack) { - vrote(&vtop[-i], &vtop[-i] - before_stack); - before_stack++; - } else if (!before_vfpreg_hfa) - before_vfpreg_hfa = &vtop[-i-1]; - 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++; - /* No need to update before_stack as no more hfa can be allocated in - VFP regs */ - if (!before_vfpreg_hfa) - before_vfpreg_hfa = &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)) { - /* Either there is HFA in VFP registers, or there is arguments on stack, - it cannot be both. Hence either before_stack already points after - the slot where the vtop[-i] SValue is moved, or before_stack will not - be used */ - if (before_vfpreg_hfa) { - vrote(&vtop[-i], &vtop[-i] - before_vfpreg_hfa); - before_vfpreg_hfa++; - } + ncrn = (ncrn + (align-1)/4) & ~((align/4) - 1); + 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]; - } - } - else { + *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_vfpreg_hfa if not set since there will no - longer be any structure assigned 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 */ } -#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; - } -#endif + 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 + + Returns the number of SValue added by this function on the value stack */ +static int copy_params(int nb_args, struct plan *plan, int todo) +{ + int size, align, r, i, nb_extra_sval = 0; + struct param_plan *pplan; + + /* 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) { + int padding = 0; + size = type_size(&pplan->sval->type, &align); + /* align to stack align size */ + size = (size + 3) & ~3; + if (i == STACK_CLASS && pplan->prev) + padding = pplan->start - pplan->prev->end; + size += padding; /* Add padding if any */ + /* allocate the necessary size on stack */ + gadd_sp(-size); + /* generate structure store */ + r = get_reg(RC_INT); + o(0xE28D0000|(intr(r)<<12)|padding); /* add r, sp, padding */ + vset(&vtop->type, r | VT_LVAL, 0); + vswap(); + vstore(); /* memcpy to current sp + potential padding */ + + /* 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; - - if (size == 12) - r|=0x400000; - else if(size == 8) - r|=0x8000; + 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; - o(0xED2D0100|r|(size>>2)); -#endif - 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 + if (size == 12) + r |= 0x400000; + else if(size == 8) + r|=0x8000; + + o(0xED2D0100|r|(size>>2)); /* some kind of vpush for FPA */ #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++; + } 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; } - 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(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++; - } + vtop--; } - args_size-=nb_regs*4; } - 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++; + /* 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) { + int r; + pplan->sval->r = pplan->start; + /* An SValue can only pin 2 registers at best (r and r2) but a structure + can occupy more than 2 registers. Thus, we need to push on the value + stack some fake parameter to have on SValue for each registers used + by a structure (r2 is not used). */ + for (r = pplan->start + 1; r <= pplan->end; r++) { + if (todo & (1 << r)) { + nb_extra_sval++; + vpushi(0); + vtop->r = r; + } } - if (nb_fregs) { - gadd_sp(nb_fregs*4); - args_size-=nb_fregs*4; } } - vrotb(keep); - gcall_or_jmp(0); - if (args_size) - gadd_sp(args_size); + return nb_extra_sval; +} + +/* 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 r, args_size; + int variadic, def_float_abi = float_abi; + int todo; + struct plan plan; + #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; + if (float_abi == ARM_HARD_FLOAT) { + variadic = (vtop[-nb_args].type.ref->c == FUNC_ELLIPSIS); + if (variadic || floats_in_core_regs(&vtop[-nb_args])) + float_abi = ARM_SOFTFP_FLOAT; } -#ifdef TCC_ARM_VFP -#ifdef TCC_ARM_HARDFLOAT - else if(variadic && is_float(vtop->type.ref->type.t)) { -#else - else if(is_float(vtop->type.ref->type.t)) { #endif + /* 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); + + args_size = assign_regs(nb_args, float_abi, &plan, &todo); + +#ifdef TCC_ARM_EABI + if (args_size & 7) { /* Stack must be 8 byte aligned at fct call for EABI */ + args_size = (args_size + 7) & ~7; + o(0xE24DD004); /* sub sp, sp, #4 */ + } +#endif + + nb_args += 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); /* pop all parameters passed on the stack */ +#if defined(TCC_ARM_EABI) && defined(TCC_ARM_VFP) + if(float_abi == ARM_SOFTFP_FLOAT && is_float(vtop->type.ref->type.t)) { 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 */ + float_abi = def_float_abi; } /* generate function prolog of type 't' */ void gfunc_prolog(CType *func_type) { Sym *sym,*sym2; - int n,nf,size,align, variadic, struct_ret = 0; -#ifdef TCC_ARM_HARDFLOAT + int n, nf, size, align, struct_ret = 0; + int addr, pn, sn; /* pn=core, sn=stack */ 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 - && 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; func_vc = 12; /* Offset from fp of the place to store the result */ } - for(sym2=sym->next;sym2 && (n<4 || nf<16);sym2=sym2->next) { + 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) - || is_float_hgen_aggr(&sym2->type))) { - int tmpnf = assign_fpreg(&avregs, align, size) + 1; +#ifdef TCC_ARM_EABI + if (float_abi == ARM_HARD_FLOAT && !func_var && + (is_float(sym2->type.t) || is_hgen_float_aggr(&sym2->type))) { + int tmpnf = assign_vfpreg(&avregs, align, size); + tmpnf += (size + 3) / 4; nf = (tmpnf > nf) ? tmpnf : nf; } else #endif @@ -1193,9 +1284,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 @@ -1212,50 +1303,49 @@ void gfunc_prolog(CType *func_type) o(0xE92D5800); /* save fp, ip, lr */ o(0xE1A0B00D); /* mov fp, sp */ func_sub_sp_offset = ind; - o(0xE1A00000); /* nop, leave space for stack adjustment in epilogue */ - { - int addr, pn = struct_ret, sn = 0; /* pn=core, sn=stack */ + o(0xE1A00000); /* nop, leave space for stack adjustment in epilog */ -#ifdef TCC_ARM_HARDFLOAT +#ifdef TCC_ARM_EABI + if (float_abi == ARM_HARD_FLOAT) { func_vc += nf * 4; avregs = AVAIL_REGS_INITIALIZER; + } #endif - while ((sym = sym->next)) { - CType *type; - type = &sym->type; - size = type_size(type, &align); - size = (size + 3) >> 2; - 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); - if (fpn >= 0) { - addr = fpn * 4; - } else - goto from_stack; - } else + pn = struct_ret, sn = 0; + while ((sym = sym->next)) { + CType *type; + type = &sym->type; + size = type_size(type, &align); + size = (size + 3) >> 2; + align = (align + 3) & ~3; +#ifdef TCC_ARM_EABI + if (float_abi == ARM_HARD_FLOAT && !func_var && (is_float(sym->type.t) + || is_hgen_float_aggr(&sym->type))) { + int fpn = assign_vfpreg(&avregs, align, size << 2); + if (fpn >= 0) + addr = fpn * 4; + else + goto from_stack; + } else #endif - if (pn < 4) { + if (pn < 4) { #ifdef TCC_ARM_EABI pn = (pn + (align-1)/4) & -(align/4); #endif - addr = (nf + pn) * 4; - pn += size; - if (!sn && pn > 4) - sn = (pn - 4); - } else { -#ifdef TCC_ARM_HARDFLOAT + addr = (nf + pn) * 4; + pn += size; + if (!sn && pn > 4) + sn = (pn - 4); + } else { from_stack: -#endif #ifdef TCC_ARM_EABI sn = (sn + (align-1)/4) & -(align/4); #endif - addr = (n + nf + sn) * 4; - sn += size; - } - sym_push(sym->v & ~SYM_FIELD, type, VT_LOCAL | lvalue_type(type->t), addr+12); + addr = (n + nf + sn) * 4; + sn += size; } + sym_push(sym->v & ~SYM_FIELD, type, VT_LOCAL | lvalue_type(type->t), + addr + 12); } last_itod_magic=0; leaffunc = 1; @@ -1267,10 +1357,10 @@ void gfunc_epilog(void) { uint32_t x; int diff; -#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)) { + /* Copy float return value to core register if base standard is used and + float computation is made with VFP */ +#if defined(TCC_ARM_EABI) && defined(TCC_ARM_VFP) + if ((float_abi == ARM_SOFTFP_FLOAT || func_var) && is_float(func_vt.t)) { if((func_vt.t & VT_BTYPE) == VT_FLOAT) o(0xEE100A10); /* fmrs r0, s0 */ else { @@ -1328,7 +1418,7 @@ int gtst(int inv, int t) op|=encbranch(r,t,1); o(op); t=r; - } else if (v == VT_JMP || v == VT_JMPI) { + } else { /* VT_JMP || VT_JMPI */ if ((v & 1) == inv) { if(!vtop->c.i) vtop->c.i=t; @@ -1350,29 +1440,6 @@ int gtst(int inv, int t) t = gjmp(t); gsym(vtop->c.i); } - } else { - if (is_float(vtop->type.t)) { - r=gv(RC_FLOAT); -#ifdef TCC_ARM_VFP - o(0xEEB50A40|(vfpr(r)<<12)|T2CPR(vtop->type.t)); /* fcmpzX */ - o(0xEEF1FA10); /* fmstat */ -#else - o(0xEE90F118|(fpr(r)<<16)); -#endif - vtop->r = VT_CMP; - vtop->c.i = TOK_NE; - return gtst(inv, t); - } else if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { - /* constant jmp optimization */ - if ((vtop->c.i != 0) != inv) - t = gjmp(t); - } else { - v = gv(RC_INT); - o(0xE3300000|(intr(v)<<16)); - vtop->r = VT_CMP; - vtop->c.i = TOK_NE; - return gtst(inv, t); - } } vtop--; return t; @@ -1630,7 +1697,7 @@ void gen_opf(int op) case TOK_UGE: op=TOK_GE; break; case TOK_UGT: op=TOK_GT; break; } - + vtop->r = VT_CMP; vtop->c.i = op; return; @@ -1781,7 +1848,7 @@ void gen_opf(int op) r=fpr(gv(RC_FLOAT)); vswap(); r2=fpr(gv(RC_FLOAT)); - } + } break; default: if(op >= TOK_ULT && op <= TOK_GT) { @@ -1793,7 +1860,7 @@ void gen_opf(int op) case TOK_UGE: case TOK_ULE: case TOK_UGT: - tcc_error("unsigned comparision on floats?"); + tcc_error("unsigned comparison on floats?"); break; case TOK_LT: op=TOK_Nset; |
