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.cpp225
1 files changed, 189 insertions, 36 deletions
diff --git a/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp b/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp
index 31488cb1c..a362e6e97 100644
--- a/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp
@@ -48,9 +48,10 @@ public:
bool run()
{
+ m_changed = false;
for (unsigned block = 0; block < m_graph.m_blocks.size(); ++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?
+ return m_changed;
}
private:
@@ -193,18 +194,18 @@ private:
return NoNode;
}
- NodeIndex globalVarLoadElimination(unsigned varNumber, JSGlobalObject* globalObject)
+ NodeIndex globalVarLoadElimination(WriteBarrier<Unknown>* registerPointer)
{
for (unsigned i = m_indexInBlock; i--;) {
NodeIndex index = m_currentBlock->at(i);
Node& node = m_graph[index];
switch (node.op()) {
case GetGlobalVar:
- if (node.varNumber() == varNumber && codeBlock()->globalObjectFor(node.codeOrigin) == globalObject)
+ if (node.registerPointer() == registerPointer)
return index;
break;
case PutGlobalVar:
- if (node.varNumber() == varNumber && codeBlock()->globalObjectFor(node.codeOrigin) == globalObject)
+ if (node.registerPointer() == registerPointer)
return node.child1().index();
break;
default:
@@ -216,7 +217,30 @@ private:
return NoNode;
}
- NodeIndex globalVarStoreElimination(unsigned varNumber, JSGlobalObject* globalObject)
+ bool globalVarWatchpointElimination(WriteBarrier<Unknown>* registerPointer)
+ {
+ for (unsigned i = m_indexInBlock; i--;) {
+ NodeIndex index = m_currentBlock->at(i);
+ Node& node = m_graph[index];
+ switch (node.op()) {
+ case GlobalVarWatchpoint:
+ if (node.registerPointer() == registerPointer)
+ return true;
+ break;
+ case PutGlobalVar:
+ if (node.registerPointer() == registerPointer)
+ return false;
+ break;
+ default:
+ break;
+ }
+ if (m_graph.clobbersWorld(index))
+ break;
+ }
+ return false;
+ }
+
+ NodeIndex globalVarStoreElimination(WriteBarrier<Unknown>* registerPointer)
{
for (unsigned i = m_indexInBlock; i--;) {
NodeIndex index = m_currentBlock->at(i);
@@ -225,12 +249,13 @@ private:
continue;
switch (node.op()) {
case PutGlobalVar:
- if (node.varNumber() == varNumber && codeBlock()->globalObjectFor(node.codeOrigin) == globalObject)
+ case PutGlobalVarCheck:
+ if (node.registerPointer() == registerPointer)
return index;
break;
case GetGlobalVar:
- if (node.varNumber() == varNumber && codeBlock()->globalObjectFor(node.codeOrigin) == globalObject)
+ if (node.registerPointer() == registerPointer)
return NoNode;
break;
@@ -316,6 +341,12 @@ private:
return true;
break;
+ case StructureTransitionWatchpoint:
+ if (node.child1() == child1
+ && structureSet.contains(node.structure()))
+ return true;
+ break;
+
case PutStructure:
if (node.child1() == child1
&& structureSet.contains(node.structureTransitionData().newStructure))
@@ -347,6 +378,53 @@ private:
return false;
}
+ bool structureTransitionWatchpointElimination(Structure* structure, NodeIndex child1)
+ {
+ for (unsigned i = m_indexInBlock; i--;) {
+ NodeIndex index = m_currentBlock->at(i);
+ if (index == child1)
+ break;
+
+ Node& node = m_graph[index];
+ switch (node.op()) {
+ case CheckStructure:
+ if (node.child1() == child1
+ && node.structureSet().containsOnly(structure))
+ return true;
+ break;
+
+ case PutStructure:
+ ASSERT(node.structureTransitionData().previousStructure != structure);
+ break;
+
+ case PutByOffset:
+ // Setting a property cannot change the structure.
+ break;
+
+ case PutByVal:
+ case PutByValAlias:
+ 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 false;
+
+ case StructureTransitionWatchpoint:
+ if (node.structure() == structure && node.child1() == child1)
+ return true;
+ break;
+
+ default:
+ if (m_graph.clobbersWorld(index))
+ return false;
+ break;
+ }
+ }
+ return false;
+ }
+
NodeIndex putStructureStoreElimination(NodeIndex child1)
{
for (unsigned i = m_indexInBlock; i--;) {
@@ -538,8 +616,8 @@ private:
Node& node = m_graph[index];
switch (node.op()) {
case GetIndexedPropertyStorage: {
- PredictedType basePrediction = m_graph[node.child2()].prediction();
- bool nodeHasIntegerIndexPrediction = !(!(basePrediction & PredictInt32) && basePrediction);
+ SpeculatedType basePrediction = m_graph[node.child2()].prediction();
+ bool nodeHasIntegerIndexPrediction = !(!(basePrediction & SpecInt32) && basePrediction);
if (node.child1() == child1 && hasIntegerIndexPrediction == nodeHasIntegerIndexPrediction)
return index;
break;
@@ -556,7 +634,7 @@ private:
break;
case PutByVal:
- if (isFixedIndexedStorageObjectPrediction(m_graph[node.child1()].prediction()) && m_graph.byValIsPure(node))
+ if (isFixedIndexedStorageObjectSpeculation(m_graph[node.child1()].prediction()) && m_graph.byValIsPure(node))
break;
return NoNode;
@@ -619,9 +697,22 @@ private:
return NoNode;
}
- // This returns the Flush node that is keeping a SetLocal alive.
- NodeIndex setLocalStoreElimination(VirtualRegister local, NodeIndex expectedNodeIndex)
+ struct SetLocalStoreEliminationResult {
+ SetLocalStoreEliminationResult()
+ : mayBeAccessed(false)
+ , mayExit(false)
+ , mayClobberWorld(false)
+ {
+ }
+
+ bool mayBeAccessed;
+ bool mayExit;
+ bool mayClobberWorld;
+ };
+ SetLocalStoreEliminationResult setLocalStoreElimination(
+ VirtualRegister local, NodeIndex expectedNodeIndex)
{
+ SetLocalStoreEliminationResult result;
for (unsigned i = m_indexInBlock; i--;) {
NodeIndex index = m_currentBlock->at(i);
Node& node = m_graph[index];
@@ -631,46 +722,67 @@ private:
case GetLocal:
case Flush:
if (node.local() == local)
- return NoNode;
+ result.mayBeAccessed = true;
break;
case GetLocalUnlinked:
if (node.unlinkedLocal() == local)
- return NoNode;
+ result.mayBeAccessed = true;
break;
case SetLocal: {
if (node.local() != local)
break;
if (index != expectedNodeIndex)
- return NoNode;
+ result.mayBeAccessed = true;
if (m_graph[index].refCount() > 1)
- return NoNode;
- return index;
+ result.mayBeAccessed = true;
+ return result;
}
case GetScopeChain:
if (m_graph.uncheckedActivationRegisterFor(node.codeOrigin) == local)
- return NoNode;
+ result.mayBeAccessed = true;
+ break;
+
+ case CheckArgumentsNotCreated:
+ case GetMyArgumentsLength:
+ case GetMyArgumentsLengthSafe:
+ if (m_graph.uncheckedArgumentsRegisterFor(node.codeOrigin) == local)
+ result.mayBeAccessed = true;
+ break;
+
+ case GetMyArgumentByVal:
+ case GetMyArgumentByValSafe:
+ result.mayBeAccessed = true;
+ break;
+
+ case GetByVal:
+ // If this is accessing arguments then it's potentially accessing locals.
+ if (m_graph[node.child1()].shouldSpeculateArguments())
+ result.mayBeAccessed = true;
break;
+ case CreateArguments:
case TearOffActivation:
case TearOffArguments:
// If an activation is being torn off then it means that captured variables
// are live. We could be clever here and check if the local qualifies as an
// argument register. But that seems like it would buy us very little since
// any kind of tear offs are rare to begin with.
- return NoNode;
+ result.mayBeAccessed = true;
+ break;
default:
- if (m_graph.clobbersWorld(index))
- return NoNode;
break;
}
- if (node.canExit())
- return NoNode;
+ result.mayExit |= node.canExit();
+ result.mayClobberWorld |= m_graph.clobbersWorld(index);
}
- return NoNode;
+ ASSERT_NOT_REACHED();
+ // Be safe in release mode.
+ result.mayBeAccessed = true;
+ return result;
}
void performSubstitution(Edge& child, bool addRef = true)
@@ -848,35 +960,64 @@ private:
m_graph.ref(phiIndex);
}
}
+ m_changed = true;
break;
}
case GetLocalUnlinked: {
NodeIndex relevantLocalOpIgnored;
- setReplacement(getLocalLoadElimination(node.unlinkedLocal(), relevantLocalOpIgnored));
+ m_changed |= setReplacement(getLocalLoadElimination(node.unlinkedLocal(), relevantLocalOpIgnored));
break;
}
case Flush: {
- if (m_fixpointState == FixpointNotConverged)
- break;
VariableAccessData* variableAccessData = node.variableAccessData();
- if (!variableAccessData->isCaptured())
- break;
VirtualRegister local = variableAccessData->local();
- NodeIndex replacementIndex = setLocalStoreElimination(local, node.child1().index());
- if (replacementIndex == NoNode)
- break;
+ NodeIndex replacementIndex = node.child1().index();
Node& replacement = m_graph[replacementIndex];
+ if (replacement.op() != SetLocal)
+ break;
+ ASSERT(replacement.variableAccessData() == variableAccessData);
+ // FIXME: We should be able to remove SetLocals that can exit; we just need
+ // to replace them with appropriate type checks.
+ if (m_fixpointState == FixpointNotConverged) {
+ // Need to be conservative at this time; if the SetLocal has any chance of performing
+ // any speculations then we cannot do anything.
+ if (variableAccessData->isCaptured()) {
+ // Captured SetLocals never speculate and hence never exit.
+ } else {
+ if (variableAccessData->shouldUseDoubleFormat())
+ break;
+ SpeculatedType prediction = variableAccessData->argumentAwarePrediction();
+ if (isInt32Speculation(prediction))
+ break;
+ if (isArraySpeculation(prediction))
+ break;
+ if (isBooleanSpeculation(prediction))
+ break;
+ }
+ } else {
+ if (replacement.canExit())
+ break;
+ }
+ SetLocalStoreEliminationResult result =
+ setLocalStoreElimination(local, replacementIndex);
+ if (result.mayBeAccessed || result.mayClobberWorld)
+ break;
ASSERT(replacement.op() == SetLocal);
ASSERT(replacement.refCount() == 1);
ASSERT(replacement.shouldGenerate());
+ // FIXME: Investigate using mayExit as a further optimization.
node.setOpAndDefaultFlags(Phantom);
NodeIndex dataNodeIndex = replacement.child1().index();
ASSERT(m_graph[dataNodeIndex].hasResult());
m_graph.clearAndDerefChild1(node);
node.children.child1() = Edge(dataNodeIndex);
m_graph.ref(dataNodeIndex);
+ NodeIndex oldTailIndex = m_currentBlock->variablesAtTail.operand(local);
+ if (oldTailIndex == m_compileIndex)
+ m_currentBlock->variablesAtTail.operand(local) = replacementIndex;
+ m_changed = true;
break;
}
@@ -918,13 +1059,19 @@ private:
// Finally handle heap accesses. These are not quite pure, but we can still
// optimize them provided that some subtle conditions are met.
case GetGlobalVar:
- setReplacement(globalVarLoadElimination(node.varNumber(), codeBlock()->globalObjectFor(node.codeOrigin)));
+ setReplacement(globalVarLoadElimination(node.registerPointer()));
+ break;
+
+ case GlobalVarWatchpoint:
+ if (globalVarWatchpointElimination(node.registerPointer()))
+ eliminate();
break;
case PutGlobalVar:
+ case PutGlobalVarCheck:
if (m_fixpointState == FixpointNotConverged)
break;
- eliminate(globalVarStoreElimination(node.varNumber(), codeBlock()->globalObjectFor(node.codeOrigin)));
+ eliminate(globalVarStoreElimination(node.registerPointer()));
break;
case GetByVal:
@@ -944,6 +1091,11 @@ private:
eliminate();
break;
+ case StructureTransitionWatchpoint:
+ if (structureTransitionWatchpointElimination(node.structure(), node.child1().index()))
+ eliminate();
+ break;
+
case PutStructure:
if (m_fixpointState == FixpointNotConverged)
break;
@@ -956,8 +1108,8 @@ private:
break;
case GetIndexedPropertyStorage: {
- PredictedType basePrediction = m_graph[node.child2()].prediction();
- bool nodeHasIntegerIndexPrediction = !(!(basePrediction & PredictInt32) && basePrediction);
+ SpeculatedType basePrediction = m_graph[node.child2()].prediction();
+ bool nodeHasIntegerIndexPrediction = !(!(basePrediction & SpecInt32) && basePrediction);
setReplacement(getIndexedPropertyStorageLoadElimination(node.child1().index(), nodeHasIntegerIndexPrediction));
break;
}
@@ -1010,6 +1162,7 @@ private:
Vector<NodeIndex, 16> m_replacements;
FixedArray<unsigned, LastNodeType> m_lastSeen;
OptimizationFixpointState m_fixpointState;
+ bool m_changed; // Only tracks changes that have a substantive effect on other optimizations.
};
bool performCSE(Graph& graph, OptimizationFixpointState fixpointState)