summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp')
-rw-r--r--Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp1253
1 files changed, 816 insertions, 437 deletions
diff --git a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
index d859849a3..d090a8677 100644
--- a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011, 2012, 2013, 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2011-2016 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -30,20 +30,13 @@
#include "DFGGraph.h"
#include "DFGPhase.h"
-#include "Operations.h"
+#include "JSCInlines.h"
namespace JSC { namespace DFG {
-SpeculatedType resultOfToPrimitive(SpeculatedType type)
-{
- if (type & SpecObject) {
- // Objects get turned into strings. So if the input has hints of objectness,
- // the output will have hinsts of stringiness.
- return mergeSpeculations(type & ~SpecObject, SpecString);
- }
-
- return type;
-}
+namespace {
+
+bool verboseFixPointLoops = false;
class PredictionPropagationPhase : public Phase {
public:
@@ -56,12 +49,47 @@ public:
{
ASSERT(m_graph.m_form == ThreadedCPS);
ASSERT(m_graph.m_unificationState == GloballyUnified);
+
+ propagateThroughArgumentPositions();
+
+ processInvariants();
+
+ m_pass = PrimaryPass;
+ propagateToFixpoint();
- // 1) propagate predictions
+ m_pass = RareCasePass;
+ propagateToFixpoint();
+
+ m_pass = DoubleVotingPass;
+ unsigned counter = 0;
+ do {
+ if (verboseFixPointLoops)
+ ++counter;
+ m_changed = false;
+ doRoundOfDoubleVoting();
+ if (!m_changed)
+ break;
+ m_changed = false;
+ propagateForward();
+ } while (m_changed);
+
+ if (verboseFixPointLoops)
+ dataLog("Iterated ", counter, " times in double voting fixpoint.\n");
+
+ return true;
+ }
+
+private:
+ void propagateToFixpoint()
+ {
+ unsigned counter = 0;
do {
+ if (verboseFixPointLoops)
+ ++counter;
+
m_changed = false;
-
+
// Forward propagation is near-optimal for both topologically-sorted and
// DFS-sorted code.
propagateForward();
@@ -75,22 +103,11 @@ public:
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;
+ if (verboseFixPointLoops)
+ dataLog("Iterated ", counter, " times in propagateToFixpoint.\n");
}
-private:
bool setPrediction(SpeculatedType prediction)
{
ASSERT(m_currentNode->hasResult());
@@ -113,11 +130,14 @@ private:
SpeculatedType speculatedDoubleTypeForPrediction(SpeculatedType value)
{
- if (!isFullNumberSpeculation(value))
- return SpecDouble;
- if (value & SpecDoubleNaN)
- return SpecDouble;
- return SpecDoubleReal;
+ SpeculatedType result = SpecDoubleReal;
+ if (value & SpecDoubleImpureNaN)
+ result |= SpecDoubleImpureNaN;
+ if (value & SpecDoublePureNaN)
+ result |= SpecDoublePureNaN;
+ if (!isFullNumberOrBooleanSpeculation(value))
+ result |= SpecDoublePureNaN;
+ return result;
}
SpeculatedType speculatedDoubleTypeForPredictions(SpeculatedType left, SpeculatedType right)
@@ -132,20 +152,11 @@ private:
bool changed = false;
switch (op) {
- case JSConstant:
- case WeakJSConstant: {
- SpeculatedType type = speculationFromValue(m_graph.valueOfJSConstant(node));
- if (type == SpecInt52AsDouble)
- type = SpecInt52;
- changed |= setPrediction(type);
- break;
- }
-
case GetLocal: {
VariableAccessData* variable = node->variableAccessData();
SpeculatedType prediction = variable->prediction();
- if (variable->shouldNeverUnbox() && (prediction & SpecInt52))
- prediction = (prediction | SpecInt52AsDouble) & ~SpecInt52;
+ if (!variable->couldRepresentInt52() && (prediction & SpecInt52Only))
+ prediction = (prediction | SpecAnyIntAsDouble) & ~SpecInt52Only;
if (prediction)
changed |= mergePrediction(prediction);
break;
@@ -156,44 +167,12 @@ private:
changed |= variableAccessData->predict(node->child1()->prediction());
break;
}
-
- case BitAnd:
- case BitOr:
- case BitXor:
- case BitRShift:
- case BitLShift:
- case BitURShift:
- case ArithIMul: {
- changed |= setPrediction(SpecInt32);
- break;
- }
-
- case ArrayPop:
- case ArrayPush:
- case RegExpExec:
- case RegExpTest:
- case GetById:
- case GetByIdFlush:
- case GetMyArgumentByValSafe:
- case GetByOffset:
- case Call:
- case Construct:
- case GetGlobalVar:
- case GetClosureVar: {
- changed |= setPrediction(node->getHeapPrediction());
- break;
- }
-
- case StringCharCodeAt: {
- changed |= setPrediction(SpecInt32);
- break;
- }
case UInt32ToNumber: {
- // FIXME: Support Int52.
- // https://bugs.webkit.org/show_bug.cgi?id=125704
- if (nodeCanSpeculateInt32(node->arithNodeFlags()))
- changed |= mergePrediction(SpecInt32);
+ if (node->canSpeculateInt32(m_pass))
+ changed |= mergePrediction(SpecInt32Only);
+ else if (enableInt52())
+ changed |= mergePrediction(SpecAnyInt);
else
changed |= mergePrediction(SpecBytecodeNumber);
break;
@@ -204,33 +183,43 @@ private:
SpeculatedType right = node->child2()->prediction();
if (left && right) {
- if (isFullNumberSpeculationExpectingDefined(left) && isFullNumberSpeculationExpectingDefined(right)) {
- if (m_graph.addSpeculationMode(node) != DontSpeculateInt32)
- changed |= mergePrediction(SpecInt32);
- else if (m_graph.addShouldSpeculateMachineInt(node))
- changed |= mergePrediction(SpecInt52);
+ if (isFullNumberOrBooleanSpeculationExpectingDefined(left)
+ && isFullNumberOrBooleanSpeculationExpectingDefined(right)) {
+ if (m_graph.addSpeculationMode(node, m_pass) != DontSpeculateInt32)
+ changed |= mergePrediction(SpecInt32Only);
+ else if (m_graph.addShouldSpeculateAnyInt(node))
+ changed |= mergePrediction(SpecInt52Only);
else
changed |= mergePrediction(speculatedDoubleTypeForPredictions(left, right));
- } else if (!(left & SpecFullNumber) || !(right & SpecFullNumber)) {
+ } else if (isStringOrStringObjectSpeculation(left) && isStringOrStringObjectSpeculation(right)) {
// left or right is definitely something other than a number.
changed |= mergePrediction(SpecString);
- } else
- changed |= mergePrediction(SpecString | SpecInt32 | SpecDouble);
+ } else {
+ changed |= mergePrediction(SpecInt32Only);
+ if (node->mayHaveDoubleResult())
+ changed |= mergePrediction(SpecBytecodeDouble);
+ if (node->mayHaveNonNumberResult())
+ changed |= mergePrediction(SpecString);
+ }
}
break;
}
-
+
case ArithAdd: {
SpeculatedType left = node->child1()->prediction();
SpeculatedType right = node->child2()->prediction();
if (left && right) {
- if (m_graph.addSpeculationMode(node) != DontSpeculateInt32)
- changed |= mergePrediction(SpecInt32);
- else if (m_graph.addShouldSpeculateMachineInt(node))
- changed |= mergePrediction(SpecInt52);
- else
+ if (m_graph.addSpeculationMode(node, m_pass) != DontSpeculateInt32)
+ changed |= mergePrediction(SpecInt32Only);
+ else if (m_graph.addShouldSpeculateAnyInt(node))
+ changed |= mergePrediction(SpecInt52Only);
+ else if (isFullNumberOrBooleanSpeculation(left) && isFullNumberOrBooleanSpeculation(right))
changed |= mergePrediction(speculatedDoubleTypeForPredictions(left, right));
+ else if (node->mayHaveNonIntResult() || (left & SpecBytecodeDouble) || (right & SpecBytecodeDouble))
+ changed |= mergePrediction(SpecInt32Only | SpecBytecodeDouble);
+ else
+ changed |= mergePrediction(SpecInt32Only);
}
break;
}
@@ -238,38 +227,50 @@ private:
case ArithSub: {
SpeculatedType left = node->child1()->prediction();
SpeculatedType right = node->child2()->prediction();
-
+
if (left && right) {
- if (m_graph.addSpeculationMode(node) != DontSpeculateInt32)
- changed |= mergePrediction(SpecInt32);
- else if (m_graph.addShouldSpeculateMachineInt(node))
- changed |= mergePrediction(SpecInt52);
+ if (isFullNumberOrBooleanSpeculationExpectingDefined(left)
+ && isFullNumberOrBooleanSpeculationExpectingDefined(right)) {
+ if (m_graph.addSpeculationMode(node, m_pass) != DontSpeculateInt32)
+ changed |= mergePrediction(SpecInt32Only);
+ else if (m_graph.addShouldSpeculateAnyInt(node))
+ changed |= mergePrediction(SpecInt52Only);
+ else
+ changed |= mergePrediction(speculatedDoubleTypeForPredictions(left, right));
+ } else if (node->mayHaveNonIntResult() || (left & SpecBytecodeDouble) || (right & SpecBytecodeDouble))
+ changed |= mergePrediction(SpecInt32Only | SpecBytecodeDouble);
else
- changed |= mergePrediction(speculatedDoubleTypeForPredictions(left, right));
+ changed |= mergePrediction(SpecInt32Only);
}
break;
}
-
- case ArithNegate:
- if (node->child1()->prediction()) {
- if (m_graph.negateShouldSpeculateInt32(node))
- changed |= mergePrediction(SpecInt32);
- else if (m_graph.negateShouldSpeculateMachineInt(node))
- changed |= mergePrediction(SpecInt52);
- else
+
+ case ArithNegate: {
+ SpeculatedType prediction = node->child1()->prediction();
+ if (prediction) {
+ if (isInt32OrBooleanSpeculation(prediction) && node->canSpeculateInt32(m_pass))
+ changed |= mergePrediction(SpecInt32Only);
+ else if (m_graph.unaryArithShouldSpeculateAnyInt(node, m_pass))
+ changed |= mergePrediction(SpecInt52Only);
+ else if (isBytecodeNumberSpeculation(prediction))
changed |= mergePrediction(speculatedDoubleTypeForPrediction(node->child1()->prediction()));
+ else {
+ changed |= mergePrediction(SpecInt32Only);
+ if (node->mayHaveDoubleResult())
+ changed |= mergePrediction(SpecBytecodeDouble);
+ }
}
break;
-
+ }
case ArithMin:
case ArithMax: {
SpeculatedType left = node->child1()->prediction();
SpeculatedType right = node->child2()->prediction();
if (left && right) {
- if (Node::shouldSpeculateInt32ForArithmetic(node->child1().node(), node->child2().node())
- && nodeCanSpeculateInt32(node->arithNodeFlags()))
- changed |= mergePrediction(SpecInt32);
+ if (Node::shouldSpeculateInt32OrBooleanForArithmetic(node->child1().node(), node->child2().node())
+ && node->canSpeculateInt32(m_pass))
+ changed |= mergePrediction(SpecInt32Only);
else
changed |= mergePrediction(speculatedDoubleTypeForPredictions(left, right));
}
@@ -281,235 +282,770 @@ private:
SpeculatedType right = node->child2()->prediction();
if (left && right) {
- if (m_graph.mulShouldSpeculateInt32(node))
- changed |= mergePrediction(SpecInt32);
- else if (m_graph.mulShouldSpeculateMachineInt(node))
- changed |= mergePrediction(SpecInt52);
- else
- changed |= mergePrediction(speculatedDoubleTypeForPredictions(left, right));
+ // FIXME: We're currently relying on prediction propagation and backwards propagation
+ // whenever we can, and only falling back on result flags if that fails. And the result
+ // flags logic doesn't know how to use backwards propagation. We should get rid of the
+ // prediction propagation logic and rely solely on the result type.
+ if (isFullNumberOrBooleanSpeculationExpectingDefined(left)
+ && isFullNumberOrBooleanSpeculationExpectingDefined(right)) {
+ if (m_graph.binaryArithShouldSpeculateInt32(node, m_pass))
+ changed |= mergePrediction(SpecInt32Only);
+ else if (m_graph.binaryArithShouldSpeculateAnyInt(node, m_pass))
+ changed |= mergePrediction(SpecInt52Only);
+ else
+ changed |= mergePrediction(speculatedDoubleTypeForPredictions(left, right));
+ } else {
+ if (node->mayHaveNonIntResult()
+ || (left & SpecBytecodeDouble)
+ || (right & SpecBytecodeDouble))
+ changed |= mergePrediction(SpecInt32Only | SpecBytecodeDouble);
+ else
+ changed |= mergePrediction(SpecInt32Only);
+ }
}
break;
}
-
- case ArithDiv: {
+
+ case ArithDiv:
+ case ArithMod: {
SpeculatedType left = node->child1()->prediction();
SpeculatedType right = node->child2()->prediction();
if (left && right) {
- if (Node::shouldSpeculateInt32ForArithmetic(node->child1().node(), node->child2().node())
- && nodeCanSpeculateInt32(node->arithNodeFlags()))
- changed |= mergePrediction(SpecInt32);
+ if (isFullNumberOrBooleanSpeculationExpectingDefined(left)
+ && isFullNumberOrBooleanSpeculationExpectingDefined(right)) {
+ if (m_graph.binaryArithShouldSpeculateInt32(node, m_pass))
+ changed |= mergePrediction(SpecInt32Only);
+ else
+ changed |= mergePrediction(SpecBytecodeDouble);
+ } else
+ changed |= mergePrediction(SpecInt32Only | SpecBytecodeDouble);
+ }
+ break;
+ }
+
+ case ArithAbs: {
+ SpeculatedType childPrediction = node->child1()->prediction();
+ if (isInt32OrBooleanSpeculation(childPrediction)
+ && node->canSpeculateInt32(m_pass))
+ changed |= mergePrediction(SpecInt32Only);
+ else
+ changed |= mergePrediction(SpecBytecodeDouble);
+ break;
+ }
+
+ case GetByVal: {
+ if (!node->child1()->prediction())
+ break;
+
+ ArrayMode arrayMode = node->arrayMode().refine(
+ m_graph, node,
+ node->child1()->prediction(),
+ node->child2()->prediction(),
+ SpecNone);
+
+ switch (arrayMode.type()) {
+ case Array::Int32:
+ if (arrayMode.isOutOfBounds())
+ changed |= mergePrediction(node->getHeapPrediction() | SpecInt32Only);
else
- changed |= mergePrediction(SpecDouble);
+ changed |= mergePrediction(SpecInt32Only);
+ break;
+ case Array::Double:
+ if (arrayMode.isOutOfBounds())
+ changed |= mergePrediction(node->getHeapPrediction() | SpecDoubleReal);
+ else if (node->getHeapPrediction() & SpecNonIntAsDouble)
+ changed |= mergePrediction(SpecDoubleReal);
+ else
+ changed |= mergePrediction(SpecAnyIntAsDouble);
+ break;
+ case Array::Float32Array:
+ case Array::Float64Array:
+ changed |= mergePrediction(SpecFullDouble);
+ break;
+ case Array::Uint32Array:
+ if (isInt32SpeculationForArithmetic(node->getHeapPrediction()))
+ changed |= mergePrediction(SpecInt32Only);
+ else if (enableInt52())
+ changed |= mergePrediction(SpecAnyInt);
+ else
+ changed |= mergePrediction(SpecInt32Only | SpecAnyIntAsDouble);
+ break;
+ case Array::Int8Array:
+ case Array::Uint8Array:
+ case Array::Int16Array:
+ case Array::Uint16Array:
+ case Array::Int32Array:
+ changed |= mergePrediction(SpecInt32Only);
+ break;
+ default:
+ changed |= mergePrediction(node->getHeapPrediction());
+ break;
+ }
+ break;
+ }
+
+ case ToThis: {
+ // ToThis in methods for primitive types should speculate primitive types in strict mode.
+ ECMAMode ecmaMode = m_graph.executableFor(node->origin.semantic)->isStrictMode() ? StrictMode : NotStrictMode;
+ if (ecmaMode == StrictMode) {
+ if (node->child1()->shouldSpeculateBoolean()) {
+ changed |= mergePrediction(SpecBoolean);
+ break;
+ }
+
+ if (node->child1()->shouldSpeculateInt32()) {
+ changed |= mergePrediction(SpecInt32Only);
+ break;
+ }
+
+ if (enableInt52() && node->child1()->shouldSpeculateAnyInt()) {
+ changed |= mergePrediction(SpecAnyInt);
+ break;
+ }
+
+ if (node->child1()->shouldSpeculateNumber()) {
+ changed |= mergePrediction(SpecBytecodeNumber);
+ break;
+ }
+
+ if (node->child1()->shouldSpeculateSymbol()) {
+ changed |= mergePrediction(SpecSymbol);
+ break;
+ }
+
+ if (node->child1()->shouldSpeculateStringIdent()) {
+ changed |= mergePrediction(SpecStringIdent);
+ break;
+ }
+
+ if (node->child1()->shouldSpeculateString()) {
+ changed |= mergePrediction(SpecString);
+ break;
+ }
+ } else {
+ if (node->child1()->shouldSpeculateString()) {
+ changed |= mergePrediction(SpecStringObject);
+ break;
+ }
+ }
+
+ SpeculatedType prediction = node->child1()->prediction();
+ if (prediction) {
+ if (prediction & ~SpecObject) {
+ // Wrapper objects are created only in sloppy mode.
+ if (ecmaMode != StrictMode) {
+ prediction &= SpecObject;
+ prediction = mergeSpeculations(prediction, SpecObjectOther);
+ }
+ }
+ changed |= mergePrediction(prediction);
}
break;
}
- case ArithMod: {
+ case ToPrimitive: {
+ SpeculatedType child = node->child1()->prediction();
+ if (child)
+ changed |= mergePrediction(resultOfToPrimitive(child));
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ m_changed |= changed;
+ }
+
+ void propagateForward()
+ {
+ for (Node* node : m_dependentNodes) {
+ m_currentNode = node;
+ propagate(m_currentNode);
+ }
+ }
+
+ void propagateBackward()
+ {
+ for (unsigned i = m_dependentNodes.size(); i--;) {
+ m_currentNode = m_dependentNodes[i];
+ propagate(m_currentNode);
+ }
+ }
+
+ void doDoubleVoting(Node* node, float weight)
+ {
+ // Loop pre-headers created by OSR entrypoint creation may have NaN weight to indicate
+ // that we actually don't know they weight. Assume that they execute once. This turns
+ // out to be an OK assumption since the pre-header doesn't have any meaningful code.
+ if (weight != weight)
+ weight = 1;
+
+ switch (node->op()) {
+ case ValueAdd:
+ case ArithAdd:
+ case ArithSub: {
+ SpeculatedType left = node->child1()->prediction();
+ SpeculatedType right = node->child2()->prediction();
+
+ DoubleBallot ballot;
+
+ if (isFullNumberSpeculation(left)
+ && isFullNumberSpeculation(right)
+ && !m_graph.addShouldSpeculateInt32(node, m_pass)
+ && !m_graph.addShouldSpeculateAnyInt(node))
+ ballot = VoteDouble;
+ else
+ ballot = VoteValue;
+
+ m_graph.voteNode(node->child1(), ballot, weight);
+ m_graph.voteNode(node->child2(), ballot, weight);
+ break;
+ }
+
+ case ArithMul: {
+ SpeculatedType left = node->child1()->prediction();
+ SpeculatedType right = node->child2()->prediction();
+
+ DoubleBallot ballot;
+
+ if (isFullNumberSpeculation(left)
+ && isFullNumberSpeculation(right)
+ && !m_graph.binaryArithShouldSpeculateInt32(node, m_pass)
+ && !m_graph.binaryArithShouldSpeculateAnyInt(node, m_pass))
+ ballot = VoteDouble;
+ else
+ ballot = VoteValue;
+
+ m_graph.voteNode(node->child1(), ballot, weight);
+ m_graph.voteNode(node->child2(), ballot, weight);
+ break;
+ }
+
+ case ArithMin:
+ case ArithMax:
+ case ArithMod:
+ case ArithDiv: {
SpeculatedType left = node->child1()->prediction();
SpeculatedType right = node->child2()->prediction();
+
+ DoubleBallot ballot;
+
+ if (isFullNumberSpeculation(left)
+ && isFullNumberSpeculation(right)
+ && !m_graph.binaryArithShouldSpeculateInt32(node, m_pass))
+ ballot = VoteDouble;
+ else
+ ballot = VoteValue;
+
+ m_graph.voteNode(node->child1(), ballot, weight);
+ m_graph.voteNode(node->child2(), ballot, weight);
+ break;
+ }
+
+ case ArithAbs:
+ DoubleBallot ballot;
+ if (node->child1()->shouldSpeculateNumber()
+ && !m_graph.unaryArithShouldSpeculateInt32(node, m_pass))
+ ballot = VoteDouble;
+ else
+ ballot = VoteValue;
+
+ m_graph.voteNode(node->child1(), ballot, weight);
+ break;
+
+ case ArithSqrt:
+ case ArithCos:
+ case ArithSin:
+ case ArithTan:
+ case ArithLog:
+ if (node->child1()->shouldSpeculateNumber())
+ m_graph.voteNode(node->child1(), VoteDouble, weight);
+ else
+ m_graph.voteNode(node->child1(), VoteValue, weight);
+ break;
+
+ case SetLocal: {
+ SpeculatedType prediction = node->child1()->prediction();
+ if (isDoubleSpeculation(prediction))
+ node->variableAccessData()->vote(VoteDouble, weight);
+ else if (!isFullNumberSpeculation(prediction)
+ || isInt32Speculation(prediction) || isAnyIntSpeculation(prediction))
+ node->variableAccessData()->vote(VoteValue, weight);
+ break;
+ }
+
+ case PutByValDirect:
+ case PutByVal:
+ case PutByValAlias: {
+ Edge child1 = m_graph.varArgChild(node, 0);
+ Edge child2 = m_graph.varArgChild(node, 1);
+ Edge child3 = m_graph.varArgChild(node, 2);
+ m_graph.voteNode(child1, VoteValue, weight);
+ m_graph.voteNode(child2, VoteValue, weight);
+ switch (node->arrayMode().type()) {
+ case Array::Double:
+ m_graph.voteNode(child3, VoteDouble, weight);
+ break;
+ default:
+ m_graph.voteNode(child3, VoteValue, weight);
+ break;
+ }
+ break;
+ }
- if (left && right) {
- if (Node::shouldSpeculateInt32ForArithmetic(node->child1().node(), node->child2().node())
- && nodeCanSpeculateInt32(node->arithNodeFlags()))
- changed |= mergePrediction(SpecInt32);
- else
- changed |= mergePrediction(SpecDouble);
+ case MovHint:
+ // Ignore these since they have no effect on in-DFG execution.
+ break;
+
+ default:
+ m_graph.voteChildren(node, VoteValue, weight);
+ break;
+ }
+ }
+
+ void doRoundOfDoubleVoting()
+ {
+ for (unsigned i = 0; i < m_graph.m_variableAccessData.size(); ++i)
+ m_graph.m_variableAccessData[i].find()->clearVotes();
+ for (BlockIndex blockIndex = 0; blockIndex < m_graph.numBlocks(); ++blockIndex) {
+ BasicBlock* block = m_graph.block(blockIndex);
+ if (!block)
+ continue;
+ ASSERT(block->isReachable);
+ for (unsigned i = 0; i < block->size(); ++i) {
+ m_currentNode = block->at(i);
+ doDoubleVoting(m_currentNode, block->executionCount);
}
+ }
+ for (unsigned i = 0; i < m_graph.m_variableAccessData.size(); ++i) {
+ VariableAccessData* variableAccessData = &m_graph.m_variableAccessData[i];
+ if (!variableAccessData->isRoot())
+ continue;
+ m_changed |= variableAccessData->tallyVotesForShouldUseDoubleFormat();
+ }
+ propagateThroughArgumentPositions();
+ for (unsigned i = 0; i < m_graph.m_variableAccessData.size(); ++i) {
+ VariableAccessData* variableAccessData = &m_graph.m_variableAccessData[i];
+ if (!variableAccessData->isRoot())
+ continue;
+ m_changed |= variableAccessData->makePredictionForDoubleFormat();
+ }
+ }
+
+ void propagateThroughArgumentPositions()
+ {
+ for (unsigned i = 0; i < m_graph.m_argumentPositions.size(); ++i)
+ m_changed |= m_graph.m_argumentPositions[i].mergeArgumentPredictionAwareness();
+ }
+
+ // Sets any predictions that do not depends on other nodes.
+ void processInvariants()
+ {
+ for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
+ for (Node* node : *block) {
+ m_currentNode = node;
+ processInvariantsForNode();
+ }
+ }
+ }
+
+ void processInvariantsForNode()
+ {
+ switch (m_currentNode->op()) {
+ case JSConstant: {
+ SpeculatedType type = speculationFromValue(m_currentNode->asJSValue());
+ if (type == SpecAnyIntAsDouble && enableInt52())
+ type = SpecInt52Only;
+ setPrediction(type);
+ break;
+ }
+ case DoubleConstant: {
+ SpeculatedType type = speculationFromValue(m_currentNode->asJSValue());
+ setPrediction(type);
+ break;
+ }
+ case BitAnd:
+ case BitOr:
+ case BitXor:
+ case BitRShift:
+ case BitLShift:
+ case BitURShift:
+ case ArithIMul:
+ case ArithClz32: {
+ setPrediction(SpecInt32Only);
+ break;
+ }
+
+ case ArrayPop:
+ case ArrayPush:
+ case RegExpExec:
+ case RegExpTest:
+ case StringReplace:
+ case StringReplaceRegExp:
+ case GetById:
+ case GetByIdFlush:
+ case GetByIdWithThis:
+ case TryGetById:
+ case GetByValWithThis:
+ case GetByOffset:
+ case MultiGetByOffset:
+ case GetDirectPname:
+ case Call:
+ case DirectCall:
+ case TailCallInlinedCaller:
+ case DirectTailCallInlinedCaller:
+ case Construct:
+ case DirectConstruct:
+ case CallVarargs:
+ case CallEval:
+ case TailCallVarargsInlinedCaller:
+ case ConstructVarargs:
+ case CallForwardVarargs:
+ case ConstructForwardVarargs:
+ case TailCallForwardVarargsInlinedCaller:
+ case GetGlobalVar:
+ case GetGlobalLexicalVariable:
+ case GetClosureVar:
+ case GetFromArguments:
+ case LoadFromJSMapBucket:
+ case ToNumber:
+ case GetArgument:
+ case CallDOMGetter: {
+ setPrediction(m_currentNode->getHeapPrediction());
+ break;
+ }
+
+ case GetDynamicVar: {
+ setPrediction(SpecBytecodeTop);
break;
}
+ case GetGetterSetterByOffset:
+ case GetExecutable: {
+ setPrediction(SpecCellOther);
+ break;
+ }
+
+ case GetGetter:
+ case GetSetter:
+ case GetCallee:
+ case NewFunction:
+ case NewGeneratorFunction:
+ case NewAsyncFunction: {
+ setPrediction(SpecFunction);
+ break;
+ }
+
+ case GetArgumentCountIncludingThis: {
+ setPrediction(SpecInt32Only);
+ break;
+ }
+
+ case MapHash:
+ setPrediction(SpecInt32Only);
+ break;
+ case GetMapBucket:
+ setPrediction(SpecCellOther);
+ break;
+ case IsNonEmptyMapBucket:
+ setPrediction(SpecBoolean);
+ break;
+
+ case GetRestLength: {
+ setPrediction(SpecInt32Only);
+ break;
+ }
+
+ case GetTypedArrayByteOffset:
+ case GetArrayLength: {
+ setPrediction(SpecInt32Only);
+ break;
+ }
+
+ case StringCharCodeAt: {
+ setPrediction(SpecInt32Only);
+ break;
+ }
+
+ case ToLowerCase:
+ setPrediction(SpecString);
+ break;
+
+ case ArithPow:
case ArithSqrt:
+ case ArithFRound:
case ArithSin:
- case ArithCos: {
- changed |= setPrediction(SpecDouble);
+ case ArithCos:
+ case ArithTan:
+ case ArithLog: {
+ setPrediction(SpecBytecodeDouble);
break;
}
-
- case ArithAbs: {
- SpeculatedType child = node->child1()->prediction();
- if (isInt32SpeculationForArithmetic(child)
- && nodeCanSpeculateInt32(node->arithNodeFlags()))
- changed |= mergePrediction(SpecInt32);
+
+ case ArithRound:
+ case ArithFloor:
+ case ArithCeil:
+ case ArithTrunc: {
+ if (isInt32OrBooleanSpeculation(m_currentNode->getHeapPrediction())
+ && m_graph.roundShouldSpeculateInt32(m_currentNode, m_pass))
+ setPrediction(SpecInt32Only);
else
- changed |= mergePrediction(speculatedDoubleTypeForPrediction(child));
+ setPrediction(SpecBytecodeDouble);
break;
}
-
+
+ case ArithRandom: {
+ setPrediction(SpecDoubleReal);
+ break;
+ }
+ case DeleteByVal:
+ case DeleteById:
case LogicalNot:
case CompareLess:
case CompareLessEq:
case CompareGreater:
case CompareGreaterEq:
case CompareEq:
- case CompareEqConstant:
case CompareStrictEq:
- case CompareStrictEqConstant:
+ case CompareEqPtr:
+ case OverridesHasInstance:
case InstanceOf:
+ case InstanceOfCustom:
+ case IsEmpty:
case IsUndefined:
case IsBoolean:
case IsNumber:
- case IsString:
case IsObject:
- case IsFunction: {
- changed |= setPrediction(SpecBoolean);
+ case IsObjectOrNull:
+ case IsFunction:
+ case IsCellWithType:
+ case IsTypedArrayView: {
+ setPrediction(SpecBoolean);
break;
}
case TypeOf: {
- changed |= setPrediction(SpecString);
+ setPrediction(SpecStringIdent);
break;
}
-
- case GetByVal: {
- if (!node->child1()->prediction())
- break;
- if (!node->getHeapPrediction())
- break;
-
- if (node->child1()->shouldSpeculateFloat32Array()
- || node->child1()->shouldSpeculateFloat64Array())
- changed |= mergePrediction(SpecDouble);
- else if (node->child1()->shouldSpeculateUint32Array()) {
- if (isInt32Speculation(node->getHeapPrediction()))
- changed |= mergePrediction(SpecInt32);
- else
- changed |= mergePrediction(SpecInt52);
- } else
- changed |= mergePrediction(node->getHeapPrediction());
- break;
- }
-
- case GetMyArgumentsLengthSafe: {
- changed |= setPrediction(SpecInt32);
- break;
- }
-
- case GetClosureRegisters:
- case GetButterfly:
+ case GetButterfly:
case GetIndexedPropertyStorage:
case AllocatePropertyStorage:
case ReallocatePropertyStorage: {
- changed |= setPrediction(SpecOther);
+ setPrediction(SpecOther);
break;
}
- case ToThis: {
- SpeculatedType prediction = node->child1()->prediction();
- if (prediction) {
- if (prediction & ~SpecObject) {
- prediction &= SpecObject;
- prediction = mergeSpeculations(prediction, SpecObjectOther);
- }
- changed |= mergePrediction(prediction);
- }
+ case CheckDOM:
+ break;
+
+ case CallObjectConstructor: {
+ setPrediction(SpecObject);
break;
}
-
- case GetMyScope:
- case SkipTopScope:
- case SkipScope: {
- changed |= setPrediction(SpecObjectOther);
+ case SkipScope:
+ case GetGlobalObject: {
+ setPrediction(SpecObjectOther);
break;
}
-
- case GetCallee: {
- changed |= setPrediction(SpecFunction);
+
+ case ResolveScope: {
+ setPrediction(SpecObjectOther);
break;
}
case CreateThis:
case NewObject: {
- changed |= setPrediction(SpecFinalObject);
+ setPrediction(SpecFinalObject);
break;
}
+ case ArraySlice:
+ case NewArrayWithSpread:
case NewArray:
case NewArrayWithSize:
+ case CreateRest:
case NewArrayBuffer: {
- changed |= setPrediction(SpecArray);
+ setPrediction(SpecArray);
break;
}
+
+ case Spread:
+ setPrediction(SpecCellOther);
+ break;
case NewTypedArray: {
- changed |= setPrediction(speculationFromTypedArrayType(node->typedArrayType()));
+ setPrediction(speculationFromTypedArrayType(m_currentNode->typedArrayType()));
+ break;
+ }
+
+ case NewRegexp: {
+ setPrediction(SpecRegExpObject);
break;
}
- case NewRegexp:
case CreateActivation: {
- changed |= setPrediction(SpecObjectOther);
+ setPrediction(SpecObjectOther);
break;
}
case StringFromCharCode: {
- changed |= setPrediction(SpecString);
- changed |= node->child1()->mergeFlags(NodeBytecodeUsesAsNumber | NodeBytecodeUsesAsInt);
+ setPrediction(SpecString);
+ m_currentNode->child1()->mergeFlags(NodeBytecodeUsesAsNumber | NodeBytecodeUsesAsInt);
break;
}
case StringCharAt:
+ case CallStringConstructor:
case ToString:
- case MakeRope: {
- changed |= setPrediction(SpecString);
+ case NumberToStringWithRadix:
+ case MakeRope:
+ case StrCat: {
+ setPrediction(SpecString);
break;
}
-
- case ToPrimitive: {
- SpeculatedType child = node->child1()->prediction();
- if (child)
- changed |= mergePrediction(resultOfToPrimitive(child));
+ case NewStringObject: {
+ setPrediction(SpecStringObject);
break;
}
- case NewStringObject: {
- changed |= setPrediction(SpecStringObject);
+ case CreateDirectArguments: {
+ setPrediction(SpecDirectArguments);
break;
}
- case CreateArguments: {
- changed |= setPrediction(SpecArguments);
+ case CreateScopedArguments: {
+ setPrediction(SpecScopedArguments);
break;
}
- case NewFunction: {
- SpeculatedType child = node->child1()->prediction();
- if (child & SpecEmpty)
- changed |= mergePrediction((child & ~SpecEmpty) | SpecFunction);
- else
- changed |= mergePrediction(child);
+ case CreateClonedArguments: {
+ setPrediction(SpecObjectOther);
break;
}
- case NewFunctionNoCheck:
- case NewFunctionExpression: {
- changed |= setPrediction(SpecFunction);
+ case FiatInt52: {
+ RELEASE_ASSERT(enableInt52());
+ setPrediction(SpecAnyInt);
break;
}
-
+
+ case GetScope:
+ setPrediction(SpecObjectOther);
+ break;
+
+ case In:
+ setPrediction(SpecBoolean);
+ break;
+
+ case HasOwnProperty:
+ setPrediction(SpecBoolean);
+ break;
+
+ case GetEnumerableLength: {
+ setPrediction(SpecInt32Only);
+ break;
+ }
+ case HasGenericProperty:
+ case HasStructureProperty:
+ case HasIndexedProperty: {
+ setPrediction(SpecBoolean);
+ break;
+ }
+ case GetPropertyEnumerator: {
+ setPrediction(SpecCell);
+ break;
+ }
+ case GetEnumeratorStructurePname: {
+ setPrediction(SpecCell | SpecOther);
+ break;
+ }
+ case GetEnumeratorGenericPname: {
+ setPrediction(SpecCell | SpecOther);
+ break;
+ }
+ case ToIndexString: {
+ setPrediction(SpecString);
+ break;
+ }
+ case ParseInt: {
+ // We expect this node to almost always produce an int32. However,
+ // it's possible it produces NaN or integers out of int32 range. We
+ // rely on the heap prediction since the parseInt() call profiled
+ // its result.
+ setPrediction(m_currentNode->getHeapPrediction());
+ break;
+ }
+
+ case GetLocal:
+ case SetLocal:
+ case UInt32ToNumber:
+ case ValueAdd:
+ case ArithAdd:
+ case ArithSub:
+ case ArithNegate:
+ case ArithMin:
+ case ArithMax:
+ case ArithMul:
+ case ArithDiv:
+ case ArithMod:
+ case ArithAbs:
+ case GetByVal:
+ case ToThis:
+ case ToPrimitive: {
+ m_dependentNodes.append(m_currentNode);
+ break;
+ }
+
case PutByValAlias:
- case GetArrayLength:
- case GetTypedArrayByteOffset:
- case Int32ToDouble:
case DoubleAsInt32:
case GetLocalUnlinked:
- case GetMyArgumentsLength:
- case GetMyArgumentByVal:
- case PhantomPutStructure:
- case PhantomArguments:
case CheckArray:
+ case CheckTypeInfoFlags:
case Arrayify:
case ArrayifyToStructure:
case CheckTierUpInLoop:
case CheckTierUpAtReturn:
case CheckTierUpAndOSREnter:
case InvalidationPoint:
- case Int52ToValue:
- case Int52ToDouble:
case CheckInBounds:
- case ValueToInt32: {
+ case ValueToInt32:
+ case DoubleRep:
+ case ValueRep:
+ case Int52Rep:
+ case Int52Constant:
+ case Identity:
+ case BooleanToNumber:
+ case PhantomNewObject:
+ case PhantomNewFunction:
+ case PhantomNewGeneratorFunction:
+ case PhantomNewAsyncFunction:
+ case PhantomCreateActivation:
+ case PhantomDirectArguments:
+ case PhantomCreateRest:
+ case PhantomSpread:
+ case PhantomNewArrayWithSpread:
+ case PhantomClonedArguments:
+ case GetMyArgumentByVal:
+ case GetMyArgumentByValOutOfBounds:
+ case PutHint:
+ case CheckStructureImmediate:
+ case MaterializeNewObject:
+ case MaterializeCreateActivation:
+ case PutStack:
+ case KillStack:
+ case StoreBarrier:
+ case FencedStoreBarrier:
+ case GetStack:
+ case GetRegExpObjectLastIndex:
+ case SetRegExpObjectLastIndex:
+ case RecordRegExpCachedResult:
+ case LazyJSConstant:
+ case CallDOM: {
// This node should never be visible at this stage of compilation. It is
// inserted by fixup(), which follows this phase.
- RELEASE_ASSERT_NOT_REACHED();
+ DFG_CRASH(m_graph, m_currentNode, "Unexpected node during prediction propagation");
break;
}
@@ -520,69 +1056,72 @@ private:
break;
case Upsilon:
- case GetArgument:
// These don't get inserted until we go into SSA.
RELEASE_ASSERT_NOT_REACHED();
break;
- case GetScope:
- changed |= setPrediction(SpecObjectOther);
- break;
-
- case In:
- changed |= setPrediction(SpecBoolean);
- break;
-
- case Identity:
- changed |= mergePrediction(node->child1()->prediction());
- break;
-
#ifndef NDEBUG
// These get ignored because they don't return anything.
- case StoreBarrier:
- case ConditionalStoreBarrier:
- case StoreBarrierWithNullCheck:
case PutByValDirect:
+ case PutByValWithThis:
+ case PutByIdWithThis:
case PutByVal:
case PutClosureVar:
+ case PutToArguments:
case Return:
+ case TailCall:
+ case DirectTailCall:
+ case TailCallVarargs:
+ case TailCallForwardVarargs:
case Throw:
case PutById:
+ case PutByIdFlush:
case PutByIdDirect:
case PutByOffset:
+ case MultiPutByOffset:
+ case PutGetterById:
+ case PutSetterById:
+ case PutGetterSetterById:
+ case PutGetterByVal:
+ case PutSetterByVal:
+ case DefineDataProperty:
+ case DefineAccessorProperty:
case DFG::Jump:
case Branch:
case Switch:
- case Breakpoint:
- case ProfileWillCall:
- case ProfileDidCall:
- case CheckHasInstance:
- case ThrowReferenceError:
+ case ProfileType:
+ case ProfileControlFlow:
+ case ThrowStaticError:
case ForceOSRExit:
case SetArgument:
+ case SetFunctionName:
case CheckStructure:
- case CheckExecutable:
- case StructureTransitionWatchpoint:
- case CheckFunction:
+ case CheckCell:
+ case CheckNotEmpty:
+ case CheckStringIdent:
+ case CheckBadCell:
case PutStructure:
- case TearOffActivation:
- case TearOffArguments:
- case CheckArgumentsNotCreated:
- case VariableWatchpoint:
- case VarInjectionWatchpoint:
- case AllocationProfileWatchpoint:
case Phantom:
case Check:
- case PutGlobalVar:
+ case PutGlobalVariable:
case CheckWatchdogTimer:
+ case LogShadowChickenPrologue:
+ case LogShadowChickenTail:
case Unreachable:
case LoopHint:
case NotifyWrite:
- case FunctionReentryWatchpoint:
- case TypedArrayWatchpoint:
case ConstantStoragePointer:
case MovHint:
case ZombieHint:
+ case ExitOK:
+ case LoadVarargs:
+ case ForwardVarargs:
+ case PutDynamicVar:
+ case NukeStructureAndSetButterfly:
+ break;
+
+ // This gets ignored because it only pretends to produce a value.
+ case BottomValue:
break;
// This gets ignored because it already has a prediction.
@@ -603,192 +1142,32 @@ private:
break;
#endif
}
-
- m_changed |= changed;
- }
-
- void propagateForward()
- {
- for (BlockIndex blockIndex = 0; blockIndex < m_graph.numBlocks(); ++blockIndex) {
- BasicBlock* block = m_graph.block(blockIndex);
- if (!block)
- continue;
- ASSERT(block->isReachable);
- for (unsigned i = 0; i < block->size(); ++i) {
- m_currentNode = block->at(i);
- propagate(m_currentNode);
- }
- }
}
-
- void propagateBackward()
- {
- for (BlockIndex blockIndex = m_graph.numBlocks(); blockIndex--;) {
- BasicBlock* block = m_graph.block(blockIndex);
- if (!block)
- continue;
- ASSERT(block->isReachable);
- for (unsigned i = block->size(); i--;) {
- m_currentNode = block->at(i);
- propagate(m_currentNode);
- }
- }
- }
-
- void doDoubleVoting(Node* node)
+
+ SpeculatedType resultOfToPrimitive(SpeculatedType type)
{
- switch (node->op()) {
- case ValueAdd:
- case ArithAdd:
- case ArithSub: {
- SpeculatedType left = node->child1()->prediction();
- SpeculatedType right = node->child2()->prediction();
-
- DoubleBallot ballot;
-
- if (isFullNumberSpeculationExpectingDefined(left) && isFullNumberSpeculationExpectingDefined(right)
- && !m_graph.addShouldSpeculateInt32(node)
- && !m_graph.addShouldSpeculateMachineInt(node))
- ballot = VoteDouble;
- else
- ballot = VoteValue;
-
- m_graph.voteNode(node->child1(), ballot);
- m_graph.voteNode(node->child2(), ballot);
- break;
- }
-
- case ArithMul: {
- SpeculatedType left = node->child1()->prediction();
- SpeculatedType right = node->child2()->prediction();
-
- DoubleBallot ballot;
-
- if (isFullNumberSpeculation(left) && isFullNumberSpeculation(right)
- && !m_graph.mulShouldSpeculateInt32(node)
- && !m_graph.mulShouldSpeculateMachineInt(node))
- ballot = VoteDouble;
- else
- ballot = VoteValue;
-
- m_graph.voteNode(node->child1(), ballot);
- m_graph.voteNode(node->child2(), ballot);
- break;
- }
+ if (type & SpecObject) {
+ // We try to be optimistic here about StringObjects since it's unlikely that
+ // someone overrides the valueOf or toString methods.
+ if (type & SpecStringObject && m_graph.canOptimizeStringObjectAccess(m_currentNode->origin.semantic))
+ return mergeSpeculations(type & ~SpecObject, SpecString);
- case ArithMin:
- case ArithMax:
- case ArithMod:
- case ArithDiv: {
- SpeculatedType left = node->child1()->prediction();
- SpeculatedType right = node->child2()->prediction();
-
- DoubleBallot ballot;
-
- if (isFullNumberSpeculation(left) && isFullNumberSpeculation(right)
- && !(Node::shouldSpeculateInt32ForArithmetic(node->child1().node(), node->child2().node()) && node->canSpeculateInt32()))
- ballot = VoteDouble;
- else
- ballot = VoteValue;
-
- m_graph.voteNode(node->child1(), ballot);
- m_graph.voteNode(node->child2(), ballot);
- break;
- }
-
- case ArithAbs:
- DoubleBallot ballot;
- if (!(node->child1()->shouldSpeculateInt32ForArithmetic() && node->canSpeculateInt32()))
- ballot = VoteDouble;
- else
- ballot = VoteValue;
-
- m_graph.voteNode(node->child1(), ballot);
- break;
-
- case ArithSqrt:
- case ArithCos:
- case ArithSin:
- m_graph.voteNode(node->child1(), VoteDouble);
- break;
-
- case SetLocal: {
- SpeculatedType prediction = node->child1()->prediction();
- if (isDoubleSpeculation(prediction))
- node->variableAccessData()->vote(VoteDouble);
- else if (
- !isFullNumberSpeculation(prediction)
- || isInt32Speculation(prediction) || isMachineIntSpeculation(prediction))
- node->variableAccessData()->vote(VoteValue);
- break;
+ return mergeSpeculations(type & ~SpecObject, SpecPrimitive);
}
- case PutByValDirect:
- case PutByVal:
- case PutByValAlias: {
- Edge child1 = m_graph.varArgChild(node, 0);
- Edge child2 = m_graph.varArgChild(node, 1);
- Edge child3 = m_graph.varArgChild(node, 2);
- m_graph.voteNode(child1, VoteValue);
- m_graph.voteNode(child2, VoteValue);
- switch (node->arrayMode().type()) {
- case Array::Double:
- m_graph.voteNode(child3, VoteDouble);
- break;
- default:
- m_graph.voteNode(child3, VoteValue);
- break;
- }
- break;
- }
-
- case MovHint:
- // Ignore these since they have no effect on in-DFG execution.
- break;
-
- default:
- m_graph.voteChildren(node, VoteValue);
- break;
- }
+ return type;
}
-
- void doRoundOfDoubleVoting()
- {
- for (unsigned i = 0; i < m_graph.m_variableAccessData.size(); ++i)
- m_graph.m_variableAccessData[i].find()->clearVotes();
- for (BlockIndex blockIndex = 0; blockIndex < m_graph.numBlocks(); ++blockIndex) {
- BasicBlock* block = m_graph.block(blockIndex);
- if (!block)
- continue;
- ASSERT(block->isReachable);
- for (unsigned i = 0; i < block->size(); ++i) {
- m_currentNode = block->at(i);
- doDoubleVoting(m_currentNode);
- }
- }
- for (unsigned i = 0; i < m_graph.m_variableAccessData.size(); ++i) {
- VariableAccessData* variableAccessData = &m_graph.m_variableAccessData[i];
- if (!variableAccessData->isRoot())
- continue;
- m_changed |= variableAccessData->tallyVotesForShouldUseDoubleFormat();
- }
- for (unsigned i = 0; i < m_graph.m_argumentPositions.size(); ++i)
- m_changed |= m_graph.m_argumentPositions[i].mergeArgumentPredictionAwareness();
- for (unsigned i = 0; i < m_graph.m_variableAccessData.size(); ++i) {
- VariableAccessData* variableAccessData = &m_graph.m_variableAccessData[i];
- if (!variableAccessData->isRoot())
- continue;
- m_changed |= variableAccessData->makePredictionForDoubleFormat();
- }
- }
-
+
+ Vector<Node*> m_dependentNodes;
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.
};
+
+} // Anonymous namespace.
bool performPredictionPropagation(Graph& graph)
{
- SamplingRegion samplingRegion("DFG Prediction Propagation Phase");
return runPhase<PredictionPropagationPhase>(graph);
}