Math.random should have an intrinsic thunk and it should be later handled as a DFG...
authorutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 15 Dec 2015 03:51:42 +0000 (03:51 +0000)
committerutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 15 Dec 2015 03:51:42 +0000 (03:51 +0000)
https://bugs.webkit.org/show_bug.cgi?id=152133

Reviewed by Geoffrey Garen.

Source/JavaScriptCore:

In this patch, we implement new RandomIntrinsic. It emits a machine code to generate random numbers efficiently.
And later it will be recognized by DFG and converted to ArithRandom node.
It provides type information SpecDoubleReal since Math.random only generates a number within [0, 1.0).

Currently, only 64bit version is supported. On 32bit environment, ArithRandom will be converted to callOperation.
While it emits a function call, ArithRandom node on 32bit still represents SpecDoubleReal as a result type.

* dfg/DFGAbstractHeap.h:
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::handleIntrinsicCall):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGNodeType.h:
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* dfg/DFGPredictionPropagationPhase.cpp:
(JSC::DFG::PredictionPropagationPhase::propagate):
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.h:
(JSC::DFG::SpeculativeJIT::callOperation):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
(JSC::DFG::SpeculativeJIT::compileArithRandom):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
(JSC::DFG::SpeculativeJIT::compileArithRandom):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToLLVM.cpp:
(JSC::FTL::DFG::LowerDFGToLLVM::compileNode):
(JSC::FTL::DFG::LowerDFGToLLVM::compileArithRandom):
* jit/AssemblyHelpers.cpp:
(JSC::emitRandomThunkImpl):
(JSC::AssemblyHelpers::emitRandomThunk):
* jit/AssemblyHelpers.h:
* jit/JITOperations.h:
* jit/ThunkGenerators.cpp:
(JSC::randomThunkGenerator):
* jit/ThunkGenerators.h:
* runtime/Intrinsic.h:
* runtime/JSGlobalObject.h:
(JSC::JSGlobalObject::weakRandomOffset):
* runtime/MathObject.cpp:
(JSC::MathObject::finishCreation):
* runtime/VM.cpp:
(JSC::thunkGeneratorForIntrinsic):
* tests/stress/random-53bit.js: Added.
(test):
* tests/stress/random-in-range.js: Added.
(test):

Source/WTF:

Change 64bit random to double logic to convert efficiently.

* wtf/WeakRandom.h:
(WTF::WeakRandom::get):
(WTF::WeakRandom::lowOffset):
(WTF::WeakRandom::highOffset):

LayoutTests:

Add new regression test.

* js/regress/math-random-expected.txt: Added.
* js/regress/math-random.html: Added.
* js/regress/script-tests/math-random.js: Added.
(test):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@194087 268f45cc-cd09-0410-ab3c-d52691b4dbfc

34 files changed:
LayoutTests/ChangeLog
LayoutTests/js/regress/math-random-expected.txt [new file with mode: 0644]
LayoutTests/js/regress/math-random.html [new file with mode: 0644]
LayoutTests/js/regress/script-tests/math-random.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/dfg/DFGAbstractHeap.h
Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGClobberize.h
Source/JavaScriptCore/dfg/DFGDoesGC.cpp
Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
Source/JavaScriptCore/dfg/DFGNodeType.h
Source/JavaScriptCore/dfg/DFGOperations.cpp
Source/JavaScriptCore/dfg/DFGOperations.h
Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
Source/JavaScriptCore/dfg/DFGSafeToExecute.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
Source/JavaScriptCore/ftl/FTLCapabilities.cpp
Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
Source/JavaScriptCore/jit/AssemblyHelpers.cpp
Source/JavaScriptCore/jit/AssemblyHelpers.h
Source/JavaScriptCore/jit/JITOperations.h
Source/JavaScriptCore/jit/ThunkGenerators.cpp
Source/JavaScriptCore/jit/ThunkGenerators.h
Source/JavaScriptCore/runtime/Intrinsic.h
Source/JavaScriptCore/runtime/JSGlobalObject.h
Source/JavaScriptCore/runtime/MathObject.cpp
Source/JavaScriptCore/runtime/VM.cpp
Source/JavaScriptCore/tests/stress/random-53bit.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/random-in-range.js [new file with mode: 0644]
Source/WTF/ChangeLog
Source/WTF/wtf/WeakRandom.h

index 5554683..cf68f94 100644 (file)
@@ -1,3 +1,17 @@
+2015-12-14  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        Math.random should have an intrinsic thunk and it should be later handled as a DFG Node
+        https://bugs.webkit.org/show_bug.cgi?id=152133
+
+        Reviewed by Geoffrey Garen.
+
+        Add new regression test.
+
+        * js/regress/math-random-expected.txt: Added.
+        * js/regress/math-random.html: Added.
+        * js/regress/script-tests/math-random.js: Added.
+        (test):
+
 2015-12-14  Joseph Pecoraro  <pecoraro@apple.com>
 
         Web Inspector: Stack traces in console incorrectly show "(anonymous function)" for global code
diff --git a/LayoutTests/js/regress/math-random-expected.txt b/LayoutTests/js/regress/math-random-expected.txt
new file mode 100644 (file)
index 0000000..1172d4b
--- /dev/null
@@ -0,0 +1,10 @@
+JSRegress/math-random
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/regress/math-random.html b/LayoutTests/js/regress/math-random.html
new file mode 100644 (file)
index 0000000..bed92f2
--- /dev/null
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<script src="../../resources/regress-pre.js"></script>
+<script src="script-tests/math-random.js"></script>
+<script src="../../resources/regress-post.js"></script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/js/regress/script-tests/math-random.js b/LayoutTests/js/regress/script-tests/math-random.js
new file mode 100644 (file)
index 0000000..5ac92df
--- /dev/null
@@ -0,0 +1,15 @@
+function test()
+{
+    var result = false;
+    for (var i = 0; i < 100; ++i) {
+        if (Math.random() < 0.5)
+            result = true;
+        if (Math.random() >= 0.5)
+            result = true;
+    }
+    return result;
+}
+noInline(test);
+
+for (var i = 0; i < 1e4; ++i)
+    test();
index dbdf2ed..536f6de 100644 (file)
@@ -1,3 +1,68 @@
+2015-12-14  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        Math.random should have an intrinsic thunk and it should be later handled as a DFG Node
+        https://bugs.webkit.org/show_bug.cgi?id=152133
+
+        Reviewed by Geoffrey Garen.
+
+        In this patch, we implement new RandomIntrinsic. It emits a machine code to generate random numbers efficiently.
+        And later it will be recognized by DFG and converted to ArithRandom node.
+        It provides type information SpecDoubleReal since Math.random only generates a number within [0, 1.0).
+
+        Currently, only 64bit version is supported. On 32bit environment, ArithRandom will be converted to callOperation.
+        While it emits a function call, ArithRandom node on 32bit still represents SpecDoubleReal as a result type.
+
+        * dfg/DFGAbstractHeap.h:
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::handleIntrinsicCall):
+        * dfg/DFGClobberize.h:
+        (JSC::DFG::clobberize):
+        * dfg/DFGDoesGC.cpp:
+        (JSC::DFG::doesGC):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        * dfg/DFGNodeType.h:
+        * dfg/DFGOperations.cpp:
+        * dfg/DFGOperations.h:
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        (JSC::DFG::PredictionPropagationPhase::propagate):
+        * dfg/DFGSafeToExecute.h:
+        (JSC::DFG::safeToExecute):
+        * dfg/DFGSpeculativeJIT.h:
+        (JSC::DFG::SpeculativeJIT::callOperation):
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        (JSC::DFG::SpeculativeJIT::compileArithRandom):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        (JSC::DFG::SpeculativeJIT::compileArithRandom):
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        * ftl/FTLLowerDFGToLLVM.cpp:
+        (JSC::FTL::DFG::LowerDFGToLLVM::compileNode):
+        (JSC::FTL::DFG::LowerDFGToLLVM::compileArithRandom):
+        * jit/AssemblyHelpers.cpp:
+        (JSC::emitRandomThunkImpl):
+        (JSC::AssemblyHelpers::emitRandomThunk):
+        * jit/AssemblyHelpers.h:
+        * jit/JITOperations.h:
+        * jit/ThunkGenerators.cpp:
+        (JSC::randomThunkGenerator):
+        * jit/ThunkGenerators.h:
+        * runtime/Intrinsic.h:
+        * runtime/JSGlobalObject.h:
+        (JSC::JSGlobalObject::weakRandomOffset):
+        * runtime/MathObject.cpp:
+        (JSC::MathObject::finishCreation):
+        * runtime/VM.cpp:
+        (JSC::thunkGeneratorForIntrinsic):
+        * tests/stress/random-53bit.js: Added.
+        (test):
+        * tests/stress/random-in-range.js: Added.
+        (test):
+
 2015-12-14  Benjamin Poulain  <benjamin@webkit.org>
 
         Rename FTL::Output's ceil64() to doubleCeil()
index 99cc75f..4dec8fa 100644 (file)
@@ -69,6 +69,7 @@ namespace JSC { namespace DFG {
     macro(TypedArrayProperties) \
     macro(HeapObjectCount) /* Used to reflect the fact that some allocations reveal object identity */\
     macro(RegExpState) \
+    macro(MathDotRandomState) \
     macro(InternalState) \
     macro(Absolute) \
     /* Use this for writes only, to indicate that this may fire watchpoints. Usually this is never directly written but instead we test to see if a node clobbers this; it just so happens that you have to write world to clobber it. */\
index dc57fdb..226eae3 100644 (file)
@@ -822,6 +822,11 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         break;
     }
 
+    case ArithRandom: {
+        forNode(node).setType(m_graph, SpecDoubleReal);
+        break;
+    }
+
     case ArithRound: {
         JSValue operand = forNode(node->child1()).value();
         if (operand && operand.isNumber()) {
index 00e91a4..66193be 100644 (file)
@@ -2214,6 +2214,14 @@ bool ByteCodeParser::handleIntrinsicCall(int resultOperand, Intrinsic intrinsic,
         set(VirtualRegister(resultOperand), addToGraph(ArithIMul, left, right));
         return true;
     }
+
+    case RandomIntrinsic: {
+        if (argumentCountIncludingThis != 1)
+            return false;
+        insertChecks();
+        set(VirtualRegister(resultOperand), addToGraph(ArithRandom));
+        return true;
+    }
         
     case FRoundIntrinsic: {
         if (argumentCountIncludingThis != 2)
index 29fe50c..fd60373 100644 (file)
@@ -163,6 +163,11 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
     case TypeOf:
         def(PureValue(node));
         return;
+
+    case ArithRandom:
+        read(MathDotRandomState);
+        write(MathDotRandomState);
+        return;
         
     case HasGenericProperty:
     case HasStructureProperty:
index 16f973d..db4fc21 100644 (file)
@@ -85,6 +85,7 @@ bool doesGC(Graph& graph, Node* node)
     case ArithMax:
     case ArithPow:
     case ArithSqrt:
+    case ArithRandom:
     case ArithRound:
     case ArithFRound:
     case ArithSin:
index cb85e09..9198cd8 100644 (file)
@@ -351,6 +351,11 @@ private:
             break;
         }
 
+        case ArithRandom: {
+            node->setResult(NodeResultDouble);
+            break;
+        }
+
         case ArithRound: {
             if (node->child1()->shouldSpeculateInt32OrBooleanForArithmetic() && node->canSpeculateInt32(FixupPass)) {
                 fixIntOrBooleanEdge(node->child1());
index 008d8c7..ac699e1 100644 (file)
@@ -152,6 +152,7 @@ namespace JSC { namespace DFG {
     macro(ArithMax, NodeResultNumber) \
     macro(ArithFRound, NodeResultNumber) \
     macro(ArithPow, NodeResultNumber) \
+    macro(ArithRandom, NodeResultDouble | NodeMustGenerate) \
     macro(ArithRound, NodeResultNumber) \
     macro(ArithSqrt, NodeResultNumber) \
     macro(ArithSin, NodeResultNumber) \
index f23772b..7bcd75d 100644 (file)
@@ -1228,6 +1228,13 @@ double JIT_OPERATION operationFModOnInts(int32_t a, int32_t b)
     return fmod(a, b);
 }
 
+#if USE(JSVALUE32_64)
+double JIT_OPERATION operationRandom(JSGlobalObject* globalObject)
+{
+    return globalObject->weakRandomNumber();
+}
+#endif
+
 JSCell* JIT_OPERATION operationStringFromCharCode(ExecState* exec, int32_t op1)
 {
     VM* vm = &exec->vm();
index 13aac7d..c25da06 100644 (file)
@@ -150,6 +150,10 @@ void JIT_OPERATION debugOperationPrintSpeculationFailure(ExecState*, void*, void
 
 void JIT_OPERATION triggerReoptimizationNow(CodeBlock*, OSRExitBase*) WTF_INTERNAL;
 
+#if USE(JSVALUE32_64)
+double JIT_OPERATION operationRandom(JSGlobalObject*);
+#endif
+
 #if ENABLE(FTL_JIT)
 void JIT_OPERATION triggerTierUpNow(ExecState*) WTF_INTERNAL;
 void JIT_OPERATION triggerTierUpNowInLoop(ExecState*) WTF_INTERNAL;
index a34d446..413bf89 100644 (file)
@@ -377,6 +377,11 @@ private:
             break;
         }
 
+        case ArithRandom: {
+            changed |= setPrediction(SpecDoubleReal);
+            break;
+        }
+
         case ArithRound: {
             if (isInt32OrBooleanSpeculation(node->getHeapPrediction()) && m_graph.roundShouldSpeculateInt32(node, m_pass))
                 changed |= setPrediction(SpecInt32);
index 616c3b0..0f456c9 100644 (file)
@@ -177,6 +177,7 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node)
     case ArithMin:
     case ArithMax:
     case ArithPow:
+    case ArithRandom:
     case ArithSqrt:
     case ArithFRound:
     case ArithRound:
index 939aca1..1f6d3ca 100755 (executable)
@@ -1550,6 +1550,11 @@ public:
 #define SH4_32BIT_DUMMY_ARG
 #endif
 
+    JITCompiler::Call callOperation(D_JITOperation_G operation, FPRReg result, JSGlobalObject* globalObject)
+    {
+        m_jit.setupArguments(TrustedImmPtr(globalObject));
+        return appendCallSetResult(operation, result);
+    }
     JITCompiler::Call callOperation(Z_JITOperation_D operation, GPRReg result, FPRReg arg1)
     {
         prepareForExternalCall();
@@ -2225,6 +2230,7 @@ public:
     void compileArithMod(Node*);
     void compileArithPow(Node*);
     void compileArithRound(Node*);
+    void compileArithRandom(Node*);
     void compileArithSqrt(Node*);
     void compileArithLog(Node*);
     void compileConstantStoragePointer(Node*);
index 3b7197d..2f1d76f 100644 (file)
@@ -2287,6 +2287,10 @@ void SpeculativeJIT::compile(Node* node)
         break;
     }
 
+    case ArithRandom:
+        compileArithRandom(node);
+        break;
+
     case ArithRound:
         compileArithRound(node);
         break;
@@ -4846,6 +4850,18 @@ void SpeculativeJIT::blessBoolean(GPRReg)
 {
 }
 
+void SpeculativeJIT::compileArithRandom(Node* node)
+{
+    JSGlobalObject* globalObject = m_jit.graph().globalObjectFor(node->origin.semantic);
+
+    flushRegisters();
+
+    FPRResult result(this);
+    callOperation(operationRandom, result.fpr(), globalObject);
+    // operationRandom does not raise any exception.
+    doubleResult(result.fpr(), node);
+}
+
 #endif
 
 } } // namespace JSC::DFG
index b987cd4..b491161 100644 (file)
@@ -2409,6 +2409,10 @@ void SpeculativeJIT::compile(Node* node)
         break;
     }
 
+    case ArithRandom:
+        compileArithRandom(node);
+        break;
+
     case ArithRound:
         compileArithRound(node);
         break;
@@ -4952,6 +4956,17 @@ void SpeculativeJIT::speculateDoubleRepMachineInt(Edge edge)
             JITCompiler::TrustedImm64(JSValue::notInt52)));
 }
 
+void SpeculativeJIT::compileArithRandom(Node* node)
+{
+    JSGlobalObject* globalObject = m_jit.graph().globalObjectFor(node->origin.semantic);
+    GPRTemporary temp1(this);
+    GPRTemporary temp2(this);
+    GPRTemporary temp3(this);
+    FPRTemporary result(this);
+    m_jit.emitRandomThunk(globalObject, temp1.gpr(), temp2.gpr(), temp3.gpr(), result.fpr());
+    doubleResult(result.fpr(), node);
+}
+
 #endif
 
 } } // namespace JSC::DFG
index 8728d9e..5c3b81c 100644 (file)
@@ -94,6 +94,7 @@ inline CapabilityLevel canCompile(Node* node)
     case ArithSin:
     case ArithCos:
     case ArithPow:
+    case ArithRandom:
     case ArithRound:
     case ArithSqrt:
     case ArithLog:
index 144e3ef..e23be70 100644 (file)
@@ -625,6 +625,9 @@ private:
         case ArithPow:
             compileArithPow();
             break;
+        case ArithRandom:
+            compileArithRandom();
+            break;
         case ArithRound:
             compileArithRound();
             break;
@@ -2119,6 +2122,53 @@ private:
         }
     }
 
+    void compileArithRandom()
+    {
+        JSGlobalObject* globalObject = m_graph.globalObjectFor(m_node->origin.semantic);
+
+        // Inlined WeakRandom::advance().
+        // uint64_t x = m_low;
+        void* lowAddress = reinterpret_cast<uint8_t*>(globalObject) + JSGlobalObject::weakRandomOffset() + WeakRandom::lowOffset();
+        LValue low = m_out.load64(m_out.absolute(lowAddress));
+        // uint64_t y = m_high;
+        void* highAddress = reinterpret_cast<uint8_t*>(globalObject) + JSGlobalObject::weakRandomOffset() + WeakRandom::highOffset();
+        LValue high = m_out.load64(m_out.absolute(highAddress));
+        // m_low = y;
+        m_out.store64(high, m_out.absolute(lowAddress));
+
+        // x ^= x << 23;
+        LValue phase1 = m_out.bitXor(m_out.shl(low, m_out.constInt64(23)), low);
+
+        // x ^= x >> 17;
+        LValue phase2 = m_out.bitXor(m_out.lShr(phase1, m_out.constInt64(17)), phase1);
+
+        // x ^= y ^ (y >> 26);
+        LValue phase3 = m_out.bitXor(m_out.bitXor(high, m_out.lShr(high, m_out.constInt64(26))), phase2);
+
+        // m_high = x;
+        m_out.store64(phase3, m_out.absolute(highAddress));
+
+        // return x + y;
+        LValue random64 = m_out.add(phase3, high);
+
+        // Extract random 53bit. [0, 53] bit is safe integer number ranges in double representation.
+        LValue random53 = m_out.bitAnd(random64, m_out.constInt64((1ULL << 53) - 1));
+
+        LValue double53Integer = m_out.intToDouble(random53);
+
+        // Convert `(53bit double integer value) / (1 << 53)` to `(53bit double integer value) * (1.0 / (1 << 53))`.
+        // In latter case, `1.0 / (1 << 53)` will become a double value represented as (mantissa = 0 & exp = 970, it means 1e-(2**54)).
+        static const double scale = 1.0 / (1ULL << 53);
+
+        // Multiplying 1e-(2**54) with the double integer does not change anything of the mantissa part of the double integer.
+        // It just reduces the exp part of the given 53bit double integer.
+        // (Except for 0.0. This is specially handled and in this case, exp just becomes 0.)
+        // Now we get 53bit precision random double value in [0, 1).
+        LValue result = m_out.doubleMul(double53Integer, m_out.constDouble(scale));
+
+        setDouble(result);
+    }
+
     void compileArithRound()
     {
         LBasicBlock realPartIsMoreThanHalf = FTL_NEW_BLOCK(m_out, ("ArithRound should round down"));
index f26f2f5..cf71c4c 100644 (file)
@@ -420,6 +420,103 @@ void AssemblyHelpers::emitStoreStructureWithTypeInfo(AssemblyHelpers& jit, Trust
 #endif
 }
 
+#if USE(JSVALUE64)
+template<typename LoadFromHigh, typename StoreToHigh, typename LoadFromLow, typename StoreToLow>
+void emitRandomThunkImpl(AssemblyHelpers& jit, GPRReg scratch0, GPRReg scratch1, GPRReg scratch2, FPRReg result, const LoadFromHigh& loadFromHigh, const StoreToHigh& storeToHigh, const LoadFromLow& loadFromLow, const StoreToLow& storeToLow)
+{
+    // Inlined WeakRandom::advance().
+    // uint64_t x = m_low;
+    loadFromLow(scratch0);
+    // uint64_t y = m_high;
+    loadFromHigh(scratch1);
+    // m_low = y;
+    storeToLow(scratch1);
+
+    // x ^= x << 23;
+    jit.move(scratch0, scratch2);
+    jit.lshift64(AssemblyHelpers::TrustedImm32(23), scratch2);
+    jit.xor64(scratch2, scratch0);
+
+    // x ^= x >> 17;
+    jit.move(scratch0, scratch2);
+    jit.rshift64(AssemblyHelpers::TrustedImm32(17), scratch2);
+    jit.xor64(scratch2, scratch0);
+
+    // x ^= y ^ (y >> 26);
+    jit.move(scratch1, scratch2);
+    jit.rshift64(AssemblyHelpers::TrustedImm32(26), scratch2);
+    jit.xor64(scratch1, scratch2);
+    jit.xor64(scratch2, scratch0);
+
+    // m_high = x;
+    storeToHigh(scratch0);
+
+    // return x + y;
+    jit.add64(scratch1, scratch0);
+
+    // Extract random 53bit. [0, 53] bit is safe integer number ranges in double representation.
+    jit.move(AssemblyHelpers::TrustedImm64((1ULL << 53) - 1), scratch1);
+    jit.and64(scratch1, scratch0);
+    // Now, scratch0 is always in range of int64_t. Safe to convert it to double with cvtsi2sdq.
+    jit.convertInt64ToDouble(scratch0, result);
+
+    // Convert `(53bit double integer value) / (1 << 53)` to `(53bit double integer value) * (1.0 / (1 << 53))`.
+    // In latter case, `1.0 / (1 << 53)` will become a double value represented as (mantissa = 0 & exp = 970, it means 1e-(2**54)).
+    static const double scale = 1.0 / (1ULL << 53);
+
+    // Multiplying 1e-(2**54) with the double integer does not change anything of the mantissa part of the double integer.
+    // It just reduces the exp part of the given 53bit double integer.
+    // (Except for 0.0. This is specially handled and in this case, exp just becomes 0.)
+    // Now we get 53bit precision random double value in [0, 1).
+    jit.move(AssemblyHelpers::TrustedImmPtr(&scale), scratch1);
+    jit.mulDouble(AssemblyHelpers::Address(scratch1), result);
+}
+
+void AssemblyHelpers::emitRandomThunk(JSGlobalObject* globalObject, GPRReg scratch0, GPRReg scratch1, GPRReg scratch2, FPRReg result)
+{
+    void* lowAddress = reinterpret_cast<uint8_t*>(globalObject) + JSGlobalObject::weakRandomOffset() + WeakRandom::lowOffset();
+    void* highAddress = reinterpret_cast<uint8_t*>(globalObject) + JSGlobalObject::weakRandomOffset() + WeakRandom::highOffset();
+
+    auto loadFromHigh = [&](GPRReg high) {
+        load64(highAddress, high);
+    };
+    auto storeToHigh = [&](GPRReg high) {
+        store64(high, highAddress);
+    };
+    auto loadFromLow = [&](GPRReg low) {
+        load64(lowAddress, low);
+    };
+    auto storeToLow = [&](GPRReg low) {
+        store64(low, lowAddress);
+    };
+
+    emitRandomThunkImpl(*this, scratch0, scratch1, scratch2, result, loadFromHigh, storeToHigh, loadFromLow, storeToLow);
+}
+
+void AssemblyHelpers::emitRandomThunk(GPRReg scratch0, GPRReg scratch1, GPRReg scratch2, GPRReg scratch3, FPRReg result)
+{
+    emitGetFromCallFrameHeaderPtr(JSStack::Callee, scratch3);
+    emitLoadStructure(scratch3, scratch3, scratch0);
+    loadPtr(Address(scratch3, Structure::globalObjectOffset()), scratch3);
+    // Now, scratch3 holds JSGlobalObject*.
+
+    auto loadFromHigh = [&](GPRReg high) {
+        load64(Address(scratch3, JSGlobalObject::weakRandomOffset() + WeakRandom::highOffset()), high);
+    };
+    auto storeToHigh = [&](GPRReg high) {
+        store64(high, Address(scratch3, JSGlobalObject::weakRandomOffset() + WeakRandom::highOffset()));
+    };
+    auto loadFromLow = [&](GPRReg low) {
+        load64(Address(scratch3, JSGlobalObject::weakRandomOffset() + WeakRandom::lowOffset()), low);
+    };
+    auto storeToLow = [&](GPRReg low) {
+        store64(low, Address(scratch3, JSGlobalObject::weakRandomOffset() + WeakRandom::lowOffset()));
+    };
+
+    emitRandomThunkImpl(*this, scratch0, scratch1, scratch2, result, loadFromHigh, storeToHigh, loadFromLow, storeToLow);
+}
+#endif
+
 } // namespace JSC
 
 #endif // ENABLE(JIT)
index 2b44cd9..b2519ec 100644 (file)
@@ -1371,6 +1371,11 @@ public:
         if (stackOffset)
             addPtr(TrustedImm32(stackOffset), stackPointerRegister);
     }
+
+#if USE(JSVALUE64)
+    void emitRandomThunk(JSGlobalObject*, GPRReg scratch0, GPRReg scratch1, GPRReg scratch2, FPRReg result);
+    void emitRandomThunk(GPRReg scratch0, GPRReg scratch1, GPRReg scratch2, GPRReg scratch3, FPRReg result);
+#endif
     
 protected:
     VM* m_vm;
index 2542550..d44570b 100644 (file)
@@ -164,6 +164,7 @@ typedef JSCell* JIT_OPERATION (*C_JITOperation_EStZ)(ExecState*, Structure*, int
 typedef JSCell* JIT_OPERATION (*C_JITOperation_EStZZ)(ExecState*, Structure*, int32_t, int32_t);
 typedef JSCell* JIT_OPERATION (*C_JITOperation_EZ)(ExecState*, int32_t);
 typedef double JIT_OPERATION (*D_JITOperation_D)(double);
+typedef double JIT_OPERATION (*D_JITOperation_G)(JSGlobalObject*);
 typedef double JIT_OPERATION (*D_JITOperation_DD)(double, double);
 typedef double JIT_OPERATION (*D_JITOperation_ZZ)(int32_t, int32_t);
 typedef double JIT_OPERATION (*D_JITOperation_EJ)(ExecState*, EncodedJSValue);
index 519ffe3..7f5469a 100644 (file)
@@ -1004,6 +1004,22 @@ MacroAssemblerCodeRef imulThunkGenerator(VM* vm)
     return jit.finalize(vm->jitStubs->ctiNativeTailCall(vm), "imul");
 }
 
+MacroAssemblerCodeRef randomThunkGenerator(VM* vm)
+{
+    SpecializedThunkJIT jit(vm, 0);
+    if (!jit.supportsFloatingPoint())
+        return MacroAssemblerCodeRef::createSelfManagedCodeRef(vm->jitStubs->ctiNativeCall(vm));
+
+#if USE(JSVALUE64)
+    jit.emitRandomThunk(SpecializedThunkJIT::regT0, SpecializedThunkJIT::regT1, SpecializedThunkJIT::regT2, SpecializedThunkJIT::regT3, SpecializedThunkJIT::fpRegT0);
+    jit.returnDouble(SpecializedThunkJIT::fpRegT0);
+
+    return jit.finalize(vm->jitStubs->ctiNativeTailCall(vm), "random");
+#else
+    return MacroAssemblerCodeRef::createSelfManagedCodeRef(vm->jitStubs->ctiNativeCall(vm));
+#endif
+}
+
 }
 
 #endif // ENABLE(JIT)
index ad4cddd..9fb8abb 100644 (file)
@@ -62,6 +62,7 @@ MacroAssemblerCodeRef roundThunkGenerator(VM*);
 MacroAssemblerCodeRef sqrtThunkGenerator(VM*);
 MacroAssemblerCodeRef powThunkGenerator(VM*);
 MacroAssemblerCodeRef imulThunkGenerator(VM*);
+MacroAssemblerCodeRef randomThunkGenerator(VM*);
 
 }
 #endif // ENABLE(JIT)
index d4befb6..d6ccc1b 100644 (file)
@@ -53,6 +53,7 @@ enum Intrinsic {
     RegExpTestIntrinsic,
     StringPrototypeValueOfIntrinsic,
     IMulIntrinsic,
+    RandomIntrinsic,
     FRoundIntrinsic,
 
     // Getter intrinsics.
index 62d1495..1f01455 100644 (file)
@@ -665,6 +665,7 @@ public:
 
     TemplateRegistry& templateRegistry() { return m_templateRegistry; }
 
+    static ptrdiff_t weakRandomOffset() { return OBJECT_OFFSETOF(JSGlobalObject, m_weakRandom); }
     double weakRandomNumber() { return m_weakRandom.get(); }
     unsigned weakRandomInteger() { return m_weakRandom.getUint32(); }
 
index 1a3be23..997b064 100644 (file)
@@ -121,7 +121,7 @@ void MathObject::finishCreation(VM& vm, JSGlobalObject* globalObject)
     putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "max"), 2, mathProtoFuncMax, MaxIntrinsic, DontEnum);
     putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "min"), 2, mathProtoFuncMin, MinIntrinsic, DontEnum);
     putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "pow"), 2, mathProtoFuncPow, PowIntrinsic, DontEnum);
-    putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "random"), 0, mathProtoFuncRandom, NoIntrinsic, DontEnum);
+    putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "random"), 0, mathProtoFuncRandom, RandomIntrinsic, DontEnum);
     putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "round"), 1, mathProtoFuncRound, RoundIntrinsic, DontEnum);
     putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "sign"), 1, mathProtoFuncSign, NoIntrinsic, DontEnum);
     putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(&vm, "sin"), 1, mathProtoFuncSin, SinIntrinsic, DontEnum);
index bf567c1..56e483c 100644 (file)
@@ -448,6 +448,8 @@ static ThunkGenerator thunkGeneratorForIntrinsic(Intrinsic intrinsic)
         return logThunkGenerator;
     case IMulIntrinsic:
         return imulThunkGenerator;
+    case RandomIntrinsic:
+        return randomThunkGenerator;
     default:
         return 0;
     }
diff --git a/Source/JavaScriptCore/tests/stress/random-53bit.js b/Source/JavaScriptCore/tests/stress/random-53bit.js
new file mode 100644 (file)
index 0000000..5f3e40f
--- /dev/null
@@ -0,0 +1,24 @@
+function test() {
+    var MAX = 30;
+    var found53Bit = false;
+    var foundLessThan53Bit = false;
+
+    for (var i = 0; i < MAX; ++i) {
+        var str = Math.random().toString(2);
+        // 53 bit + '0.'.length
+        if (str.length === (53 + 2))
+            found53Bit = true;
+        else if (str.length < (53 + 2))
+            foundLessThan53Bit = true;
+
+        if (found53Bit && foundLessThan53Bit)
+            return true;
+    }
+    return false;
+}
+noInline(test);
+
+for (var i = 0; i < 1e4; ++i) {
+    if (!test())
+        throw new Error("OUT");
+}
diff --git a/Source/JavaScriptCore/tests/stress/random-in-range.js b/Source/JavaScriptCore/tests/stress/random-in-range.js
new file mode 100644 (file)
index 0000000..9ec0d9f
--- /dev/null
@@ -0,0 +1,14 @@
+function test() {
+    var value = Math.random();
+    if (value >= 1.0)
+        return false;
+    if (value < 0)
+        return false;
+    return true;
+}
+noInline(test);
+
+for (var i = 0; i < 1e4; ++i) {
+    if (!test())
+        throw new Error("OUT");
+}
index f2707dc..5920cc3 100644 (file)
@@ -1,3 +1,17 @@
+2015-12-14  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        Math.random should have an intrinsic thunk and it should be later handled as a DFG Node
+        https://bugs.webkit.org/show_bug.cgi?id=152133
+
+        Reviewed by Geoffrey Garen.
+
+        Change 64bit random to double logic to convert efficiently.
+
+        * wtf/WeakRandom.h:
+        (WTF::WeakRandom::get):
+        (WTF::WeakRandom::lowOffset):
+        (WTF::WeakRandom::highOffset):
+
 2015-12-14  Sukolsak Sakshuwong  <sukolsak@gmail.com>
 
         Make UCharIterator createIterator(StringView) visible to other classes
index a34ec25..9c0f589 100644 (file)
@@ -37,6 +37,8 @@
 
 namespace WTF {
 
+// The code used to generate random numbers are inlined manually in JIT code.
+// So it needs to stay in sync with the JIT one.
 class WeakRandom {
 public:
     WeakRandom(unsigned seed = cryptographicallyRandomNumber())
@@ -60,7 +62,8 @@ public:
 
     double get()
     {
-        return advance() / (std::numeric_limits<uint64_t>::max() + 1.0);
+        uint64_t value = advance() & ((1ULL << 53) - 1);
+        return value * (1.0 / (1ULL << 53));
     }
 
     unsigned getUint32()
@@ -81,6 +84,9 @@ public:
         }
     }
 
+    static unsigned lowOffset() { return OBJECT_OFFSETOF(WeakRandom, m_low); }
+    static unsigned highOffset() { return OBJECT_OFFSETOF(WeakRandom, m_high); }
+
 private:
     uint64_t advance()
     {