Bug 322529 part 3 - Fix LRandom JIT code to use the new algorithm. r=arai,jwalden
authorJan de Mooij <jdemooij@mozilla.com>
Wed, 02 Dec 2015 13:56:00 +0100
changeset 309394 c511942454b6b48093d50e0f90085dd6dd638006
parent 309393 182369db983e66c276948d44bc8251b73b68c0ba
child 309395 a0d56a24423cb7af41e2e5e533f961f2a31a8bf8
push id5513
push userraliiev@mozilla.com
push dateMon, 25 Jan 2016 13:55:34 +0000
treeherdermozilla-beta@5ee97dd05b5c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersarai, jwalden
bugs322529
milestone45.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 322529 part 3 - Fix LRandom JIT code to use the new algorithm. r=arai,jwalden
js/src/jit/CodeGenerator.cpp
js/src/jit/CodeGenerator.h
js/src/jit/CompileWrappers.cpp
js/src/jit/CompileWrappers.h
js/src/jit/MCallOptimize.cpp
js/src/jit/shared/LIR-shared.h
js/src/jit/x86/Lowering-x86.cpp
mfbt/XorShift128PlusRNG.h
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -10339,133 +10339,80 @@ CodeGenerator::visitCheckReturn(LCheckRe
     Label bail, noChecks;
     masm.branchTestObject(Assembler::Equal, returnValue, &noChecks);
     masm.branchTestUndefined(Assembler::NotEqual, returnValue, &bail);
     masm.branchTestMagicValue(Assembler::Equal, thisValue, JS_UNINITIALIZED_LEXICAL, &bail);
     bailoutFrom(&bail, ins->snapshot());
     masm.bind(&noChecks);
 }
 
-// Out-of-line math_random_no_outparam call for LRandom.
-class OutOfLineRandom : public OutOfLineCodeBase<CodeGenerator>
-{
-    LRandom* lir_;
-
-  public:
-    explicit OutOfLineRandom(LRandom* lir)
-      : lir_(lir)
-    { }
-
-    void accept(CodeGenerator* codegen) {
-        codegen->visitOutOfLineRandom(this);
-    }
-
-    LRandom* lir() const {
-        return lir_;
-    }
-};
-
-static const uint64_t RNG_HIGH_MASK = (0xFFFFFFFFFFFFFFFFULL >>
-                                       (RNG_STATE_WIDTH - RNG_HIGH_BITS)) << RNG_LOW_BITS;
-static const double RNG_DSCALE_INV = 1 / RNG_DSCALE;
-
 void
 CodeGenerator::visitRandom(LRandom* ins)
 {
+    using mozilla::non_crypto::XorShift128PlusRNG;
+
     FloatRegister output = ToFloatRegister(ins->output());
-    Register JSCompartmentReg = ToRegister(ins->temp1());
+    Register tempReg = ToRegister(ins->temp0());
+
 #ifdef JS_PUNBOX64
-    Register64 rngStateReg = Register64(ToRegister(ins->tempMaybeEAX()));
-    Register64 highReg = Register64(ToRegister(ins->tempMaybeEDX()));
+    Register64 s0Reg(ToRegister(ins->temp1()));
+    Register64 s1Reg(ToRegister(ins->temp2()));
 #else
-    Register64 rngStateReg = Register64(ToRegister(ins->temp2()), ToRegister(ins->temp3()));
-    Register64 highReg = Register64(ToRegister(ins->tempMaybeEAX()), ToRegister(ins->tempMaybeEDX()));
-#endif
-    // tempReg is used only on x86.
-    Register tempReg = ToRegister(ins->tempMaybeEAX());
-
-    // rngState = cx->compartment()->rngState;
-    masm.loadJSContext(JSCompartmentReg);
-    masm.loadPtr(Address(JSCompartmentReg, JSContext::offsetOfCompartment()), JSCompartmentReg);
-    masm.load64(Address(JSCompartmentReg, JSCompartment::offsetOfRngState()), rngStateReg);
-
-    // if rngState == 0, escape from inlined code and call
-    // math_random_no_outparam.
-    OutOfLineRandom* ool = new(alloc()) OutOfLineRandom(ins);
-    addOutOfLineCode(ool, ins->mir());
-    masm.branchTest64(Assembler::Zero, rngStateReg, rngStateReg, tempReg, ool->entry());
-
-    // rngState = rngState * RNG_MULTIPLIER;
-    masm.mul64(Imm64(RNG_MULTIPLIER), rngStateReg);
-
-    // rngState += RNG_ADDEND;
-    masm.add64(Imm32(RNG_ADDEND), rngStateReg);
-
-    // rngState &= RNG_MASK;
-    masm.and64(Imm64(RNG_MASK), rngStateReg);
-
-    // if rngState == 0, escape from inlined code and call
-    // math_random_no_outparam.
-    masm.branchTest64(Assembler::Zero, rngStateReg, rngStateReg, tempReg, ool->entry());
-
-    // high = (rngState >> (RNG_STATE_WIDTH - RNG_HIGH_BITS)) << RNG_LOW_BITS;
-    masm.move64(rngStateReg, highReg);
-    masm.lshift64(Imm32(RNG_LOW_BITS - (RNG_STATE_WIDTH - RNG_HIGH_BITS)), highReg);
-    masm.and64(Imm64(RNG_HIGH_MASK), highReg);
-#ifdef JS_CODEGEN_X86
-    // eax and edx are overwritten by mul64 on x86.
-    masm.push64(highReg);
+    Register64 s0Reg(ToRegister(ins->temp1()), ToRegister(ins->temp2()));
+    Register64 s1Reg(ToRegister(ins->temp3()), ToRegister(ins->temp4()));
 #endif
 
-    // rngState = rngState * RNG_MULTIPLIER;
-    masm.mul64(Imm64(RNG_MULTIPLIER), rngStateReg);
-
-    // rngState += RNG_ADDEND;
-    masm.add64(Imm32(RNG_ADDEND), rngStateReg);
-
-    // rngState &= RNG_MASK;
-    masm.and64(Imm64(RNG_MASK), rngStateReg);
-
-    // cx->compartment()->rngState = rngState;
-    masm.store64(rngStateReg, Address(JSCompartmentReg, JSCompartment::offsetOfRngState()));
-
-    // low = rngState >> (RNG_STATE_WIDTH - RNG_LOW_BITS);
-    const Register64& lowReg = rngStateReg;
-    masm.rshift64(Imm32(RNG_STATE_WIDTH - RNG_LOW_BITS), lowReg);
-
-    // output = double(high | low);
-#ifdef JS_CODEGEN_X86
-    masm.pop64(highReg);
-#endif
-    masm.or64(highReg, lowReg);
-    masm.convertUInt64ToDouble(lowReg, tempReg, output);
-
-    // output = output * RNG_DSCALE_INV;
-    masm.mulDoublePtr(ImmPtr(&RNG_DSCALE_INV), tempReg, output);
-
-    masm.bind(ool->rejoin());
-}
-
-void
-CodeGenerator::visitOutOfLineRandom(OutOfLineRandom* ool)
-{
-    LRandom* ins = ool->lir();
-    Register temp1 = ToRegister(ins->tempMaybeEAX());
-    Register temp2 = ToRegister(ins->tempMaybeEDX());
-    MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg);
-
-    LiveRegisterSet regs;
-    setReturnDoubleRegs(&regs);
-    saveVolatile(regs);
-
-    masm.loadJSContext(temp1);
-
-    masm.setupUnalignedABICall(temp2);
-    masm.passABIArg(temp1);
-    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, math_random_no_outparam), MoveOp::DOUBLE);
-
-    restoreVolatile(regs);
-
-    masm.jump(ool->rejoin());
+    const void* rng = gen->compartment->addressOfRandomNumberGenerator();
+    masm.movePtr(ImmPtr(rng), tempReg);
+
+    static_assert(sizeof(XorShift128PlusRNG) == 2 * sizeof(uint64_t),
+                  "Code below assumes XorShift128PlusRNG contains two uint64_t values");
+
+    Address state0Addr(tempReg, XorShift128PlusRNG::offsetOfState0());
+    Address state1Addr(tempReg, XorShift128PlusRNG::offsetOfState1());
+
+    // uint64_t s1 = mState[0];
+    masm.load64(state0Addr, s1Reg);
+
+    // s1 ^= s1 << 23;
+    masm.move64(s1Reg, s0Reg);
+    masm.lshift64(Imm32(23), s1Reg);
+    masm.xor64(s0Reg, s1Reg);
+
+    // s1 ^= s1 >> 17
+    masm.move64(s1Reg, s0Reg);
+    masm.rshift64(Imm32(17), s1Reg);
+    masm.xor64(s0Reg, s1Reg);
+
+    // const uint64_t s0 = mState[1];
+    masm.load64(state1Addr, s0Reg);
+
+    // mState[0] = s0;
+    masm.store64(s0Reg, state0Addr);
+
+    // s1 ^= s0
+    masm.xor64(s0Reg, s1Reg);
+
+    // s1 ^= s0 >> 26
+    masm.rshift64(Imm32(26), s0Reg);
+    masm.xor64(s0Reg, s1Reg);
+
+    // mState[1] = s1
+    masm.store64(s1Reg, state1Addr);
+
+    // s1 += mState[0]
+    masm.load64(state0Addr, s0Reg);
+    masm.add64(s0Reg, s1Reg);
+
+    // See comment in XorShift128PlusRNG::nextDouble().
+    static const int MantissaBits = FloatingPoint<double>::kExponentShift + 1;
+    static const double ScaleInv = double(1) / (1ULL << MantissaBits);
+
+    masm.and64(Imm64((1ULL << MantissaBits) - 1), s1Reg);
+
+    masm.convertUInt64ToDouble(s1Reg, tempReg, output);
+
+    // output *= ScaleInv
+    masm.mulDoublePtr(ImmPtr(&ScaleInv), tempReg, output);
 }
 
 } // namespace jit
 } // namespace js
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -42,17 +42,16 @@ class OutOfLineUnboxFloatingPoint;
 class OutOfLineStoreElementHole;
 class OutOfLineTypeOfV;
 class OutOfLineUpdateCache;
 class OutOfLineCallPostWriteBarrier;
 class OutOfLineIsCallable;
 class OutOfLineRegExpExec;
 class OutOfLineRegExpTest;
 class OutOfLineLambdaArrow;
-class OutOfLineRandom;
 
 class CodeGenerator : public CodeGeneratorSpecific
 {
     void generateArgumentsChecks(bool bailout = true);
     bool generateBody();
 
     ConstantOrRegister toConstantOrRegister(LInstruction* lir, size_t n, MIRType type);
 
@@ -383,17 +382,16 @@ class CodeGenerator : public CodeGenerat
     void emitAssertResultV(const ValueOperand output, const TemporaryTypeSet* typeset);
     void emitAssertObjectOrStringResult(Register input, MIRType type, const TemporaryTypeSet* typeset);
 
     void visitInterruptCheck(LInterruptCheck* lir);
     void visitAsmJSInterruptCheck(LAsmJSInterruptCheck* lir);
     void visitRecompileCheck(LRecompileCheck* ins);
 
     void visitRandom(LRandom* ins);
-    void visitOutOfLineRandom(OutOfLineRandom* ool);
 
     IonScriptCounts* extractScriptCounts() {
         IonScriptCounts* counts = scriptCounts_;
         scriptCounts_ = nullptr;  // prevent delete in dtor
         return counts;
     }
 
   private:
--- a/js/src/jit/CompileWrappers.cpp
+++ b/js/src/jit/CompileWrappers.cpp
@@ -251,16 +251,22 @@ CompileCompartment::runtime()
 }
 
 const void*
 CompileCompartment::addressOfEnumerators()
 {
     return &compartment()->enumerators;
 }
 
+const void*
+CompileCompartment::addressOfRandomNumberGenerator()
+{
+    return compartment()->randomNumberGenerator.ptr();
+}
+
 const JitCompartment*
 CompileCompartment::jitCompartment()
 {
     return compartment()->jitCompartment();
 }
 
 bool
 CompileCompartment::hasObjectMetadataCallback()
--- a/js/src/jit/CompileWrappers.h
+++ b/js/src/jit/CompileWrappers.h
@@ -110,16 +110,17 @@ class CompileCompartment
 
   public:
     static CompileCompartment* get(JSCompartment* comp);
 
     CompileZone* zone();
     CompileRuntime* runtime();
 
     const void* addressOfEnumerators();
+    const void* addressOfRandomNumberGenerator();
 
     const JitCompartment* jitCompartment();
 
     bool hasObjectMetadataCallback();
 
     // Mirror CompartmentOptions.
     void setSingletonsAsValues();
 };
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -1367,16 +1367,19 @@ IonBuilder::inlineMathRandom(CallInfo& c
     if (callInfo.constructing()) {
         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
         return InliningStatus_NotInlined;
     }
 
     if (getInlineReturnType() != MIRType_Double)
         return InliningStatus_NotInlined;
 
+    MOZ_ASSERT(script()->compartment()->randomNumberGenerator.isSome(),
+               "MRandom JIT code depends on RNG being initialized");
+
     callInfo.setImplicitlyUsedUnchecked();
 
     MRandom* rand = MRandom::New(alloc());
     current->add(rand);
     current->push(rand);
     return InliningStatus_Inlined;
 }
 
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -7311,47 +7311,45 @@ class LArrowNewTarget : public LInstruct
 #else
 # define LRANDOM_NUM_TEMPS 5
 #endif
 
 class LRandom : public LInstructionHelper<1, 0, LRANDOM_NUM_TEMPS>
 {
   public:
     LIR_HEADER(Random)
-    LRandom(const LDefinition &tempMaybeEAX, const LDefinition &tempMaybeEDX,
-            const LDefinition &temp1
+    LRandom(const LDefinition &temp0, const LDefinition &temp1,
+            const LDefinition &temp2
 #ifndef JS_PUNBOX64
-            , const LDefinition &temp2, const LDefinition &temp3
+            , const LDefinition &temp3, const LDefinition &temp4
 #endif
             )
     {
-        setTemp(0, tempMaybeEAX);
-        setTemp(1, tempMaybeEDX);
-        setTemp(2, temp1);
+        setTemp(0, temp0);
+        setTemp(1, temp1);
+        setTemp(2, temp2);
 #ifndef JS_PUNBOX64
-        setTemp(3, temp2);
-        setTemp(4, temp3);
+        setTemp(3, temp3);
+        setTemp(4, temp4);
 #endif
     }
-    // On x86, following 2 methods return eax and edx necessary for mull.
-    // On others, following 2 methods return ordinary temporary registers.
-    const LDefinition* tempMaybeEAX() {
+    const LDefinition* temp0() {
         return getTemp(0);
     }
-    const LDefinition* tempMaybeEDX() {
+    const LDefinition* temp1() {
         return getTemp(1);
     }
-    const LDefinition *temp1() {
+    const LDefinition *temp2() {
         return getTemp(2);
     }
 #ifndef JS_PUNBOX64
-    const LDefinition *temp2() {
+    const LDefinition *temp3() {
         return getTemp(3);
     }
-    const LDefinition *temp3() {
+    const LDefinition *temp4() {
         return getTemp(4);
     }
 #endif
 
     MRandom* mir() const {
         return mir_->toRandom();
     }
 };
--- a/js/src/jit/x86/Lowering-x86.cpp
+++ b/js/src/jit/x86/Lowering-x86.cpp
@@ -436,16 +436,15 @@ LIRGeneratorX86::visitSubstr(MSubstr* in
                                          tempByteOpRegister());
     define(lir, ins);
     assignSafepoint(lir, ins);
 }
 
 void
 LIRGeneratorX86::visitRandom(MRandom* ins)
 {
-    // eax and edx are necessary for mull.
-    LRandom *lir = new(alloc()) LRandom(tempFixed(eax),
-                                        tempFixed(edx),
+    LRandom *lir = new(alloc()) LRandom(temp(),
+                                        temp(),
                                         temp(),
                                         temp(),
                                         temp());
     defineFixed(lir, ins, LFloatReg(ReturnDoubleReg));
 }
--- a/mfbt/XorShift128PlusRNG.h
+++ b/mfbt/XorShift128PlusRNG.h
@@ -47,16 +47,22 @@ class XorShift128PlusRNG {
    * should have an almost even mix of zero and one bits.
    */
   XorShift128PlusRNG(uint64_t aInitial0, uint64_t aInitial1) {
     setState(aInitial0, aInitial1);
   }
 
   /* Return a pseudo-random 64-bit number. */
   uint64_t next() {
+    /*
+     * The offsetOfState*() methods below are provided so that exceedingly-rare
+     * callers that want to observe or poke at RNG state in C++ type-system-
+     * ignoring means can do so. Don't change the next() or nextDouble()
+     * algorithms without altering code that uses offsetOfState*()!
+     */
     uint64_t s1 = mState[0];
     const uint64_t s0 = mState[1];
     mState[0] = s0;
     s1 ^= s1 << 23;
     mState[1] = s1 ^ s0 ^ (s1 >> 17) ^ (s0 >> 26);
     return mState[1] + s0;
   }
 
@@ -84,14 +90,21 @@ class XorShift128PlusRNG {
    * both be zero; ideally, they should have an almost even mix of zero and one
    * bits.
    */
   void setState(uint64_t aState0, uint64_t aState1) {
     MOZ_ASSERT(aState0 || aState1);
     mState[0] = aState0;
     mState[1] = aState1;
   }
+
+  static size_t offsetOfState0() {
+    return offsetof(XorShift128PlusRNG, mState[0]);
+  }
+  static size_t offsetOfState1() {
+    return offsetof(XorShift128PlusRNG, mState[1]);
+  }
 };
 
 } // namespace non_crypto
 } // namespace mozilla
 
 #endif // mozilla_XorShift128Plus_h