diff options
author | Alex Rønne Petersen <alexrp@xamarin.com> | 2013-12-03 10:04:58 +0100 |
---|---|---|
committer | Jo Shields <directhex@apebox.org> | 2013-12-03 22:47:04 +0000 |
commit | 17c6b0281a753d368bc3e46f7992e2965fcfd6e0 (patch) | |
tree | 50fe7da942cba41ace72841b0972dd1605d59137 | |
parent | d0a215f5626219ff7927f576588a777e5331c7be (diff) | |
download | mono-17c6b0281a753d368bc3e46f7992e2965fcfd6e0.tar.gz |
Merge branch 'armhf' of github.com:alexrp/mono
(cherry picked from commit b9bcafb382666ccf2f2848065f1760c8f322fa4d)
-rwxr-xr-x | configure.in | 8 | ||||
-rw-r--r-- | mono/mini/exceptions-arm.c | 20 | ||||
-rw-r--r-- | mono/mini/mini-arm.c | 311 | ||||
-rw-r--r-- | mono/mini/mini-arm.h | 43 | ||||
-rw-r--r-- | mono/mini/mini.h | 4 | ||||
-rw-r--r-- | mono/mini/tramp-arm.c | 42 | ||||
-rw-r--r-- | mono/utils/mono-hwcap-arm.c | 18 | ||||
-rw-r--r-- | mono/utils/mono-hwcap-arm.h | 2 |
8 files changed, 380 insertions, 68 deletions
diff --git a/configure.in b/configure.in index 899fe44614..58a33130c7 100755 --- a/configure.in +++ b/configure.in @@ -3071,6 +3071,8 @@ if test ${TARGET} = ARM; then return 0; ], [ arm_v5=yes + + arm_ver=ARMv5 ], []) AC_TRY_COMPILE([], [ @@ -3081,6 +3083,8 @@ if test ${TARGET} = ARM; then ], [ arm_v5=yes arm_v6=yes + + arm_ver=ARMv6 ], []) AC_TRY_COMPILE([], [ @@ -3092,8 +3096,12 @@ if test ${TARGET} = ARM; then arm_v5=yes arm_v6=yes arm_v7=yes + + arm_ver=ARMv7 ], []) + AC_MSG_RESULT($arm_ver) + if test x$arm_v5 = xyes; then AC_DEFINE(HAVE_ARMV5, 1, [ARM v5]) CPPFLAGS_FOR_LIBGC="$CPPFLAGS_FOR_LIBGC -DHAVE_ARMV5=1" diff --git a/mono/mini/exceptions-arm.c b/mono/mini/exceptions-arm.c index 8931dd8467..c124170519 100644 --- a/mono/mini/exceptions-arm.c +++ b/mono/mini/exceptions-arm.c @@ -60,10 +60,10 @@ mono_arch_get_restore_context (MonoTrampInfo **info, gboolean aot) ctx_reg = ARMREG_R0; -#if defined(ARM_FPU_VFP) - ARM_ADD_REG_IMM8 (code, ARMREG_IP, ctx_reg, G_STRUCT_OFFSET (MonoContext, fregs)); - ARM_FLDMD (code, ARM_VFP_D0, 16, ARMREG_IP); -#endif + if (!mono_arch_is_soft_float ()) { + ARM_ADD_REG_IMM8 (code, ARMREG_IP, ctx_reg, G_STRUCT_OFFSET (MonoContext, fregs)); + ARM_FLDMD (code, ARM_VFP_D0, 16, ARMREG_IP); + } /* move pc to PC */ ARM_LDR_IMM (code, ARMREG_IP, ctx_reg, G_STRUCT_OFFSET (MonoContext, pc)); @@ -220,12 +220,12 @@ get_throw_trampoline (int size, gboolean corlib, gboolean rethrow, gboolean llvm mono_add_unwind_op_offset (unwind_ops, code, start, ARMREG_LR, - sizeof (mgreg_t)); /* Save fp regs */ - ARM_SUB_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, sizeof (double) * 16); - cfa_offset += sizeof (double) * 16; - mono_add_unwind_op_def_cfa_offset (unwind_ops, code, start, cfa_offset); -#if defined(ARM_FPU_VFP) - ARM_FSTMD (code, ARM_VFP_D0, 16, ARMREG_SP); -#endif + if (!mono_arch_is_soft_float ()) { + ARM_SUB_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, sizeof (double) * 16); + cfa_offset += sizeof (double) * 16; + mono_add_unwind_op_def_cfa_offset (unwind_ops, code, start, cfa_offset); + ARM_FSTMD (code, ARM_VFP_D0, 16, ARMREG_SP); + } /* Param area */ ARM_SUB_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, 8); diff --git a/mono/mini/mini-arm.c b/mono/mini/mini-arm.c index 544607bd90..97c5ed306c 100644 --- a/mono/mini/mini-arm.c +++ b/mono/mini/mini-arm.c @@ -30,11 +30,26 @@ #error "ARM_FPU_NONE is defined while one of ARM_FPU_VFP/ARM_FPU_VFP_HARD is defined" #endif -#if defined(MONO_ARCH_SOFT_FLOAT_FALLBACK) +/* + * IS_SOFT_FLOAT: Is full software floating point used? + * IS_HARD_FLOAT: Is full hardware floating point used? + * IS_VFP: Is hardware floating point with software ABI used? + * + * These are not necessarily constants, e.g. IS_SOFT_FLOAT and + * IS_VFP may delegate to mono_arch_is_soft_float (). + */ + +#if defined(ARM_FPU_VFP_HARD) +#define IS_SOFT_FLOAT (FALSE) +#define IS_HARD_FLOAT (TRUE) +#define IS_VFP (TRUE) +#elif defined(ARM_FPU_NONE) #define IS_SOFT_FLOAT (mono_arch_is_soft_float ()) +#define IS_HARD_FLOAT (FALSE) #define IS_VFP (!mono_arch_is_soft_float ()) #else #define IS_SOFT_FLOAT (FALSE) +#define IS_HARD_FLOAT (FALSE) #define IS_VFP (TRUE) #endif @@ -105,6 +120,9 @@ static gboolean iphone_abi = FALSE; */ static MonoArmFPU arm_fpu; +static int vfp_scratch1 = ARM_VFP_F28; +static int vfp_scratch2 = ARM_VFP_F30; + static int i8_align; static volatile int ss_trigger_var = 0; @@ -354,7 +372,6 @@ mono_arm_load_jumptable_entry (guint8 *code, gpointer* jte, ARMReg reg) } #endif - static guint8* emit_move_return_value (MonoCompile *cfg, MonoInst *ins, guint8 *code) { @@ -364,10 +381,18 @@ emit_move_return_value (MonoCompile *cfg, MonoInst *ins, guint8 *code) case OP_FCALL_MEMBASE: if (IS_VFP) { if (((MonoCallInst*)ins)->signature->ret->type == MONO_TYPE_R4) { - ARM_FMSR (code, ins->dreg, ARMREG_R0); - ARM_CVTS (code, ins->dreg, ins->dreg); + if (IS_HARD_FLOAT) { + ARM_CVTS (code, ins->dreg, ARM_VFP_F0); + } else { + ARM_FMSR (code, ins->dreg, ARMREG_R0); + ARM_CVTS (code, ins->dreg, ins->dreg); + } } else { - ARM_FMDRR (code, ARMREG_R0, ARMREG_R1, ins->dreg); + if (IS_HARD_FLOAT) { + ARM_CPYD (code, ins->dreg, ARM_VFP_D0); + } else { + ARM_FMDRR (code, ARMREG_R0, ARMREG_R1, ins->dreg); + } } } break; @@ -465,6 +490,37 @@ emit_save_lmf (MonoCompile *cfg, guint8 *code, gint32 lmf_offset) return code; } +typedef struct { + gint32 vreg; + gint32 hreg; +} FloatArgData; + +static guint8 * +emit_float_args (MonoCompile *cfg, MonoCallInst *inst, guint8 *code, int *max_len, guint *offset) +{ + GSList *list; + + for (list = inst->float_args; list; list = list->next) { + FloatArgData *fad = list->data; + MonoInst *var = get_vreg_to_inst (cfg, fad->vreg); + + *max_len += 4; + + if (*offset + *max_len > cfg->code_size) { + cfg->code_size += *max_len; + cfg->native_code = g_realloc (cfg->native_code, cfg->code_size); + + code = cfg->native_code + *offset; + } + + ARM_FLDS (code, fad->hreg, var->inst_basereg, var->inst_offset); + + *offset = code - cfg->native_code; + } + + return code; +} + /* * emit_save_lmf: * @@ -904,6 +960,12 @@ mono_arch_is_soft_float (void) } #endif +gboolean +mono_arm_is_hard_float (void) +{ + return arm_fpu == MONO_ARM_FPU_VFP_HARD; +} + static gboolean is_regsize_var (MonoGenericSharingContext *gsctx, MonoType *t) { if (t->byref) @@ -1152,10 +1214,97 @@ add_general (guint *gr, guint *stack_size, ArgInfo *ainfo, gboolean simple) (*gr) ++; } +static void inline +add_float (guint *fpr, guint *stack_size, ArgInfo *ainfo, gboolean is_double, gint *float_spare) +{ + /* + * If we're calling a function like this: + * + * void foo(float a, double b, float c) + * + * We pass a in s0 and b in d1. That leaves us + * with s1 being unused. The armhf ABI recognizes + * this and requires register assignment to then + * use that for the next single-precision arg, + * i.e. c in this example. So float_spare either + * tells us which reg to use for the next single- + * precision arg, or it's -1, meaning use *fpr. + * + * Note that even though most of the JIT speaks + * double-precision, fpr represents single- + * precision registers. + * + * See parts 5.5 and 6.1.2 of the AAPCS for how + * this all works. + */ + + if (*fpr < ARM_VFP_F16 || (!is_double && *float_spare >= 0)) { + ainfo->storage = RegTypeFP; + + if (is_double) { + /* + * If we're passing a double-precision value + * and *fpr is odd (e.g. it's s1, s3, ...) + * we need to use the next even register. So + * we mark the current *fpr as a spare that + * can be used for the next single-precision + * value. + */ + if (*fpr % 2) { + *float_spare = *fpr; + (*fpr)++; + } + + /* + * At this point, we have an even register + * so we assign that and move along. + */ + ainfo->reg = *fpr; + *fpr += 2; + } else if (*float_spare >= 0) { + /* + * We're passing a single-precision value + * and it looks like a spare single- + * precision register is available. Let's + * use it. + */ + + ainfo->reg = *float_spare; + *float_spare = -1; + } else { + /* + * If we hit this branch, we're passing a + * single-precision value and we can simply + * use the next available register. + */ + + ainfo->reg = *fpr; + (*fpr)++; + } + } else { + /* + * We've exhausted available floating point + * regs, so pass the rest on the stack. + */ + + if (is_double) { + *stack_size += 7; + *stack_size &= ~7; + } + + ainfo->offset = *stack_size; + ainfo->reg = ARMREG_SP; + ainfo->storage = RegTypeBase; + + *stack_size += 8; + } +} + static CallInfo* get_call_info (MonoGenericSharingContext *gsctx, MonoMemPool *mp, MonoMethodSignature *sig) { - guint i, gr, pstart; + guint i, gr, fpr, pstart; + gint float_spare; int n = sig->hasthis + sig->param_count; MonoType *simpletype; guint32 stack_size = 0; @@ -1170,6 +1319,8 @@ get_call_info (MonoGenericSharingContext *gsctx, MonoMemPool *mp, MonoMethodSign cinfo->nargs = n; gr = ARMREG_R0; + fpr = ARM_VFP_F0; + float_spare = -1; t = mini_type_get_underlying_type (gsctx, sig->ret); if (MONO_TYPE_ISSTRUCT (t)) { @@ -1222,6 +1373,7 @@ get_call_info (MonoGenericSharingContext *gsctx, MonoMemPool *mp, MonoMethodSign /* Prevent implicit arguments and sig_cookie from being passed in registers */ gr = ARMREG_R3 + 1; + fpr = ARM_VFP_F16; /* Emit the signature cookie just before the implicit arguments */ add_general (&gr, &stack_size, &cinfo->sig_cookie, TRUE); } @@ -1263,7 +1415,6 @@ get_call_info (MonoGenericSharingContext *gsctx, MonoMemPool *mp, MonoMethodSign case MONO_TYPE_STRING: case MONO_TYPE_SZARRAY: case MONO_TYPE_ARRAY: - case MONO_TYPE_R4: cinfo->args [n].size = sizeof (gpointer); add_general (&gr, &stack_size, ainfo, TRUE); n++; @@ -1344,11 +1495,30 @@ get_call_info (MonoGenericSharingContext *gsctx, MonoMemPool *mp, MonoMethodSign } case MONO_TYPE_U8: case MONO_TYPE_I8: - case MONO_TYPE_R8: ainfo->size = 8; add_general (&gr, &stack_size, ainfo, FALSE); n++; break; + case MONO_TYPE_R4: + ainfo->size = 4; + + if (IS_HARD_FLOAT) + add_float (&fpr, &stack_size, ainfo, FALSE, &float_spare); + else + add_general (&gr, &stack_size, ainfo, TRUE); + + n++; + break; + case MONO_TYPE_R8: + ainfo->size = 8; + + if (IS_HARD_FLOAT) + add_float (&fpr, &stack_size, ainfo, TRUE, &float_spare); + else + add_general (&gr, &stack_size, ainfo, FALSE); + + n++; + break; case MONO_TYPE_VAR: case MONO_TYPE_MVAR: /* gsharedvt arguments are passed by ref */ @@ -1376,6 +1546,7 @@ get_call_info (MonoGenericSharingContext *gsctx, MonoMemPool *mp, MonoMethodSign /* Prevent implicit arguments and sig_cookie from being passed in registers */ gr = ARMREG_R3 + 1; + fpr = ARM_VFP_F16; /* Emit the signature cookie just before the implicit arguments */ add_general (&gr, &stack_size, &cinfo->sig_cookie, TRUE); } @@ -1411,9 +1582,13 @@ get_call_info (MonoGenericSharingContext *gsctx, MonoMemPool *mp, MonoMethodSign case MONO_TYPE_R4: case MONO_TYPE_R8: cinfo->ret.storage = RegTypeFP; - cinfo->ret.reg = ARMREG_R0; - /* FIXME: cinfo->ret.reg = ???; - cinfo->ret.storage = RegTypeFP;*/ + + if (IS_HARD_FLOAT) { + cinfo->ret.reg = ARM_VFP_F0; + } else { + cinfo->ret.reg = ARMREG_R0; + } + break; case MONO_TYPE_GENERICINST: if (!mono_type_generic_inst_is_valuetype (simpletype)) { @@ -2104,19 +2279,50 @@ mono_arch_emit_call (MonoCompile *cfg, MonoCallInst *call) } break; case RegTypeFP: { - /* FIXME: */ - NOT_IMPLEMENTED; -#if 0 - arg->backend.reg3 = ainfo->reg; - /* FP args are passed in int regs */ - call->used_iregs |= 1 << ainfo->reg; + int fdreg = mono_alloc_freg (cfg); + if (ainfo->size == 8) { - arg->opcode = OP_OUTARG_R8; - call->used_iregs |= 1 << (ainfo->reg + 1); + MONO_INST_NEW (cfg, ins, OP_FMOVE); + ins->sreg1 = in->dreg; + ins->dreg = fdreg; + MONO_ADD_INS (cfg->cbb, ins); + + mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, ainfo->reg, TRUE); } else { - arg->opcode = OP_OUTARG_R4; + FloatArgData *fad; + + /* + * Mono's register allocator doesn't speak single-precision registers that + * overlap double-precision registers (i.e. armhf). So we have to work around + * the register allocator and load the value from memory manually. + * + * So we create a variable for the float argument and an instruction to store + * the argument into the variable. We then store the list of these arguments + * in cfg->float_args. This list is then used by emit_float_args later to + * pass the arguments in the various call opcodes. + * + * This is not very nice, and we should really try to fix the allocator. + */ + + MonoInst *float_arg = mono_compile_create_var (cfg, &mono_defaults.single_class->byval_arg, OP_LOCAL); + + /* Make sure the instruction isn't seen as pointless and removed. + */ + float_arg->flags |= MONO_INST_VOLATILE; + + MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, float_arg->dreg, in->dreg); + + /* We use the dreg to look up the instruction later. The hreg is used to + * emit the instruction that loads the value into the FP reg. + */ + fad = mono_mempool_alloc0 (cfg->mempool, sizeof (FloatArgData)); + fad->vreg = float_arg->dreg; + fad->hreg = ainfo->reg; + + call->float_args = g_slist_append_mempool (cfg->mempool, call->float_args, fad); } -#endif + + call->used_iregs |= 1 << ainfo->reg; cfg->flags |= MONO_CFG_HAS_FPOUT; break; } @@ -2238,6 +2444,7 @@ mono_arch_emit_setret (MonoCompile *cfg, MonoMethod *method, MonoInst *val) } break; case MONO_ARM_FPU_VFP: + case MONO_ARM_FPU_VFP_HARD: if (ret->type == MONO_TYPE_R8 || ret->type == MONO_TYPE_R4) { MonoInst *ins; @@ -3209,10 +3416,10 @@ emit_float_to_int (MonoCompile *cfg, guchar *code, int dreg, int sreg, int size, /* sreg is a float, dreg is an integer reg */ if (IS_VFP) { if (is_signed) - ARM_TOSIZD (code, ARM_VFP_F0, sreg); + ARM_TOSIZD (code, vfp_scratch1, sreg); else - ARM_TOUIZD (code, ARM_VFP_F0, sreg); - ARM_FMRS (code, dreg, ARM_VFP_F0); + ARM_TOUIZD (code, vfp_scratch1, sreg); + ARM_FMRS (code, dreg, vfp_scratch1); } if (!is_signed) { if (size == 1) @@ -4364,6 +4571,10 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) case OP_VOIDCALL: case OP_CALL: call = (MonoCallInst*)ins; + + if (IS_HARD_FLOAT) + code = emit_float_args (cfg, call, code, &max_len, &offset); + if (ins->flags & MONO_INST_HAS_METHOD) mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_METHOD, call->method); else @@ -4379,6 +4590,9 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) case OP_VCALL2_REG: case OP_VOIDCALL_REG: case OP_CALL_REG: + if (IS_HARD_FLOAT) + code = emit_float_args (cfg, (MonoCallInst *)ins, code, &max_len, &offset); + code = emit_call_reg (code, ins->sreg1); ins->flags |= MONO_INST_GC_CALLSITE; ins->backend.pc_offset = code - cfg->native_code; @@ -4394,6 +4608,10 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) g_assert (ins->sreg1 != ARMREG_LR); call = (MonoCallInst*)ins; + + if (IS_HARD_FLOAT) + code = emit_float_args (cfg, call, code, &max_len, &offset); + if (call->dynamic_imt_arg || call->method->klass->flags & TYPE_ATTRIBUTE_INTERFACE) imt_arg = TRUE; if (!arm_is_imm12 (ins->inst_offset)) @@ -4770,34 +4988,41 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) break; case OP_STORER4_MEMBASE_REG: g_assert (arm_is_fpimm8 (ins->inst_offset)); - ARM_CVTD (code, ARM_VFP_F0, ins->sreg1); - ARM_FSTS (code, ARM_VFP_F0, ins->inst_destbasereg, ins->inst_offset); + ARM_CVTD (code, vfp_scratch1, ins->sreg1); + ARM_FSTS (code, vfp_scratch1, ins->inst_destbasereg, ins->inst_offset); break; case OP_LOADR4_MEMBASE: g_assert (arm_is_fpimm8 (ins->inst_offset)); - ARM_FLDS (code, ARM_VFP_F0, ins->inst_basereg, ins->inst_offset); - ARM_CVTS (code, ins->dreg, ARM_VFP_F0); + ARM_FLDS (code, vfp_scratch1, ins->inst_basereg, ins->inst_offset); + ARM_CVTS (code, ins->dreg, vfp_scratch1); break; case OP_ICONV_TO_R_UN: { g_assert_not_reached (); break; } case OP_ICONV_TO_R4: - ARM_FMSR (code, ARM_VFP_F0, ins->sreg1); - ARM_FSITOS (code, ARM_VFP_F0, ARM_VFP_F0); - ARM_CVTS (code, ins->dreg, ARM_VFP_F0); + ARM_FMSR (code, vfp_scratch1, ins->sreg1); + ARM_FSITOS (code, vfp_scratch1, vfp_scratch1); + ARM_CVTS (code, ins->dreg, vfp_scratch1); break; case OP_ICONV_TO_R8: - ARM_FMSR (code, ARM_VFP_F0, ins->sreg1); - ARM_FSITOD (code, ins->dreg, ARM_VFP_F0); + ARM_FMSR (code, vfp_scratch1, ins->sreg1); + ARM_FSITOD (code, ins->dreg, vfp_scratch1); break; case OP_SETFRET: if (mono_method_signature (cfg->method)->ret->type == MONO_TYPE_R4) { ARM_CVTD (code, ARM_VFP_F0, ins->sreg1); - ARM_FMRS (code, ARMREG_R0, ARM_VFP_F0); + + if (!IS_HARD_FLOAT) { + ARM_FMRS (code, ARMREG_R0, ARM_VFP_F0); + } } else { - ARM_FMRRD (code, ARMREG_R0, ARMREG_R1, ins->sreg1); + if (IS_HARD_FLOAT) { + ARM_CPYD (code, ARM_VFP_D0, ins->sreg1); + } else { + ARM_FMRRD (code, ARMREG_R0, ARMREG_R1, ins->sreg1); + } } break; case OP_FCONV_TO_I1: @@ -4975,18 +5200,18 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb) jte [0] = GUINT_TO_POINTER (0xffffffff); jte [1] = GUINT_TO_POINTER (0x7fefffff); code = mono_arm_load_jumptable_entry_addr (code, jte, ARMREG_IP); - ARM_FLDD (code, ARM_VFP_D0, ARMREG_IP, 0); + ARM_FLDD (code, vfp_scratch1, ARMREG_IP, 0); } #else - ARM_ABSD (code, ARM_VFP_D1, ins->sreg1); - ARM_FLDD (code, ARM_VFP_D0, ARMREG_PC, 0); + ARM_ABSD (code, vfp_scratch2, ins->sreg1); + ARM_FLDD (code, vfp_scratch1, ARMREG_PC, 0); ARM_B (code, 1); *(guint32*)code = 0xffffffff; code += 4; *(guint32*)code = 0x7fefffff; code += 4; #endif - ARM_CMPD (code, ARM_VFP_D1, ARM_VFP_D0); + ARM_CMPD (code, vfp_scratch2, vfp_scratch1); ARM_FMSTAT (code); EMIT_COND_SYSTEM_EXCEPTION_FLAGS (ARMCOND_GT, "ArithmeticException"); ARM_CMPD (code, ins->sreg1, ins->sreg1); @@ -5456,7 +5681,13 @@ mono_arch_emit_prolog (MonoCompile *cfg) break; } } else if (ainfo->storage == RegTypeFP) { - g_assert_not_reached (); + code = mono_arm_emit_load_imm (code, ARMREG_IP, inst->inst_offset); + ARM_ADD_REG_REG (code, ARMREG_IP, ARMREG_IP, inst->inst_basereg); + + if (ainfo->size == 8) + ARM_FSTD (code, ainfo->reg, ARMREG_IP, 0); + else + ARM_FSTS (code, ainfo->reg, ARMREG_IP, 0); } else if (ainfo->storage == RegTypeStructByVal) { int doffset = inst->inst_offset; int soffset = 0; diff --git a/mono/mini/mini-arm.h b/mono/mini/mini-arm.h index 93a256b563..c1f21f8995 100644 --- a/mono/mini/mini-arm.h +++ b/mono/mini/mini-arm.h @@ -19,10 +19,6 @@ #define MONO_ARCH_SOFT_FLOAT_FALLBACK 1 #endif -#ifdef ARM_FPU_VFP_HARD -#error "hardfp-abi not yet supported." -#endif - #if defined(__ARM_EABI__) #if G_BYTE_ORDER == G_LITTLE_ENDIAN #define ARM_ARCHITECTURE "armel" @@ -56,10 +52,9 @@ #endif #define MONO_MAX_IREGS 16 -#define MONO_MAX_FREGS 16 +#define MONO_MAX_FREGS 32 #define MONO_SAVED_GREGS 10 /* r4-r11, ip, lr */ -#define MONO_SAVED_FREGS 8 /* r4-r11, ip, lr: registers saved in the LMF */ #define MONO_ARM_REGSAVE_MASK 0x5ff0 @@ -71,9 +66,32 @@ #define MONO_ARCH_CALLEE_REGS ((1<<ARMREG_R0) | (1<<ARMREG_R1) | (1<<ARMREG_R2) | (1<<ARMREG_R3) | (1<<ARMREG_IP)) #define MONO_ARCH_CALLEE_SAVED_REGS ((1<<ARMREG_V1) | (1<<ARMREG_V2) | (1<<ARMREG_V3) | (1<<ARMREG_V4) | (1<<ARMREG_V5) | (1<<ARMREG_V6) | (1<<ARMREG_V7)) -/* Every double precision vfp register, d0/d1 is reserved for a scratch reg */ -#define MONO_ARCH_CALLEE_FREGS 0x55555550 -#define MONO_ARCH_CALLEE_SAVED_FREGS 0 +/* + * TODO: Make use of VFP v3 registers d16-d31. + */ + +/* + * TODO: We can't use registers d8-d15 in hard float mode because the + * register allocator doesn't allocate floating point registers globally. + */ + +#if defined(ARM_FPU_VFP_HARD) +#define MONO_SAVED_FREGS 16 +/* + * d8-d15 must be preserved across function calls. We use d14-d15 as + * scratch registers in the JIT. The rest have no meaning tied to them. + */ +#define MONO_ARCH_CALLEE_FREGS 0x00005555 +#define MONO_ARCH_CALLEE_SAVED_FREGS 0x55550000 +#else +#define MONO_SAVED_FREGS 0 +/* + * No registers need to be preserved across function calls. We use d14-d15 + * as scratch registers in the JIT. The rest have no meaning tied to them. + */ +#define MONO_ARCH_CALLEE_FREGS 0x05555555 +#define MONO_ARCH_CALLEE_SAVED_FREGS 0x00000000 +#endif #define MONO_ARCH_USE_FPSTACK FALSE #define MONO_ARCH_FPSTACK_SIZE 0 @@ -164,6 +182,10 @@ struct MonoLMF { mgreg_t sp; mgreg_t ip; mgreg_t fp; + /* Currently only used in trampolines on armhf to hold d0-d15. We don't really + * need to put d0-d7 in the LMF, but it simplifies the trampoline code. + */ + double fregs [16]; /* all but sp and pc: matches the PUSH instruction layout in the trampolines * 0-4 should be considered undefined (execpt in the magic tramp) * sp is saved at IP. @@ -291,4 +313,7 @@ guint8* mono_arm_load_jumptable_entry (guint8 *code, gpointer *jte, ARMReg reg) MONO_INTERNAL; #endif +gboolean +mono_arm_is_hard_float (void) MONO_INTERNAL; + #endif /* __MONO_MINI_ARM_H__ */ diff --git a/mono/mini/mini.h b/mono/mini/mini.h index a99aa427ab..16f58a2bb7 100644 --- a/mono/mini/mini.h +++ b/mono/mini/mini.h @@ -846,6 +846,10 @@ struct MonoCallInst { LLVMCallInfo *cinfo; int rgctx_arg_reg, imt_arg_reg; #endif +#ifdef TARGET_ARM + /* See the comment in mini-arm.c!mono_arch_emit_call for RegTypeFP. */ + GSList *float_args; +#endif }; struct MonoCallArgParm { diff --git a/mono/mini/tramp-arm.c b/mono/mini/tramp-arm.c index 39070e6760..35e4eb9a7c 100644 --- a/mono/mini/tramp-arm.c +++ b/mono/mini/tramp-arm.c @@ -16,6 +16,7 @@ #include <mono/metadata/marshal.h> #include <mono/metadata/tabledefs.h> #include <mono/arch/arm/arm-codegen.h> +#include <mono/arch/arm/arm-vfp-codegen.h> #include "mini.h" #include "mini-arm.h" @@ -179,7 +180,7 @@ emit_bx (guint8* code, int reg) return code; } -/* Stack size for trampoline function +/* Stack size for trampoline function */ #define STACK ALIGN_TO (sizeof (MonoLMF), 8) @@ -201,7 +202,7 @@ mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type, MonoTrampInf gpointer *constants; #endif - int cfa_offset, lmf_offset, regsave_size, lr_offset; + int cfa_offset, regsave_size, lr_offset; GSList *unwind_ops = NULL; MonoJumpInfo *ji = NULL; int buf_len; @@ -213,7 +214,12 @@ mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type, MonoTrampInf /* Now we'll create in 'buf' the ARM trampoline code. This is the trampoline code common to all methods */ - buf_len = 212; + buf_len = 272; + + /* Add space for saving/restoring VFP regs. */ + if (mono_arm_is_hard_float ()) + buf_len += 8 * 2; + code = buf = mono_global_codeman_reserve (buf_len); /* @@ -222,8 +228,6 @@ mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type, MonoTrampInf * saved as sp + LR_OFFSET by the push in the specific trampoline */ - /* The offset of lmf inside the stack frame */ - lmf_offset = STACK - sizeof (MonoLMF); /* The size of the area already allocated by the push in the specific trampoline */ regsave_size = 14 * sizeof (mgreg_t); /* The offset where lr was saved inside the regsave area */ @@ -288,11 +292,13 @@ mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type, MonoTrampInf * The pointer to the struct is put in r1. * the iregs array is already allocated on the stack by push. */ - ARM_SUB_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, STACK - regsave_size); + code = mono_arm_emit_load_imm (code, ARMREG_R2, STACK - regsave_size); + ARM_SUB_REG_REG (code, ARMREG_SP, ARMREG_SP, ARMREG_R2); cfa_offset += STACK - regsave_size; mono_add_unwind_op_def_cfa_offset (unwind_ops, code, buf, cfa_offset); /* V1 == lmf */ - ARM_ADD_REG_IMM8 (code, ARMREG_V1, ARMREG_SP, STACK - sizeof (MonoLMF)); + code = mono_arm_emit_load_imm (code, ARMREG_R2, STACK - sizeof (MonoLMF)); + ARM_ADD_REG_REG (code, ARMREG_V1, ARMREG_SP, ARMREG_R2); /* * The stack now looks like: @@ -316,7 +322,8 @@ mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type, MonoTrampInf ARM_STR_IMM (code, ARMREG_R2, ARMREG_V1, G_STRUCT_OFFSET (MonoLMF, method)); } /* save caller SP */ - ARM_ADD_REG_IMM8 (code, ARMREG_R2, ARMREG_SP, cfa_offset); + code = mono_arm_emit_load_imm (code, ARMREG_R2, cfa_offset); + ARM_ADD_REG_REG (code, ARMREG_R2, ARMREG_SP, ARMREG_R2); ARM_STR_IMM (code, ARMREG_R2, ARMREG_V1, G_STRUCT_OFFSET (MonoLMF, sp)); /* save caller FP */ ARM_LDR_IMM (code, ARMREG_R2, ARMREG_V1, (G_STRUCT_OFFSET (MonoLMF, iregs) + ARMREG_FP*4)); @@ -329,11 +336,22 @@ mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type, MonoTrampInf } ARM_STR_IMM (code, ARMREG_R2, ARMREG_V1, G_STRUCT_OFFSET (MonoLMF, ip)); + /* Save VFP registers. */ + if (mono_arm_is_hard_float ()) { + /* + * Strictly speaking, we don't have to save d0-d7 in the LMF, but + * it's easier than attempting to store them on the stack since + * this trampoline code is pretty messy. + */ + ARM_ADD_REG_IMM8 (code, ARMREG_R0, ARMREG_V1, G_STRUCT_OFFSET (MonoLMF, fregs)); + ARM_FSTMD (code, ARM_VFP_D0, 8, ARMREG_R0); + } + /* * Now we're ready to call xxx_trampoline (). */ /* Arg 1: the saved registers */ - ARM_ADD_REG_IMM8 (code, ARMREG_R0, ARMREG_V1, G_STRUCT_OFFSET (MonoLMF, iregs)); + ARM_ADD_REG_IMM (code, ARMREG_R0, ARMREG_V1, G_STRUCT_OFFSET (MonoLMF, iregs), 0); /* Arg 2: code (next address to the instruction that called us) */ if (tramp_type == MONO_TRAMPOLINE_JUMP) { @@ -413,6 +431,12 @@ mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type, MonoTrampInf /* *(lmf_addr) = previous_lmf */ ARM_STR_IMM (code, ARMREG_IP, ARMREG_LR, G_STRUCT_OFFSET (MonoLMF, previous_lmf)); + /* Restore VFP registers. */ + if (mono_arm_is_hard_float ()) { + ARM_ADD_REG_IMM8 (code, ARMREG_R0, ARMREG_V1, G_STRUCT_OFFSET (MonoLMF, fregs)); + ARM_FLDMD (code, ARM_VFP_D0, 8, ARMREG_R0); + } + /* Non-standard function epilogue. Instead of doing a proper * return, we just jump to the compiled code. */ diff --git a/mono/utils/mono-hwcap-arm.c b/mono/utils/mono-hwcap-arm.c index d5d5f00ecb..e87635bb22 100644 --- a/mono/utils/mono-hwcap-arm.c +++ b/mono/utils/mono-hwcap-arm.c @@ -35,6 +35,8 @@ gboolean mono_hwcap_arm_is_v6 = FALSE; gboolean mono_hwcap_arm_is_v7 = FALSE; gboolean mono_hwcap_arm_is_v7s = FALSE; gboolean mono_hwcap_arm_has_vfp = FALSE; +gboolean mono_hwcap_arm_has_vfp3 = FALSE; +gboolean mono_hwcap_arm_has_vfp3_d16 = FALSE; gboolean mono_hwcap_arm_has_thumb = FALSE; gboolean mono_hwcap_arm_has_thumb2 = FALSE; @@ -60,6 +62,14 @@ mono_hwcap_arch_init (void) if (hwcap & 0x00000064) mono_hwcap_arm_has_vfp = TRUE; + /* HWCAP_ARM_VFPv3 */ + if (hwcap & 0x00002000) + mono_hwcap_arm_has_vfp3 = TRUE; + + /* HWCAP_ARM_VFPv3D16 */ + if (hwcap & 0x00004000) + mono_hwcap_arm_has_vfp3_d16 = TRUE; + /* TODO: Find a way to detect Thumb 2. */ } @@ -136,6 +146,12 @@ mono_hwcap_arch_init (void) if (strstr (line, "vfp")) mono_hwcap_arm_has_vfp = TRUE; + if (strstr (line, "vfpv3")) + mono_hwcap_arm_has_vfp3 = TRUE; + + if (strstr (line, "vfpv3-d16")) + mono_hwcap_arm_has_vfp3_d16 = TRUE; + continue; } } @@ -154,6 +170,8 @@ mono_hwcap_print(FILE *f) g_fprintf (f, "mono_hwcap_arm_is_v7 = %i\n", mono_hwcap_arm_is_v7); g_fprintf (f, "mono_hwcap_arm_is_v7s = %i\n", mono_hwcap_arm_is_v7s); g_fprintf (f, "mono_hwcap_arm_has_vfp = %i\n", mono_hwcap_arm_has_vfp); + g_fprintf (f, "mono_hwcap_arm_has_vfp3 = %i\n", mono_hwcap_arm_has_vfp3); + g_fprintf (f, "mono_hwcap_arm_has_vfp3_d16 = %i\n", mono_hwcap_arm_has_vfp3_d16); g_fprintf (f, "mono_hwcap_arm_has_thumb = %i\n", mono_hwcap_arm_has_thumb); g_fprintf (f, "mono_hwcap_arm_has_thumb2 = %i\n", mono_hwcap_arm_has_thumb2); } diff --git a/mono/utils/mono-hwcap-arm.h b/mono/utils/mono-hwcap-arm.h index 6bc9c3b907..76a20a1f53 100644 --- a/mono/utils/mono-hwcap-arm.h +++ b/mono/utils/mono-hwcap-arm.h @@ -8,6 +8,8 @@ extern gboolean mono_hwcap_arm_is_v6; extern gboolean mono_hwcap_arm_is_v7; extern gboolean mono_hwcap_arm_is_v7s; extern gboolean mono_hwcap_arm_has_vfp; +extern gboolean mono_hwcap_arm_has_vfp3; +extern gboolean mono_hwcap_arm_has_vfp3_d16; extern gboolean mono_hwcap_arm_has_thumb; extern gboolean mono_hwcap_arm_has_thumb2; |