Bug 818889 - Add stubs for double arithmetic. r=djvj
authorJan de Mooij <jdemooij@mozilla.com>
Fri, 07 Dec 2012 19:15:31 +0100
changeset 115272 7ccc81e3c9dae48b4973c40fb19581a653a79771
parent 115271 c7505a7eda40f0a52240796f0f08291f1ce14331
child 115273 c44f7953174ab3e1e01b1d3c2fc90f2942e6fce4
push id1402
push userjandemooij@gmail.com
push dateFri, 07 Dec 2012 18:23:51 +0000
reviewersdjvj
bugs818889
milestone20.0a1
Bug 818889 - Add stubs for double arithmetic. r=djvj
js/src/ion/BaselineIC.cpp
js/src/ion/BaselineIC.h
js/src/ion/arm/BaselineIC-arm.cpp
js/src/ion/arm/BaselineRegisters-arm.h
js/src/ion/arm/MacroAssembler-arm.cpp
js/src/ion/arm/MacroAssembler-arm.h
js/src/ion/shared/MacroAssembler-x86-shared.h
js/src/ion/x64/BaselineRegisters-x64.h
js/src/ion/x86/BaselineRegisters-x86.h
--- a/js/src/ion/BaselineIC.cpp
+++ b/js/src/ion/BaselineIC.cpp
@@ -77,16 +77,46 @@ ICStub::trace(JSTracer *trc)
         MarkShape(trc, &globalStub->shape(), "baseline-global-stub-shape");
         break;
       }
       default:
         break;
     }
 }
 
+void
+ICFallbackStub::unlinkStubsWithKind(ICStub::Kind kind)
+{
+    ICStub *stub = icEntry_->firstStub();
+    ICStub *last = NULL;
+    do {
+        if (stub->kind() == kind) {
+            JS_ASSERT(stub->next());
+
+            // If stub is the last optimized stub, update lastStubPtrAddr.
+            if (stub->next() == this) {
+                JS_ASSERT(lastStubPtrAddr_ == stub->addressOfNext());
+                if (last)
+                    lastStubPtrAddr_ = last->addressOfNext();
+                else
+                    lastStubPtrAddr_ = icEntry()->addressOfFirstStub();
+                *lastStubPtrAddr_ = this;
+            }
+
+            JS_ASSERT(numOptimizedStubs_ > 0);
+            numOptimizedStubs_--;
+
+            stub = stub->next();
+            continue;
+        }
+
+        last = stub;
+        stub = stub->next();
+    } while (stub);
+}
 
 ICMonitoredStub::ICMonitoredStub(Kind kind, IonCode *stubCode, ICStub *firstMonitorStub)
   : ICStub(kind, ICStub::Monitored, stubCode),
     firstMonitorStub_(firstMonitorStub)
 {
     // If the first monitored stub is a ICTypeMonitor_Fallback stub, then
     // double check that _its_ firstMonitorStub is the same as this one.
     JS_ASSERT_IF(firstMonitorStub_->isTypeMonitor_Fallback(),
@@ -679,27 +709,58 @@ DoBinaryArithFallback(JSContext *cx, ICB
 
     // Check to see if a new stub should be generated.
     if (stub->numOptimizedStubs() >= ICBinaryArith_Fallback::MAX_OPTIMIZED_STUBS) {
         // TODO: Discard all stubs in this IC and replace with inert megamorphic stub.
         // But for now we just bail.
         return true;
     }
 
-    if (!lhs.isInt32() || !rhs.isInt32())
+    // Handle only int32 or double.
+    if (!lhs.isNumber() || !rhs.isNumber())
         return true;
 
-    // Try to generate new stubs.
+    JS_ASSERT(ret.isNumber());
+
+    if (lhs.isDouble() || rhs.isDouble() || ret.isDouble()) {
+        switch (op) {
+          case JSOP_ADD:
+          case JSOP_SUB:
+          case JSOP_MUL:
+          case JSOP_DIV:
+          case JSOP_MOD: {
+            // Unlink int32 stubs, it's faster to always use the double stub.
+            stub->unlinkStubsWithKind(ICStub::BinaryArith_Int32);
+
+            ICBinaryArith_Double::Compiler compiler(cx, op);
+            ICStub *doubleStub = compiler.getStub(ICStubSpace::StubSpaceFor(script));
+            if (!doubleStub)
+                return false;
+            stub->addNewStub(doubleStub);
+            return true;
+          }
+          case JSOP_URSH:
+            // Fall-through to int32 case.
+            break;
+          default:
+            // TODO: attach double stub for bitwise ops.
+            return true;
+        }
+    }
+
     // TODO: unlink previous !allowDouble stub.
-    bool allowDouble = ret.isDouble();
-    ICBinaryArith_Int32::Compiler compilerInt32(cx, op, allowDouble);
-    ICStub *int32Stub = compilerInt32.getStub(ICStubSpace::StubSpaceFor(script));
-    if (!int32Stub)
-        return false;
-    stub->addNewStub(int32Stub);
+    if (lhs.isInt32() && rhs.isInt32()) {
+        bool allowDouble = ret.isDouble();
+        ICBinaryArith_Int32::Compiler compilerInt32(cx, op, allowDouble);
+        ICStub *int32Stub = compilerInt32.getStub(ICStubSpace::StubSpaceFor(script));
+        if (!int32Stub)
+            return false;
+        stub->addNewStub(int32Stub);
+    }
+
     return true;
 }
 
 typedef bool (*DoBinaryArithFallbackFn)(JSContext *, ICBinaryArith_Fallback *, HandleValue, HandleValue,
                                         MutableHandleValue);
 static const VMFunction DoBinaryArithFallbackInfo =
     FunctionInfo<DoBinaryArithFallbackFn>(DoBinaryArithFallback);
 
@@ -713,16 +774,80 @@ ICBinaryArith_Fallback::Compiler::genera
 
     masm.pushValue(R1);
     masm.pushValue(R0);
     masm.push(BaselineStubReg);
 
     return tailCallVM(DoBinaryArithFallbackInfo, masm);
 }
 
+void
+ICBinaryArith_Double::Compiler::ensureDouble(MacroAssembler &masm, const ValueOperand &source,
+                                             FloatRegister dest, Label *failure)
+{
+    // If source is a double, load it into dest. If source is int32,
+    // convert it to double. Else, branch to failure.
+
+    Label isDouble, done;
+    Register tag = masm.splitTagForTest(source);
+    masm.branchTestDouble(Assembler::Equal, tag, &isDouble);
+    masm.branchTestInt32(Assembler::NotEqual, tag, failure);
+
+    Register payload = masm.extractInt32(source, ExtractTemp0);
+    masm.convertInt32ToDouble(payload, dest);
+    masm.jump(&done);
+
+    masm.bind(&isDouble);
+    masm.unboxDouble(source, dest);
+
+    masm.bind(&done);
+}
+
+bool
+ICBinaryArith_Double::Compiler::generateStubCode(MacroAssembler &masm)
+{
+    Label failure;
+    ensureDouble(masm, R0, FloatReg0, &failure);
+    ensureDouble(masm, R1, FloatReg1, &failure);
+
+    switch (op) {
+      case JSOP_ADD:
+        masm.addDouble(FloatReg1, FloatReg0);
+        break;
+      case JSOP_SUB:
+        masm.subDouble(FloatReg1, FloatReg0);
+        break;
+      case JSOP_MUL:
+        masm.mulDouble(FloatReg1, FloatReg0);
+        break;
+      case JSOP_DIV:
+        masm.divDouble(FloatReg1, FloatReg0);
+        break;
+      case JSOP_MOD:
+        masm.setupUnalignedABICall(2, R0.scratchReg());
+        masm.passABIArg(FloatReg0);
+        masm.passABIArg(FloatReg1);
+        masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, NumberMod), MacroAssembler::DOUBLE);
+        JS_ASSERT(ReturnFloatReg == FloatReg0);
+        break;
+      default:
+        JS_NOT_REACHED("Unexpected op");
+        return false;
+    }
+
+    masm.boxDouble(FloatReg0, R0);
+
+    EmitReturnFromIC(masm);
+
+    // Failure case - jump to next stub
+    masm.bind(&failure);
+    EmitStubGuardFailure(masm);
+    return true;
+}
+
 //
 // UnaryArith_Fallback
 //
 
 static bool
 DoUnaryArithFallback(JSContext *cx, ICUnaryArith_Fallback *stub, HandleValue val,
                      MutableHandleValue res)
 {
--- a/js/src/ion/BaselineIC.h
+++ b/js/src/ion/BaselineIC.h
@@ -272,16 +272,17 @@ class ICEntry
                                 \
     _(ToBool_Fallback)          \
     _(ToBool_Int32)             \
                                 \
     _(ToNumber_Fallback)        \
                                 \
     _(BinaryArith_Fallback)     \
     _(BinaryArith_Int32)        \
+    _(BinaryArith_Double)       \
                                 \
     _(UnaryArith_Fallback)      \
     _(UnaryArith_Int32)         \
                                 \
     _(Call_Fallback)            \
     _(Call_Scripted)            \
                                 \
     _(GetElem_Fallback)         \
@@ -550,16 +551,17 @@ class ICFallbackStub : public ICStub
             if (stub->kind() == kind)
                 return true;
 
             stub = stub->next();
         } while (stub);
 
         return false;
     }
+    void unlinkStubsWithKind(ICStub::Kind kind);
 };
 
 // Monitored stubs are IC stubs that feed a single resulting value out to a
 // type monitor operation.
 class ICMonitoredStub : public ICStub
 {
   protected:
     // Pointer to the start of the type monitoring stub chain.
@@ -1225,16 +1227,46 @@ class ICUnaryArith_Fallback : public ICF
         {}
 
         ICStub *getStub(ICStubSpace *space) {
             return ICUnaryArith_Fallback::New(space, getStubCode());
         }
     };
 };
 
+class ICBinaryArith_Double : public ICStub
+{
+    friend class ICStubSpace;
+
+    ICBinaryArith_Double(IonCode *stubCode)
+      : ICStub(BinaryArith_Double, stubCode)
+    {}
+
+  public:
+    static inline ICBinaryArith_Double *New(ICStubSpace *space, IonCode *code) {
+        return space->allocate<ICBinaryArith_Double>(code);
+    }
+
+    class Compiler : public ICMultiStubCompiler {
+      protected:
+        void ensureDouble(MacroAssembler &masm, const ValueOperand &source, FloatRegister dest,
+                          Label *failure);
+        bool generateStubCode(MacroAssembler &masm);
+
+      public:
+        Compiler(JSContext *cx, JSOp op)
+          : ICMultiStubCompiler(cx, ICStub::BinaryArith_Double, op)
+        {}
+
+        ICStub *getStub(ICStubSpace *space) {
+            return ICBinaryArith_Double::New(space, getStubCode());
+        }
+    };
+};
+
 class ICUnaryArith_Int32 : public ICStub
 {
     friend class ICStubSpace;
 
     ICUnaryArith_Int32(IonCode *stubCode)
       : ICStub(UnaryArith_Int32, stubCode)
     {}
 
--- a/js/src/ion/arm/BaselineIC-arm.cpp
+++ b/js/src/ion/arm/BaselineIC-arm.cpp
@@ -107,26 +107,21 @@ ICBinaryArith_Int32::Compiler::generateS
         masm.ma_cmp(R0.payloadReg(), Imm32(0), Assembler::LessThan);
         masm.j(Assembler::Equal, &failure);
 
         // The call will preserve registers r4-r11. Save R0 and the link register.
         JS_ASSERT(R1 == ValueOperand(r5, r4));
         JS_ASSERT(R0 == ValueOperand(r3, r2));
         masm.moveValue(R0, savedValue);
 
-        Register savedLr = savedRegs.takeAny();
-        masm.mov(lr, savedLr);
-
         masm.setupAlignedABICall(2);
         masm.passABIArg(R0.payloadReg());
         masm.passABIArg(R1.payloadReg());
         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, __aeabi_idivmod));
 
-        masm.mov(savedLr, lr);
-
         // idivmod returns the quotient in r0, and the remainder in r1.
         if (op_ == JSOP_DIV) {
             // Result is a double if the remainder != 0.
             masm.branch32(Assembler::NotEqual, r1, Imm32(0), &revertRegister);
             masm.tagValue(JSVAL_TYPE_INT32, r0, R0);
         } else {
             // If X % Y == 0 and X < 0, the result is -0.
             Label done;
--- a/js/src/ion/arm/BaselineRegisters-arm.h
+++ b/js/src/ion/arm/BaselineRegisters-arm.h
@@ -42,13 +42,17 @@ static const Register ExtractTemp1      
 static const Register BaselineSecondScratchReg = r6;
 
 // R7 - R9 are generally available for use within stubcode.
 
 // Note that BaselineTailCallReg is actually just the link
 // register.  In ARM code emission, we do not clobber BaselineTailCallReg
 // since we keep the return address for calls there.
 
+// FloatReg0 must be equal to ReturnFloatReg.
+static const FloatRegister FloatReg0      = d1;
+static const FloatRegister FloatReg1      = d2;
+
 } // namespace ion
 } // namespace js
 
 #endif
 
--- a/js/src/ion/arm/MacroAssembler-arm.cpp
+++ b/js/src/ion/arm/MacroAssembler-arm.cpp
@@ -61,16 +61,40 @@ MacroAssemblerARM::branchTruncateDouble(
     ma_vcvt_F64_I32(src, ScratchFloatReg);
     ma_vxfer(ScratchFloatReg, dest);
     ma_cmp(dest, Imm32(0x7fffffff));
     ma_cmp(dest, Imm32(0x80000000), Assembler::NotEqual);
     ma_b(fail, Assembler::Equal);
 }
 
 void
+MacroAssemblerARM::addDouble(FloatRegister src, FloatRegister dest)
+{
+    ma_vadd(dest, src, dest);
+}
+
+void
+MacroAssemblerARM::subDouble(FloatRegister src, FloatRegister dest)
+{
+    ma_vsub(dest, src, dest);
+}
+
+void
+MacroAssemblerARM::mulDouble(FloatRegister src, FloatRegister dest)
+{
+    ma_vmul(dest, src, dest);
+}
+
+void
+MacroAssemblerARM::divDouble(FloatRegister src, FloatRegister dest)
+{
+    ma_vdiv(dest, src, dest);
+}
+
+void
 MacroAssemblerARM::inc64(AbsoluteAddress dest)
 {
 
     ma_strd(r0, r1, EDtrAddr(sp, EDtrOffImm(-8)), PreIndex);
 
     ma_mov(Imm32((int32_t)dest.addr), ScratchRegister);
 
     ma_ldrd(EDtrAddr(ScratchRegister, EDtrOffImm(0)), r0, r1);
@@ -2780,18 +2804,26 @@ MacroAssemblerARMCompat::callWithABI(voi
         emitter.emit(moveResolver_);
         emitter.finish();
     }
     for (int i = 0; i < 2; i++) {
         if (!floatArgsInGPR[i].isInvalid())
             ma_vxfer(floatArgsInGPR[i], Register::FromCode(i*2), Register::FromCode(i*2+1));
     }
     checkStackAlignment();
+
+    // Save the lr register if we need to preserve it.
+    if (secondScratchReg_ != lr)
+        ma_mov(lr, secondScratchReg_);
+
     ma_call(fun);
 
+    if (secondScratchReg_ != lr)
+        ma_mov(secondScratchReg_, lr);
+
     if (result == DOUBLE) {
         // Move double from r0/r1 to ReturnFloatReg.
         as_vxfer(r0, r1, ReturnFloatReg, CoreToFloat);
     }
 
     freeStack(stackAdjust);
     if (dynamicAlignment_) {
         // x86 supports pop esp.  on arm, that isn't well defined, so just
--- a/js/src/ion/arm/MacroAssembler-arm.h
+++ b/js/src/ion/arm/MacroAssembler-arm.h
@@ -43,16 +43,21 @@ class MacroAssemblerARM : public Assembl
         secondScratchReg_ = reg;
     }
 
     void convertInt32ToDouble(const Register &src, const FloatRegister &dest);
     void convertUInt32ToDouble(const Register &src, const FloatRegister &dest);
     void convertDoubleToFloat(const FloatRegister &src, const FloatRegister &dest);
     void branchTruncateDouble(const FloatRegister &src, const Register &dest, Label *fail);
 
+    void addDouble(FloatRegister src, FloatRegister dest);
+    void subDouble(FloatRegister src, FloatRegister dest);
+    void mulDouble(FloatRegister src, FloatRegister dest);
+    void divDouble(FloatRegister src, FloatRegister dest);
+
     void inc64(AbsoluteAddress dest);
 
     // somewhat direct wrappers for the low-level assembler funcitons
     // bitops
     // attempt to encode a virtual alu instruction using
     // two real instructions.
   private:
     bool alu_dbl(Register src1, Imm32 imm, Register dest, ALUOp op,
--- a/js/src/ion/shared/MacroAssembler-x86-shared.h
+++ b/js/src/ion/shared/MacroAssembler-x86-shared.h
@@ -248,16 +248,25 @@ class MacroAssemblerX86Shared : public A
         movsd(src, Operand(dest));
     }
     void zeroDouble(FloatRegister reg) {
         xorpd(reg, reg);
     }
     void addDouble(FloatRegister src, FloatRegister dest) {
         addsd(src, dest);
     }
+    void subDouble(FloatRegister src, FloatRegister dest) {
+        subsd(src, dest);
+    }
+    void mulDouble(FloatRegister src, FloatRegister dest) {
+        mulsd(src, dest);
+    }
+    void divDouble(FloatRegister src, FloatRegister dest) {
+        divsd(src, dest);
+    }
     void convertDoubleToFloat(const FloatRegister &src, const FloatRegister &dest) {
         cvtsd2ss(src, dest);
     }
     void loadFloatAsDouble(const Register &src, FloatRegister dest) {
         movd(src, dest);
         cvtss2sd(dest, dest);
     }
     void loadFloatAsDouble(const Address &src, FloatRegister dest) {
--- a/js/src/ion/x64/BaselineRegisters-x64.h
+++ b/js/src/ion/x64/BaselineRegisters-x64.h
@@ -23,13 +23,17 @@ static const ValueOperand R2(rax);
 // BaselineTailCallReg and BaselineStubReg reuse
 // registers from R2.
 static const Register BaselineTailCallReg = rsi;
 static const Register BaselineStubReg     = rdi;
 
 static const Register ExtractTemp0        = r14;
 static const Register ExtractTemp1        = r15;
 
+// FloatReg0 must be equal to ReturnFloatReg.
+static const FloatRegister FloatReg0      = xmm0;
+static const FloatRegister FloatReg1      = xmm1;
+
 } // namespace ion
 } // namespace js
 
 #endif
 
--- a/js/src/ion/x86/BaselineRegisters-x86.h
+++ b/js/src/ion/x86/BaselineRegisters-x86.h
@@ -24,13 +24,17 @@ static const ValueOperand R2(esi, edi);
 // BaselineTailCallReg and BaselineStubReg reuse
 // registers from R2.
 static const Register BaselineTailCallReg = esi;
 static const Register BaselineStubReg     = edi;
 
 static const Register ExtractTemp0        = InvalidReg;
 static const Register ExtractTemp1        = InvalidReg;
 
+// FloatReg0 must be equal to ReturnFloatReg.
+static const FloatRegister FloatReg0      = xmm0;
+static const FloatRegister FloatReg1      = xmm1;
+
 } // namespace ion
 } // namespace js
 
 #endif