Latest emscripten life benchmark is 4x slower because the DFG doesn't realize that arithmetic on booleans is a thing
https://bugs.webkit.org/show_bug.cgi?id=133136
Reviewed by Oliver Hunt.
Source/JavaScriptCore:
Some key concepts:
- Except for the prediction propagation and type fixup phases, which are super early in
the pipeline, nobody has to know about the fact that booleans may flow into numerical
operations because there will just be a BooleanToNumber node that will take a value
and, if that value is a boolean, will convert it to the equivalent numerical value. It
will have a BooleanUse mode where it will also speculate that the input is a boolean
but it can also do UntypedUse in which case it will pass through any non-booleans.
This operation is very easy to model in all of the compiler tiers.
- No changes to the baseline JIT. The Baseline JIT will still believe that boolean
inputs require taking the slow path and it will still report that it took slow path
for any such operations. The DFG will now be smart enough to ignore baseline JIT slow
path profiling on operations that were known to have had boolean inputs. That's a
little quirky, but it's probably easier than modifying the baseline JIT to track
booleans correctly.
4.1x speed-up on the emscripten "life" benchmark. Up to 10x speed-up on microbenchmarks.
* bytecode/SpeculatedType.h:
(JSC::isInt32OrBooleanSpeculation):
(JSC::isInt32SpeculationForArithmetic):
(JSC::isInt32OrBooleanSpeculationForArithmetic):
(JSC::isInt32OrBooleanSpeculationExpectingDefined):
(JSC::isInt52Speculation):
(JSC::isMachineIntSpeculation):
(JSC::isFullNumberOrBooleanSpeculation):
(JSC::isFullNumberOrBooleanSpeculationExpectingDefined):
(JSC::isInt32SpeculationExpectingDefined): Deleted.
(JSC::isMachineIntSpeculationExpectingDefined): Deleted.
(JSC::isMachineIntSpeculationForArithmetic): Deleted.
(JSC::isBytecodeNumberSpeculationExpectingDefined): Deleted.
(JSC::isFullNumberSpeculationExpectingDefined): Deleted.
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGAllocator.h:
(JSC::DFG::Allocator<T>::indexOf):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::makeSafe):
(JSC::DFG::ByteCodeParser::makeDivSafe):
(JSC::DFG::ByteCodeParser::handleIntrinsic):
* dfg/DFGCSEPhase.cpp:
(JSC::DFG::CSEPhase::performNodeCSE):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGCommon.h:
* dfg/DFGConstantFoldingPhase.cpp:
(JSC::DFG::ConstantFoldingPhase::foldConstants):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
(JSC::DFG::FixupPhase::fixIntConvertingEdge):
(JSC::DFG::FixupPhase::fixIntOrBooleanEdge):
(JSC::DFG::FixupPhase::fixDoubleOrBooleanEdge):
(JSC::DFG::FixupPhase::attemptToMakeIntegerAdd):
(JSC::DFG::FixupPhase::fixIntEdge): Deleted.
* dfg/DFGGraph.h:
(JSC::DFG::Graph::addSpeculationMode):
(JSC::DFG::Graph::valueAddSpeculationMode):
(JSC::DFG::Graph::arithAddSpeculationMode):
(JSC::DFG::Graph::addShouldSpeculateInt32):
(JSC::DFG::Graph::mulShouldSpeculateInt32):
(JSC::DFG::Graph::mulShouldSpeculateMachineInt):
(JSC::DFG::Graph::negateShouldSpeculateInt32):
(JSC::DFG::Graph::negateShouldSpeculateMachineInt):
(JSC::DFG::Graph::addImmediateShouldSpeculateInt32):
(JSC::DFG::Graph::mulImmediateShouldSpeculateInt32): Deleted.
* dfg/DFGNode.h:
(JSC::DFG::Node::sawBooleans):
(JSC::DFG::Node::shouldSpeculateInt32OrBoolean):
(JSC::DFG::Node::shouldSpeculateInt32ForArithmetic):
(JSC::DFG::Node::shouldSpeculateInt32OrBooleanForArithmetic):
(JSC::DFG::Node::shouldSpeculateInt32OrBooleanExpectingDefined):
(JSC::DFG::Node::shouldSpeculateMachineInt):
(JSC::DFG::Node::shouldSpeculateDouble):
(JSC::DFG::Node::shouldSpeculateNumberOrBoolean):
(JSC::DFG::Node::shouldSpeculateNumberOrBooleanExpectingDefined):
(JSC::DFG::Node::shouldSpeculateNumber):
(JSC::DFG::Node::canSpeculateInt32):
(JSC::DFG::Node::canSpeculateInt52):
(JSC::DFG::Node::sourceFor):
(JSC::DFG::Node::shouldSpeculateInt32ExpectingDefined): Deleted.
(JSC::DFG::Node::shouldSpeculateMachineIntForArithmetic): Deleted.
(JSC::DFG::Node::shouldSpeculateMachineIntExpectingDefined): Deleted.
(JSC::DFG::Node::shouldSpeculateDoubleForArithmetic): Deleted.
(JSC::DFG::Node::shouldSpeculateNumberExpectingDefined): Deleted.
* dfg/DFGNodeFlags.cpp:
(JSC::DFG::dumpNodeFlags):
* dfg/DFGNodeFlags.h:
(JSC::DFG::nodeMayOverflow):
(JSC::DFG::nodeMayNegZero):
(JSC::DFG::nodeCanSpeculateInt32):
(JSC::DFG::nodeCanSpeculateInt52):
* dfg/DFGNodeType.h:
* dfg/DFGPredictionPropagationPhase.cpp:
(JSC::DFG::PredictionPropagationPhase::run):
(JSC::DFG::PredictionPropagationPhase::propagateToFixpoint):
(JSC::DFG::PredictionPropagationPhase::speculatedDoubleTypeForPrediction):
(JSC::DFG::PredictionPropagationPhase::propagate):
(JSC::DFG::PredictionPropagationPhase::doDoubleVoting):
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileValueToInt32):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToLLVM.cpp:
(JSC::FTL::LowerDFGToLLVM::compileNode):
(JSC::FTL::LowerDFGToLLVM::compileValueToInt32):
(JSC::FTL::LowerDFGToLLVM::compileBooleanToNumber):
* runtime/JSCJSValue.h:
* runtime/JSCJSValueInlines.h:
(JSC::JSValue::asInt32ForArithmetic):
* tests/stress/max-boolean-exit.js: Added.
(foo):
(test):
* tests/stress/mul-boolean-exit.js: Added.
(foo):
(test):
* tests/stress/plus-boolean-exit.js: Added.
(foo):
(test):
* tests/stress/plus-boolean-or-double.js: Added.
(foo):
(test):
* tests/stress/plus-boolean-or-int.js: Added.
(foo):
(test):
LayoutTests:
* js/regress/abs-boolean-expected.txt: Added.
* js/regress/abs-boolean.html: Added.
* js/regress/div-boolean-double-expected.txt: Added.
* js/regress/div-boolean-double.html: Added.
* js/regress/div-boolean-expected.txt: Added.
* js/regress/div-boolean.html: Added.
* js/regress/max-boolean-expected.txt: Added.
* js/regress/max-boolean.html: Added.
* js/regress/min-boolean-expected.txt: Added.
* js/regress/min-boolean.html: Added.
* js/regress/minus-boolean-double-expected.txt: Added.
* js/regress/minus-boolean-double.html: Added.
* js/regress/minus-boolean-expected.txt: Added.
* js/regress/minus-boolean.html: Added.
* js/regress/mod-boolean-double-expected.txt: Added.
* js/regress/mod-boolean-double.html: Added.
* js/regress/mod-boolean-expected.txt: Added.
* js/regress/mod-boolean.html: Added.
* js/regress/mul-boolean-double-expected.txt: Added.
* js/regress/mul-boolean-double.html: Added.
* js/regress/mul-boolean-expected.txt: Added.
* js/regress/mul-boolean.html: Added.
* js/regress/neg-boolean-expected.txt: Added.
* js/regress/neg-boolean.html: Added.
* js/regress/plus-boolean-arith-expected.txt: Added.
* js/regress/plus-boolean-arith.html: Added.
* js/regress/plus-boolean-double-expected.txt: Added.
* js/regress/plus-boolean-double.html: Added.
* js/regress/plus-boolean-expected.txt: Added.
* js/regress/plus-boolean.html: Added.
* js/regress/script-tests/abs-boolean.js: Added.
* js/regress/script-tests/div-boolean-double.js: Added.
* js/regress/script-tests/div-boolean.js: Added.
* js/regress/script-tests/max-boolean.js: Added.
* js/regress/script-tests/min-boolean.js: Added.
* js/regress/script-tests/minus-boolean-double.js: Added.
* js/regress/script-tests/minus-boolean.js: Added.
* js/regress/script-tests/mod-boolean-double.js: Added.
* js/regress/script-tests/mod-boolean.js: Added.
* js/regress/script-tests/mul-boolean-double.js: Added.
* js/regress/script-tests/mul-boolean.js: Added.
* js/regress/script-tests/neg-boolean.js: Added.
* js/regress/script-tests/plus-boolean-arith.js: Added.
* js/regress/script-tests/plus-boolean-double.js: Added.
* js/regress/script-tests/plus-boolean.js: Added.
* js/regress/script-tests/sin-boolean.js: Added.
* js/regress/sin-boolean-expected.txt: Added.
* js/regress/sin-boolean.html: Added.
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@169354 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
index a8f404a..ef0945a 100644
--- a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
+++ b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
@@ -265,6 +265,26 @@
node->setCanExit(true);
break;
}
+
+ case BooleanToNumber: {
+ JSValue concreteValue = forNode(node->child1()).value();
+ if (concreteValue) {
+ if (concreteValue.isBoolean())
+ setConstant(node, jsNumber(concreteValue.asBoolean()));
+ else
+ setConstant(node, concreteValue);
+ break;
+ }
+ AbstractValue& value = forNode(node);
+ value = forNode(node->child1());
+ if (node->child1().useKind() == UntypedUse && !(value.m_type & ~SpecBoolean))
+ m_state.setFoundConstants(true);
+ if (value.m_type & SpecBoolean) {
+ value.merge(SpecInt32);
+ value.filter(~SpecBoolean);
+ }
+ break;
+ }
case DoubleAsInt32: {
JSValue child = forNode(node->child1()).value();
@@ -292,7 +312,7 @@
break;
}
if (child.isBoolean()) {
- setConstant(node, JSValue(child.asBoolean()));
+ setConstant(node, jsNumber(child.asBoolean()));
break;
}
if (child.isUndefinedOrNull()) {
diff --git a/Source/JavaScriptCore/dfg/DFGAllocator.h b/Source/JavaScriptCore/dfg/DFGAllocator.h
index 696a516..6e20fb3 100644
--- a/Source/JavaScriptCore/dfg/DFGAllocator.h
+++ b/Source/JavaScriptCore/dfg/DFGAllocator.h
@@ -153,11 +153,14 @@
template<typename T>
unsigned Allocator<T>::indexOf(const T* object)
{
- unsigned baseIndex = 0;
+ unsigned numRegions = 0;
+ for (Region* region = m_regionHead; region; region = region->m_next)
+ numRegions++;
+ unsigned regionIndex = 0;
for (Region* region = m_regionHead; region; region = region->m_next) {
if (region->isInThisRegion(object))
- return baseIndex + (object - region->data());
- baseIndex += Region::numberOfThingsPerRegion();
+ return (numRegions - 1 - regionIndex) * Region::numberOfThingsPerRegion() + (object - region->data());
+ regionIndex++;
}
CRASH();
return 0;
diff --git a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
index 416408e..ee5c5f5 100644
--- a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
+++ b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
@@ -899,15 +899,15 @@
Node* makeSafe(Node* node)
{
- bool likelyToTakeSlowCase;
- if (!isX86() && node->op() == ArithMod)
- likelyToTakeSlowCase = false;
- else
- likelyToTakeSlowCase = m_inlineStackTop->m_profiledBlock->likelyToTakeSlowCase(m_currentIndex);
+ if (m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, Overflow))
+ node->mergeFlags(NodeMayOverflowInDFG);
+ if (m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, NegativeZero))
+ node->mergeFlags(NodeMayNegZeroInDFG);
- if (!likelyToTakeSlowCase
- && !m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, Overflow)
- && !m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, NegativeZero))
+ if (!isX86() && node->op() == ArithMod)
+ return node;
+
+ if (!m_inlineStackTop->m_profiledBlock->likelyToTakeSlowCase(m_currentIndex))
return node;
switch (node->op()) {
@@ -916,15 +916,15 @@
case ArithSub:
case ValueAdd:
case ArithMod: // for ArithMod "MayOverflow" means we tried to divide by zero, or we saw double.
- node->mergeFlags(NodeMayOverflow);
+ node->mergeFlags(NodeMayOverflowInBaseline);
break;
case ArithNegate:
// Currently we can't tell the difference between a negation overflowing
// (i.e. -(1 << 31)) or generating negative zero (i.e. -0). If it took slow
// path then we assume that it did both of those things.
- node->mergeFlags(NodeMayOverflow);
- node->mergeFlags(NodeMayNegZero);
+ node->mergeFlags(NodeMayOverflowInBaseline);
+ node->mergeFlags(NodeMayNegZeroInBaseline);
break;
case ArithMul:
@@ -933,10 +933,10 @@
// https://bugs.webkit.org/show_bug.cgi?id=132470
if (m_inlineStackTop->m_profiledBlock->likelyToTakeDeepestSlowCase(m_currentIndex)
|| m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, Overflow))
- node->mergeFlags(NodeMayOverflow | NodeMayNegZero);
+ node->mergeFlags(NodeMayOverflowInBaseline | NodeMayNegZeroInBaseline);
else if (m_inlineStackTop->m_profiledBlock->likelyToTakeSlowCase(m_currentIndex)
|| m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, NegativeZero))
- node->mergeFlags(NodeMayNegZero);
+ node->mergeFlags(NodeMayNegZeroInBaseline);
break;
default:
@@ -951,20 +951,22 @@
{
ASSERT(node->op() == ArithDiv);
+ if (m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, Overflow))
+ node->mergeFlags(NodeMayOverflowInDFG);
+ if (m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, NegativeZero))
+ node->mergeFlags(NodeMayNegZeroInDFG);
+
// The main slow case counter for op_div in the old JIT counts only when
// the operands are not numbers. We don't care about that since we already
// have speculations in place that take care of that separately. We only
// care about when the outcome of the division is not an integer, which
// is what the special fast case counter tells us.
- if (!m_inlineStackTop->m_profiledBlock->couldTakeSpecialFastCase(m_currentIndex)
- && !m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, Overflow)
- && !m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, NegativeZero))
+ if (!m_inlineStackTop->m_profiledBlock->couldTakeSpecialFastCase(m_currentIndex))
return node;
- // FIXME: It might be possible to make this more granular. The DFG certainly can
- // distinguish between negative zero and overflow in its exit profiles.
- node->mergeFlags(NodeMayOverflow | NodeMayNegZero);
+ // FIXME: It might be possible to make this more granular.
+ node->mergeFlags(NodeMayOverflowInBaseline | NodeMayNegZeroInBaseline);
return node;
}
@@ -1559,7 +1561,7 @@
Node* node = addToGraph(ArithAbs, get(virtualRegisterForArgument(1, registerOffset)));
if (m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, Overflow))
- node->mergeFlags(NodeMayOverflow);
+ node->mergeFlags(NodeMayOverflowInDFG);
set(VirtualRegister(resultOperand), node);
return true;
}
diff --git a/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp b/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp
index 29f811c..0e2811f 100644
--- a/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp
@@ -1133,6 +1133,7 @@
case DoubleRep:
case ValueRep:
case Int52Rep:
+ case BooleanToNumber:
if (cseMode == StoreElimination)
break;
setReplacement(pureCSE(node));
diff --git a/Source/JavaScriptCore/dfg/DFGClobberize.h b/Source/JavaScriptCore/dfg/DFGClobberize.h
index f398a49..b60e15c 100644
--- a/Source/JavaScriptCore/dfg/DFGClobberize.h
+++ b/Source/JavaScriptCore/dfg/DFGClobberize.h
@@ -131,6 +131,7 @@
case DoubleRep:
case ValueRep:
case Int52Rep:
+ case BooleanToNumber:
return;
case MovHint:
diff --git a/Source/JavaScriptCore/dfg/DFGCommon.h b/Source/JavaScriptCore/dfg/DFGCommon.h
index d742bb9..dbe7ca2 100644
--- a/Source/JavaScriptCore/dfg/DFGCommon.h
+++ b/Source/JavaScriptCore/dfg/DFGCommon.h
@@ -98,6 +98,54 @@
enum NoResultTag { NoResult };
+// The prediction propagator effectively does four passes, with the last pass
+// being done by the separate FixuPhase.
+enum PredictionPass {
+ // We're converging in a straght-forward forward flow fixpoint. This is the
+ // most conventional part of the propagator - it makes only monotonic decisions
+ // based on value profiles and rare case profiles. It ignores baseline JIT rare
+ // case profiles. The goal here is to develop a good guess of which variables
+ // are likely to be purely numerical, which generally doesn't require knowing
+ // the rare case profiles.
+ PrimaryPass,
+
+ // At this point we know what is numerical and what isn't. Non-numerical inputs
+ // to arithmetic operations will not have useful information in the Baseline JIT
+ // rare case profiles because Baseline may take slow path on non-numerical
+ // inputs even if the DFG could handle the input on the fast path. Boolean
+ // inputs are the most obvious example. This pass of prediction propagation will
+ // use Baseline rare case profiles for purely numerical operations and it will
+ // ignore them for everything else. The point of this pass is to develop a good
+ // guess of which variables are likely to be doubles.
+ //
+ // This pass is intentionally weird and goes against what is considered good
+ // form when writing a static analysis: a new data flow of booleans will cause
+ // us to ignore rare case profiles except that by then, we will have already
+ // propagated double types based on our prior assumption that we shouldn't
+ // ignore rare cases. This probably won't happen because the PrimaryPass is
+ // almost certainly going to establish what is and isn't numerical. But it's
+ // conceivable that during this pass we will discover a new boolean data flow.
+ // This ends up being sound because the prediction propagator could literally
+ // make any guesses it wants and still be sound (worst case, we OSR exit more
+ // often or use too general of types are run a bit slower). This will converge
+ // because we force monotonicity on the types of nodes and variables. So, the
+ // worst thing that can happen is that we violate basic laws of theoretical
+ // decency.
+ RareCasePass,
+
+ // At this point we know what is numerical and what isn't, and we also know what
+ // is a double and what isn't. So, we start forcing variables to be double.
+ // Doing so may have a cascading effect so this is a fixpoint. It's monotonic
+ // in the sense that once a variable is forced double, it cannot be forced in
+ // the other direction.
+ DoubleVotingPass,
+
+ // This pass occurs once we have converged. At this point we are just installing
+ // type checks based on the conclusions we have already reached. It's important
+ // for this pass to reach the same conclusions that DoubleVotingPass reached.
+ FixupPass
+};
+
enum OptimizationFixpointState { BeforeFixpoint, FixpointNotConverged, FixpointConverged };
// Describes the form you can expect the entire graph to be in.
diff --git a/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp b/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
index c08b3ac..d0059a8 100644
--- a/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
@@ -79,6 +79,13 @@
bool eliminated = false;
switch (node->op()) {
+ case BooleanToNumber: {
+ if (node->child1().useKind() == UntypedUse
+ && !m_interpreter.needsTypeCheck(node->child1(), SpecBoolean))
+ node->child1().setUseKind(BooleanUse);
+ break;
+ }
+
case CheckArgumentsNotCreated: {
if (!isEmptySpeculation(
m_state.variables().operand(
diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
index 3d8a7ee..7835e75 100644
--- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
@@ -102,14 +102,14 @@
case BitRShift:
case BitLShift:
case BitURShift: {
- fixIntEdge(node->child1());
- fixIntEdge(node->child2());
+ fixIntConvertingEdge(node->child1());
+ fixIntConvertingEdge(node->child2());
break;
}
case ArithIMul: {
- fixIntEdge(node->child1());
- fixIntEdge(node->child2());
+ fixIntConvertingEdge(node->child1());
+ fixIntConvertingEdge(node->child2());
node->setOp(ArithMul);
node->setArithMode(Arith::Unchecked);
node->child1().setUseKind(Int32Use);
@@ -118,10 +118,10 @@
}
case UInt32ToNumber: {
- fixIntEdge(node->child1());
+ fixIntConvertingEdge(node->child1());
if (bytecodeCanTruncateInteger(node->arithNodeFlags()))
node->convertToIdentity();
- else if (nodeCanSpeculateInt32(node->arithNodeFlags()))
+ else if (node->canSpeculateInt32(FixupPass))
node->setArithMode(Arith::CheckOverflow);
else {
node->setArithMode(Arith::DoOverflow);
@@ -136,9 +136,9 @@
node->clearFlags(NodeMustGenerate | NodeClobbersWorld);
break;
}
- if (Node::shouldSpeculateNumberExpectingDefined(node->child1().node(), node->child2().node())) {
- fixEdge<DoubleRepUse>(node->child1());
- fixEdge<DoubleRepUse>(node->child2());
+ if (Node::shouldSpeculateNumberOrBooleanExpectingDefined(node->child1().node(), node->child2().node())) {
+ fixDoubleOrBooleanEdge(node->child1());
+ fixDoubleOrBooleanEdge(node->child2());
node->setOp(ArithAdd);
node->clearFlags(NodeMustGenerate | NodeClobbersWorld);
node->setResult(NodeResultDouble);
@@ -180,15 +180,15 @@
case ArithSub: {
if (attemptToMakeIntegerAdd(node))
break;
- fixEdge<DoubleRepUse>(node->child1());
- fixEdge<DoubleRepUse>(node->child2());
+ fixDoubleOrBooleanEdge(node->child1());
+ fixDoubleOrBooleanEdge(node->child2());
node->setResult(NodeResultDouble);
break;
}
case ArithNegate: {
- if (m_graph.negateShouldSpeculateInt32(node)) {
- fixEdge<Int32Use>(node->child1());
+ if (m_graph.negateShouldSpeculateInt32(node, FixupPass)) {
+ fixIntOrBooleanEdge(node->child1());
if (bytecodeCanTruncateInteger(node->arithNodeFlags()))
node->setArithMode(Arith::Unchecked);
else if (bytecodeCanIgnoreNegativeZero(node->arithNodeFlags()))
@@ -197,7 +197,7 @@
node->setArithMode(Arith::CheckOverflowAndNegativeZero);
break;
}
- if (m_graph.negateShouldSpeculateMachineInt(node)) {
+ if (m_graph.negateShouldSpeculateMachineInt(node, FixupPass)) {
fixEdge<Int52RepUse>(node->child1());
if (bytecodeCanIgnoreNegativeZero(node->arithNodeFlags()))
node->setArithMode(Arith::CheckOverflow);
@@ -206,15 +206,15 @@
node->setResult(NodeResultInt52);
break;
}
- fixEdge<DoubleRepUse>(node->child1());
+ fixDoubleOrBooleanEdge(node->child1());
node->setResult(NodeResultDouble);
break;
}
case ArithMul: {
- if (m_graph.mulShouldSpeculateInt32(node)) {
- fixEdge<Int32Use>(node->child1());
- fixEdge<Int32Use>(node->child2());
+ if (m_graph.mulShouldSpeculateInt32(node, FixupPass)) {
+ fixIntOrBooleanEdge(node->child1());
+ fixIntOrBooleanEdge(node->child2());
if (bytecodeCanTruncateInteger(node->arithNodeFlags()))
node->setArithMode(Arith::Unchecked);
else if (bytecodeCanIgnoreNegativeZero(node->arithNodeFlags()))
@@ -223,7 +223,7 @@
node->setArithMode(Arith::CheckOverflowAndNegativeZero);
break;
}
- if (m_graph.mulShouldSpeculateMachineInt(node)) {
+ if (m_graph.mulShouldSpeculateMachineInt(node, FixupPass)) {
fixEdge<Int52RepUse>(node->child1());
fixEdge<Int52RepUse>(node->child2());
if (bytecodeCanIgnoreNegativeZero(node->arithNodeFlags()))
@@ -233,19 +233,19 @@
node->setResult(NodeResultInt52);
break;
}
- fixEdge<DoubleRepUse>(node->child1());
- fixEdge<DoubleRepUse>(node->child2());
+ fixDoubleOrBooleanEdge(node->child1());
+ fixDoubleOrBooleanEdge(node->child2());
node->setResult(NodeResultDouble);
break;
}
case ArithDiv:
case ArithMod: {
- if (Node::shouldSpeculateInt32ForArithmetic(node->child1().node(), node->child2().node())
- && node->canSpeculateInt32()) {
+ if (Node::shouldSpeculateInt32OrBooleanForArithmetic(node->child1().node(), node->child2().node())
+ && node->canSpeculateInt32(FixupPass)) {
if (optimizeForX86() || optimizeForARM64() || optimizeForARMv7s()) {
- fixEdge<Int32Use>(node->child1());
- fixEdge<Int32Use>(node->child2());
+ fixIntOrBooleanEdge(node->child1());
+ fixIntOrBooleanEdge(node->child2());
if (bytecodeCanTruncateInteger(node->arithNodeFlags()))
node->setArithMode(Arith::Unchecked);
else if (bytecodeCanIgnoreNegativeZero(node->arithNodeFlags()))
@@ -256,8 +256,8 @@
}
// This will cause conversion nodes to be inserted later.
- fixEdge<DoubleRepUse>(node->child1());
- fixEdge<DoubleRepUse>(node->child2());
+ fixDoubleOrBooleanEdge(node->child1());
+ fixDoubleOrBooleanEdge(node->child2());
// But we have to make sure that everything is phantom'd until after the
// DoubleAsInt32 node, which occurs after the Div/Mod node that the conversions
@@ -279,33 +279,33 @@
node->setArithMode(Arith::CheckOverflowAndNegativeZero);
break;
}
- fixEdge<DoubleRepUse>(node->child1());
- fixEdge<DoubleRepUse>(node->child2());
+ fixDoubleOrBooleanEdge(node->child1());
+ fixDoubleOrBooleanEdge(node->child2());
node->setResult(NodeResultDouble);
break;
}
case ArithMin:
case ArithMax: {
- if (Node::shouldSpeculateInt32ForArithmetic(node->child1().node(), node->child2().node())
- && node->canSpeculateInt32()) {
- fixEdge<Int32Use>(node->child1());
- fixEdge<Int32Use>(node->child2());
+ if (Node::shouldSpeculateInt32OrBooleanForArithmetic(node->child1().node(), node->child2().node())
+ && node->canSpeculateInt32(FixupPass)) {
+ fixIntOrBooleanEdge(node->child1());
+ fixIntOrBooleanEdge(node->child2());
break;
}
- fixEdge<DoubleRepUse>(node->child1());
- fixEdge<DoubleRepUse>(node->child2());
+ fixDoubleOrBooleanEdge(node->child1());
+ fixDoubleOrBooleanEdge(node->child2());
node->setResult(NodeResultDouble);
break;
}
case ArithAbs: {
- if (node->child1()->shouldSpeculateInt32ForArithmetic()
- && node->canSpeculateInt32()) {
- fixEdge<Int32Use>(node->child1());
+ if (node->child1()->shouldSpeculateInt32OrBooleanForArithmetic()
+ && node->canSpeculateInt32(FixupPass)) {
+ fixIntOrBooleanEdge(node->child1());
break;
}
- fixEdge<DoubleRepUse>(node->child1());
+ fixDoubleOrBooleanEdge(node->child1());
node->setResult(NodeResultDouble);
break;
}
@@ -314,7 +314,7 @@
case ArithFRound:
case ArithSin:
case ArithCos: {
- fixEdge<DoubleRepUse>(node->child1());
+ fixDoubleOrBooleanEdge(node->child1());
node->setResult(NodeResultDouble);
break;
}
@@ -324,8 +324,8 @@
fixEdge<BooleanUse>(node->child1());
else if (node->child1()->shouldSpeculateObjectOrOther())
fixEdge<ObjectOrOtherUse>(node->child1());
- else if (node->child1()->shouldSpeculateInt32())
- fixEdge<Int32Use>(node->child1());
+ else if (node->child1()->shouldSpeculateInt32OrBoolean())
+ fixIntOrBooleanEdge(node->child1());
else if (node->child1()->shouldSpeculateNumber())
fixEdge<DoubleRepUse>(node->child1());
else if (node->child1()->shouldSpeculateString())
@@ -350,9 +350,16 @@
case CompareLessEq:
case CompareGreater:
case CompareGreaterEq: {
- if (Node::shouldSpeculateInt32(node->child1().node(), node->child2().node())) {
- fixEdge<Int32Use>(node->child1());
- fixEdge<Int32Use>(node->child2());
+ if (node->op() == CompareEq
+ && Node::shouldSpeculateBoolean(node->child1().node(), node->child2().node())) {
+ fixEdge<BooleanUse>(node->child1());
+ fixEdge<BooleanUse>(node->child2());
+ node->clearFlags(NodeMustGenerate | NodeClobbersWorld);
+ break;
+ }
+ if (Node::shouldSpeculateInt32OrBoolean(node->child1().node(), node->child2().node())) {
+ fixIntOrBooleanEdge(node->child1());
+ fixIntOrBooleanEdge(node->child2());
node->clearFlags(NodeMustGenerate | NodeClobbersWorld);
break;
}
@@ -363,20 +370,14 @@
node->clearFlags(NodeMustGenerate | NodeClobbersWorld);
break;
}
- if (Node::shouldSpeculateNumber(node->child1().node(), node->child2().node())) {
- fixEdge<DoubleRepUse>(node->child1());
- fixEdge<DoubleRepUse>(node->child2());
+ if (Node::shouldSpeculateNumberOrBoolean(node->child1().node(), node->child2().node())) {
+ fixDoubleOrBooleanEdge(node->child1());
+ fixDoubleOrBooleanEdge(node->child2());
node->clearFlags(NodeMustGenerate | NodeClobbersWorld);
break;
}
if (node->op() != CompareEq)
break;
- if (Node::shouldSpeculateBoolean(node->child1().node(), node->child2().node())) {
- fixEdge<BooleanUse>(node->child1());
- fixEdge<BooleanUse>(node->child2());
- node->clearFlags(NodeMustGenerate | NodeClobbersWorld);
- break;
- }
if (node->child1()->shouldSpeculateStringIdent() && node->child2()->shouldSpeculateStringIdent()) {
fixEdge<StringIdentUse>(node->child1());
fixEdge<StringIdentUse>(node->child2());
@@ -604,10 +605,6 @@
fixEdge<KnownCellUse>(child1);
fixEdge<Int32Use>(child2);
fixEdge<Int32Use>(child3);
- if (child3->prediction() & SpecInt52)
- fixEdge<Int52RepUse>(child3);
- else
- fixEdge<Int32Use>(child3);
break;
case Array::Double:
fixEdge<KnownCellUse>(child1);
@@ -624,17 +621,17 @@
fixEdge<KnownCellUse>(child1);
fixEdge<Int32Use>(child2);
if (child3->shouldSpeculateInt32())
- fixEdge<Int32Use>(child3);
+ fixIntOrBooleanEdge(child3);
else if (child3->shouldSpeculateMachineInt())
fixEdge<Int52RepUse>(child3);
else
- fixEdge<DoubleRepUse>(child3);
+ fixDoubleOrBooleanEdge(child3);
break;
case Array::Float32Array:
case Array::Float64Array:
fixEdge<KnownCellUse>(child1);
fixEdge<Int32Use>(child2);
- fixEdge<DoubleRepUse>(child3);
+ fixDoubleOrBooleanEdge(child3);
break;
case Array::Contiguous:
case Array::ArrayStorage:
@@ -706,10 +703,10 @@
fixEdge<BooleanUse>(node->child1());
else if (node->child1()->shouldSpeculateObjectOrOther())
fixEdge<ObjectOrOtherUse>(node->child1());
- else if (node->child1()->shouldSpeculateInt32())
- fixEdge<Int32Use>(node->child1());
- else if (node->child1()->shouldSpeculateNumber())
- fixEdge<DoubleRepUse>(node->child1());
+ else if (node->child1()->shouldSpeculateInt32OrBoolean())
+ fixIntOrBooleanEdge(node->child1());
+ else if (node->child1()->shouldSpeculateNumberOrBoolean())
+ fixDoubleOrBooleanEdge(node->child1());
Node* logicalNot = node->child1().node();
if (logicalNot->op() == LogicalNot) {
@@ -1019,6 +1016,7 @@
case DoubleConstant:
case Int52Constant:
case Identity: // This should have been cleaned up.
+ case BooleanToNumber:
// These are just nodes that we don't currently expect to see during fixup.
// If we ever wanted to insert them prior to fixup, then we just have to create
// fixup rules for them.
@@ -1579,11 +1577,11 @@
m_insertionSet.insert(indexInBlock, barrierNode);
}
- void fixIntEdge(Edge& edge)
+ void fixIntConvertingEdge(Edge& edge)
{
Node* node = edge.node();
- if (node->shouldSpeculateInt32()) {
- fixEdge<Int32Use>(edge);
+ if (node->shouldSpeculateInt32OrBoolean()) {
+ fixIntOrBooleanEdge(edge);
return;
}
@@ -1592,8 +1590,6 @@
useKind = Int52RepUse;
else if (node->shouldSpeculateNumber())
useKind = DoubleRepUse;
- else if (node->shouldSpeculateBoolean())
- useKind = BooleanUse;
else
useKind = NotCellUse;
Node* newNode = m_insertionSet.insertNode(
@@ -1605,6 +1601,50 @@
addRequiredPhantom(node);
}
+ void fixIntOrBooleanEdge(Edge& edge)
+ {
+ Node* node = edge.node();
+ if (!node->sawBooleans()) {
+ fixEdge<Int32Use>(edge);
+ return;
+ }
+
+ UseKind useKind;
+ if (node->shouldSpeculateBoolean())
+ useKind = BooleanUse;
+ else
+ useKind = UntypedUse;
+ Node* newNode = m_insertionSet.insertNode(
+ m_indexInBlock, SpecInt32, BooleanToNumber, m_currentNode->origin,
+ Edge(node, useKind));
+ observeUseKindOnNode(node, useKind);
+
+ edge = Edge(newNode, Int32Use);
+ addRequiredPhantom(node);
+ }
+
+ void fixDoubleOrBooleanEdge(Edge& edge)
+ {
+ Node* node = edge.node();
+ if (!node->sawBooleans()) {
+ fixEdge<DoubleRepUse>(edge);
+ return;
+ }
+
+ UseKind useKind;
+ if (node->shouldSpeculateBoolean())
+ useKind = BooleanUse;
+ else
+ useKind = UntypedUse;
+ Node* newNode = m_insertionSet.insertNode(
+ m_indexInBlock, SpecInt32, BooleanToNumber, m_currentNode->origin,
+ Edge(node, useKind));
+ observeUseKindOnNode(node, useKind);
+
+ edge = Edge(newNode, DoubleRepUse);
+ addRequiredPhantom(node);
+ }
+
void truncateConstantToInt32(Edge& edge)
{
Node* oldNode = edge.node();
@@ -1646,11 +1686,11 @@
bool attemptToMakeIntegerAdd(Node* node)
{
- AddSpeculationMode mode = m_graph.addSpeculationMode(node);
+ AddSpeculationMode mode = m_graph.addSpeculationMode(node, FixupPass);
if (mode != DontSpeculateInt32) {
truncateConstantsIfNecessary(node, mode);
- fixEdge<Int32Use>(node->child1());
- fixEdge<Int32Use>(node->child2());
+ fixIntOrBooleanEdge(node->child1());
+ fixIntOrBooleanEdge(node->child2());
if (bytecodeCanTruncateInteger(node->arithNodeFlags()))
node->setArithMode(Arith::Unchecked);
else
diff --git a/Source/JavaScriptCore/dfg/DFGGraph.h b/Source/JavaScriptCore/dfg/DFGGraph.h
index 0a709b9..593bfc5 100644
--- a/Source/JavaScriptCore/dfg/DFGGraph.h
+++ b/Source/JavaScriptCore/dfg/DFGGraph.h
@@ -195,42 +195,52 @@
return speculationFromValue(node->valueOfJSConstant(m_codeBlock));
}
- AddSpeculationMode addSpeculationMode(Node* add, bool leftShouldSpeculateInt32, bool rightShouldSpeculateInt32)
+ AddSpeculationMode addSpeculationMode(Node* add, bool leftShouldSpeculateInt32, bool rightShouldSpeculateInt32, PredictionPass pass)
{
ASSERT(add->op() == ValueAdd || add->op() == ArithAdd || add->op() == ArithSub);
+ RareCaseProfilingSource source = add->sourceFor(pass);
+
Node* left = add->child1().node();
Node* right = add->child2().node();
if (left->hasConstant())
- return addImmediateShouldSpeculateInt32(add, rightShouldSpeculateInt32, left);
+ return addImmediateShouldSpeculateInt32(add, rightShouldSpeculateInt32, left, source);
if (right->hasConstant())
- return addImmediateShouldSpeculateInt32(add, leftShouldSpeculateInt32, right);
+ return addImmediateShouldSpeculateInt32(add, leftShouldSpeculateInt32, right, source);
- return (leftShouldSpeculateInt32 && rightShouldSpeculateInt32 && add->canSpeculateInt32()) ? SpeculateInt32 : DontSpeculateInt32;
+ return (leftShouldSpeculateInt32 && rightShouldSpeculateInt32 && add->canSpeculateInt32(source)) ? SpeculateInt32 : DontSpeculateInt32;
}
- AddSpeculationMode valueAddSpeculationMode(Node* add)
+ AddSpeculationMode valueAddSpeculationMode(Node* add, PredictionPass pass)
{
- return addSpeculationMode(add, add->child1()->shouldSpeculateInt32ExpectingDefined(), add->child2()->shouldSpeculateInt32ExpectingDefined());
+ return addSpeculationMode(
+ add,
+ add->child1()->shouldSpeculateInt32OrBooleanExpectingDefined(),
+ add->child2()->shouldSpeculateInt32OrBooleanExpectingDefined(),
+ pass);
}
- AddSpeculationMode arithAddSpeculationMode(Node* add)
+ AddSpeculationMode arithAddSpeculationMode(Node* add, PredictionPass pass)
{
- return addSpeculationMode(add, add->child1()->shouldSpeculateInt32ForArithmetic(), add->child2()->shouldSpeculateInt32ForArithmetic());
+ return addSpeculationMode(
+ add,
+ add->child1()->shouldSpeculateInt32OrBooleanForArithmetic(),
+ add->child2()->shouldSpeculateInt32OrBooleanForArithmetic(),
+ pass);
}
- AddSpeculationMode addSpeculationMode(Node* add)
+ AddSpeculationMode addSpeculationMode(Node* add, PredictionPass pass)
{
if (add->op() == ValueAdd)
- return valueAddSpeculationMode(add);
+ return valueAddSpeculationMode(add, pass);
- return arithAddSpeculationMode(add);
+ return arithAddSpeculationMode(add, pass);
}
- bool addShouldSpeculateInt32(Node* add)
+ bool addShouldSpeculateInt32(Node* add, PredictionPass pass)
{
- return addSpeculationMode(add) != DontSpeculateInt32;
+ return addSpeculationMode(add, pass) != DontSpeculateInt32;
}
bool addShouldSpeculateMachineInt(Node* add)
@@ -250,18 +260,18 @@
return speculation && !hasExitSite(add, Int52Overflow);
}
- bool mulShouldSpeculateInt32(Node* mul)
+ bool mulShouldSpeculateInt32(Node* mul, PredictionPass pass)
{
ASSERT(mul->op() == ArithMul);
Node* left = mul->child1().node();
Node* right = mul->child2().node();
- return Node::shouldSpeculateInt32ForArithmetic(left, right)
- && mul->canSpeculateInt32();
+ return Node::shouldSpeculateInt32OrBooleanForArithmetic(left, right)
+ && mul->canSpeculateInt32(mul->sourceFor(pass));
}
- bool mulShouldSpeculateMachineInt(Node* mul)
+ bool mulShouldSpeculateMachineInt(Node* mul, PredictionPass pass)
{
ASSERT(mul->op() == ArithMul);
@@ -272,24 +282,25 @@
Node* right = mul->child2().node();
return Node::shouldSpeculateMachineInt(left, right)
- && mul->canSpeculateInt52()
+ && mul->canSpeculateInt52(pass)
&& !hasExitSite(mul, Int52Overflow);
}
- bool negateShouldSpeculateInt32(Node* negate)
+ bool negateShouldSpeculateInt32(Node* negate, PredictionPass pass)
{
ASSERT(negate->op() == ArithNegate);
- return negate->child1()->shouldSpeculateInt32ForArithmetic() && negate->canSpeculateInt32();
+ return negate->child1()->shouldSpeculateInt32OrBooleanForArithmetic()
+ && negate->canSpeculateInt32(pass);
}
- bool negateShouldSpeculateMachineInt(Node* negate)
+ bool negateShouldSpeculateMachineInt(Node* negate, PredictionPass pass)
{
ASSERT(negate->op() == ArithNegate);
if (!enableInt52())
return false;
return negate->child1()->shouldSpeculateMachineInt()
&& !hasExitSite(negate, Int52Overflow)
- && negate->canSpeculateInt52();
+ && negate->canSpeculateInt52(pass);
}
VirtualRegister bytecodeRegisterForArgument(CodeOrigin codeOrigin, int argument)
@@ -856,19 +867,19 @@
void handleSuccessor(Vector<BasicBlock*, 16>& worklist, BasicBlock*, BasicBlock* successor);
void addForDepthFirstSort(Vector<BasicBlock*>& result, Vector<BasicBlock*, 16>& worklist, HashSet<BasicBlock*>& seen, BasicBlock*);
- AddSpeculationMode addImmediateShouldSpeculateInt32(Node* add, bool variableShouldSpeculateInt32, Node* immediate)
+ AddSpeculationMode addImmediateShouldSpeculateInt32(Node* add, bool variableShouldSpeculateInt32, Node* immediate, RareCaseProfilingSource source)
{
ASSERT(immediate->hasConstant());
JSValue immediateValue = immediate->valueOfJSConstant(m_codeBlock);
- if (!immediateValue.isNumber())
+ if (!immediateValue.isNumber() && !immediateValue.isBoolean())
return DontSpeculateInt32;
if (!variableShouldSpeculateInt32)
return DontSpeculateInt32;
- if (immediateValue.isInt32())
- return add->canSpeculateInt32() ? SpeculateInt32 : DontSpeculateInt32;
+ if (immediateValue.isInt32() || immediateValue.isBoolean())
+ return add->canSpeculateInt32(source) ? SpeculateInt32 : DontSpeculateInt32;
double doubleImmediate = immediateValue.asDouble();
const double twoToThe48 = 281474976710656.0;
@@ -877,30 +888,6 @@
return bytecodeCanTruncateInteger(add->arithNodeFlags()) ? SpeculateInt32AndTruncateConstants : DontSpeculateInt32;
}
-
- bool mulImmediateShouldSpeculateInt32(Node* mul, Node* variable, Node* immediate)
- {
- ASSERT(immediate->hasConstant());
-
- JSValue immediateValue = immediate->valueOfJSConstant(m_codeBlock);
- if (!immediateValue.isInt32())
- return false;
-
- if (!variable->shouldSpeculateInt32ForArithmetic())
- return false;
-
- int32_t intImmediate = immediateValue.asInt32();
- // Doubles have a 53 bit mantissa so we expect a multiplication of 2^31 (the highest
- // magnitude possible int32 value) and any value less than 2^22 to not result in any
- // rounding in a double multiplication - hence it will be equivalent to an integer
- // multiplication, if we are doing int32 truncation afterwards (which is what
- // canSpeculateInt32() implies).
- const int32_t twoToThe22 = 1 << 22;
- if (intImmediate <= -twoToThe22 || intImmediate >= twoToThe22)
- return mul->canSpeculateInt32() && !nodeMayOverflow(mul->arithNodeFlags());
-
- return mul->canSpeculateInt32();
- }
};
#define DFG_NODE_DO_TO_CHILDREN(graph, node, thingToDo) do { \
diff --git a/Source/JavaScriptCore/dfg/DFGNode.h b/Source/JavaScriptCore/dfg/DFGNode.h
index 0364ff2..76dc71b 100644
--- a/Source/JavaScriptCore/dfg/DFGNode.h
+++ b/Source/JavaScriptCore/dfg/DFGNode.h
@@ -1437,14 +1437,29 @@
return isInt32Speculation(prediction());
}
+ bool sawBooleans()
+ {
+ return !!(prediction() & SpecBoolean);
+ }
+
+ bool shouldSpeculateInt32OrBoolean()
+ {
+ return isInt32OrBooleanSpeculation(prediction());
+ }
+
bool shouldSpeculateInt32ForArithmetic()
{
return isInt32SpeculationForArithmetic(prediction());
}
- bool shouldSpeculateInt32ExpectingDefined()
+ bool shouldSpeculateInt32OrBooleanForArithmetic()
{
- return isInt32SpeculationExpectingDefined(prediction());
+ return isInt32OrBooleanSpeculationForArithmetic(prediction());
+ }
+
+ bool shouldSpeculateInt32OrBooleanExpectingDefined()
+ {
+ return isInt32OrBooleanSpeculationExpectingDefined(prediction());
}
bool shouldSpeculateMachineInt()
@@ -1452,34 +1467,24 @@
return isMachineIntSpeculation(prediction());
}
- bool shouldSpeculateMachineIntForArithmetic()
- {
- return isMachineIntSpeculationForArithmetic(prediction());
- }
-
- bool shouldSpeculateMachineIntExpectingDefined()
- {
- return isMachineIntSpeculationExpectingDefined(prediction());
- }
-
bool shouldSpeculateDouble()
{
return isDoubleSpeculation(prediction());
}
- bool shouldSpeculateDoubleForArithmetic()
- {
- return isDoubleSpeculationForArithmetic(prediction());
- }
-
bool shouldSpeculateNumber()
{
return isFullNumberSpeculation(prediction());
}
- bool shouldSpeculateNumberExpectingDefined()
+ bool shouldSpeculateNumberOrBoolean()
{
- return isFullNumberSpeculationExpectingDefined(prediction());
+ return isFullNumberOrBooleanSpeculation(prediction());
+ }
+
+ bool shouldSpeculateNumberOrBooleanExpectingDefined()
+ {
+ return isFullNumberOrBooleanSpeculationExpectingDefined(prediction());
}
bool shouldSpeculateBoolean()
@@ -1617,14 +1622,22 @@
return op1->shouldSpeculateInt32() && op2->shouldSpeculateInt32();
}
- static bool shouldSpeculateInt32ForArithmetic(Node* op1, Node* op2)
+ static bool shouldSpeculateInt32OrBoolean(Node* op1, Node* op2)
{
- return op1->shouldSpeculateInt32ForArithmetic() && op2->shouldSpeculateInt32ForArithmetic();
+ return op1->shouldSpeculateInt32OrBoolean()
+ && op2->shouldSpeculateInt32OrBoolean();
}
- static bool shouldSpeculateInt32ExpectingDefined(Node* op1, Node* op2)
+ static bool shouldSpeculateInt32OrBooleanForArithmetic(Node* op1, Node* op2)
{
- return op1->shouldSpeculateInt32ExpectingDefined() && op2->shouldSpeculateInt32ExpectingDefined();
+ return op1->shouldSpeculateInt32OrBooleanForArithmetic()
+ && op2->shouldSpeculateInt32OrBooleanForArithmetic();
+ }
+
+ static bool shouldSpeculateInt32OrBooleanExpectingDefined(Node* op1, Node* op2)
+ {
+ return op1->shouldSpeculateInt32OrBooleanExpectingDefined()
+ && op2->shouldSpeculateInt32OrBooleanExpectingDefined();
}
static bool shouldSpeculateMachineInt(Node* op1, Node* op2)
@@ -1632,29 +1645,21 @@
return op1->shouldSpeculateMachineInt() && op2->shouldSpeculateMachineInt();
}
- static bool shouldSpeculateMachineIntForArithmetic(Node* op1, Node* op2)
- {
- return op1->shouldSpeculateMachineIntForArithmetic() && op2->shouldSpeculateMachineIntForArithmetic();
- }
-
- static bool shouldSpeculateMachineIntExpectingDefined(Node* op1, Node* op2)
- {
- return op1->shouldSpeculateMachineIntExpectingDefined() && op2->shouldSpeculateMachineIntExpectingDefined();
- }
-
- static bool shouldSpeculateDoubleForArithmetic(Node* op1, Node* op2)
- {
- return op1->shouldSpeculateDoubleForArithmetic() && op2->shouldSpeculateDoubleForArithmetic();
- }
-
static bool shouldSpeculateNumber(Node* op1, Node* op2)
{
return op1->shouldSpeculateNumber() && op2->shouldSpeculateNumber();
}
- static bool shouldSpeculateNumberExpectingDefined(Node* op1, Node* op2)
+ static bool shouldSpeculateNumberOrBoolean(Node* op1, Node* op2)
{
- return op1->shouldSpeculateNumberExpectingDefined() && op2->shouldSpeculateNumberExpectingDefined();
+ return op1->shouldSpeculateNumberOrBoolean()
+ && op2->shouldSpeculateNumberOrBoolean();
+ }
+
+ static bool shouldSpeculateNumberOrBooleanExpectingDefined(Node* op1, Node* op2)
+ {
+ return op1->shouldSpeculateNumberOrBooleanExpectingDefined()
+ && op2->shouldSpeculateNumberOrBooleanExpectingDefined();
}
static bool shouldSpeculateFinalObject(Node* op1, Node* op2)
@@ -1667,14 +1672,31 @@
return op1->shouldSpeculateArray() && op2->shouldSpeculateArray();
}
- bool canSpeculateInt32()
+ bool canSpeculateInt32(RareCaseProfilingSource source)
{
- return nodeCanSpeculateInt32(arithNodeFlags());
+ return nodeCanSpeculateInt32(arithNodeFlags(), source);
}
- bool canSpeculateInt52()
+ bool canSpeculateInt52(RareCaseProfilingSource source)
{
- return nodeCanSpeculateInt52(arithNodeFlags());
+ return nodeCanSpeculateInt52(arithNodeFlags(), source);
+ }
+
+ RareCaseProfilingSource sourceFor(PredictionPass pass)
+ {
+ if (pass == PrimaryPass || child1()->sawBooleans() || (child2() && child2()->sawBooleans()))
+ return DFGRareCase;
+ return AllRareCases;
+ }
+
+ bool canSpeculateInt32(PredictionPass pass)
+ {
+ return canSpeculateInt32(sourceFor(pass));
+ }
+
+ bool canSpeculateInt52(PredictionPass pass)
+ {
+ return canSpeculateInt52(sourceFor(pass));
}
void dumpChildren(PrintStream& out)
diff --git a/Source/JavaScriptCore/dfg/DFGNodeFlags.cpp b/Source/JavaScriptCore/dfg/DFGNodeFlags.cpp
index 8021582..e3181ca 100644
--- a/Source/JavaScriptCore/dfg/DFGNodeFlags.cpp
+++ b/Source/JavaScriptCore/dfg/DFGNodeFlags.cpp
@@ -91,11 +91,17 @@
out.print(comma, "UseAsOther");
}
- if (flags & NodeMayOverflow)
- out.print(comma, "MayOverflow");
+ if (flags & NodeMayOverflowInBaseline)
+ out.print(comma, "MayOverflowInBaseline");
- if (flags & NodeMayNegZero)
- out.print(comma, "MayNegZero");
+ if (flags & NodeMayOverflowInDFG)
+ out.print(comma, "MayOverflowInDFG");
+
+ if (flags & NodeMayNegZeroInBaseline)
+ out.print(comma, "MayNegZeroInBaseline");
+
+ if (flags & NodeMayNegZeroInDFG)
+ out.print(comma, "MayNegZeroInDFG");
if (flags & NodeBytecodeUsesAsInt)
out.print(comma, "UseAsInt");
diff --git a/Source/JavaScriptCore/dfg/DFGNodeFlags.h b/Source/JavaScriptCore/dfg/DFGNodeFlags.h
index bec0ec4..8243b75 100644
--- a/Source/JavaScriptCore/dfg/DFGNodeFlags.h
+++ b/Source/JavaScriptCore/dfg/DFGNodeFlags.h
@@ -49,26 +49,28 @@
#define NodeClobbersWorld 0x0020
#define NodeMightClobber 0x0040
-#define NodeBehaviorMask 0x0180
-#define NodeMayOverflow 0x0080
-#define NodeMayNegZero 0x0100
+#define NodeBehaviorMask 0x0780
+#define NodeMayOverflowInBaseline 0x0080
+#define NodeMayOverflowInDFG 0x0100
+#define NodeMayNegZeroInBaseline 0x0200
+#define NodeMayNegZeroInDFG 0x0400
-#define NodeBytecodeBackPropMask 0x3E00
+#define NodeBytecodeBackPropMask 0xf800
#define NodeBytecodeUseBottom 0x0000
-#define NodeBytecodeUsesAsNumber 0x0200 // The result of this computation may be used in a context that observes fractional, or bigger-than-int32, results.
-#define NodeBytecodeNeedsNegZero 0x0400 // The result of this computation may be used in a context that observes -0.
-#define NodeBytecodeUsesAsOther 0x0800 // The result of this computation may be used in a context that distinguishes between NaN and other things (like undefined).
+#define NodeBytecodeUsesAsNumber 0x0800 // The result of this computation may be used in a context that observes fractional, or bigger-than-int32, results.
+#define NodeBytecodeNeedsNegZero 0x1000 // The result of this computation may be used in a context that observes -0.
+#define NodeBytecodeUsesAsOther 0x2000 // The result of this computation may be used in a context that distinguishes between NaN and other things (like undefined).
#define NodeBytecodeUsesAsValue (NodeBytecodeUsesAsNumber | NodeBytecodeNeedsNegZero | NodeBytecodeUsesAsOther)
-#define NodeBytecodeUsesAsInt 0x1000 // The result of this computation is known to be used in a context that prefers, but does not require, integer values.
-#define NodeBytecodeUsesAsArrayIndex 0x2000 // The result of this computation is known to be used in a context that strongly prefers integer values, to the point that we should avoid using doubles if at all possible.
+#define NodeBytecodeUsesAsInt 0x4000 // The result of this computation is known to be used in a context that prefers, but does not require, integer values.
+#define NodeBytecodeUsesAsArrayIndex 0x8000 // The result of this computation is known to be used in a context that strongly prefers integer values, to the point that we should avoid using doubles if at all possible.
#define NodeArithFlagsMask (NodeBehaviorMask | NodeBytecodeBackPropMask)
-#define NodeDoesNotExit 0x4000 // This flag is negated to make it natural for the default to be that a node does exit.
+#define NodeDoesNotExit 0x10000 // This flag is negated to make it natural for the default to be that a node does exit.
-#define NodeRelevantToOSR 0x8000
+#define NodeRelevantToOSR 0x20000
-#define NodeIsFlushed 0x10000 // Used by Graph::computeIsFlushed(), will tell you which local nodes are backwards-reachable from a Flush.
+#define NodeIsFlushed 0x40000 // Used by Graph::computeIsFlushed(), will tell you which local nodes are backwards-reachable from a Flush.
typedef uint32_t NodeFlags;
@@ -87,30 +89,60 @@
return !(flags & NodeBytecodeNeedsNegZero);
}
-static inline bool nodeMayOverflow(NodeFlags flags)
+enum RareCaseProfilingSource {
+ BaselineRareCase, // Comes from slow case counting in the baseline JIT.
+ DFGRareCase, // Comes from OSR exit profiles.
+ AllRareCases
+};
+
+static inline bool nodeMayOverflow(NodeFlags flags, RareCaseProfilingSource source)
{
- return !!(flags & NodeMayOverflow);
+ NodeFlags mask;
+ switch (source) {
+ case BaselineRareCase:
+ mask = NodeMayOverflowInBaseline;
+ break;
+ case DFGRareCase:
+ mask = NodeMayOverflowInDFG;
+ break;
+ case AllRareCases:
+ mask = NodeMayOverflowInBaseline | NodeMayOverflowInDFG;
+ break;
+ }
+ return !!(flags & mask);
}
-static inline bool nodeMayNegZero(NodeFlags flags)
+static inline bool nodeMayNegZero(NodeFlags flags, RareCaseProfilingSource source)
{
- return !!(flags & NodeMayNegZero);
+ NodeFlags mask;
+ switch (source) {
+ case BaselineRareCase:
+ mask = NodeMayNegZeroInBaseline;
+ break;
+ case DFGRareCase:
+ mask = NodeMayNegZeroInDFG;
+ break;
+ case AllRareCases:
+ mask = NodeMayNegZeroInBaseline | NodeMayNegZeroInDFG;
+ break;
+ }
+ return !!(flags & mask);
}
-static inline bool nodeCanSpeculateInt32(NodeFlags flags)
+static inline bool nodeCanSpeculateInt32(NodeFlags flags, RareCaseProfilingSource source)
{
- if (nodeMayOverflow(flags))
+ if (nodeMayOverflow(flags, source))
return !bytecodeUsesAsNumber(flags);
- if (nodeMayNegZero(flags))
+ if (nodeMayNegZero(flags, source))
return bytecodeCanIgnoreNegativeZero(flags);
return true;
}
-static inline bool nodeCanSpeculateInt52(NodeFlags flags)
+static inline bool nodeCanSpeculateInt52(NodeFlags flags, RareCaseProfilingSource source)
{
- if (nodeMayNegZero(flags))
+ if (nodeMayNegZero(flags, source))
return bytecodeCanIgnoreNegativeZero(flags);
return true;
diff --git a/Source/JavaScriptCore/dfg/DFGNodeType.h b/Source/JavaScriptCore/dfg/DFGNodeType.h
index 2c31fb2..702a5f9 100644
--- a/Source/JavaScriptCore/dfg/DFGNodeType.h
+++ b/Source/JavaScriptCore/dfg/DFGNodeType.h
@@ -109,6 +109,8 @@
macro(ValueToInt32, NodeResultInt32) \
/* Used to box the result of URShift nodes (result has range 0..2^32-1). */\
macro(UInt32ToNumber, NodeResultNumber) \
+ /* Converts booleans to numbers but passes everything else through. */\
+ macro(BooleanToNumber, NodeResultJS) \
\
/* Attempt to truncate a double to int32; this will exit if it can't do it. */\
macro(DoubleAsInt32, NodeResultInt32) \
diff --git a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
index 144e46d..e9c3552 100644
--- a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
@@ -56,9 +56,29 @@
{
ASSERT(m_graph.m_form == ThreadedCPS);
ASSERT(m_graph.m_unificationState == GloballyUnified);
-
- // 1) propagate predictions
+ m_pass = PrimaryPass;
+ propagateToFixpoint();
+
+ m_pass = RareCasePass;
+ propagateToFixpoint();
+
+ m_pass = DoubleVotingPass;
+ do {
+ m_changed = false;
+ doRoundOfDoubleVoting();
+ if (!m_changed)
+ break;
+ m_changed = false;
+ propagateForward();
+ } while (m_changed);
+
+ return true;
+ }
+
+private:
+ void propagateToFixpoint()
+ {
do {
m_changed = false;
@@ -75,22 +95,8 @@
m_changed = false;
propagateBackward();
} while (m_changed);
-
- // 2) repropagate predictions while doing double voting.
-
- do {
- m_changed = false;
- doRoundOfDoubleVoting();
- if (!m_changed)
- break;
- m_changed = false;
- propagateForward();
- } while (m_changed);
-
- return true;
}
-private:
bool setPrediction(SpeculatedType prediction)
{
ASSERT(m_currentNode->hasResult());
@@ -118,7 +124,7 @@
result |= SpecDoubleImpureNaN;
if (value & SpecDoublePureNaN)
result |= SpecDoublePureNaN;
- if (!isFullNumberSpeculation(value))
+ if (!isFullNumberOrBooleanSpeculation(value))
result |= SpecDoublePureNaN;
return result;
}
@@ -197,7 +203,7 @@
case UInt32ToNumber: {
// FIXME: Support Int52.
// https://bugs.webkit.org/show_bug.cgi?id=125704
- if (nodeCanSpeculateInt32(node->arithNodeFlags()))
+ if (node->canSpeculateInt32(m_pass))
changed |= mergePrediction(SpecInt32);
else
changed |= mergePrediction(SpecBytecodeNumber);
@@ -209,14 +215,17 @@
SpeculatedType right = node->child2()->prediction();
if (left && right) {
- if (isFullNumberSpeculationExpectingDefined(left) && isFullNumberSpeculationExpectingDefined(right)) {
- if (m_graph.addSpeculationMode(node) != DontSpeculateInt32)
+ if (isFullNumberOrBooleanSpeculationExpectingDefined(left)
+ && isFullNumberOrBooleanSpeculationExpectingDefined(right)) {
+ if (m_graph.addSpeculationMode(node, m_pass) != DontSpeculateInt32)
changed |= mergePrediction(SpecInt32);
else if (m_graph.addShouldSpeculateMachineInt(node))
changed |= mergePrediction(SpecInt52);
else
changed |= mergePrediction(speculatedDoubleTypeForPredictions(left, right));
- } else if (!(left & SpecFullNumber) || !(right & SpecFullNumber)) {
+ } else if (
+ !(left & (SpecFullNumber | SpecBoolean))
+ || !(right & (SpecFullNumber | SpecBoolean))) {
// left or right is definitely something other than a number.
changed |= mergePrediction(SpecString);
} else
@@ -230,7 +239,7 @@
SpeculatedType right = node->child2()->prediction();
if (left && right) {
- if (m_graph.addSpeculationMode(node) != DontSpeculateInt32)
+ if (m_graph.addSpeculationMode(node, m_pass) != DontSpeculateInt32)
changed |= mergePrediction(SpecInt32);
else if (m_graph.addShouldSpeculateMachineInt(node))
changed |= mergePrediction(SpecInt52);
@@ -245,7 +254,7 @@
SpeculatedType right = node->child2()->prediction();
if (left && right) {
- if (m_graph.addSpeculationMode(node) != DontSpeculateInt32)
+ if (m_graph.addSpeculationMode(node, m_pass) != DontSpeculateInt32)
changed |= mergePrediction(SpecInt32);
else if (m_graph.addShouldSpeculateMachineInt(node))
changed |= mergePrediction(SpecInt52);
@@ -257,9 +266,9 @@
case ArithNegate:
if (node->child1()->prediction()) {
- if (m_graph.negateShouldSpeculateInt32(node))
+ if (m_graph.negateShouldSpeculateInt32(node, m_pass))
changed |= mergePrediction(SpecInt32);
- else if (m_graph.negateShouldSpeculateMachineInt(node))
+ else if (m_graph.negateShouldSpeculateMachineInt(node, m_pass))
changed |= mergePrediction(SpecInt52);
else
changed |= mergePrediction(speculatedDoubleTypeForPrediction(node->child1()->prediction()));
@@ -272,8 +281,8 @@
SpeculatedType right = node->child2()->prediction();
if (left && right) {
- if (Node::shouldSpeculateInt32ForArithmetic(node->child1().node(), node->child2().node())
- && nodeCanSpeculateInt32(node->arithNodeFlags()))
+ if (Node::shouldSpeculateInt32OrBooleanForArithmetic(node->child1().node(), node->child2().node())
+ && node->canSpeculateInt32(m_pass))
changed |= mergePrediction(SpecInt32);
else
changed |= mergePrediction(speculatedDoubleTypeForPredictions(left, right));
@@ -286,9 +295,9 @@
SpeculatedType right = node->child2()->prediction();
if (left && right) {
- if (m_graph.mulShouldSpeculateInt32(node))
+ if (m_graph.mulShouldSpeculateInt32(node, m_pass))
changed |= mergePrediction(SpecInt32);
- else if (m_graph.mulShouldSpeculateMachineInt(node))
+ else if (m_graph.mulShouldSpeculateMachineInt(node, m_pass))
changed |= mergePrediction(SpecInt52);
else
changed |= mergePrediction(speculatedDoubleTypeForPredictions(left, right));
@@ -301,8 +310,8 @@
SpeculatedType right = node->child2()->prediction();
if (left && right) {
- if (Node::shouldSpeculateInt32ForArithmetic(node->child1().node(), node->child2().node())
- && nodeCanSpeculateInt32(node->arithNodeFlags()))
+ if (Node::shouldSpeculateInt32OrBooleanForArithmetic(node->child1().node(), node->child2().node())
+ && node->canSpeculateInt32(m_pass))
changed |= mergePrediction(SpecInt32);
else
changed |= mergePrediction(SpecBytecodeDouble);
@@ -315,8 +324,8 @@
SpeculatedType right = node->child2()->prediction();
if (left && right) {
- if (Node::shouldSpeculateInt32ForArithmetic(node->child1().node(), node->child2().node())
- && nodeCanSpeculateInt32(node->arithNodeFlags()))
+ if (Node::shouldSpeculateInt32OrBooleanForArithmetic(node->child1().node(), node->child2().node())
+ && node->canSpeculateInt32(m_pass))
changed |= mergePrediction(SpecInt32);
else
changed |= mergePrediction(SpecBytecodeDouble);
@@ -334,8 +343,8 @@
case ArithAbs: {
SpeculatedType child = node->child1()->prediction();
- if (isInt32SpeculationForArithmetic(child)
- && nodeCanSpeculateInt32(node->arithNodeFlags()))
+ if (isInt32OrBooleanSpeculationForArithmetic(child)
+ && node->canSpeculateInt32(m_pass))
changed |= mergePrediction(SpecInt32);
else
changed |= mergePrediction(speculatedDoubleTypeForPrediction(child));
@@ -532,7 +541,8 @@
case ValueRep:
case DoubleConstant:
case Int52Constant:
- case Identity: {
+ case Identity:
+ case BooleanToNumber: {
// This node should never be visible at this stage of compilation. It is
// inserted by fixup(), which follows this phase.
RELEASE_ASSERT_NOT_REACHED();
@@ -675,8 +685,9 @@
DoubleBallot ballot;
- if (isFullNumberSpeculationExpectingDefined(left) && isFullNumberSpeculationExpectingDefined(right)
- && !m_graph.addShouldSpeculateInt32(node)
+ if (isFullNumberSpeculation(left)
+ && isFullNumberSpeculation(right)
+ && !m_graph.addShouldSpeculateInt32(node, m_pass)
&& !m_graph.addShouldSpeculateMachineInt(node))
ballot = VoteDouble;
else
@@ -693,9 +704,10 @@
DoubleBallot ballot;
- if (isFullNumberSpeculation(left) && isFullNumberSpeculation(right)
- && !m_graph.mulShouldSpeculateInt32(node)
- && !m_graph.mulShouldSpeculateMachineInt(node))
+ if (isFullNumberSpeculation(left)
+ && isFullNumberSpeculation(right)
+ && !m_graph.mulShouldSpeculateInt32(node, m_pass)
+ && !m_graph.mulShouldSpeculateMachineInt(node, m_pass))
ballot = VoteDouble;
else
ballot = VoteValue;
@@ -714,8 +726,9 @@
DoubleBallot ballot;
- if (isFullNumberSpeculation(left) && isFullNumberSpeculation(right)
- && !(Node::shouldSpeculateInt32ForArithmetic(node->child1().node(), node->child2().node()) && node->canSpeculateInt32()))
+ if (isFullNumberSpeculation(left)
+ && isFullNumberSpeculation(right)
+ && !(Node::shouldSpeculateInt32OrBooleanForArithmetic(node->child1().node(), node->child2().node()) && node->canSpeculateInt32(m_pass)))
ballot = VoteDouble;
else
ballot = VoteValue;
@@ -727,7 +740,8 @@
case ArithAbs:
DoubleBallot ballot;
- if (!(node->child1()->shouldSpeculateInt32ForArithmetic() && node->canSpeculateInt32()))
+ if (node->child1()->shouldSpeculateNumber()
+ && !(node->child1()->shouldSpeculateInt32OrBooleanForArithmetic() && node->canSpeculateInt32(m_pass)))
ballot = VoteDouble;
else
ballot = VoteValue;
@@ -738,7 +752,10 @@
case ArithSqrt:
case ArithCos:
case ArithSin:
- m_graph.voteNode(node->child1(), VoteDouble, weight);
+ if (node->child1()->shouldSpeculateNumber())
+ m_graph.voteNode(node->child1(), VoteDouble, weight);
+ else
+ m_graph.voteNode(node->child1(), VoteValue, weight);
break;
case SetLocal: {
@@ -813,6 +830,7 @@
Node* m_currentNode;
bool m_changed;
+ PredictionPass m_pass; // We use different logic for considering predictions depending on how far along we are in propagation.
};
bool performPredictionPropagation(Graph& graph)
diff --git a/Source/JavaScriptCore/dfg/DFGSafeToExecute.h b/Source/JavaScriptCore/dfg/DFGSafeToExecute.h
index f235cd8..af59026 100644
--- a/Source/JavaScriptCore/dfg/DFGSafeToExecute.h
+++ b/Source/JavaScriptCore/dfg/DFGSafeToExecute.h
@@ -253,6 +253,7 @@
case ValueRep:
case DoubleRep:
case Int52Rep:
+ case BooleanToNumber:
return true;
case GetByVal:
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
index 4db4af4..f75afd6 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
@@ -1853,14 +1853,6 @@
void SpeculativeJIT::compileValueToInt32(Node* node)
{
switch (node->child1().useKind()) {
- case Int32Use: {
- SpeculateInt32Operand op1(this, node->child1());
- GPRTemporary result(this, Reuse, op1);
- m_jit.move(op1.gpr(), result.gpr());
- int32Result(result.gpr(), node, op1.format());
- return;
- }
-
#if USE(JSVALUE64)
case Int52RepUse: {
SpeculateStrictInt52Operand op1(this, node->child1());
@@ -2013,17 +2005,6 @@
return;
}
- case BooleanUse: {
- SpeculateBooleanOperand op1(this, node->child1());
- GPRTemporary result(this, Reuse, op1);
-
- m_jit.move(op1.gpr(), result.gpr());
- m_jit.and32(JITCompiler::TrustedImm32(1), result.gpr());
-
- int32Result(result.gpr(), node);
- return;
- }
-
default:
ASSERT(!m_compileOkay);
return;
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
index 4c8ff96..80f6563 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
@@ -2948,6 +2948,48 @@
break;
}
+ case BooleanToNumber: {
+ switch (node->child1().useKind()) {
+ case BooleanUse: {
+ SpeculateBooleanOperand value(this, node->child1());
+ GPRTemporary result(this); // FIXME: We could reuse, but on speculation fail would need recovery to restore tag (akin to add).
+
+ m_jit.move(value.gpr(), result.gpr());
+
+ int32Result(result.gpr(), node);
+ break;
+ }
+
+ case UntypedUse: {
+ JSValueOperand value(this, node->child1());
+ GPRTemporary resultTag(this);
+ GPRTemporary resultPayload(this);
+
+ GPRReg valueTagGPR = value.tagGPR();
+ GPRReg valuePayloadGPR = value.payloadGPR();
+ GPRReg resultTagGPR = resultTag.gpr();
+ GPRReg resultPayloadGPR = resultPayload.gpr();
+
+ m_jit.move(valuePayloadGPR, resultPayloadGPR);
+ JITCompiler::Jump isBoolean = m_jit.branch32(
+ JITCompiler::Equal, valueTagGPR, TrustedImm32(JSValue::BooleanTag));
+ m_jit.move(valueTagGPR, resultTagGPR);
+ JITCompiler::Jump done = m_jit.jump();
+ isBoolean.link(&m_jit);
+ m_jit.move(TrustedImm32(JSValue::Int32Tag), resultTagGPR);
+ done.link(&m_jit);
+
+ jsValueResult(resultTagGPR, resultPayloadGPR, node);
+ break;
+ }
+
+ default:
+ RELEASE_ASSERT_NOT_REACHED();
+ break;
+ }
+ break;
+ }
+
case ToPrimitive: {
RELEASE_ASSERT(node->child1().useKind() == UntypedUse);
JSValueOperand op1(this, node->child1());
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
index 3a3bb56..f30cf69 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
@@ -3053,6 +3053,47 @@
break;
}
+ case BooleanToNumber: {
+ switch (node->child1().useKind()) {
+ case BooleanUse: {
+ JSValueOperand value(this, node->child1(), ManualOperandSpeculation);
+ GPRTemporary result(this); // FIXME: We could reuse, but on speculation fail would need recovery to restore tag (akin to add).
+
+ m_jit.move(value.gpr(), result.gpr());
+ m_jit.xor64(TrustedImm32(static_cast<int32_t>(ValueFalse)), result.gpr());
+ DFG_TYPE_CHECK(
+ JSValueRegs(value.gpr()), node->child1(), SpecBoolean, m_jit.branchTest64(
+ JITCompiler::NonZero, result.gpr(), TrustedImm32(static_cast<int32_t>(~1))));
+
+ int32Result(result.gpr(), node);
+ break;
+ }
+
+ case UntypedUse: {
+ JSValueOperand value(this, node->child1());
+ GPRTemporary result(this);
+
+ m_jit.move(value.gpr(), result.gpr());
+ m_jit.xor64(TrustedImm32(static_cast<int32_t>(ValueFalse)), result.gpr());
+ JITCompiler::Jump isBoolean = m_jit.branchTest64(
+ JITCompiler::Zero, result.gpr(), TrustedImm32(static_cast<int32_t>(~1)));
+ m_jit.move(value.gpr(), result.gpr());
+ JITCompiler::Jump done = m_jit.jump();
+ isBoolean.link(&m_jit);
+ m_jit.or64(GPRInfo::tagTypeNumberRegister, result.gpr());
+ done.link(&m_jit);
+
+ jsValueResult(result.gpr(), node);
+ break;
+ }
+
+ default:
+ RELEASE_ASSERT_NOT_REACHED();
+ break;
+ }
+ break;
+ }
+
case ToPrimitive: {
RELEASE_ASSERT(node->child1().useKind() == UntypedUse);
JSValueOperand op1(this, node->child1());