summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/JavaScriptCore/dfg/DFGCSEPhase.cpp')
-rw-r--r--Source/JavaScriptCore/dfg/DFGCSEPhase.cpp217
1 files changed, 135 insertions, 82 deletions
diff --git a/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp b/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp
index 020b1cfd2..842bcc236 100644
--- a/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp
@@ -35,8 +35,9 @@ namespace JSC { namespace DFG {
class CSEPhase : public Phase {
public:
- CSEPhase(Graph& graph)
+ CSEPhase(Graph& graph, OptimizationFixpointState fixpointState)
: Phase(graph, "common subexpression elimination")
+ , m_fixpointState(fixpointState)
{
// Replacements are used to implement local common subexpression elimination.
m_replacements.resize(m_graph.size());
@@ -45,10 +46,11 @@ public:
m_replacements[i] = NoNode;
}
- void run()
+ bool run()
{
for (unsigned block = 0; block < m_graph.m_blocks.size(); ++block)
- performBlockCSE(*m_graph.m_blocks[block]);
+ performBlockCSE(m_graph.m_blocks[block].get());
+ return true; // Maybe we'll need to make this reason about whether it changed the graph in an actionable way?
}
private:
@@ -123,50 +125,20 @@ private:
return NoNode;
}
- bool isPredictedNumerical(Node& node)
+ NodeIndex constantCSE(Node& node)
{
- PredictedType left = m_graph[node.child1()].prediction();
- PredictedType right = m_graph[node.child2()].prediction();
- return isNumberPrediction(left) && isNumberPrediction(right);
- }
-
- bool logicalNotIsPure(Node& node)
- {
- PredictedType prediction = m_graph[node.child1()].prediction();
- return isBooleanPrediction(prediction) || !prediction;
- }
-
- bool byValIsPure(Node& node)
- {
- return m_graph[node.child2()].shouldSpeculateInteger()
- && ((node.op() == PutByVal || node.op() == PutByValAlias)
- ? isActionableMutableArrayPrediction(m_graph[node.child1()].prediction())
- : isActionableArrayPrediction(m_graph[node.child1()].prediction()));
- }
-
- bool clobbersWorld(NodeIndex nodeIndex)
- {
- Node& node = m_graph[nodeIndex];
- if (node.flags() & NodeClobbersWorld)
- return true;
- if (!(node.flags() & NodeMightClobber))
- return false;
- switch (node.op()) {
- case ValueAdd:
- case CompareLess:
- case CompareLessEq:
- case CompareGreater:
- case CompareGreaterEq:
- case CompareEq:
- return !isPredictedNumerical(node);
- case LogicalNot:
- return !logicalNotIsPure(node);
- case GetByVal:
- return !byValIsPure(node);
- default:
- ASSERT_NOT_REACHED();
- return true; // If by some oddity we hit this case in release build it's safer to have CSE assume the worst.
+ for (unsigned i = endIndexForPureCSE(); i--;) {
+ NodeIndex index = m_currentBlock->at(i);
+ Node& otherNode = m_graph[index];
+ if (otherNode.op() != JSConstant)
+ continue;
+
+ if (otherNode.constantNumber() != node.constantNumber())
+ continue;
+
+ return index;
}
+ return NoNode;
}
NodeIndex impureCSE(Node& node)
@@ -199,7 +171,7 @@ private:
}
}
}
- if (clobbersWorld(index))
+ if (m_graph.clobbersWorld(index))
break;
}
return NoNode;
@@ -222,7 +194,7 @@ private:
default:
break;
}
- if (clobbersWorld(index))
+ if (m_graph.clobbersWorld(index))
break;
}
return NoNode;
@@ -238,14 +210,14 @@ private:
Node& node = m_graph[index];
switch (node.op()) {
case GetByVal:
- if (!byValIsPure(node))
+ if (!m_graph.byValIsPure(node))
return NoNode;
if (node.child1() == child1 && canonicalize(node.child2()) == canonicalize(child2))
return index;
break;
case PutByVal:
case PutByValAlias:
- if (!byValIsPure(node))
+ if (!m_graph.byValIsPure(node))
return NoNode;
if (node.child1() == child1 && canonicalize(node.child2()) == canonicalize(child2))
return node.child3().index();
@@ -264,7 +236,7 @@ private:
// A push cannot affect previously existing elements in the array.
break;
default:
- if (clobbersWorld(index))
+ if (m_graph.clobbersWorld(index))
return NoNode;
break;
}
@@ -315,7 +287,7 @@ private:
case PutByVal:
case PutByValAlias:
- if (byValIsPure(node)) {
+ if (m_graph.byValIsPure(node)) {
// If PutByVal speculates that it's accessing an array with an
// integer index, then it's impossible for it to cause a structure
// change.
@@ -324,7 +296,7 @@ private:
return false;
default:
- if (clobbersWorld(index))
+ if (m_graph.clobbersWorld(index))
return false;
break;
}
@@ -336,7 +308,7 @@ private:
{
for (unsigned i = m_indexInBlock; i--;) {
NodeIndex index = m_currentBlock->at(i);
- if (index == child1)
+ if (index == child1)
break;
Node& node = m_graph[index];
@@ -349,7 +321,7 @@ private:
case PutByOffset:
if (m_graph.m_storageAccessData[node.storageAccessDataIndex()].identifierNumber == identifierNumber) {
- if (node.child2() == child1)
+ if (node.child1() == child1) // Must be same property storage.
return node.child3().index();
return NoNode;
}
@@ -361,7 +333,7 @@ private:
case PutByVal:
case PutByValAlias:
- if (byValIsPure(node)) {
+ if (m_graph.byValIsPure(node)) {
// If PutByVal speculates that it's accessing an array with an
// integer index, then it's impossible for it to cause a structure
// change.
@@ -370,7 +342,7 @@ private:
return NoNode;
default:
- if (clobbersWorld(index))
+ if (m_graph.clobbersWorld(index))
return NoNode;
break;
}
@@ -400,7 +372,7 @@ private:
case PutByVal:
case PutByValAlias:
- if (byValIsPure(node)) {
+ if (m_graph.byValIsPure(node)) {
// If PutByVal speculates that it's accessing an array with an
// integer index, then it's impossible for it to cause a structure
// change.
@@ -409,7 +381,7 @@ private:
return NoNode;
default:
- if (clobbersWorld(index))
+ if (m_graph.clobbersWorld(index))
return NoNode;
break;
}
@@ -445,12 +417,12 @@ private:
break;
case PutByVal:
- if (isFixedIndexedStorageObjectPrediction(m_graph[node.child1()].prediction()) && byValIsPure(node))
+ if (isFixedIndexedStorageObjectPrediction(m_graph[node.child1()].prediction()) && m_graph.byValIsPure(node))
break;
return NoNode;
default:
- if (clobbersWorld(index))
+ if (m_graph.clobbersWorld(index))
return NoNode;
break;
}
@@ -470,6 +442,44 @@ private:
return NoNode;
}
+ NodeIndex getLocalLoadElimination(VirtualRegister local, NodeIndex& relevantLocalOp)
+ {
+ relevantLocalOp = NoNode;
+
+ for (unsigned i = m_indexInBlock; i--;) {
+ NodeIndex index = m_currentBlock->at(i);
+ Node& node = m_graph[index];
+ switch (node.op()) {
+ case GetLocal:
+ if (node.local() == local) {
+ relevantLocalOp = index;
+ return index;
+ }
+ break;
+
+ case GetLocalUnlinked:
+ if (node.unlinkedLocal() == local) {
+ relevantLocalOp = index;
+ return index;
+ }
+ break;
+
+ case SetLocal:
+ if (node.local() == local) {
+ relevantLocalOp = index;
+ return node.child1().index();
+ }
+ break;
+
+ default:
+ if (m_graph.clobbersWorld(index))
+ return NoNode;
+ break;
+ }
+ }
+ return NoNode;
+ }
+
void performSubstitution(Edge& child, bool addRef = true)
{
// Check if this operand is actually unused.
@@ -491,15 +501,15 @@ private:
m_graph[child].ref();
}
- void setReplacement(NodeIndex replacement)
+ bool setReplacement(NodeIndex replacement)
{
if (replacement == NoNode)
- return;
+ return false;
// Be safe. Don't try to perform replacements if the predictions don't
// agree.
if (m_graph[m_compileIndex].prediction() != m_graph[replacement].prediction())
- return;
+ return false;
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
dataLog(" Replacing @%u -> @%u", m_compileIndex, replacement);
@@ -511,6 +521,8 @@ private:
// At this point we will eliminate all references to this node.
m_replacements[m_compileIndex] = replacement;
+
+ return true;
}
void eliminate()
@@ -594,9 +606,51 @@ private:
case IsObject:
case IsFunction:
case DoubleAsInt32:
+ case LogicalNot:
setReplacement(pureCSE(node));
break;
+ case GetLocal: {
+ VariableAccessData* variableAccessData = node.variableAccessData();
+ if (!variableAccessData->isCaptured())
+ break;
+ NodeIndex relevantLocalOp;
+ NodeIndex possibleReplacement = getLocalLoadElimination(variableAccessData->local(), relevantLocalOp);
+ ASSERT(relevantLocalOp == NoNode
+ || m_graph[relevantLocalOp].op() == GetLocalUnlinked
+ || m_graph[relevantLocalOp].variableAccessData() == variableAccessData);
+ NodeIndex phiIndex = node.child1().index();
+ if (!setReplacement(possibleReplacement))
+ break;
+ NodeIndex oldTailIndex = m_currentBlock->variablesAtTail.operand(
+ variableAccessData->local());
+ if (oldTailIndex == m_compileIndex) {
+ m_currentBlock->variablesAtTail.operand(variableAccessData->local()) =
+ relevantLocalOp;
+
+ // Maintain graph integrity: since we're replacing a GetLocal with a GetLocalUnlinked,
+ // make sure that the GetLocalUnlinked is now linked.
+ if (m_graph[relevantLocalOp].op() == GetLocalUnlinked) {
+ m_graph[relevantLocalOp].setOp(GetLocal);
+ m_graph[relevantLocalOp].children.child1() = Edge(phiIndex);
+ m_graph.ref(phiIndex);
+ }
+ }
+ break;
+ }
+
+ case GetLocalUnlinked: {
+ NodeIndex relevantLocalOpIgnored;
+ setReplacement(getLocalLoadElimination(node.unlinkedLocal(), relevantLocalOpIgnored));
+ break;
+ }
+
+ case JSConstant:
+ // This is strange, but necessary. Some phases will convert nodes to constants,
+ // which may result in duplicated constants. We use CSE to clean this up.
+ setReplacement(constantCSE(node));
+ break;
+
case GetArrayLength:
setReplacement(impureCSE(node));
break;
@@ -613,18 +667,9 @@ private:
case CompareGreater:
case CompareGreaterEq:
case CompareEq: {
- if (isPredictedNumerical(node)) {
- NodeIndex replacementIndex = pureCSE(node);
- if (replacementIndex != NoNode && isPredictedNumerical(m_graph[replacementIndex]))
- setReplacement(replacementIndex);
- }
- break;
- }
-
- case LogicalNot: {
- if (logicalNotIsPure(node)) {
+ if (m_graph.isPredictedNumerical(node)) {
NodeIndex replacementIndex = pureCSE(node);
- if (replacementIndex != NoNode && logicalNotIsPure(m_graph[replacementIndex]))
+ if (replacementIndex != NoNode && m_graph.isPredictedNumerical(m_graph[replacementIndex]))
setReplacement(replacementIndex);
}
break;
@@ -637,12 +682,14 @@ private:
break;
case GetByVal:
- if (byValIsPure(node))
+ if (m_graph.byValIsPure(node))
setReplacement(getByValLoadElimination(node.child1().index(), node.child2().index()));
break;
case PutByVal:
- if (byValIsPure(node) && getByValLoadElimination(node.child1().index(), node.child2().index()) != NoNode)
+ if (m_graph.byValIsPure(node)
+ && !m_graph[node.child1()].shouldSpeculateArguments()
+ && getByValLoadElimination(node.child1().index(), node.child2().index()) != NoNode)
node.setOp(PutByValAlias);
break;
@@ -682,14 +729,19 @@ private:
#endif
}
- void performBlockCSE(BasicBlock& block)
+ void performBlockCSE(BasicBlock* block)
{
- m_currentBlock = &block;
+ if (!block)
+ return;
+ if (!block->isReachable)
+ return;
+
+ m_currentBlock = block;
for (unsigned i = 0; i < LastNodeType; ++i)
m_lastSeen[i] = UINT_MAX;
- for (m_indexInBlock = 0; m_indexInBlock < block.size(); ++m_indexInBlock) {
- m_compileIndex = block[m_indexInBlock];
+ for (m_indexInBlock = 0; m_indexInBlock < block->size(); ++m_indexInBlock) {
+ m_compileIndex = block->at(m_indexInBlock);
performNodeCSE(m_graph[m_compileIndex]);
}
}
@@ -699,11 +751,12 @@ private:
unsigned m_indexInBlock;
Vector<NodeIndex, 16> m_replacements;
FixedArray<unsigned, LastNodeType> m_lastSeen;
+ OptimizationFixpointState m_fixpointState;
};
-void performCSE(Graph& graph)
+bool performCSE(Graph& graph, OptimizationFixpointState fixpointState)
{
- runPhase<CSEPhase>(graph);
+ return runPhase<CSEPhase>(graph, fixpointState);
}
} } // namespace JSC::DFG