Bug 1438727: [Part 10] Implement flexible{quotient,remainder}32 r=tcampbell
authorMatthew Gaudet <mgaudet@mozilla.com>
Tue, 08 May 2018 14:04:41 -0400
changeset 428597 b0ff1059472bf9435ae1c99dcd1a1471045262ba
parent 428596 4545c5afeb75244bc89c8a7f6405f105b3c6ecf5
child 428598 5b25886cfb6444c950bb40b01c306b0d74d1abc3
push id34337
push userncsoregi@mozilla.com
push dateThu, 26 Jul 2018 21:58:45 +0000
treeherdermozilla-central@8f2f847b2f9d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstcampbell
bugs1438727
milestone63.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 1438727: [Part 10] Implement flexible{quotient,remainder}32 r=tcampbell
js/src/jit/MacroAssembler.h
js/src/jit/arm/MacroAssembler-arm.cpp
js/src/jit/arm64/MacroAssembler-arm64.cpp
js/src/jit/mips-shared/MacroAssembler-mips-shared.cpp
js/src/jit/x86-shared/MacroAssembler-x86-shared.cpp
js/src/jsapi-tests/testJitMacroAssembler.cpp
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -819,16 +819,38 @@ class MacroAssembler : public MacroAssem
     // Perform an integer division, returning the remainder part.
     // rhs must not be zero, and the division must not overflow.
     //
     // On x86_shared, srcDest must be eax and edx will be clobbered.
     // On ARM, the chip must have hardware division instructions.
     inline void remainder32(Register rhs, Register srcDest, bool isUnsigned) PER_SHARED_ARCH;
 
     // Perform an integer division, returning the integer part rounded toward zero.
+    // rhs must not be zero, and the division must not overflow.
+    //
+    // This variant preserves registers, and doesn't require hardware division
+    // instructions on ARM (will call out to a runtime routine).
+    //
+    // rhs is preserved, srdDest is clobbered.
+    void flexibleRemainder32(Register rhs, Register srcDest, bool isUnsigned,
+                             const LiveRegisterSet& volatileLiveRegs)
+                             DEFINED_ON(mips_shared, arm, arm64, x86_shared);
+
+    // Perform an integer division, returning the integer part rounded toward zero.
+    // rhs must not be zero, and the division must not overflow.
+    //
+    // This variant preserves registers, and doesn't require hardware division
+    // instructions on ARM (will call out to a runtime routine).
+    //
+    // rhs is preserved, srdDest is clobbered.
+    void flexibleQuotient32(Register rhs, Register srcDest, bool isUnsigned,
+                            const LiveRegisterSet& volatileLiveRegs)
+                            DEFINED_ON(mips_shared, arm, arm64, x86_shared);
+
+    // Perform an integer division, returning the integer part rounded toward zero.
     // rhs must not be zero, and the division must not overflow. The remainder
     // is stored into the third argument register here.
     //
     // This variant preserves registers, and doesn't require hardware division
     // instructions on ARM (will call out to a runtime routine).
     //
     // rhs is preserved, srdDest and remOutput are clobbered.
     void flexibleDivMod32(Register rhs, Register srcDest, Register remOutput,
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -5925,16 +5925,70 @@ MacroAssembler::convertUInt64ToDouble(Re
     addDouble(scratchDouble, dest);
 }
 
 extern "C" {
     extern MOZ_EXPORT int64_t __aeabi_idivmod(int,int);
     extern MOZ_EXPORT int64_t __aeabi_uidivmod(int,int);
 }
 
+inline void
+EmitRemainderOrQuotient(bool isRemainder, MacroAssembler& masm,
+                        Register rhs, Register lhsOutput, bool isSigned,
+                        const LiveRegisterSet& volatileLiveRegs)
+{
+    // Currently this helper can't handle this situation.
+    MOZ_ASSERT(lhsOutput != rhs);
+
+    if (HasIDIV()) {
+        if (isRemainder)
+            masm.remainder32(rhs, lhsOutput, isSigned);
+        else
+            masm.quotient32(rhs, lhsOutput, isSigned);
+    } else {
+        // Ensure that the output registers are saved and restored properly,
+        MOZ_ASSERT(volatileLiveRegs.has(ReturnRegVal0));
+        MOZ_ASSERT(volatileLiveRegs.has(ReturnRegVal1));
+
+        masm.PushRegsInMask(volatileLiveRegs);
+        {
+            ScratchRegisterScope scratch(masm);
+            masm.setupUnalignedABICall(scratch);
+        }
+        masm.passABIArg(lhsOutput);
+        masm.passABIArg(rhs);
+        masm.callWithABI(isSigned ? JS_FUNC_TO_DATA_PTR(void*, __aeabi_uidivmod) :
+                                    JS_FUNC_TO_DATA_PTR(void*, __aeabi_idivmod),
+                         MoveOp::GENERAL,
+                         CheckUnsafeCallWithABI::DontCheckOther);
+        if (isRemainder)
+            masm.mov(ReturnRegVal1, lhsOutput);
+        else
+            masm.mov(ReturnRegVal0, lhsOutput);
+
+        LiveRegisterSet ignore;
+        ignore.add(lhsOutput);
+        masm.PopRegsInMaskIgnore(volatileLiveRegs, ignore);
+    }
+}
+
+void
+MacroAssembler::flexibleQuotient32(Register rhs, Register srcDest, bool isUnsigned,
+                                   const LiveRegisterSet& volatileLiveRegs)
+{
+    EmitRemainderOrQuotient(false, *this, rhs, srcDest, isUnsigned, volatileLiveRegs);
+}
+
+void
+MacroAssembler::flexibleRemainder32(Register rhs, Register srcDest, bool isUnsigned,
+                                    const LiveRegisterSet& volatileLiveRegs)
+{
+    EmitRemainderOrQuotient(true, *this, rhs, srcDest, isUnsigned, volatileLiveRegs);
+}
+
 void
 MacroAssembler::flexibleDivMod32(Register rhs, Register lhsOutput, Register remOutput,
                                  bool isUnsigned, const LiveRegisterSet& volatileLiveRegs)
 {
     // Currently this helper can't handle this situation.
     MOZ_ASSERT(lhsOutput != rhs);
 
     if (HasIDIV()) {
--- a/js/src/jit/arm64/MacroAssembler-arm64.cpp
+++ b/js/src/jit/arm64/MacroAssembler-arm64.cpp
@@ -1856,16 +1856,30 @@ MacroAssembler::atomicEffectOpJS(Scalar:
 void
 MacroAssembler::atomicEffectOpJS(Scalar::Type arrayType, const Synchronization& sync, AtomicOp op,
                            Register value, const Address& mem, Register temp)
 {
     atomicEffectOp(arrayType, sync, op, value, mem, temp);
 }
 
 void
+MacroAssembler::flexibleQuotient32(Register rhs, Register srcDest, bool isUnsigned,
+                                   const LiveRegisterSet&)
+{
+    quotient32(rhs, srcDest, isUnsigned);
+}
+
+void
+MacroAssembler::flexibleRemainder32(Register rhs, Register srcDest, bool isUnsigned,
+                                    const LiveRegisterSet&)
+{
+    remainder32(rhs, srcDest, isUnsigned);
+}
+
+void
 MacroAssembler::flexibleDivMod32(Register rhs, Register srcDest, Register remOutput,
                                  bool isUnsigned, const LiveRegisterSet&)
 {
     vixl::UseScratchRegisterScope temps(this);
     ARMRegister scratch = temps.AcquireW();
     ARMRegister src = temps.AcquireW();
 
     // Preserve src for remainder computation
--- a/js/src/jit/mips-shared/MacroAssembler-mips-shared.cpp
+++ b/js/src/jit/mips-shared/MacroAssembler-mips-shared.cpp
@@ -2780,16 +2780,30 @@ void
 MacroAssembler::atomicEffectOpJS(Scalar::Type arrayType, const Synchronization& sync, AtomicOp op,
                                  Register value, const Address& mem, Register valueTemp,
                                  Register offsetTemp, Register maskTemp)
 {
     atomicEffectOp(arrayType, sync, op, value, mem, valueTemp, offsetTemp, maskTemp);
 }
 
 void
+MacroAssembler::flexibleQuotient32(Register rhs, Register srcDest, bool isUnsigned,
+                                   const LiveRegisterSet&)
+{
+    quotient32(rhs, srcDest, isUnsigned);
+}
+
+void
+MacroAssembler::flexibleRemainder32(Register rhs, Register srcDest, bool isUnsigned,
+                                    const LiveRegisterSet&)
+{
+    remainder32(rhs, srcDest, isUnsigned);
+}
+
+void
 MacroAssembler::flexibleDivMod32(Register rhs, Register srcDest, Register remOutput,
                                  bool isUnsigned, const LiveRegisterSet&)
 {
     if (isUnsigned)
         as_divu(srcDest, rhs);
     else
         as_div(srcDest, rhs);
     as_mfhi(remOutput);
--- a/js/src/jit/x86-shared/MacroAssembler-x86-shared.cpp
+++ b/js/src/jit/x86-shared/MacroAssembler-x86-shared.cpp
@@ -379,16 +379,51 @@ MacroAssembler::flexibleDivMod32(Registe
         resolution.addMove(edx, remOutput);
     }
     if (oom())
         return;
 
     PopRegsInMask(preserve);
 }
 
+void
+MacroAssembler::flexibleQuotient32(Register rhs, Register srcDest, bool isUnsigned,
+                                   const LiveRegisterSet& volatileLiveRegs)
+{
+    // Choose an arbitrary register that isn't eax, edx, rhs or srcDest;
+    AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
+    regs.takeUnchecked(eax);
+    regs.takeUnchecked(edx);
+    regs.takeUnchecked(rhs);
+    regs.takeUnchecked(srcDest);
+
+    Register remOut = regs.takeAny();
+    push(remOut);
+    flexibleDivMod32(rhs, srcDest, remOut, isUnsigned, volatileLiveRegs);
+    pop(remOut);
+}
+
+void
+MacroAssembler::flexibleRemainder32(Register rhs, Register srcDest, bool isUnsigned,
+                                    const LiveRegisterSet& volatileLiveRegs)
+{
+    // Choose an arbitrary register that isn't eax, edx, rhs or srcDest
+    AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
+    regs.takeUnchecked(eax);
+    regs.takeUnchecked(edx);
+    regs.takeUnchecked(rhs);
+    regs.takeUnchecked(srcDest);
+
+    Register remOut = regs.takeAny();
+    push(remOut);
+    flexibleDivMod32(rhs, srcDest, remOut, isUnsigned, volatileLiveRegs);
+    mov(remOut, srcDest);
+    pop(remOut);
+}
+
 // ===============================================================
 // Stack manipulation functions.
 
 void
 MacroAssembler::PushRegsInMask(LiveRegisterSet set)
 {
     FloatRegisterSet fpuSet(set.fpus().reduceSetForPush());
     unsigned numFpu = fpuSet.size();
--- a/js/src/jsapi-tests/testJitMacroAssembler.cpp
+++ b/js/src/jsapi-tests/testJitMacroAssembler.cpp
@@ -106,16 +106,110 @@ BEGIN_TEST(testJitMacroAssembler_flexibl
             }
         }
     }
 
     return Execute(cx, masm);
 }
 END_TEST(testJitMacroAssembler_flexibleDivMod)
 
+BEGIN_TEST(testJitMacroAssembler_flexibleRemainder)
+{
+    StackMacroAssembler masm(cx);
+
+    if (!Prepare(masm))
+        return false;
+
+    // Test case divides 9/2;
+    const uintptr_t dividend = 9;
+    const uintptr_t divisor = 2;
+    const uintptr_t remainder_result  = 1;
+
+    AllocatableGeneralRegisterSet leftOutputHandSides(GeneralRegisterSet::All());
+
+    while (!leftOutputHandSides.empty()) {
+        Register lhsOutput = leftOutputHandSides.takeAny();
+
+        AllocatableGeneralRegisterSet rightHandSides(GeneralRegisterSet::All());
+        while (!rightHandSides.empty()) {
+            Register rhs = rightHandSides.takeAny();
+
+            if (lhsOutput == rhs)
+                continue;
+
+            AllocatableRegisterSet regs(RegisterSet::Volatile());
+            LiveRegisterSet save(regs.asLiveSet());
+
+            Label next, fail;
+            masm.mov(ImmWord(dividend), lhsOutput);
+            masm.mov(ImmWord(divisor), rhs);
+            masm.flexibleRemainder32(rhs, lhsOutput, false, save);
+            masm.branch32(Assembler::NotEqual, AbsoluteAddress(&remainder_result), lhsOutput, &fail);
+            // Ensure RHS was not clobbered
+            masm.branch32(Assembler::NotEqual, AbsoluteAddress(&divisor), rhs, &fail);
+            masm.jump(&next);
+            masm.bind(&fail);
+            masm.printf("Failed\n");
+            masm.breakpoint();
+
+            masm.bind(&next);
+        }
+    }
+
+    return Execute(cx, masm);
+}
+END_TEST(testJitMacroAssembler_flexibleRemainder)
+
+BEGIN_TEST(testJitMacroAssembler_flexibleQuotient)
+{
+    StackMacroAssembler masm(cx);
+
+    if (!Prepare(masm))
+        return false;
+
+    // Test case divides 9/2;
+    const uintptr_t dividend = 9;
+    const uintptr_t divisor = 2;
+    const uintptr_t quotient_result  = 4;
+
+    AllocatableGeneralRegisterSet leftOutputHandSides(GeneralRegisterSet::All());
+
+    while (!leftOutputHandSides.empty()) {
+        Register lhsOutput = leftOutputHandSides.takeAny();
+
+        AllocatableGeneralRegisterSet rightHandSides(GeneralRegisterSet::All());
+        while (!rightHandSides.empty()) {
+            Register rhs = rightHandSides.takeAny();
+
+            if (lhsOutput == rhs)
+                continue;
+
+            AllocatableRegisterSet regs(RegisterSet::Volatile());
+            LiveRegisterSet save(regs.asLiveSet());
+
+            Label next, fail;
+            masm.mov(ImmWord(dividend), lhsOutput);
+            masm.mov(ImmWord(divisor), rhs);
+            masm.flexibleQuotient32(rhs, lhsOutput, false, save);
+            masm.branch32(Assembler::NotEqual, AbsoluteAddress(&quotient_result), lhsOutput, &fail);
+            // Ensure RHS was not clobbered
+            masm.branch32(Assembler::NotEqual, AbsoluteAddress(&divisor), rhs, &fail);
+            masm.jump(&next);
+            masm.bind(&fail);
+            masm.printf("Failed\n");
+            masm.breakpoint();
+
+            masm.bind(&next);
+        }
+    }
+
+    return Execute(cx, masm);
+}
+END_TEST(testJitMacroAssembler_flexibleQuotient)
+
 BEGIN_TEST(testJitMacroAssembler_truncateDoubleToInt64)
 {
     StackMacroAssembler masm(cx);
 
     if (!Prepare(masm))
         return false;
 
     AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());