Bug 1184959 part 2 - ARM ABIArgGenerator: Add support for soft-fp. r=h4writer
authorNicolas B. Pierron <nicolas.b.pierron@mozilla.com>
Mon, 17 Aug 2015 11:32:15 +0200
changeset 258026 011f8d81a9ae1e11e09bd46916cdca9c433cb779
parent 258025 28c84f5978dae7e26dad0fee66b96646241d928d
child 258027 939b1402b1f57adb5b77ddf33bbf061692cf1c4d
push id29241
push userkwierso@gmail.com
push dateTue, 18 Aug 2015 00:00:46 +0000
treeherdermozilla-central@6ae3e9ff53b2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersh4writer
bugs1184959
milestone43.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 1184959 part 2 - ARM ABIArgGenerator: Add support for soft-fp. r=h4writer
js/src/asmjs/AsmJSValidate.cpp
js/src/jit/RegisterSets.h
js/src/jit/arm/Architecture-arm.h
js/src/jit/arm/Assembler-arm.cpp
js/src/jit/arm/Assembler-arm.h
--- a/js/src/asmjs/AsmJSValidate.cpp
+++ b/js/src/asmjs/AsmJSValidate.cpp
@@ -10982,16 +10982,21 @@ GenerateEntry(ModuleCompiler& m, unsigne
     for (ABIArgTypeIter iter(func.sig().args()); !iter.done(); iter++) {
         unsigned argOffset = iter.index() * sizeof(AsmJSModule::EntryArg);
         Address src(argv, argOffset);
         MIRType type = iter.mirType();
         switch (iter->kind()) {
           case ABIArg::GPR:
             masm.load32(src, iter->gpr());
             break;
+#ifdef JS_CODEGEN_REGISTER_PAIR
+          case ABIArg::GPR_PAIR:
+            MOZ_CRASH("AsmJS uses hardfp for function calls.");
+            break;
+#endif
           case ABIArg::FPU: {
             static_assert(sizeof(AsmJSModule::EntryArg) >= jit::Simd128DataSize,
                           "EntryArg must be big enough to store SIMD values");
             switch (type) {
               case MIRType_Int32x4:
                 masm.loadUnalignedInt32x4(src, iter->fpu());
                 break;
               case MIRType_Float32x4:
@@ -11094,16 +11099,21 @@ FillArgumentArray(ModuleCompiler& m, con
     MacroAssembler& masm = m.masm();
 
     for (ABIArgTypeIter i(argTypes); !i.done(); i++) {
         Address dstAddr(masm.getStackPointer(), offsetToArgs + i.index() * sizeof(Value));
         switch (i->kind()) {
           case ABIArg::GPR:
             masm.storeValue(JSVAL_TYPE_INT32, i->gpr(), dstAddr);
             break;
+#ifdef JS_CODEGEN_REGISTER_PAIR
+          case ABIArg::GPR_PAIR:
+            MOZ_CRASH("AsmJS uses hardfp for function calls.");
+            break;
+#endif
           case ABIArg::FPU:
             masm.canonicalizeDouble(i->fpu());
             masm.storeDouble(i->fpu(), dstAddr);
             break;
           case ABIArg::Stack:
             if (i.mirType() == MIRType_Int32) {
                 Address src(masm.getStackPointer(), offsetToCallerStackArgs + i->offsetFromArgBase());
 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
--- a/js/src/jit/RegisterSets.h
+++ b/js/src/jit/RegisterSets.h
@@ -1215,34 +1215,69 @@ class AnyRegisterIterator
             return AnyRegister(*geniter_);
         return AnyRegister(*floatiter_);
     }
 };
 
 class ABIArg
 {
   public:
-    enum Kind { GPR, FPU, Stack };
+    enum Kind {
+        GPR,
+#ifdef JS_CODEGEN_REGISTER_PAIR
+        GPR_PAIR,
+#endif
+        FPU,
+        Stack
+    };
 
   private:
     Kind kind_;
     union {
         Register::Code gpr_;
         FloatRegister::Code fpu_;
         uint32_t offset_;
     } u;
 
   public:
     ABIArg() : kind_(Kind(-1)) { u.offset_ = -1; }
     explicit ABIArg(Register gpr) : kind_(GPR) { u.gpr_ = gpr.code(); }
+    explicit ABIArg(Register gprLow, Register gprHigh)
+    {
+#if defined(JS_CODEGEN_REGISTER_PAIR)
+        kind_ = GPR_PAIR;
+#else
+        MOZ_CRASH("Unsupported type of ABI argument.");
+#endif
+        u.gpr_ = gprLow.code();
+        MOZ_ASSERT(u.gpr_ % 2 == 0);
+        MOZ_ASSERT(u.gpr_ + 1 == gprHigh.code());
+    }
     explicit ABIArg(FloatRegister fpu) : kind_(FPU) { u.fpu_ = fpu.code(); }
     explicit ABIArg(uint32_t offset) : kind_(Stack) { u.offset_ = offset; }
 
     Kind kind() const { return kind_; }
-    Register gpr() const { MOZ_ASSERT(kind() == GPR); return Register::FromCode(u.gpr_); }
+#ifdef JS_CODEGEN_REGISTER_PAIR
+    bool isGeneralRegPair() const { return kind_ == GPR_PAIR; }
+#else
+    bool isGeneralRegPair() const { return false; }
+#endif
+
+    Register gpr() const {
+        MOZ_ASSERT(kind() == GPR);
+        return Register::FromCode(u.gpr_);
+    }
+    Register evenGpr() const {
+        MOZ_ASSERT(isGeneralRegPair());
+        return Register::FromCode(u.gpr_);
+    }
+    Register oddGpr() const {
+        MOZ_ASSERT(isGeneralRegPair());
+        return Register::FromCode(u.gpr_ + 1);
+    }
     FloatRegister fpu() const { MOZ_ASSERT(kind() == FPU); return FloatRegister::FromCode(u.fpu_); }
     uint32_t offsetFromArgBase() const { MOZ_ASSERT(kind() == Stack); return u.offset_; }
 
     bool argInRegister() const { return kind() != Stack; }
     AnyRegister reg() const { return kind_ == GPR ? AnyRegister(gpr()) : AnyRegister(fpu()); }
 };
 
 // Get the set of registers which should be saved by a block of code which
--- a/js/src/jit/arm/Architecture-arm.h
+++ b/js/src/jit/arm/Architecture-arm.h
@@ -634,16 +634,20 @@ static inline bool UseHardFpABI()
 #if defined(JS_CODEGEN_ARM_HARDFP)
     return true;
 #else
     return false;
 #endif
 }
 #endif
 
+// In order to handle SoftFp ABI calls, we need to be able to express that we
+// have ABIArg which are represented by pair of general purpose registers.
+#define JS_CODEGEN_REGISTER_PAIR 1
+
 // See the comments above AsmJSMappedSize in AsmJSValidate.h for more info.
 // TODO: Implement this for ARM. Note that it requires Codegen to respect the
 // offset field of AsmJSHeapAccess.
 static const size_t AsmJSCheckedImmediateRange = 0;
 static const size_t AsmJSImmediateRange = 0;
 
 } // namespace jit
 } // namespace js
--- a/js/src/jit/arm/Assembler-arm.cpp
+++ b/js/src/jit/arm/Assembler-arm.cpp
@@ -19,27 +19,77 @@
 
 using namespace js;
 using namespace js::jit;
 
 using mozilla::CountLeadingZeroes32;
 
 void dbg_break() {}
 
-// Note this is used for inter-AsmJS calls and may pass arguments and results in
-// floating point registers even if the system ABI does not.
+// The ABIArgGenerator is used for making system ABI calls and for inter-AsmJS
+// calls. The system ABI can either be SoftFp or HardFp, and inter-AsmJS calls
+// are always HardFp calls. The initialization defaults to HardFp, and the ABI
+// choice is made before any system ABI calls with the method "setUseHardFp".
 ABIArgGenerator::ABIArgGenerator()
   : intRegIndex_(0),
     floatRegIndex_(0),
     stackOffset_(0),
-    current_()
+    current_(),
+    useHardFp_(true)
 { }
 
+// See the "Parameter Passing" section of the "Procedure Call Standard for the
+// ARM Architecture" documentation.
+ABIArg
+ABIArgGenerator::softNext(MIRType type)
+{
+    switch (type) {
+      case MIRType_Int32:
+      case MIRType_Pointer:
+        if (intRegIndex_ == NumIntArgRegs) {
+            current_ = ABIArg(stackOffset_);
+            stackOffset_ += sizeof(uint32_t);
+            break;
+        }
+        current_ = ABIArg(Register::FromCode(intRegIndex_));
+        intRegIndex_++;
+        break;
+      case MIRType_Float32:
+        if (intRegIndex_ == NumIntArgRegs) {
+            current_ = ABIArg(stackOffset_);
+            stackOffset_ += sizeof(uint32_t);
+            break;
+        }
+        current_ = ABIArg(Register::FromCode(intRegIndex_));
+        intRegIndex_++;
+        break;
+      case MIRType_Double:
+        // Make sure to use an even register index. Increase to next even number
+        // when odd.
+        intRegIndex_ = (intRegIndex_ + 1) & ~1;
+        if (intRegIndex_ == NumIntArgRegs) {
+            // Align the stack on 8 bytes.
+            static const int align = sizeof(double) - 1;
+            stackOffset_ = (stackOffset_ + align) & ~align;
+            current_ = ABIArg(stackOffset_);
+            stackOffset_ += sizeof(double);
+            break;
+        }
+        current_ = ABIArg(Register::FromCode(intRegIndex_), Register::FromCode(intRegIndex_ + 1));
+        intRegIndex_ += 2;
+        break;
+      default:
+        MOZ_CRASH("Unexpected argument type");
+    }
+
+    return current_;
+}
+
 ABIArg
-ABIArgGenerator::next(MIRType type)
+ABIArgGenerator::hardNext(MIRType type)
 {
     switch (type) {
       case MIRType_Int32:
       case MIRType_Pointer:
         if (intRegIndex_ == NumIntArgRegs) {
             current_ = ABIArg(stackOffset_);
             stackOffset_ += sizeof(uint32_t);
             break;
@@ -54,35 +104,45 @@ ABIArgGenerator::next(MIRType type)
             current_ = ABIArg(stackOffset_);
             stackOffset_ += sizeof(uint64_t);
             break;
         }
         current_ = ABIArg(VFPRegister(floatRegIndex_, VFPRegister::Single));
         floatRegIndex_++;
         break;
       case MIRType_Double:
-        // Bump the number of used registers up to the next multiple of two.
+        // Double register are composed of 2 float registers, thus we have to
+        // skip any float register which cannot be used in a pair of float
+        // registers in which a double value can be stored.
         floatRegIndex_ = (floatRegIndex_ + 1) & ~1;
         if (floatRegIndex_ == NumFloatArgRegs) {
             static const int align = sizeof(double) - 1;
             stackOffset_ = (stackOffset_ + align) & ~align;
             current_ = ABIArg(stackOffset_);
             stackOffset_ += sizeof(uint64_t);
             break;
         }
         current_ = ABIArg(VFPRegister(floatRegIndex_ >> 1, VFPRegister::Double));
-        floatRegIndex_+=2;
+        floatRegIndex_ += 2;
         break;
       default:
         MOZ_CRASH("Unexpected argument type");
     }
 
     return current_;
 }
 
+ABIArg
+ABIArgGenerator::next(MIRType type)
+{
+    if (useHardFp_)
+        return hardNext(type);
+    return softNext(type);
+}
+
 const Register ABIArgGenerator::NonArgReturnReg0 = r4;
 const Register ABIArgGenerator::NonArgReturnReg1 = r5;
 const Register ABIArgGenerator::NonReturn_VolatileReg0 = r2;
 const Register ABIArgGenerator::NonReturn_VolatileReg1 = r3;
 
 // Encode a standard register when it is being used as src1, the dest, and an
 // extra register. These should never be called with an InvalidReg.
 uint32_t
--- a/js/src/jit/arm/Assembler-arm.h
+++ b/js/src/jit/arm/Assembler-arm.h
@@ -67,19 +67,33 @@ static const uint32_t NumCallTempNonArgR
 
 class ABIArgGenerator
 {
     unsigned intRegIndex_;
     unsigned floatRegIndex_;
     uint32_t stackOffset_;
     ABIArg current_;
 
+    // ARM can either use HardFp (use float registers for float arguments), or
+    // SoftFp (use general registers for float arguments) ABI.  We keep this
+    // switch as a runtime switch because AsmJS always use the HardFp back-end
+    // while the calls to native functions have to use the one provided by the
+    // system.
+    bool useHardFp_;
+
+    ABIArg softNext(MIRType argType);
+    ABIArg hardNext(MIRType argType);
+
   public:
     ABIArgGenerator();
 
+    void setUseHardFp(bool useHardFp) {
+        MOZ_ASSERT(intRegIndex_ == 0 && floatRegIndex_ == 0);
+        useHardFp_ = useHardFp;
+    }
     ABIArg next(MIRType argType);
     ABIArg& current() { return current_; }
     uint32_t stackBytesConsumedSoFar() const { return stackOffset_; }
 
     static const Register NonArgReturnReg0;
     static const Register NonArgReturnReg1;
     static const Register NonReturn_VolatileReg0;
     static const Register NonReturn_VolatileReg1;