summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Rønne Petersen <alexrp@xamarin.com>2013-12-03 10:04:58 +0100
committerJo Shields <directhex@apebox.org>2013-12-03 22:47:04 +0000
commit17c6b0281a753d368bc3e46f7992e2965fcfd6e0 (patch)
tree50fe7da942cba41ace72841b0972dd1605d59137
parentd0a215f5626219ff7927f576588a777e5331c7be (diff)
downloadmono-17c6b0281a753d368bc3e46f7992e2965fcfd6e0.tar.gz
Merge branch 'armhf' of github.com:alexrp/mono
(cherry picked from commit b9bcafb382666ccf2f2848065f1760c8f322fa4d)
-rwxr-xr-xconfigure.in8
-rw-r--r--mono/mini/exceptions-arm.c20
-rw-r--r--mono/mini/mini-arm.c311
-rw-r--r--mono/mini/mini-arm.h43
-rw-r--r--mono/mini/mini.h4
-rw-r--r--mono/mini/tramp-arm.c42
-rw-r--r--mono/utils/mono-hwcap-arm.c18
-rw-r--r--mono/utils/mono-hwcap-arm.h2
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;