diff options
Diffstat (limited to 'Source/JavaScriptCore/dfg/DFGCSEPhase.cpp')
-rw-r--r-- | Source/JavaScriptCore/dfg/DFGCSEPhase.cpp | 217 |
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 = █ + 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 |