Bug 966881 - ARM Simulator: Add support for the ARM hardfp ABI. r=mjrosenb
authorDouglas Crosher <dtc-moz@scieneer.com>
Mon, 17 Feb 2014 22:41:06 +1100
changeset 169344 e67062995f0b25951d39324622bd908d246d787b
parent 169343 3576a62195ecdef9a289aa0704a2e326b32bb204
child 169345 0f7f99d0a2f56f0ac3f4c11bc8c636e6e1d4683b
push id26245
push userryanvm@gmail.com
push dateTue, 18 Feb 2014 20:18:17 +0000
treeherdermozilla-central@9019cc90719c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmjrosenb
bugs966881
milestone30.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 966881 - ARM Simulator: Add support for the ARM hardfp ABI. r=mjrosenb
js/src/jit/CodeGenerator.cpp
js/src/jit/arm/Architecture-arm.cpp
js/src/jit/arm/Architecture-arm.h
js/src/jit/arm/Assembler-arm.h
js/src/jit/arm/CodeGenerator-arm.h
js/src/jit/arm/MacroAssembler-arm.cpp
js/src/jit/arm/MacroAssembler-arm.h
js/src/jit/arm/Simulator-arm.cpp
js/src/jit/arm/Simulator-arm.h
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -7998,18 +7998,18 @@ CodeGenerator::visitHaveSameClass(LHaveS
     return true;
 }
 
 bool
 CodeGenerator::visitAsmJSCall(LAsmJSCall *ins)
 {
     MAsmJSCall *mir = ins->mir();
 
-#if defined(JS_CODEGEN_ARM) && !defined(JS_CODEGEN_ARM_HARDFP)
-    if (mir->callee().which() == MAsmJSCall::Callee::Builtin) {
+#if defined(JS_CODEGEN_ARM)
+    if (!useHardFpABI() && mir->callee().which() == MAsmJSCall::Callee::Builtin) {
         for (unsigned i = 0, e = ins->numOperands(); i < e; i++) {
             LAllocation *a = ins->getOperand(i);
             if (a->isFloatReg()) {
                 FloatRegister fr = ToFloatRegister(a);
                 int srcId = fr.code() * 2;
                 masm.ma_vxfer(fr, Register::FromCode(srcId), Register::FromCode(srcId+1));
             }
         }
--- a/js/src/jit/arm/Architecture-arm.cpp
+++ b/js/src/jit/arm/Architecture-arm.cpp
@@ -10,16 +10,18 @@
 #include <elf.h>
 #endif
 
 #include <fcntl.h>
 #include <unistd.h>
 
 #include "jit/arm/Assembler-arm.h"
 
+#define HWCAP_USE_HARDFP_ABI (1 << 28)
+
 #if !(defined(ANDROID) || defined(MOZ_B2G)) && !defined(JS_ARM_SIMULATOR)
 #define HWCAP_ARMv7 (1 << 29)
 #include <asm/hwcap.h>
 #else
 #define HWCAP_VFP      (1<<0)
 #define HWCAP_VFPv3    (1<<1)
 #define HWCAP_VFPv3D16 (1<<2)
 #define HWCAP_VFPv4    (1<<3)
@@ -34,16 +36,20 @@ namespace jit {
 
 uint32_t GetARMFlags()
 {
     static bool isSet = false;
     static uint32_t flags = 0;
     if (isSet)
         return flags;
 
+#ifdef JS_CODEGEN_ARM_HARDFP
+    flags |= HWCAP_USE_HARDFP_ABI;
+#endif
+
     static const char *env = getenv("ARMHWCAP");
 
     if (env && env[0]) {
         if (strstr(env, "help")) {
             fflush(NULL);
             printf(
                    "\n"
                    "usage: ARMHWCAP=option,option,option,... where options can be:\n"
@@ -51,16 +57,19 @@ uint32_t GetARMFlags()
                    "  armv7    \n"
                    "  vfp      \n"
                    "  neon     \n"
                    "  vfpv3    \n"
                    "  vfpv3d16 \n"
                    "  vfpv4    \n"
                    "  idiva    \n"
                    "  idivt    \n"
+#if defined(JS_ARM_SIMULATOR)
+                   "  hardfp   \n"
+#endif
                    "\n"
                    );
             exit(0);
             /*NOTREACHED*/
         } else {
             // Canonicalize each token to have a leading and trailing space.
             const char *start = env;  // Token start.
             for (;;) {
@@ -93,16 +102,20 @@ uint32_t GetARMFlags()
                 else if (count == 5 && strncmp(start, "idiva", 5) == 0)
                     flags |= HWCAP_IDIVA;
                 else if (count == 5 && strncmp(start, "idivt", 5) == 0)
                     flags |= HWCAP_IDIVT;
                 else if (count == 4 && strncmp(start, "neon", 4) == 0)
                     flags |= HWCAP_NEON;
                 else if (count == 5 && strncmp(start, "armv7", 5) == 0)
                     flags |= HWCAP_ARMv7;
+#if defined(JS_ARM_SIMULATOR)
+                else if (count == 6 && strncmp(start, "hardfp", 6) == 0)
+                    flags |= HWCAP_USE_HARDFP_ABI;
+#endif
                 else
                     fprintf(stderr, "Warning: unexpected ARMHWCAP flag at: %s\n", start);
                 start = end;
             }
 #ifdef DEBUG
             IonSpew(IonSpew_Codegen, "ARMHWCAP: '%s'\n   flags: 0x%x\n", env, flags);
 #endif
             isSet = true;
@@ -231,16 +244,24 @@ bool hasIDIV()
 {
 #if defined HWCAP_IDIVA
     return GetARMFlags() & HWCAP_IDIVA;
 #else
     return false;
 #endif
 }
 
+// This is defined in the header and inlined when not using the simulator.
+#if defined(JS_ARM_SIMULATOR)
+bool useHardFpABI()
+{
+    return GetARMFlags() & HWCAP_USE_HARDFP_ABI;
+}
+#endif
+
 Registers::Code
 Registers::FromName(const char *name)
 {
     // Check for some register aliases first.
     if (strcmp(name, "ip") == 0)
         return ip;
     if (strcmp(name, "r13") == 0)
         return r13;
--- a/js/src/jit/arm/Architecture-arm.h
+++ b/js/src/jit/arm/Architecture-arm.h
@@ -8,19 +8,20 @@
 #define jit_arm_Architecture_arm_h
 
 #include <limits.h>
 #include <stdint.h>
 
 #include "js/Utility.h"
 
 // gcc appears to use __ARM_PCS_VFP to denote that the target is a hard-float target.
-#ifdef __ARM_PCS_VFP
+#if defined(__ARM_PCS_VFP)
 #define JS_CODEGEN_ARM_HARDFP
 #endif
+
 namespace js {
 namespace jit {
 
 // In bytes: slots needed for potential memory->memory move spills.
 //   +8 for cycles
 //   +4 for gpr spills
 //   +8 for double spills
 static const uint32_t ION_FRAME_SLACK_SIZE   = 20;
@@ -227,12 +228,27 @@ class FloatRegisters
 
 uint32_t GetARMFlags();
 bool hasMOVWT();
 bool hasVFPv3();
 bool hasVFP();
 bool has16DP();
 bool hasIDIV();
 
+// If the simulator is used then the ABI choice is dynamic.  Otherwise the ABI is static
+// and useHardFpABI is inlined so that unused branches can be optimized away.
+#if defined(JS_ARM_SIMULATOR)
+bool useHardFpABI();
+#else
+static inline bool useHardFpABI()
+{
+#if defined(JS_CODEGEN_ARM_HARDFP)
+    return true;
+#else
+    return false;
+#endif
+}
+#endif
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_arm_Architecture_arm_h */
--- a/js/src/jit/arm/Assembler-arm.h
+++ b/js/src/jit/arm/Assembler-arm.h
@@ -2088,21 +2088,20 @@ class InstructionIterator {
     Instruction *cur() const {
         return i;
     }
 };
 
 static const uint32_t NumIntArgRegs = 4;
 static const uint32_t NumFloatArgRegs = 8;
 
-#ifdef JS_CODEGEN_ARM_HARDFP
 static inline bool
 GetIntArgReg(uint32_t usedIntArgs, uint32_t usedFloatArgs, Register *out)
 {
-   if (usedIntArgs >= NumIntArgRegs)
+    if (usedIntArgs >= NumIntArgRegs)
         return false;
     *out = Register::FromCode(usedIntArgs);
     return true;
 }
 
 // Get a register in which we plan to put a quantity that will be used as an
 // integer argument.  This differs from GetIntArgReg in that if we have no more
 // actual argument registers to use we will fall back on using whatever
@@ -2118,100 +2117,85 @@ GetTempRegForIntArg(uint32_t usedIntArgs
     // can allocate.
     usedIntArgs -= NumIntArgRegs;
     if (usedIntArgs >= NumCallTempNonArgRegs)
         return false;
     *out = CallTempNonArgRegs[usedIntArgs];
     return true;
 }
 
+
+#if !defined(JS_CODEGEN_ARM_HARDFP) || defined(JS_ARM_SIMULATOR)
+
+static inline uint32_t
+GetArgStackDisp(uint32_t arg)
+{
+    JS_ASSERT(!useHardFpABI());
+    JS_ASSERT(arg >= NumIntArgRegs);
+    return (arg - NumIntArgRegs) * sizeof(intptr_t);
+}
+
+#endif
+
+
+#if defined(JS_CODEGEN_ARM_HARDFP) || defined(JS_ARM_SIMULATOR)
+
 static inline bool
 GetFloatArgReg(uint32_t usedIntArgs, uint32_t usedFloatArgs, FloatRegister *out)
 {
+    JS_ASSERT(useHardFpABI());
     if (usedFloatArgs >= NumFloatArgRegs)
         return false;
     *out = FloatRegister::FromCode(usedFloatArgs);
     return true;
 }
 
 static inline uint32_t
 GetIntArgStackDisp(uint32_t usedIntArgs, uint32_t usedFloatArgs, uint32_t *padding)
 {
+    JS_ASSERT(useHardFpABI());
     JS_ASSERT(usedIntArgs >= NumIntArgRegs);
     uint32_t doubleSlots = Max(0, (int32_t)usedFloatArgs - (int32_t)NumFloatArgRegs);
     doubleSlots *= 2;
     int intSlots = usedIntArgs - NumIntArgRegs;
     return (intSlots + doubleSlots + *padding) * sizeof(intptr_t);
 }
 
 static inline uint32_t
 GetFloat32ArgStackDisp(uint32_t usedIntArgs, uint32_t usedFloatArgs, uint32_t *padding)
 {
+    JS_ASSERT(useHardFpABI());
     JS_ASSERT(usedFloatArgs >= NumFloatArgRegs);
     uint32_t intSlots = 0;
     if (usedIntArgs > NumIntArgRegs)
         intSlots = usedIntArgs - NumIntArgRegs;
     uint32_t float32Slots = usedFloatArgs - NumFloatArgRegs;
     return (intSlots + float32Slots + *padding) * sizeof(intptr_t);
 }
 
 static inline uint32_t
 GetDoubleArgStackDisp(uint32_t usedIntArgs, uint32_t usedFloatArgs, uint32_t *padding)
 {
+    JS_ASSERT(useHardFpABI());
     JS_ASSERT(usedFloatArgs >= NumFloatArgRegs);
     uint32_t intSlots = 0;
     if (usedIntArgs > NumIntArgRegs) {
         intSlots = usedIntArgs - NumIntArgRegs;
         // update the amount of padding required.
         *padding += (*padding + usedIntArgs) % 2;
     }
     uint32_t doubleSlots = usedFloatArgs - NumFloatArgRegs;
     doubleSlots *= 2;
     return (intSlots + doubleSlots + *padding) * sizeof(intptr_t);
 }
-#else
-static inline bool
-GetIntArgReg(uint32_t arg, uint32_t floatArg, Register *out)
-{
-    if (arg < NumIntArgRegs) {
-        *out = Register::FromCode(arg);
-        return true;
-    }
-    return false;
-}
-
-// Get a register in which we plan to put a quantity that will be used as an
-// integer argument.  This differs from GetIntArgReg in that if we have no more
-// actual argument registers to use we will fall back on using whatever
-// CallTempReg* don't overlap the argument registers, and only fail once those
-// run out too.
-static inline bool
-GetTempRegForIntArg(uint32_t usedIntArgs, uint32_t usedFloatArgs, Register *out)
-{
-    if (GetIntArgReg(usedIntArgs, usedFloatArgs, out))
-        return true;
-    // Unfortunately, we have to assume things about the point at which
-    // GetIntArgReg returns false, because we need to know how many registers it
-    // can allocate.
-    usedIntArgs -= NumIntArgRegs;
-    if (usedIntArgs >= NumCallTempNonArgRegs)
-        return false;
-    *out = CallTempNonArgRegs[usedIntArgs];
-    return true;
-}
-
-static inline uint32_t
-GetArgStackDisp(uint32_t arg)
-{
-    JS_ASSERT(arg >= NumIntArgRegs);
-    return (arg - NumIntArgRegs) * sizeof(intptr_t);
-}
 
 #endif
 
+
+
 class DoubleEncoder {
     uint32_t rep(bool b, uint32_t count) {
         uint32_t ret = 0;
         for (uint32_t i = 0; i < count; i++)
             ret = (ret << 1) | b;
         return ret;
     }
 
--- a/js/src/jit/arm/CodeGenerator-arm.h
+++ b/js/src/jit/arm/CodeGenerator-arm.h
@@ -171,31 +171,29 @@ class CodeGeneratorARM : public CodeGene
     bool visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr *ins);
     bool visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc *ins);
 
     bool visitAsmJSPassStackArg(LAsmJSPassStackArg *ins);
 
     bool generateInvalidateEpilogue();
   protected:
     void postAsmJSCall(LAsmJSCall *lir) {
-#ifndef JS_CODEGEN_ARM_HARDFP
-        if (lir->mir()->callee().which() == MAsmJSCall::Callee::Builtin) {
+        if (!useHardFpABI() && lir->mir()->callee().which() == MAsmJSCall::Callee::Builtin) {
             switch (lir->mir()->type()) {
               case MIRType_Double:
                 masm.ma_vxfer(r0, r1, d0);
                 break;
               case MIRType_Float32:
                 masm.as_vxfer(r0, InvalidReg, VFPRegister(d0).singleOverlay(),
                               Assembler::CoreToFloat);
                 break;
               default:
                 break;
             }
         }
-#endif
     }
 
     bool visitEffectiveAddress(LEffectiveAddress *ins);
     bool visitUDiv(LUDiv *ins);
     bool visitUMod(LUMod *ins);
     bool visitSoftUDivOrMod(LSoftUDivOrMod *ins);
 };
 
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -3485,23 +3485,21 @@ MacroAssemblerARMCompat::breakpoint(Cond
 void
 MacroAssemblerARMCompat::setupABICall(uint32_t args)
 {
     JS_ASSERT(!inCall_);
     inCall_ = true;
     args_ = args;
     passedArgs_ = 0;
     passedArgTypes_ = 0;
-#ifdef JS_CODEGEN_ARM_HARDFP
     usedIntSlots_ = 0;
+#if defined(JS_CODEGEN_ARM_HARDFP) || defined(JS_ARM_SIMULATOR)
     usedFloatSlots_ = 0;
     usedFloat32_ = false;
     padding_ = 0;
-#else
-    usedSlots_ = 0;
 #endif
     floatArgsInGPR[0] = MoveOperand();
     floatArgsInGPR[1] = MoveOperand();
     floatArgsInGPRValid[0] = false;
     floatArgsInGPRValid[1] = false;
 }
 
 void
@@ -3520,19 +3518,19 @@ MacroAssemblerARMCompat::setupUnalignedA
 
     ma_mov(sp, scratch);
 
     // Force sp to be aligned
     ma_and(Imm32(~(StackAlignment - 1)), sp, sp);
     ma_push(scratch);
 }
 
-#ifdef JS_CODEGEN_ARM_HARDFP
-void
-MacroAssemblerARMCompat::passABIArg(const MoveOperand &from, MoveOp::Type type)
+#if defined(JS_CODEGEN_ARM_HARDFP) || defined(JS_ARM_SIMULATOR)
+void
+MacroAssemblerARMCompat::passHardFpABIArg(const MoveOperand &from, MoveOp::Type type)
 {
     MoveOperand to;
     ++passedArgs_;
     if (!enoughMemory_)
         return;
     switch (type) {
       case MoveOp::FLOAT32:
       case MoveOp::DOUBLE: {
@@ -3591,68 +3589,84 @@ MacroAssemblerARMCompat::passABIArg(cons
         break;
       }
       default:
         MOZ_ASSUME_UNREACHABLE("Unexpected argument type");
     }
 
     enoughMemory_ = moveResolver_.addMove(from, to, type);
 }
-
-#else
-void
-MacroAssemblerARMCompat::passABIArg(const MoveOperand &from, MoveOp::Type type)
+#endif
+
+#if !defined(JS_CODEGEN_ARM_HARDFP) || defined(JS_ARM_SIMULATOR)
+void
+MacroAssemblerARMCompat::passSoftFpABIArg(const MoveOperand &from, MoveOp::Type type)
 {
     MoveOperand to;
     uint32_t increment = 1;
     bool useResolver = true;
     ++passedArgs_;
     switch (type) {
       case MoveOp::DOUBLE:
         // Double arguments need to be rounded up to the nearest doubleword
         // boundary, even if it is in a register!
-        usedSlots_ = (usedSlots_ + 1) & ~1;
+        usedIntSlots_ = (usedIntSlots_ + 1) & ~1;
         increment = 2;
         passedArgTypes_ = (passedArgTypes_ << ArgType_Shift) | ArgType_Double;
         break;
       case MoveOp::FLOAT32:
         passedArgTypes_ = (passedArgTypes_ << ArgType_Shift) | ArgType_Float32;
         break;
       case MoveOp::GENERAL:
         passedArgTypes_ = (passedArgTypes_ << ArgType_Shift) | ArgType_General;
         break;
       default:
         MOZ_ASSUME_UNREACHABLE("Unexpected argument type");
     }
 
     Register destReg;
     MoveOperand dest;
-    if (GetIntArgReg(usedSlots_, 0, &destReg)) {
+    if (GetIntArgReg(usedIntSlots_, 0, &destReg)) {
         if (type == MoveOp::DOUBLE || type == MoveOp::FLOAT32) {
             floatArgsInGPR[destReg.code() >> 1] = from;
             floatArgsInGPRValid[destReg.code() >> 1] = true;
             useResolver = false;
         } else if (from.isGeneralReg() && from.reg() == destReg) {
             // No need to move anything
             useResolver = false;
         } else {
             dest = MoveOperand(destReg);
         }
     } else {
-        uint32_t disp = GetArgStackDisp(usedSlots_);
+        uint32_t disp = GetArgStackDisp(usedIntSlots_);
         dest = MoveOperand(sp, disp);
     }
 
     if (useResolver)
         enoughMemory_ = enoughMemory_ && moveResolver_.addMove(from, dest, type);
-    usedSlots_ += increment;
+    usedIntSlots_ += increment;
 }
 #endif
 
 void
+MacroAssemblerARMCompat::passABIArg(const MoveOperand &from, MoveOp::Type type)
+{
+#if defined(JS_ARM_SIMULATOR)
+    if (useHardFpABI())
+        MacroAssemblerARMCompat::passHardFpABIArg(from, type);
+    else
+        MacroAssemblerARMCompat::passSoftFpABIArg(from, type);
+#elif defined(JS_CODEGEN_ARM_HARDFP)
+    MacroAssemblerARMCompat::passHardFpABIArg(from, type);
+#else
+    MacroAssemblerARMCompat::passSoftFpABIArg(from, type);
+#endif
+}
+
+void
 MacroAssemblerARMCompat::passABIArg(const Register &reg)
 {
     passABIArg(MoveOperand(reg), MoveOp::GENERAL);
 }
 
 void
 MacroAssemblerARMCompat::passABIArg(const FloatRegister &freg, MoveOp::Type type)
 {
@@ -3666,21 +3680,21 @@ void MacroAssemblerARMCompat::checkStack
     breakpoint(NonZero);
 #endif
 }
 
 void
 MacroAssemblerARMCompat::callWithABIPre(uint32_t *stackAdjust)
 {
     JS_ASSERT(inCall_);
-#ifdef JS_CODEGEN_ARM_HARDFP
+
     *stackAdjust = ((usedIntSlots_ > NumIntArgRegs) ? usedIntSlots_ - NumIntArgRegs : 0) * sizeof(intptr_t);
-    *stackAdjust += 2*((usedFloatSlots_ > NumFloatArgRegs) ? usedFloatSlots_ - NumFloatArgRegs : 0) * sizeof(intptr_t);
-#else
-    *stackAdjust = ((usedSlots_ > NumIntArgRegs) ? usedSlots_ - NumIntArgRegs : 0) * sizeof(intptr_t);
+#if defined(JS_CODEGEN_ARM_HARDFP) || defined(JS_ARM_SIMULATOR)
+    if (useHardFpABI())
+        *stackAdjust += 2*((usedFloatSlots_ > NumFloatArgRegs) ? usedFloatSlots_ - NumFloatArgRegs : 0) * sizeof(intptr_t);
 #endif
     if (!dynamicAlignment_) {
         *stackAdjust += ComputeByteAlignment(framePushed_ + *stackAdjust, StackAlignment);
     } else {
         // sizeof(intptr_t) account for the saved stack pointer pushed by setupUnalignedABICall
         *stackAdjust += ComputeByteAlignment(*stackAdjust + sizeof(intptr_t), StackAlignment);
     }
 
@@ -3728,27 +3742,27 @@ MacroAssemblerARMCompat::callWithABIPre(
 void
 MacroAssemblerARMCompat::callWithABIPost(uint32_t stackAdjust, MoveOp::Type result)
 {
     if (secondScratchReg_ != lr)
         ma_mov(secondScratchReg_, lr);
 
     switch (result) {
       case MoveOp::DOUBLE:
-#ifndef JS_CODEGEN_ARM_HARDFP
-        // Move double from r0/r1 to ReturnFloatReg.
-        as_vxfer(r0, r1, ReturnFloatReg, CoreToFloat);
-        break;
-#endif
+        if (!useHardFpABI()) {
+            // Move double from r0/r1 to ReturnFloatReg.
+            as_vxfer(r0, r1, ReturnFloatReg, CoreToFloat);
+            break;
+        }
       case MoveOp::FLOAT32:
-#ifndef JS_CODEGEN_ARM_HARDFP
-        // Move float32 from r0 to ReturnFloatReg.
-        as_vxfer(r0, InvalidReg, VFPRegister(d0).singleOverlay(), CoreToFloat);
-        break;
-#endif
+        if (!useHardFpABI()) {
+            // Move float32 from r0 to ReturnFloatReg.
+            as_vxfer(r0, InvalidReg, VFPRegister(d0).singleOverlay(), CoreToFloat);
+            break;
+        }
       case MoveOp::GENERAL:
         break;
 
       default:
         MOZ_ASSUME_UNREACHABLE("unexpected callWithABI result");
     }
 
     freeStack(stackAdjust);
--- a/js/src/jit/arm/MacroAssembler-arm.h
+++ b/js/src/jit/arm/MacroAssembler-arm.h
@@ -440,29 +440,27 @@ class MacroAssemblerARMCompat : public M
     // not be nested.
     bool inCall_;
     uint32_t args_;
     // The actual number of arguments that were passed, used to assert that
     // the initial number of arguments declared was correct.
     uint32_t passedArgs_;
     uint32_t passedArgTypes_;
 
-#ifdef JS_CODEGEN_ARM_HARDFP
+    // ARM treats arguments as a vector in registers/memory, that looks like:
+    // { r0, r1, r2, r3, [sp], [sp,+4], [sp,+8] ... }
+    // usedIntSlots_ keeps track of how many of these have been used.
+    // It bears a passing resemblance to passedArgs_, but a single argument
+    // can effectively use between one and three slots depending on its size and
+    // alignment requirements
     uint32_t usedIntSlots_;
+#if defined(JS_CODEGEN_ARM_HARDFP) || defined(JS_ARM_SIMULATOR)
     uint32_t usedFloatSlots_;
     bool usedFloat32_;
     uint32_t padding_;
-#else
-    // ARM treats arguments as a vector in registers/memory, that looks like:
-    // { r0, r1, r2, r3, [sp], [sp,+4], [sp,+8] ... }
-    // usedSlots_ keeps track of how many of these have been used.
-    // It bears a passing resemblance to passedArgs_, but a single argument
-    // can effectively use between one and three slots depending on its size and
-    // alignment requirements
-    uint32_t usedSlots_;
 #endif
     bool dynamicAlignment_;
 
     bool enoughMemory_;
 
     // Used to work around the move resolver's lack of support for
     // moving into register pairs, which the softfp ABI needs.
     mozilla::Array<MoveOperand, 2> floatArgsInGPR;
@@ -1406,16 +1404,20 @@ class MacroAssemblerARMCompat : public M
     // automatically adjusted. It is extremely important that esp-relative
     // addresses are computed *after* setupABICall(). Furthermore, no
     // operations should be emitted while setting arguments.
     void passABIArg(const MoveOperand &from, MoveOp::Type type);
     void passABIArg(const Register &reg);
     void passABIArg(const FloatRegister &reg, MoveOp::Type type);
     void passABIArg(const ValueOperand &regs);
 
+  private:
+    void passHardFpABIArg(const MoveOperand &from, MoveOp::Type type);
+    void passSoftFpABIArg(const MoveOperand &from, MoveOp::Type type);
+
   protected:
     bool buildOOLFakeExitFrame(void *fakeReturnAddr);
 
   private:
     void callWithABIPre(uint32_t *stackAdjust);
     void callWithABIPost(uint32_t stackAdjust, MoveOp::Type result);
 
   public:
--- a/js/src/jit/arm/Simulator-arm.cpp
+++ b/js/src/jit/arm/Simulator-arm.cpp
@@ -1411,49 +1411,52 @@ ReturnType Simulator::getFromVFPRegister
            register_size * sizeof(vfp_registers_[0]));
     memcpy(&value, buffer, register_size * sizeof(vfp_registers_[0]));
     return value;
 }
 
 void
 Simulator::getFpArgs(double *x, double *y, int32_t *z)
 {
-    if (use_eabi_hardfloat()) {
+    if (useHardFpABI()) {
         *x = get_double_from_d_register(0);
         *y = get_double_from_d_register(1);
         *z = get_register(0);
     } else {
         *x = get_double_from_register_pair(0);
         *y = get_double_from_register_pair(2);
         *z = get_register(2);
     }
 }
 
 void
 Simulator::setCallResultDouble(double result)
 {
     // The return value is either in r0/r1 or d0.
-    if (use_eabi_hardfloat()) {
+    if (useHardFpABI()) {
         char buffer[2 * sizeof(vfp_registers_[0])];
         memcpy(buffer, &result, sizeof(buffer));
         // Copy result to d0.
         memcpy(vfp_registers_, buffer, sizeof(buffer));
     } else {
         char buffer[2 * sizeof(registers_[0])];
         memcpy(buffer, &result, sizeof(buffer));
         // Copy result to r0 and r1.
         memcpy(registers_, buffer, sizeof(buffer));
     }
 }
 
 void
 Simulator::setCallResultFloat(float result)
 {
-    if (use_eabi_hardfloat()) {
-        MOZ_ASSUME_UNREACHABLE("NYI");
+    if (useHardFpABI()) {
+        char buffer[sizeof(registers_[0])];
+        memcpy(buffer, &result, sizeof(buffer));
+        // Copy result to s0.
+        memcpy(vfp_registers_, buffer, sizeof(buffer));
     } else {
         char buffer[sizeof(registers_[0])];
         memcpy(buffer, &result, sizeof(buffer));
         // Copy result to r0.
         memcpy(registers_, buffer, sizeof(buffer));
     }
 }
 
@@ -2197,17 +2200,21 @@ Simulator::softwareInterrupt(SimInstruct
             getFpArgs(&dval0, &dval1, &ival);
             Prototype_Double_Double target = reinterpret_cast<Prototype_Double_Double>(external);
             double dresult = target(dval0);
             scratchVolatileRegisters(/* scratchFloat = true */);
             setCallResultDouble(dresult);
             break;
           }
           case Args_Float32_Float32: {
-            float fval0 = mozilla::BitwiseCast<float>(arg0);
+            float fval0;
+            if (useHardFpABI())
+                fval0 = get_float_from_s_register(0);
+            else
+                fval0 = mozilla::BitwiseCast<float>(arg0);
             Prototype_Float32_Float32 target = reinterpret_cast<Prototype_Float32_Float32>(external);
             float fresult = target(fval0);
             scratchVolatileRegisters(/* scratchFloat = true */);
             setCallResultFloat(fresult);
             break;
           }
           case Args_Double_Int: {
             Prototype_Double_Int target = reinterpret_cast<Prototype_Double_Int>(external);
@@ -2232,29 +2239,35 @@ Simulator::softwareInterrupt(SimInstruct
             getFpArgs(&dval0, &dval1, &ival);
             Prototype_Double_DoubleDouble target = reinterpret_cast<Prototype_Double_DoubleDouble>(external);
             double dresult = target(dval0, dval1);
             scratchVolatileRegisters(/* scratchFloat = true */);
             setCallResultDouble(dresult);
             break;
           }
           case Args_Double_IntDouble: {
-            MOZ_ASSERT(!use_eabi_hardfloat()); // NYI
             int32_t ival = get_register(0);
-            double dval0 = get_double_from_register_pair(2);
+            double dval0;
+            if (useHardFpABI())
+                dval0 = get_double_from_d_register(0);
+            else
+                dval0 = get_double_from_register_pair(2);
             Prototype_Double_IntDouble target = reinterpret_cast<Prototype_Double_IntDouble>(external);
             double dresult = target(ival, dval0);
             scratchVolatileRegisters(/* scratchFloat = true */);
             setCallResultDouble(dresult);
             break;
           }
           case Args_Int_IntDouble: {
-            MOZ_ASSERT(!use_eabi_hardfloat()); // NYI
             int32_t ival = get_register(0);
-            double dval0 = get_double_from_register_pair(2);
+            double dval0;
+            if (useHardFpABI())
+                dval0 = get_double_from_d_register(0);
+            else
+                dval0 = get_double_from_register_pair(2);
             Prototype_Int_IntDouble target = reinterpret_cast<Prototype_Int_IntDouble>(external);
             int32_t result = target(ival, dval0);
             scratchVolatileRegisters(/* scratchFloat = true */);
             set_register(r0, result);
             break;
           }
           default:
             MOZ_ASSUME_UNREACHABLE("call");
--- a/js/src/jit/arm/Simulator-arm.h
+++ b/js/src/jit/arm/Simulator-arm.h
@@ -27,16 +27,17 @@
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #ifndef jit_arm_Simulator_arm_h
 #define jit_arm_Simulator_arm_h
 
 #ifdef JS_ARM_SIMULATOR
 
 #include "jit/IonTypes.h"
+#include "jit/arm/Architecture-arm.h"
 
 namespace js {
 namespace jit {
 
 class SimulatorRuntime;
 SimulatorRuntime *CreateSimulatorRuntime();
 void DestroySimulatorRuntime(SimulatorRuntime *srt);
 
@@ -161,25 +162,16 @@ class Simulator
     // Debugger input.
     void setLastDebuggerInput(char *input);
     char *lastDebuggerInput() { return lastDebuggerInput_; }
 
     // Returns true if pc register contains one of the 'special_values' defined
     // below (bad_lr, end_sim_pc).
     bool has_bad_pc() const;
 
-    // EABI variant for double arguments in use.
-    bool use_eabi_hardfloat() {
-#if USE_EABI_HARDFLOAT
-        return true;
-#else
-        return false;
-#endif
-    }
-
   private:
     enum special_values {
         // Known bad pc value to ensure that the simulator does not execute
         // without being properly setup.
         bad_lr = -1,
         // A pc value used to signal the simulator to stop execution.  Generally
         // the lr is set to this value on transition from native C code to
         // simulated execution, so that the simulator can "return" to the native