Skip to content
Permalink
Browse files
Math.random should have an intrinsic thunk and it should be later han…
…dled as a DFG Node

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):

Canonical link: https://commits.webkit.org/170398@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@194087 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
Constellation committed Dec 15, 2015
1 parent bd0c02d commit cf5791cf759cc7b4f87655d6c741b7b0fac56bca
Showing with 431 additions and 2 deletions.
  1. +14 −0 LayoutTests/ChangeLog
  2. +10 −0 LayoutTests/js/regress/math-random-expected.txt
  3. +12 −0 LayoutTests/js/regress/math-random.html
  4. +15 −0 LayoutTests/js/regress/script-tests/math-random.js
  5. +65 −0 Source/JavaScriptCore/ChangeLog
  6. +1 −0 Source/JavaScriptCore/dfg/DFGAbstractHeap.h
  7. +5 −0 Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
  8. +8 −0 Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
  9. +5 −0 Source/JavaScriptCore/dfg/DFGClobberize.h
  10. +1 −0 Source/JavaScriptCore/dfg/DFGDoesGC.cpp
  11. +5 −0 Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
  12. +1 −0 Source/JavaScriptCore/dfg/DFGNodeType.h
  13. +7 −0 Source/JavaScriptCore/dfg/DFGOperations.cpp
  14. +4 −0 Source/JavaScriptCore/dfg/DFGOperations.h
  15. +5 −0 Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
  16. +1 −0 Source/JavaScriptCore/dfg/DFGSafeToExecute.h
  17. +6 −0 Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
  18. +16 −0 Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
  19. +15 −0 Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
  20. +1 −0 Source/JavaScriptCore/ftl/FTLCapabilities.cpp
  21. +50 −0 Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
  22. +97 −0 Source/JavaScriptCore/jit/AssemblyHelpers.cpp
  23. +5 −0 Source/JavaScriptCore/jit/AssemblyHelpers.h
  24. +1 −0 Source/JavaScriptCore/jit/JITOperations.h
  25. +16 −0 Source/JavaScriptCore/jit/ThunkGenerators.cpp
  26. +1 −0 Source/JavaScriptCore/jit/ThunkGenerators.h
  27. +1 −0 Source/JavaScriptCore/runtime/Intrinsic.h
  28. +1 −0 Source/JavaScriptCore/runtime/JSGlobalObject.h
  29. +1 −1 Source/JavaScriptCore/runtime/MathObject.cpp
  30. +2 −0 Source/JavaScriptCore/runtime/VM.cpp
  31. +24 −0 Source/JavaScriptCore/tests/stress/random-53bit.js
  32. +14 −0 Source/JavaScriptCore/tests/stress/random-in-range.js
  33. +14 −0 Source/WTF/ChangeLog
  34. +7 −1 Source/WTF/wtf/WeakRandom.h
@@ -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
@@ -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

@@ -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>
@@ -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();
@@ -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()
@@ -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. */\
@@ -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()) {
@@ -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)
@@ -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:
@@ -85,6 +85,7 @@ bool doesGC(Graph& graph, Node* node)
case ArithMax:
case ArithPow:
case ArithSqrt:
case ArithRandom:
case ArithRound:
case ArithFRound:
case ArithSin:
@@ -351,6 +351,11 @@ class FixupPhase : public Phase {
break;
}

case ArithRandom: {
node->setResult(NodeResultDouble);
break;
}

case ArithRound: {
if (node->child1()->shouldSpeculateInt32OrBooleanForArithmetic() && node->canSpeculateInt32(FixupPass)) {
fixIntOrBooleanEdge(node->child1());
@@ -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) \
@@ -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();
@@ -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;
@@ -377,6 +377,11 @@ class PredictionPropagationPhase : public Phase {
break;
}

case ArithRandom: {
changed |= setPrediction(SpecDoubleReal);
break;
}

case ArithRound: {
if (isInt32OrBooleanSpeculation(node->getHeapPrediction()) && m_graph.roundShouldSpeculateInt32(node, m_pass))
changed |= setPrediction(SpecInt32);
@@ -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:
@@ -1550,6 +1550,11 @@ class SpeculativeJIT {
#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 @@ class SpeculativeJIT {
void compileArithMod(Node*);
void compileArithPow(Node*);
void compileArithRound(Node*);
void compileArithRandom(Node*);
void compileArithSqrt(Node*);
void compileArithLog(Node*);
void compileConstantStoragePointer(Node*);
@@ -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
@@ -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
@@ -94,6 +94,7 @@ inline CapabilityLevel canCompile(Node* node)
case ArithSin:
case ArithCos:
case ArithPow:
case ArithRandom:
case ArithRound:
case ArithSqrt:
case ArithLog:
@@ -625,6 +625,9 @@ class LowerDFGToLLVM {
case ArithPow:
compileArithPow();
break;
case ArithRandom:
compileArithRandom();
break;
case ArithRound:
compileArithRound();
break;
@@ -2119,6 +2122,53 @@ class LowerDFGToLLVM {
}
}

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"));

0 comments on commit cf5791c

Please sign in to comment.