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 169648 e67062995f0b25951d39324622bd908d246d787b
parent 169647 3576a62195ecdef9a289aa0704a2e326b32bb204
child 169649 0f7f99d0a2f56f0ac3f4c11bc8c636e6e1d4683b
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
reviewersmjrosenb
bugs966881
milestone30.0a1
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