- Revision
- 118579
- Author
- [email protected]
- Date
- 2012-05-25 15:45:57 -0700 (Fri, 25 May 2012)
Log Message
DFG CSE should do redundant store elimination
https://bugs.webkit.org/show_bug.cgi?id=87161
Reviewed by Oliver Hunt.
Merge r118138 from dfgopt.
This patch adds redundant store elimination. For example, consider this
code:
o.x = 42;
o.x = 84;
If o.x is speculated to be a well-behaved field, the first assignment is
unnecessary, since the second just overwrites it. We would like to
eliminate the first assignment in these cases. The need for this
optimization arises mostly from stores that our runtime requires. For
example:
o = {f:1, g:2, h:3};
This will have four assignments to the structure for the newly created
object - one assignment for the empty structure, one for {f}, one for
{f, g}, and one for {f, g, h}. We would like to only have the last of
those assigments in this case.
Intriguingly, doing so for captured variables breaks the way arguments
simplification used to work. Consider that prior to either arguments
simplification or store elimination we will have IR that looks like:
a: SetLocal(r0, Empty)
b: SetLocal(r1, Empty)
c: GetLocal(r0)
d: CreateArguments(@c)
e: SetLocal(r0, @d)
f: SetLocal(r1, @d)
Then redundant store elimination will eliminate the stores that
initialize the arguments registers to Empty, but then arguments
simplification eliminates the stores that initialize the arguments to
the newly created arguments - and at this point we no longer have any
stores to the arguments register, leading to hilarious crashes. This
patch therefore changes arguments simplification to replace
CreateArguments with JSConstant(Empty) rather than eliminating the
SetLocals. But this revealed bugs where arguments simplification was
being overzealous, so I fixed those bugs.
This is a minor speed-up on V8/early and a handful of other tests.
* bytecode/CodeBlock.h:
(JSC::CodeBlock::uncheckedActivationRegister):
* dfg/DFGAbstractState.cpp:
(JSC::DFG::AbstractState::execute):
* dfg/DFGArgumentsSimplificationPhase.cpp:
(JSC::DFG::ArgumentsSimplificationPhase::run):
(JSC::DFG::ArgumentsSimplificationPhase::observeBadArgumentsUse):
(JSC::DFG::ArgumentsSimplificationPhase::observeBadArgumentsUses):
(JSC::DFG::ArgumentsSimplificationPhase::observeProperArgumentsUse):
* dfg/DFGCSEPhase.cpp:
(JSC::DFG::CSEPhase::globalVarStoreElimination):
(CSEPhase):
(JSC::DFG::CSEPhase::putStructureStoreElimination):
(JSC::DFG::CSEPhase::putByOffsetStoreElimination):
(JSC::DFG::CSEPhase::setLocalStoreElimination):
(JSC::DFG::CSEPhase::setReplacement):
(JSC::DFG::CSEPhase::eliminate):
(JSC::DFG::CSEPhase::performNodeCSE):
* dfg/DFGGraph.h:
(JSC::DFG::Graph::uncheckedActivationRegisterFor):
(Graph):
* dfg/DFGNode.h:
(JSC::DFG::Node::isPhantomArguments):
(Node):
(JSC::DFG::Node::hasConstant):
(JSC::DFG::Node::valueOfJSConstant):
(JSC::DFG::Node::hasStructureTransitionData):
* dfg/DFGNodeType.h:
(DFG):
* dfg/DFGPredictionPropagationPhase.cpp:
(JSC::DFG::PredictionPropagationPhase::propagate):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::computeValueRecoveryFor):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
Modified Paths
Diff
Modified: trunk/Source/_javascript_Core/ChangeLog (118578 => 118579)
--- trunk/Source/_javascript_Core/ChangeLog 2012-05-25 22:36:16 UTC (rev 118578)
+++ trunk/Source/_javascript_Core/ChangeLog 2012-05-25 22:45:57 UTC (rev 118579)
@@ -1,3 +1,92 @@
+2012-05-22 Filip Pizlo <[email protected]>
+
+ DFG CSE should do redundant store elimination
+ https://bugs.webkit.org/show_bug.cgi?id=87161
+
+ Reviewed by Oliver Hunt.
+
+ Merge r118138 from dfgopt.
+
+ This patch adds redundant store elimination. For example, consider this
+ code:
+
+ o.x = 42;
+ o.x = 84;
+
+ If o.x is speculated to be a well-behaved field, the first assignment is
+ unnecessary, since the second just overwrites it. We would like to
+ eliminate the first assignment in these cases. The need for this
+ optimization arises mostly from stores that our runtime requires. For
+ example:
+
+ o = {f:1, g:2, h:3};
+
+ This will have four assignments to the structure for the newly created
+ object - one assignment for the empty structure, one for {f}, one for
+ {f, g}, and one for {f, g, h}. We would like to only have the last of
+ those assigments in this case.
+
+ Intriguingly, doing so for captured variables breaks the way arguments
+ simplification used to work. Consider that prior to either arguments
+ simplification or store elimination we will have IR that looks like:
+
+ a: SetLocal(r0, Empty)
+ b: SetLocal(r1, Empty)
+ c: GetLocal(r0)
+ d: CreateArguments(@c)
+ e: SetLocal(r0, @d)
+ f: SetLocal(r1, @d)
+
+ Then redundant store elimination will eliminate the stores that
+ initialize the arguments registers to Empty, but then arguments
+ simplification eliminates the stores that initialize the arguments to
+ the newly created arguments - and at this point we no longer have any
+ stores to the arguments register, leading to hilarious crashes. This
+ patch therefore changes arguments simplification to replace
+ CreateArguments with JSConstant(Empty) rather than eliminating the
+ SetLocals. But this revealed bugs where arguments simplification was
+ being overzealous, so I fixed those bugs.
+
+ This is a minor speed-up on V8/early and a handful of other tests.
+
+ * bytecode/CodeBlock.h:
+ (JSC::CodeBlock::uncheckedActivationRegister):
+ * dfg/DFGAbstractState.cpp:
+ (JSC::DFG::AbstractState::execute):
+ * dfg/DFGArgumentsSimplificationPhase.cpp:
+ (JSC::DFG::ArgumentsSimplificationPhase::run):
+ (JSC::DFG::ArgumentsSimplificationPhase::observeBadArgumentsUse):
+ (JSC::DFG::ArgumentsSimplificationPhase::observeBadArgumentsUses):
+ (JSC::DFG::ArgumentsSimplificationPhase::observeProperArgumentsUse):
+ * dfg/DFGCSEPhase.cpp:
+ (JSC::DFG::CSEPhase::globalVarStoreElimination):
+ (CSEPhase):
+ (JSC::DFG::CSEPhase::putStructureStoreElimination):
+ (JSC::DFG::CSEPhase::putByOffsetStoreElimination):
+ (JSC::DFG::CSEPhase::setLocalStoreElimination):
+ (JSC::DFG::CSEPhase::setReplacement):
+ (JSC::DFG::CSEPhase::eliminate):
+ (JSC::DFG::CSEPhase::performNodeCSE):
+ * dfg/DFGGraph.h:
+ (JSC::DFG::Graph::uncheckedActivationRegisterFor):
+ (Graph):
+ * dfg/DFGNode.h:
+ (JSC::DFG::Node::isPhantomArguments):
+ (Node):
+ (JSC::DFG::Node::hasConstant):
+ (JSC::DFG::Node::valueOfJSConstant):
+ (JSC::DFG::Node::hasStructureTransitionData):
+ * dfg/DFGNodeType.h:
+ (DFG):
+ * dfg/DFGPredictionPropagationPhase.cpp:
+ (JSC::DFG::PredictionPropagationPhase::propagate):
+ * dfg/DFGSpeculativeJIT.cpp:
+ (JSC::DFG::SpeculativeJIT::computeValueRecoveryFor):
+ * dfg/DFGSpeculativeJIT32_64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * dfg/DFGSpeculativeJIT64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+
2012-05-21 Filip Pizlo <[email protected]>
DFG ConvertThis should just be a CheckStructure if the structure is known
Modified: trunk/Source/_javascript_Core/bytecode/CodeBlock.h (118578 => 118579)
--- trunk/Source/_javascript_Core/bytecode/CodeBlock.h 2012-05-25 22:36:16 UTC (rev 118578)
+++ trunk/Source/_javascript_Core/bytecode/CodeBlock.h 2012-05-25 22:45:57 UTC (rev 118579)
@@ -449,6 +449,12 @@
ASSERT(needsFullScopeChain());
return m_activationRegister;
}
+ int uncheckedActivationRegister()
+ {
+ if (!needsFullScopeChain())
+ return InvalidVirtualRegister;
+ return activationRegister();
+ }
bool usesArguments() const { return m_argumentsRegister != -1; }
bool needsActivation() const
Modified: trunk/Source/_javascript_Core/dfg/DFGAbstractState.cpp (118578 => 118579)
--- trunk/Source/_javascript_Core/dfg/DFGAbstractState.cpp 2012-05-25 22:36:16 UTC (rev 118578)
+++ trunk/Source/_javascript_Core/dfg/DFGAbstractState.cpp 2012-05-25 22:45:57 UTC (rev 118579)
@@ -262,7 +262,8 @@
switch (node.op()) {
case JSConstant:
- case WeakJSConstant: {
+ case WeakJSConstant:
+ case PhantomArguments: {
forNode(nodeIndex).set(m_graph.valueOfJSConstant(nodeIndex));
node.setCanExit(false);
break;
@@ -1403,6 +1404,7 @@
}
case PutStructure:
+ case PhantomPutStructure:
node.setCanExit(false);
clobberStructures(indexInBlock);
forNode(node.child1()).set(node.structureTransitionData().newStructure);
Modified: trunk/Source/_javascript_Core/dfg/DFGArgumentsSimplificationPhase.cpp (118578 => 118579)
--- trunk/Source/_javascript_Core/dfg/DFGArgumentsSimplificationPhase.cpp 2012-05-25 22:36:16 UTC (rev 118578)
+++ trunk/Source/_javascript_Core/dfg/DFGArgumentsSimplificationPhase.cpp 2012-05-25 22:45:57 UTC (rev 118579)
@@ -283,6 +283,14 @@
break;
}
+ case Phantom:
+ // We don't care about phantom uses, since phantom uses are all about
+ // just keeping things alive for OSR exit. If something - like the
+ // CreateArguments - is just being kept alive, then this transformation
+ // will not break this, since the Phantom will now just keep alive a
+ // PhantomArguments and OSR exit will still do the right things.
+ break;
+
default:
observeBadArgumentsUses(node);
break;
@@ -392,31 +400,6 @@
VariableAccessData* variableAccessData = node.variableAccessData();
- // If this is a store into the arguments register for an InlineCallFrame*
- // that does not create arguments, then kill it.
- int argumentsRegister =
- m_graph.uncheckedArgumentsRegisterFor(node.codeOrigin);
- if ((variableAccessData->local() == argumentsRegister
- || variableAccessData->local()
- == unmodifiedArgumentsRegister(argumentsRegister))
- && !m_createsArguments.contains(source.codeOrigin.inlineCallFrame)) {
- // Find the Flush. It should be the next instruction.
- Node& flush = m_graph[block->at(indexInBlock + 1)];
- ASSERT(flush.op() == Flush);
- ASSERT(flush.variableAccessData() == variableAccessData);
- ASSERT(flush.child1() == nodeIndex);
- // Be defensive in release mode.
- if (flush.op() != Flush
- || flush.variableAccessData() != variableAccessData
- || flush.child1() != nodeIndex)
- break;
- flush.setOpAndDefaultFlags(Nop);
- m_graph.clearAndDerefChild1(flush);
- flush.setRefCount(0);
- changed = true;
- break;
- }
-
if (variableAccessData->isCaptured())
break;
@@ -583,6 +566,35 @@
insertionSet.execute(*block);
}
+ for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) {
+ BasicBlock* block = m_graph.m_blocks[blockIndex].get();
+ if (!block)
+ continue;
+ for (unsigned indexInBlock = 0; indexInBlock < block->size(); ++indexInBlock) {
+ NodeIndex nodeIndex = block->at(indexInBlock);
+ Node& node = m_graph[nodeIndex];
+ if (!node.shouldGenerate())
+ continue;
+ if (node.op() != CreateArguments)
+ continue;
+ // If this is a CreateArguments for an InlineCallFrame* that does
+ // not create arguments, then replace it with a PhantomArguments.
+ // PhantomArguments is a constant that represents JSValue() (the
+ // empty value) in DFG and arguments creation for OSR exit.
+ if (m_createsArguments.contains(node.codeOrigin.inlineCallFrame))
+ continue;
+ Node phantom(Phantom, node.codeOrigin);
+ phantom.children = node.children;
+ phantom.ref();
+ node.setOpAndDefaultFlags(PhantomArguments);
+ node.children.reset();
+ NodeIndex phantomNodeIndex = m_graph.size();
+ m_graph.append(phantom);
+ insertionSet.append(indexInBlock, phantomNodeIndex);
+ }
+ insertionSet.execute(*block);
+ }
+
if (changed)
m_graph.collectGarbage();
@@ -659,9 +671,13 @@
}
VariableAccessData* variableAccessData = child.variableAccessData();
- if (variableAccessData->isCaptured())
+ if (variableAccessData->isCaptured()) {
+ if (child.local() == m_graph.uncheckedArgumentsRegisterFor(child.codeOrigin)
+ && node.codeOrigin.inlineCallFrame != child.codeOrigin.inlineCallFrame)
+ m_createsArguments.add(child.codeOrigin.inlineCallFrame);
return;
-
+ }
+
ArgumentsAliasingData& data = ""
data.mergeCallContext(node.codeOrigin.inlineCallFrame);
}
Modified: trunk/Source/_javascript_Core/dfg/DFGCSEPhase.cpp (118578 => 118579)
--- trunk/Source/_javascript_Core/dfg/DFGCSEPhase.cpp 2012-05-25 22:36:16 UTC (rev 118578)
+++ trunk/Source/_javascript_Core/dfg/DFGCSEPhase.cpp 2012-05-25 22:45:57 UTC (rev 118579)
@@ -200,6 +200,33 @@
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 +331,56 @@
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 +427,52 @@
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 +603,58 @@
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 +676,16 @@
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 +714,17 @@
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 +810,12 @@
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 +839,34 @@
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 GetArrayLength:
@@ -681,6 +899,12 @@
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 +921,12 @@
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 +948,12 @@
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;
Modified: trunk/Source/_javascript_Core/dfg/DFGGraph.h (118578 => 118579)
--- trunk/Source/_javascript_Core/dfg/DFGGraph.h 2012-05-25 22:36:16 UTC (rev 118578)
+++ trunk/Source/_javascript_Core/dfg/DFGGraph.h 2012-05-25 22:45:57 UTC (rev 118579)
@@ -352,6 +352,12 @@
codeOrigin.inlineCallFrame->stackOffset;
}
+ int uncheckedActivationRegisterFor(const CodeOrigin& codeOrigin)
+ {
+ ASSERT_UNUSED(codeOrigin, !codeOrigin.inlineCallFrame);
+ return m_codeBlock->uncheckedActivationRegister();
+ }
+
ValueProfile* valueProfileFor(NodeIndex nodeIndex)
{
if (nodeIndex == NoNode)
Modified: trunk/Source/_javascript_Core/dfg/DFGNode.h (118578 => 118579)
--- trunk/Source/_javascript_Core/dfg/DFGNode.h 2012-05-25 22:36:16 UTC (rev 118578)
+++ trunk/Source/_javascript_Core/dfg/DFGNode.h 2012-05-25 22:45:57 UTC (rev 118579)
@@ -201,9 +201,21 @@
return op() == WeakJSConstant;
}
+ bool isPhantomArguments()
+ {
+ return op() == PhantomArguments;
+ }
+
bool hasConstant()
{
- return isConstant() || isWeakConstant();
+ switch (op()) {
+ case JSConstant:
+ case WeakJSConstant:
+ case PhantomArguments:
+ return true;
+ default:
+ return false;
+ }
}
unsigned constantNumber()
@@ -239,9 +251,17 @@
JSValue valueOfJSConstant(CodeBlock* codeBlock)
{
- if (op() == WeakJSConstant)
+ switch (op()) {
+ case WeakJSConstant:
return JSValue(weakConstant());
- return codeBlock->constantRegister(FirstConstantRegisterIndex + constantNumber()).get();
+ case JSConstant:
+ return codeBlock->constantRegister(FirstConstantRegisterIndex + constantNumber()).get();
+ case PhantomArguments:
+ return JSValue();
+ default:
+ ASSERT_NOT_REACHED();
+ return JSValue(); // Have to return something in release mode.
+ }
}
bool isInt32Constant(CodeBlock* codeBlock)
@@ -589,7 +609,7 @@
bool hasStructureTransitionData()
{
- return op() == PutStructure;
+ return op() == PutStructure || op() == PhantomPutStructure;
}
StructureTransitionData& structureTransitionData()
Modified: trunk/Source/_javascript_Core/dfg/DFGNodeType.h (118578 => 118579)
--- trunk/Source/_javascript_Core/dfg/DFGNodeType.h 2012-05-25 22:36:16 UTC (rev 118578)
+++ trunk/Source/_javascript_Core/dfg/DFGNodeType.h 2012-05-25 22:45:57 UTC (rev 118579)
@@ -37,11 +37,11 @@
// This macro defines a set of information about all known node types, used to populate NodeId, NodeType below.
#define FOR_EACH_DFG_OP(macro) \
/* A constant in the CodeBlock's constant pool. */\
- macro(JSConstant, NodeResultJS) \
+ macro(JSConstant, NodeResultJS | NodeDoesNotExit) \
\
/* A constant not in the CodeBlock's constant pool. Uses get patched to jumps that exit the */\
/* code block. */\
- macro(WeakJSConstant, NodeResultJS) \
+ macro(WeakJSConstant, NodeResultJS | NodeDoesNotExit) \
\
/* Nodes for handling functions (both as call and as construct). */\
macro(ConvertThis, NodeResultJS) \
@@ -53,10 +53,10 @@
/* VariableAccessData, and thus will share predictions. */\
macro(GetLocal, NodeResultJS) \
macro(SetLocal, 0) \
- macro(Phantom, NodeMustGenerate) \
- macro(Nop, 0) \
- macro(Phi, 0) \
- macro(Flush, NodeMustGenerate) \
+ macro(Phantom, NodeMustGenerate | NodeDoesNotExit) \
+ macro(Nop, 0 | NodeDoesNotExit) \
+ macro(Phi, 0 | NodeDoesNotExit) \
+ macro(Flush, NodeMustGenerate | NodeDoesNotExit) \
\
/* Get the value of a local variable, without linking into the VariableAccessData */\
/* network. This is only valid for variable accesses whose predictions originated */\
@@ -64,12 +64,12 @@
macro(GetLocalUnlinked, NodeResultJS) \
\
/* Marker for an argument being set at the prologue of a function. */\
- macro(SetArgument, 0) \
+ macro(SetArgument, 0 | NodeDoesNotExit) \
\
/* Hint that inlining begins here. No code is generated for this node. It's only */\
/* used for copying OSR data into inline frame data, to support reification of */\
/* call frames of inlined functions. */\
- macro(InlineStart, 0) \
+ macro(InlineStart, 0 | NodeDoesNotExit) \
\
/* Nodes for bitwise operations. */\
macro(BitAnd, NodeResultInt32) \
@@ -118,11 +118,12 @@
macro(PutById, NodeMustGenerate | NodeClobbersWorld) \
macro(PutByIdDirect, NodeMustGenerate | NodeClobbersWorld) \
macro(CheckStructure, NodeMustGenerate) \
- macro(PutStructure, NodeMustGenerate | NodeClobbersWorld) \
+ macro(PutStructure, NodeMustGenerate) \
+ macro(PhantomPutStructure, NodeMustGenerate | NodeDoesNotExit) \
macro(GetPropertyStorage, NodeResultStorage) \
macro(GetIndexedPropertyStorage, NodeMustGenerate | NodeResultStorage) \
macro(GetByOffset, NodeResultJS) \
- macro(PutByOffset, NodeMustGenerate | NodeClobbersWorld) \
+ macro(PutByOffset, NodeMustGenerate) \
macro(GetArrayLength, NodeResultInt32) \
macro(GetArgumentsLength, NodeResultInt32) \
macro(GetStringLength, NodeResultInt32) \
@@ -139,7 +140,7 @@
macro(GetScopedVar, NodeResultJS | NodeMustGenerate) \
macro(PutScopedVar, NodeMustGenerate | NodeClobbersWorld) \
macro(GetGlobalVar, NodeResultJS | NodeMustGenerate) \
- macro(PutGlobalVar, NodeMustGenerate | NodeClobbersWorld) \
+ macro(PutGlobalVar, NodeMustGenerate) \
macro(CheckFunction, NodeMustGenerate) \
\
/* Optimizations for array mutation. */\
@@ -201,6 +202,7 @@
/* Nodes used for arguments. Similar to activation support, only it makes even less */\
/* sense. */\
macro(CreateArguments, NodeResultJS) \
+ macro(PhantomArguments, NodeResultJS | NodeDoesNotExit) \
macro(TearOffArguments, NodeMustGenerate) \
macro(GetMyArgumentsLength, NodeResultJS | NodeMustGenerate) \
macro(GetMyArgumentByVal, NodeResultJS | NodeMustGenerate) \
Modified: trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp (118578 => 118579)
--- trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp 2012-05-25 22:36:16 UTC (rev 118578)
+++ trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp 2012-05-25 22:45:57 UTC (rev 118579)
@@ -618,7 +618,9 @@
case DoubleAsInt32:
case GetLocalUnlinked:
case GetMyArgumentsLength:
- case GetMyArgumentByVal: {
+ case GetMyArgumentByVal:
+ case PhantomPutStructure:
+ case PhantomArguments: {
// This node should never be visible at this stage of compilation. It is
// inserted by fixup(), which follows this phase.
ASSERT_NOT_REACHED();
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp (118578 => 118579)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp 2012-05-25 22:36:16 UTC (rev 118578)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp 2012-05-25 22:45:57 UTC (rev 118579)
@@ -1360,13 +1360,15 @@
return ValueRecovery::argumentsThatWereNotCreated();
case HaveNode: {
- if (isConstant(valueSource.nodeIndex()))
+ Node* nodePtr = &at(valueSource.nodeIndex());
+
+ if (nodePtr->isPhantomArguments())
+ return ValueRecovery::argumentsThatWereNotCreated();
+
+ if (nodePtr->hasConstant())
return ValueRecovery::constant(valueOfJSConstant(valueSource.nodeIndex()));
- Node* nodePtr = &at(valueSource.nodeIndex());
if (!nodePtr->shouldGenerate()) {
- if (nodePtr->op() == CreateArguments)
- return ValueRecovery::argumentsThatWereNotCreated();
// It's legitimately dead. As in, nobody will ever use this node, or operand,
// ever. Set it to Undefined to make the GC happy after the OSR.
return ValueRecovery::constant(jsUndefined());
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp (118578 => 118579)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp 2012-05-25 22:36:16 UTC (rev 118578)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp 2012-05-25 22:45:57 UTC (rev 118579)
@@ -1848,6 +1848,7 @@
switch (op) {
case JSConstant:
+ case PhantomArguments:
initConstantInfo(m_compileIndex);
break;
@@ -3429,6 +3430,15 @@
break;
}
+ case PhantomPutStructure: {
+ m_jit.addWeakReferenceTransition(
+ node.codeOrigin.codeOriginOwner(),
+ node.structureTransitionData().previousStructure,
+ node.structureTransitionData().newStructure);
+ noResult(m_compileIndex);
+ break;
+ }
+
case PutStructure: {
SpeculateCellOperand base(this, node.child1());
GPRReg baseGPR = base.gpr();
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp (118578 => 118579)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp 2012-05-25 22:36:16 UTC (rev 118578)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp 2012-05-25 22:45:57 UTC (rev 118579)
@@ -1924,6 +1924,7 @@
switch (op) {
case JSConstant:
+ case PhantomArguments:
initConstantInfo(m_compileIndex);
break;
@@ -3463,6 +3464,15 @@
break;
}
+ case PhantomPutStructure: {
+ m_jit.addWeakReferenceTransition(
+ node.codeOrigin.codeOriginOwner(),
+ node.structureTransitionData().previousStructure,
+ node.structureTransitionData().newStructure);
+ noResult(m_compileIndex);
+ break;
+ }
+
case PutStructure: {
SpeculateCellOperand base(this, node.child1());
GPRReg baseGPR = base.gpr();