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.cpp263
1 files changed, 260 insertions, 3 deletions
diff --git a/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp b/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp
index 842bcc236..3eeb70e05 100644
--- a/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp
@@ -141,6 +141,22 @@ private:
return NoNode;
}
+ NodeIndex weakConstantCSE(Node& node)
+ {
+ for (unsigned i = endIndexForPureCSE(); i--;) {
+ NodeIndex index = m_currentBlock->at(i);
+ Node& otherNode = m_graph[index];
+ if (otherNode.op() != WeakJSConstant)
+ continue;
+
+ if (otherNode.weakConstant() != node.weakConstant())
+ continue;
+
+ return index;
+ }
+ return NoNode;
+ }
+
NodeIndex impureCSE(Node& node)
{
NodeIndex child1 = canonicalize(node.child1());
@@ -200,6 +216,33 @@ private:
return NoNode;
}
+ NodeIndex globalVarStoreElimination(unsigned varNumber, JSGlobalObject* globalObject)
+ {
+ for (unsigned i = m_indexInBlock; i--;) {
+ NodeIndex index = m_currentBlock->at(i);
+ Node& node = m_graph[index];
+ if (!node.shouldGenerate())
+ continue;
+ switch (node.op()) {
+ case PutGlobalVar:
+ if (node.varNumber() == varNumber && codeBlock()->globalObjectFor(node.codeOrigin) == globalObject)
+ return index;
+ break;
+
+ case GetGlobalVar:
+ if (node.varNumber() == varNumber && codeBlock()->globalObjectFor(node.codeOrigin) == globalObject)
+ return NoNode;
+ break;
+
+ default:
+ break;
+ }
+ if (m_graph.clobbersWorld(index) || node.canExit())
+ return NoNode;
+ }
+ return NoNode;
+ }
+
NodeIndex getByValLoadElimination(NodeIndex child1, NodeIndex child2)
{
for (unsigned i = m_indexInBlock; i--;) {
@@ -304,6 +347,56 @@ private:
return false;
}
+ NodeIndex putStructureStoreElimination(NodeIndex child1)
+ {
+ for (unsigned i = m_indexInBlock; i--;) {
+ NodeIndex index = m_currentBlock->at(i);
+ if (index == child1)
+ break;
+ Node& node = m_graph[index];
+ if (!node.shouldGenerate())
+ break;
+ switch (node.op()) {
+ case CheckStructure:
+ return NoNode;
+
+ case PhantomPutStructure:
+ if (node.child1() == child1) // No need to retrace our steps.
+ return NoNode;
+ break;
+
+ case PutStructure:
+ if (node.child1() == child1)
+ return index;
+ break;
+
+ // PutStructure needs to execute if we GC. Hence this needs to
+ // be careful with respect to nodes that GC.
+ case CreateArguments:
+ case TearOffArguments:
+ case NewFunctionNoCheck:
+ case NewFunction:
+ case NewFunctionExpression:
+ case CreateActivation:
+ case TearOffActivation:
+ case StrCat:
+ case ToPrimitive:
+ case NewRegexp:
+ case NewArrayBuffer:
+ case NewArray:
+ case NewObject:
+ case CreateThis:
+ return NoNode;
+
+ default:
+ break;
+ }
+ if (m_graph.clobbersWorld(index) || node.canExit())
+ return NoNode;
+ }
+ return NoNode;
+ }
+
NodeIndex getByOffsetLoadElimination(unsigned identifierNumber, NodeIndex child1)
{
for (unsigned i = m_indexInBlock; i--;) {
@@ -350,6 +443,52 @@ private:
return NoNode;
}
+ NodeIndex putByOffsetStoreElimination(unsigned identifierNumber, NodeIndex child1)
+ {
+ for (unsigned i = m_indexInBlock; i--;) {
+ NodeIndex index = m_currentBlock->at(i);
+ if (index == child1)
+ break;
+
+ Node& node = m_graph[index];
+ if (!node.shouldGenerate())
+ continue;
+ switch (node.op()) {
+ case GetByOffset:
+ if (m_graph.m_storageAccessData[node.storageAccessDataIndex()].identifierNumber == identifierNumber)
+ return NoNode;
+ break;
+
+ case PutByOffset:
+ if (m_graph.m_storageAccessData[node.storageAccessDataIndex()].identifierNumber == identifierNumber) {
+ if (node.child1() == child1) // Must be same property storage.
+ return index;
+ return NoNode;
+ }
+ break;
+
+ case PutByVal:
+ case PutByValAlias:
+ case GetByVal:
+ 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.
+ break;
+ }
+ return NoNode;
+
+ default:
+ if (m_graph.clobbersWorld(index))
+ return NoNode;
+ break;
+ }
+ if (node.canExit())
+ return NoNode;
+ }
+ return NoNode;
+ }
+
NodeIndex getPropertyStorageLoadElimination(NodeIndex child1)
{
for (unsigned i = m_indexInBlock; i--;) {
@@ -480,6 +619,58 @@ private:
return NoNode;
}
+ // This returns the Flush node that is keeping a SetLocal alive.
+ NodeIndex setLocalStoreElimination(VirtualRegister local)
+ {
+ for (unsigned i = m_indexInBlock; i--;) {
+ NodeIndex index = m_currentBlock->at(i);
+ Node& node = m_graph[index];
+ if (!node.shouldGenerate())
+ continue;
+ switch (node.op()) {
+ case GetLocal:
+ case SetLocal:
+ if (node.local() == local)
+ return NoNode;
+ break;
+
+ case GetLocalUnlinked:
+ if (node.unlinkedLocal() == local)
+ return NoNode;
+ break;
+
+ case Flush: {
+ if (node.local() != local)
+ break;
+ if (!i)
+ break;
+ NodeIndex prevIndex = m_currentBlock->at(i - 1);
+ if (prevIndex != node.child1().index())
+ break;
+ ASSERT(m_graph[prevIndex].local() == local);
+ ASSERT(m_graph[prevIndex].variableAccessData() == node.variableAccessData());
+ ASSERT(m_graph[prevIndex].shouldGenerate());
+ if (m_graph[prevIndex].refCount() > 1)
+ break;
+ return index;
+ }
+
+ case GetScopeChain:
+ if (m_graph.uncheckedActivationRegisterFor(node.codeOrigin) == local)
+ return NoNode;
+ break;
+
+ default:
+ if (m_graph.clobbersWorld(index))
+ return NoNode;
+ break;
+ }
+ if (node.canExit())
+ return NoNode;
+ }
+ return NoNode;
+ }
+
void performSubstitution(Edge& child, bool addRef = true)
{
// Check if this operand is actually unused.
@@ -501,14 +692,16 @@ private:
m_graph[child].ref();
}
- bool setReplacement(NodeIndex replacement)
+ enum PredictionHandlingMode { RequireSamePrediction, AllowPredictionMismatch };
+ bool setReplacement(NodeIndex replacement, PredictionHandlingMode predictionHandlingMode = RequireSamePrediction)
{
if (replacement == NoNode)
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())
+ if (predictionHandlingMode == RequireSamePrediction
+ && m_graph[m_compileIndex].prediction() != m_graph[replacement].prediction())
return false;
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
@@ -537,6 +730,17 @@ private:
node.setOpAndDefaultFlags(Phantom);
}
+ void eliminate(NodeIndex nodeIndex, NodeType phantomType = Phantom)
+ {
+ if (nodeIndex == NoNode)
+ return;
+ Node& node = m_graph[nodeIndex];
+ if (node.refCount() != 1)
+ return;
+ ASSERT(node.mustGenerate());
+ node.setOpAndDefaultFlags(phantomType);
+ }
+
void performNodeCSE(Node& node)
{
bool shouldGenerate = node.shouldGenerate();
@@ -622,6 +826,12 @@ private:
NodeIndex phiIndex = node.child1().index();
if (!setReplacement(possibleReplacement))
break;
+ // If the GetLocal we replaced used to refer to a SetLocal, then it now
+ // should refer to the child of the SetLocal instead.
+ if (m_graph[phiIndex].op() == SetLocal) {
+ ASSERT(node.child1().index() == phiIndex);
+ m_graph.changeEdge(node.children.child1(), m_graph[phiIndex].child1());
+ }
NodeIndex oldTailIndex = m_currentBlock->variablesAtTail.operand(
variableAccessData->local());
if (oldTailIndex == m_compileIndex) {
@@ -645,10 +855,39 @@ private:
break;
}
+ case SetLocal: {
+ if (m_fixpointState == FixpointNotConverged)
+ break;
+ VariableAccessData* variableAccessData = node.variableAccessData();
+ if (!variableAccessData->isCaptured())
+ break;
+ VirtualRegister local = variableAccessData->local();
+ NodeIndex replacementIndex = setLocalStoreElimination(local);
+ if (replacementIndex == NoNode)
+ break;
+ Node& replacement = m_graph[replacementIndex];
+ ASSERT(replacement.op() == Flush);
+ ASSERT(replacement.refCount() == 1);
+ ASSERT(replacement.shouldGenerate());
+ ASSERT(replacement.mustGenerate());
+ replacement.setOpAndDefaultFlags(Phantom);
+ NodeIndex setLocalIndex = replacement.child1().index();
+ ASSERT(m_graph[setLocalIndex].op() == SetLocal);
+ m_graph.clearAndDerefChild1(replacement);
+ replacement.children.child1() = m_graph[setLocalIndex].child1();
+ m_graph.ref(replacement.child1());
+ 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));
+ setReplacement(constantCSE(node), AllowPredictionMismatch);
+ break;
+
+ case WeakJSConstant:
+ // FIXME: have CSE for weak constants against strong constants and vice-versa.
+ setReplacement(weakConstantCSE(node));
break;
case GetArrayLength:
@@ -681,6 +920,12 @@ private:
setReplacement(globalVarLoadElimination(node.varNumber(), codeBlock()->globalObjectFor(node.codeOrigin)));
break;
+ case PutGlobalVar:
+ if (m_fixpointState == FixpointNotConverged)
+ break;
+ eliminate(globalVarStoreElimination(node.varNumber(), codeBlock()->globalObjectFor(node.codeOrigin)));
+ break;
+
case GetByVal:
if (m_graph.byValIsPure(node))
setReplacement(getByValLoadElimination(node.child1().index(), node.child2().index()));
@@ -697,6 +942,12 @@ private:
if (checkStructureLoadElimination(node.structureSet(), node.child1().index()))
eliminate();
break;
+
+ case PutStructure:
+ if (m_fixpointState == FixpointNotConverged)
+ break;
+ eliminate(putStructureStoreElimination(node.child1().index()), PhantomPutStructure);
+ break;
case CheckFunction:
if (checkFunctionElimination(node.function(), node.child1().index()))
@@ -718,6 +969,12 @@ private:
setReplacement(getByOffsetLoadElimination(m_graph.m_storageAccessData[node.storageAccessDataIndex()].identifierNumber, node.child1().index()));
break;
+ case PutByOffset:
+ if (m_fixpointState == FixpointNotConverged)
+ break;
+ eliminate(putByOffsetStoreElimination(m_graph.m_storageAccessData[node.storageAccessDataIndex()].identifierNumber, node.child1().index()));
+ break;
+
default:
// do nothing.
break;