Commit 9df7fef8 authored by fpizlo@apple.com's avatar fpizlo@apple.com

Get rid of DFG forward exiting

https://bugs.webkit.org/show_bug.cgi?id=125531

Reviewed by Oliver Hunt.
        
This finally gets rid of forward exiting. Forward exiting was always a fragile concept
since it involved the compiler trying to figure out how to "roll forward" the
execution from some DFG node to the next bytecode index. It was always easy to find
counterexamples where it broke, and it has always served as an obstacle to adding
compiler improvements - the latest being http://webkit.org/b/125523, which tried to
make DCE work for more things.
        
This change finishes the work of removing forward exiting. A lot of forward exiting
was already removed in some other bugs, but SetLocal still did forward exits. SetLocal
is in many ways the hardest to remove, since the forward exiting of SetLocal also
implied that any conversion nodes inserted before the SetLocal would then also be
marked as forward-exiting. Hence SetLocal's forward-exiting made a bunch of other
things also forward-exiting, and this was always a source of weirdo bugs.
        
SetLocal must be able to exit in case it performs a hoisted type speculation. Nodes
inserted just before SetLocal must also be able to exit - for example type check
hoisting may insert a CheckStructure, or fixup phase may insert something like
Int32ToDouble. But if any of those nodes tried to backward exit, then this could lead
to the reexecution of a side-effecting operation, for example:
        
    a: Call(...)
    b: SetLocal(@a, r1)
        
For a long time it seemed like SetLocal *had* to exit forward because of this. But
this change side-steps the problem by changing the ByteCodeParser to always emit a
kind of "two-phase commit" for stores to local variables. Now when the ByteCodeParser
wishes to store to a local, it first emits a MovHint and then enqueues a SetLocal.
The SetLocal isn't actually emitted until the beginning of the next bytecode
instruction (which the exception of op_enter and op_ret, which emit theirs immediately
since it's always safe to reexecute those bytecode instructions and since deferring
SetLocals would be weird there - op_enter has many SetLocals and op_ret is a set
followed by a jump in case of inlining, so we'd have to emit the SetLocal "after" the
jump and that would be awkward). This means that the above IR snippet would look
something like:
        
    a: Call(..., bc#42)
    b: MovHint(@a, r1, bc#42)
    c: SetLocal(@a, r1, bc#47)
        
Where the SetLocal exits "backwards" but appears at the beginning of the next bytecode
instruction. This means that by the time we get to that SetLocal, the OSR exit
analysis already knows that r1 is associated with @a, and it means that the SetLocal
or anything hoisted above it can exit backwards as normal.
        
This change also means that the "forward rewiring" can be killed. Previously, we might
have inserted a conversion node on SetLocal and then the SetLocal died (i.e. turned
into a MovHint) and the conversion node either died completely or had its lifetime
truncated to be less than the actual value's bytecode lifetime. This no longer happens
since conversion nodes are only inserted at SetLocals.
        
More precisely, this change introduces two laws that we were basically already
following anyway:
        
1) A MovHint's child should never be changed except if all other uses of that child
   are also replaced. Specifically, this prohibits insertion of conversion nodes at
   MovHints.
        
2) Anytime any child is replaced with something else, and all other uses aren't also
   replaced, we must insert a Phantom use of the original child.

This is a slight compile-time regression but has no effect on code-gen. It unlocks a
bunch of optimization opportunities so I think it's worth it.

* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::dumpAssumingJITType):
* bytecode/CodeBlock.h:
(JSC::CodeBlock::instructionCount):
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGArgumentsSimplificationPhase.cpp:
(JSC::DFG::ArgumentsSimplificationPhase::run):
* dfg/DFGArrayifySlowPathGenerator.h:
(JSC::DFG::ArrayifySlowPathGenerator::ArrayifySlowPathGenerator):
* dfg/DFGBackwardsPropagationPhase.cpp:
(JSC::DFG::BackwardsPropagationPhase::propagate):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::setDirect):
(JSC::DFG::ByteCodeParser::DelayedSetLocal::DelayedSetLocal):
(JSC::DFG::ByteCodeParser::DelayedSetLocal::execute):
(JSC::DFG::ByteCodeParser::handleInlining):
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGCSEPhase.cpp:
(JSC::DFG::CSEPhase::eliminate):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGCommon.h:
* dfg/DFGConstantFoldingPhase.cpp:
(JSC::DFG::ConstantFoldingPhase::foldConstants):
* dfg/DFGDCEPhase.cpp:
(JSC::DFG::DCEPhase::run):
(JSC::DFG::DCEPhase::fixupBlock):
(JSC::DFG::DCEPhase::cleanVariables):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
(JSC::DFG::FixupPhase::fixEdge):
(JSC::DFG::FixupPhase::injectInt32ToDoubleNode):
* dfg/DFGLICMPhase.cpp:
(JSC::DFG::LICMPhase::run):
(JSC::DFG::LICMPhase::attemptHoist):
* dfg/DFGMinifiedNode.cpp:
(JSC::DFG::MinifiedNode::fromNode):
* dfg/DFGMinifiedNode.h:
(JSC::DFG::belongsInMinifiedGraph):
(JSC::DFG::MinifiedNode::constantNumber):
(JSC::DFG::MinifiedNode::weakConstant):
* dfg/DFGNode.cpp:
(JSC::DFG::Node::hasVariableAccessData):
* dfg/DFGNode.h:
(JSC::DFG::Node::convertToPhantom):
(JSC::DFG::Node::convertToPhantomUnchecked):
(JSC::DFG::Node::convertToIdentity):
(JSC::DFG::Node::containsMovHint):
(JSC::DFG::Node::hasUnlinkedLocal):
(JSC::DFG::Node::willHaveCodeGenOrOSR):
* dfg/DFGNodeFlags.cpp:
(JSC::DFG::dumpNodeFlags):
* dfg/DFGNodeFlags.h:
* dfg/DFGNodeType.h:
* dfg/DFGOSRAvailabilityAnalysisPhase.cpp:
(JSC::DFG::OSRAvailabilityAnalysisPhase::run):
* dfg/DFGOSREntrypointCreationPhase.cpp:
(JSC::DFG::OSREntrypointCreationPhase::run):
* dfg/DFGOSRExit.cpp:
* dfg/DFGOSRExit.h:
* dfg/DFGOSRExitBase.cpp:
* dfg/DFGOSRExitBase.h:
(JSC::DFG::OSRExitBase::considerAddingAsFrequentExitSite):
* dfg/DFGPredictionPropagationPhase.cpp:
(JSC::DFG::PredictionPropagationPhase::propagate):
(JSC::DFG::PredictionPropagationPhase::doDoubleVoting):
* dfg/DFGSSAConversionPhase.cpp:
(JSC::DFG::SSAConversionPhase::run):
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::speculationCheck):
(JSC::DFG::SpeculativeJIT::emitInvalidationPoint):
(JSC::DFG::SpeculativeJIT::typeCheck):
(JSC::DFG::SpeculativeJIT::compileMovHint):
(JSC::DFG::SpeculativeJIT::compileCurrentBlock):
(JSC::DFG::SpeculativeJIT::checkArgumentTypes):
(JSC::DFG::SpeculativeJIT::compileInt32ToDouble):
* dfg/DFGSpeculativeJIT.h:
(JSC::DFG::SpeculativeJIT::detectPeepHoleBranch):
(JSC::DFG::SpeculativeJIT::needsTypeCheck):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGTypeCheckHoistingPhase.cpp:
(JSC::DFG::TypeCheckHoistingPhase::run):
(JSC::DFG::TypeCheckHoistingPhase::identifyRedundantStructureChecks):
(JSC::DFG::TypeCheckHoistingPhase::identifyRedundantArrayChecks):
* dfg/DFGValidate.cpp:
(JSC::DFG::Validate::validateCPS):
* dfg/DFGVariableAccessData.h:
(JSC::DFG::VariableAccessData::VariableAccessData):
* dfg/DFGVariableEventStream.cpp:
(JSC::DFG::VariableEventStream::reconstruct):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToLLVM.cpp:
(JSC::FTL::LowerDFGToLLVM::compileNode):
(JSC::FTL::LowerDFGToLLVM::compileGetArgument):
(JSC::FTL::LowerDFGToLLVM::compileSetLocal):
(JSC::FTL::LowerDFGToLLVM::compileMovHint):
(JSC::FTL::LowerDFGToLLVM::compileZombieHint):
(JSC::FTL::LowerDFGToLLVM::compileInt32ToDouble):
(JSC::FTL::LowerDFGToLLVM::speculate):
(JSC::FTL::LowerDFGToLLVM::typeCheck):
(JSC::FTL::LowerDFGToLLVM::appendTypeCheck):
(JSC::FTL::LowerDFGToLLVM::appendOSRExit):
(JSC::FTL::LowerDFGToLLVM::emitOSRExitCall):
* ftl/FTLOSRExit.cpp:
* ftl/FTLOSRExit.h:
* tests/stress/dead-int32-to-double.js: Added.
(foo):
* tests/stress/dead-uint32-to-number.js: Added.
(foo):



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@161126 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent dc4de52f
2013-12-28 Filip Pizlo <fpizlo@apple.com>
Get rid of DFG forward exiting
https://bugs.webkit.org/show_bug.cgi?id=125531
Reviewed by Oliver Hunt.
This finally gets rid of forward exiting. Forward exiting was always a fragile concept
since it involved the compiler trying to figure out how to "roll forward" the
execution from some DFG node to the next bytecode index. It was always easy to find
counterexamples where it broke, and it has always served as an obstacle to adding
compiler improvements - the latest being http://webkit.org/b/125523, which tried to
make DCE work for more things.
This change finishes the work of removing forward exiting. A lot of forward exiting
was already removed in some other bugs, but SetLocal still did forward exits. SetLocal
is in many ways the hardest to remove, since the forward exiting of SetLocal also
implied that any conversion nodes inserted before the SetLocal would then also be
marked as forward-exiting. Hence SetLocal's forward-exiting made a bunch of other
things also forward-exiting, and this was always a source of weirdo bugs.
SetLocal must be able to exit in case it performs a hoisted type speculation. Nodes
inserted just before SetLocal must also be able to exit - for example type check
hoisting may insert a CheckStructure, or fixup phase may insert something like
Int32ToDouble. But if any of those nodes tried to backward exit, then this could lead
to the reexecution of a side-effecting operation, for example:
a: Call(...)
b: SetLocal(@a, r1)
For a long time it seemed like SetLocal *had* to exit forward because of this. But
this change side-steps the problem by changing the ByteCodeParser to always emit a
kind of "two-phase commit" for stores to local variables. Now when the ByteCodeParser
wishes to store to a local, it first emits a MovHint and then enqueues a SetLocal.
The SetLocal isn't actually emitted until the beginning of the next bytecode
instruction (which the exception of op_enter and op_ret, which emit theirs immediately
since it's always safe to reexecute those bytecode instructions and since deferring
SetLocals would be weird there - op_enter has many SetLocals and op_ret is a set
followed by a jump in case of inlining, so we'd have to emit the SetLocal "after" the
jump and that would be awkward). This means that the above IR snippet would look
something like:
a: Call(..., bc#42)
b: MovHint(@a, r1, bc#42)
c: SetLocal(@a, r1, bc#47)
Where the SetLocal exits "backwards" but appears at the beginning of the next bytecode
instruction. This means that by the time we get to that SetLocal, the OSR exit
analysis already knows that r1 is associated with @a, and it means that the SetLocal
or anything hoisted above it can exit backwards as normal.
This change also means that the "forward rewiring" can be killed. Previously, we might
have inserted a conversion node on SetLocal and then the SetLocal died (i.e. turned
into a MovHint) and the conversion node either died completely or had its lifetime
truncated to be less than the actual value's bytecode lifetime. This no longer happens
since conversion nodes are only inserted at SetLocals.
More precisely, this change introduces two laws that we were basically already
following anyway:
1) A MovHint's child should never be changed except if all other uses of that child
are also replaced. Specifically, this prohibits insertion of conversion nodes at
MovHints.
2) Anytime any child is replaced with something else, and all other uses aren't also
replaced, we must insert a Phantom use of the original child.
This is a slight compile-time regression but has no effect on code-gen. It unlocks a
bunch of optimization opportunities so I think it's worth it.
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::dumpAssumingJITType):
* bytecode/CodeBlock.h:
(JSC::CodeBlock::instructionCount):
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGArgumentsSimplificationPhase.cpp:
(JSC::DFG::ArgumentsSimplificationPhase::run):
* dfg/DFGArrayifySlowPathGenerator.h:
(JSC::DFG::ArrayifySlowPathGenerator::ArrayifySlowPathGenerator):
* dfg/DFGBackwardsPropagationPhase.cpp:
(JSC::DFG::BackwardsPropagationPhase::propagate):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::setDirect):
(JSC::DFG::ByteCodeParser::DelayedSetLocal::DelayedSetLocal):
(JSC::DFG::ByteCodeParser::DelayedSetLocal::execute):
(JSC::DFG::ByteCodeParser::handleInlining):
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGCSEPhase.cpp:
(JSC::DFG::CSEPhase::eliminate):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGCommon.h:
* dfg/DFGConstantFoldingPhase.cpp:
(JSC::DFG::ConstantFoldingPhase::foldConstants):
* dfg/DFGDCEPhase.cpp:
(JSC::DFG::DCEPhase::run):
(JSC::DFG::DCEPhase::fixupBlock):
(JSC::DFG::DCEPhase::cleanVariables):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
(JSC::DFG::FixupPhase::fixEdge):
(JSC::DFG::FixupPhase::injectInt32ToDoubleNode):
* dfg/DFGLICMPhase.cpp:
(JSC::DFG::LICMPhase::run):
(JSC::DFG::LICMPhase::attemptHoist):
* dfg/DFGMinifiedNode.cpp:
(JSC::DFG::MinifiedNode::fromNode):
* dfg/DFGMinifiedNode.h:
(JSC::DFG::belongsInMinifiedGraph):
(JSC::DFG::MinifiedNode::constantNumber):
(JSC::DFG::MinifiedNode::weakConstant):
* dfg/DFGNode.cpp:
(JSC::DFG::Node::hasVariableAccessData):
* dfg/DFGNode.h:
(JSC::DFG::Node::convertToPhantom):
(JSC::DFG::Node::convertToPhantomUnchecked):
(JSC::DFG::Node::convertToIdentity):
(JSC::DFG::Node::containsMovHint):
(JSC::DFG::Node::hasUnlinkedLocal):
(JSC::DFG::Node::willHaveCodeGenOrOSR):
* dfg/DFGNodeFlags.cpp:
(JSC::DFG::dumpNodeFlags):
* dfg/DFGNodeFlags.h:
* dfg/DFGNodeType.h:
* dfg/DFGOSRAvailabilityAnalysisPhase.cpp:
(JSC::DFG::OSRAvailabilityAnalysisPhase::run):
* dfg/DFGOSREntrypointCreationPhase.cpp:
(JSC::DFG::OSREntrypointCreationPhase::run):
* dfg/DFGOSRExit.cpp:
* dfg/DFGOSRExit.h:
* dfg/DFGOSRExitBase.cpp:
* dfg/DFGOSRExitBase.h:
(JSC::DFG::OSRExitBase::considerAddingAsFrequentExitSite):
* dfg/DFGPredictionPropagationPhase.cpp:
(JSC::DFG::PredictionPropagationPhase::propagate):
(JSC::DFG::PredictionPropagationPhase::doDoubleVoting):
* dfg/DFGSSAConversionPhase.cpp:
(JSC::DFG::SSAConversionPhase::run):
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::speculationCheck):
(JSC::DFG::SpeculativeJIT::emitInvalidationPoint):
(JSC::DFG::SpeculativeJIT::typeCheck):
(JSC::DFG::SpeculativeJIT::compileMovHint):
(JSC::DFG::SpeculativeJIT::compileCurrentBlock):
(JSC::DFG::SpeculativeJIT::checkArgumentTypes):
(JSC::DFG::SpeculativeJIT::compileInt32ToDouble):
* dfg/DFGSpeculativeJIT.h:
(JSC::DFG::SpeculativeJIT::detectPeepHoleBranch):
(JSC::DFG::SpeculativeJIT::needsTypeCheck):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGTypeCheckHoistingPhase.cpp:
(JSC::DFG::TypeCheckHoistingPhase::run):
(JSC::DFG::TypeCheckHoistingPhase::identifyRedundantStructureChecks):
(JSC::DFG::TypeCheckHoistingPhase::identifyRedundantArrayChecks):
* dfg/DFGValidate.cpp:
(JSC::DFG::Validate::validateCPS):
* dfg/DFGVariableAccessData.h:
(JSC::DFG::VariableAccessData::VariableAccessData):
* dfg/DFGVariableEventStream.cpp:
(JSC::DFG::VariableEventStream::reconstruct):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToLLVM.cpp:
(JSC::FTL::LowerDFGToLLVM::compileNode):
(JSC::FTL::LowerDFGToLLVM::compileGetArgument):
(JSC::FTL::LowerDFGToLLVM::compileSetLocal):
(JSC::FTL::LowerDFGToLLVM::compileMovHint):
(JSC::FTL::LowerDFGToLLVM::compileZombieHint):
(JSC::FTL::LowerDFGToLLVM::compileInt32ToDouble):
(JSC::FTL::LowerDFGToLLVM::speculate):
(JSC::FTL::LowerDFGToLLVM::typeCheck):
(JSC::FTL::LowerDFGToLLVM::appendTypeCheck):
(JSC::FTL::LowerDFGToLLVM::appendOSRExit):
(JSC::FTL::LowerDFGToLLVM::emitOSRExitCall):
* ftl/FTLOSRExit.cpp:
* ftl/FTLOSRExit.h:
* tests/stress/dead-int32-to-double.js: Added.
(foo):
* tests/stress/dead-uint32-to-number.js: Added.
(foo):
2013-12-25 Commit Queue <commit-queue@webkit.org>
Unreviewed, rolling out r161033 and r161074.
......
......@@ -135,6 +135,7 @@ void CodeBlock::dumpAssumingJITType(PrintStream& out, JITCode::JITType jitType)
if (codeType() == FunctionCode)
out.print(specializationKind());
out.print(", ", instructionCount());
if (this->jitType() == JITCode::BaselineJIT && m_shouldAlwaysBeInlined)
out.print(" (SABI)");
if (ownerExecutable()->neverInline())
......
......@@ -244,7 +244,7 @@ public:
bool usesOpcode(OpcodeID);
unsigned instructionCount() { return m_instructions.size(); }
unsigned instructionCount() const { return m_instructions.size(); }
int argumentIndexAfterCapture(size_t argument);
......
......@@ -195,10 +195,10 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
break;
}
case MovHint:
case MovHintAndCheck: {
// Don't need to do anything. A MovHint is effectively a promise that the SetLocal
// was dead.
case MovHint: {
// Don't need to do anything. A MovHint only informs us about what would have happened
// in bytecode, but this code is just concerned with what is actually happening during
// DFG execution.
break;
}
......@@ -1583,6 +1583,7 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
break;
case Phantom:
case Check:
case CountExecution:
case CheckTierUpInLoop:
case CheckTierUpAtReturn:
......
......@@ -333,6 +333,11 @@ public:
// structures of another variable.
break;
case MovHint:
// We don't care about MovHints at all, since they represent what happens
// in bytecode. We rematerialize arguments objects on OSR exit anyway.
break;
default:
observeBadArgumentsUses(node);
break;
......
......@@ -63,13 +63,13 @@ public:
case Array::Int32:
case Array::Double:
case Array::Contiguous:
m_badPropertyJump = jit->backwardSpeculationCheck(Uncountable, JSValueRegs(), 0);
m_badPropertyJump = jit->speculationCheck(Uncountable, JSValueRegs(), 0);
break;
default:
break;
}
}
m_badIndexingTypeJump = jit->backwardSpeculationCheck(BadIndexingType, JSValueSource::unboxedCell(m_baseGPR), 0);
m_badIndexingTypeJump = jit->speculationCheck(BadIndexingType, JSValueSource::unboxedCell(m_baseGPR), 0);
}
protected:
......
......@@ -187,6 +187,10 @@ private:
break;
}
case MovHint:
case Check:
break;
case BitAnd:
case BitOr:
case BitXor:
......
......@@ -232,15 +232,19 @@ private:
return getDirect(m_inlineStackTop->remapOperand(operand));
}
enum SetMode { NormalSet, SetOnEntry };
enum SetMode { NormalSet, ImmediateSet };
Node* setDirect(VirtualRegister operand, Node* value, SetMode setMode = NormalSet)
{
// Is this an argument?
if (operand.isArgument())
return setArgument(operand, value, setMode);
// Must be a local.
return setLocal(operand, value, setMode);
addToGraph(MovHint, OpInfo(operand.offset()), value);
DelayedSetLocal delayed = DelayedSetLocal(operand, value);
if (setMode == NormalSet) {
m_setLocalQueue.append(delayed);
return 0;
}
return delayed.execute(this, setMode);
}
Node* set(VirtualRegister operand, Node* value, SetMode setMode = NormalSet)
......@@ -1121,6 +1125,27 @@ private:
};
InlineStackEntry* m_inlineStackTop;
struct DelayedSetLocal {
VirtualRegister m_operand;
Node* m_value;
DelayedSetLocal() { }
DelayedSetLocal(VirtualRegister operand, Node* value)
: m_operand(operand)
, m_value(value)
{
}
Node* execute(ByteCodeParser* parser, SetMode setMode = NormalSet)
{
if (m_operand.isArgument())
return parser->setArgument(m_operand, m_value, setMode);
return parser->setLocal(m_operand, m_value, setMode);
}
};
Vector<DelayedSetLocal, 2> m_setLocalQueue;
// Have we built operand maps? We initialize them lazily, and only when doing
// inlining.
......@@ -1325,9 +1350,9 @@ bool ByteCodeParser::handleInlining(Node* callTargetNode, int resultOperand, con
== callLinkStatus.isClosureCall());
if (callLinkStatus.isClosureCall()) {
VariableAccessData* calleeVariable =
set(VirtualRegister(JSStack::Callee), callTargetNode)->variableAccessData();
set(VirtualRegister(JSStack::Callee), callTargetNode, ImmediateSet)->variableAccessData();
VariableAccessData* scopeVariable =
set(VirtualRegister(JSStack::ScopeChain), addToGraph(GetScope, callTargetNode))->variableAccessData();
set(VirtualRegister(JSStack::ScopeChain), addToGraph(GetScope, callTargetNode), ImmediateSet)->variableAccessData();
calleeVariable->mergeShouldNeverUnbox(true);
scopeVariable->mergeShouldNeverUnbox(true);
......@@ -1872,6 +1897,10 @@ bool ByteCodeParser::parseBlock(unsigned limit)
}
while (true) {
for (unsigned i = 0; i < m_setLocalQueue.size(); ++i)
m_setLocalQueue[i].execute(this);
m_setLocalQueue.resize(0);
// Don't extend over jump destinations.
if (m_currentIndex == limit) {
// Ordinarily we want to plant a jump. But refuse to do this if the block is
......@@ -1903,7 +1932,7 @@ bool ByteCodeParser::parseBlock(unsigned limit)
case op_enter:
// Initialize all locals to undefined.
for (int i = 0; i < m_inlineStackTop->m_codeBlock->m_numVars; ++i)
set(virtualRegisterForLocal(i), constantUndefined(), SetOnEntry);
set(virtualRegisterForLocal(i), constantUndefined(), ImmediateSet);
NEXT_OPCODE(op_enter);
case op_touch_entry:
......@@ -2908,7 +2937,7 @@ bool ByteCodeParser::parseBlock(unsigned limit)
flushArgumentsAndCapturedVariables();
if (inlineCallFrame()) {
ASSERT(m_inlineStackTop->m_returnValue.isValid());
setDirect(m_inlineStackTop->m_returnValue, get(VirtualRegister(currentInstruction[1].u.operand)));
setDirect(m_inlineStackTop->m_returnValue, get(VirtualRegister(currentInstruction[1].u.operand)), ImmediateSet);
m_inlineStackTop->m_didReturn = true;
if (m_inlineStackTop->m_unlinkedBlocks.isEmpty()) {
// If we're returning from the first block, then we're done parsing.
......
......@@ -1051,7 +1051,7 @@ private:
if (!node)
return;
ASSERT(node->mustGenerate());
node->setOpAndDefaultNonExitFlags(phantomType);
node->setOpAndDefaultFlags(phantomType);
if (phantomType == Phantom)
eliminateIrrelevantPhantomChildren(node);
......
......@@ -129,9 +129,9 @@ void clobberize(Graph& graph, Node* node, ReadFunctor& read, WriteFunctor& write
case ConstantStoragePointer:
case UInt32ToNumber:
case DoubleAsInt32:
case Check:
return;
case MovHintAndCheck:
case MovHint:
case ZombieHint:
case Upsilon:
......
......@@ -182,8 +182,6 @@ enum RefCountState {
enum OperandSpeculationMode { AutomaticOperandSpeculation, ManualOperandSpeculation };
enum SpeculationDirection { ForwardSpeculation, BackwardSpeculation };
enum ProofStatus { NeedsCheck, IsProved };
inline bool isProved(ProofStatus proofStatus)
......
......@@ -110,12 +110,8 @@ private:
m_interpreter.execute(indexInBlock); // Catch the fact that we may filter on cell.
AdjacencyList children = node->children;
children.removeEdge(0);
if (!!children.child1()) {
Node phantom(Phantom, node->codeOrigin, children);
if (node->flags() & NodeExitsForward)
phantom.mergeFlags(NodeExitsForward);
m_insertionSet.insertNode(indexInBlock, SpecNone, phantom);
}
if (!!children.child1())
m_insertionSet.insertNode(indexInBlock, SpecNone, Phantom, node->codeOrigin, children);
node->children.setChild2(Edge());
node->children.setChild3(Edge());
node->convertToStructureTransitionWatchpoint(structure);
......
......@@ -113,8 +113,12 @@ public:
for (unsigned i = depthFirst.size(); i--;)
fixupBlock(depthFirst[i]);
} else {
RELEASE_ASSERT(m_graph.m_form == ThreadedCPS);
for (BlockIndex blockIndex = 0; blockIndex < m_graph.numBlocks(); ++blockIndex)
fixupBlock(m_graph.block(blockIndex));
cleanVariables(m_graph.m_arguments);
}
m_graph.m_refCountState = ExactRefCount;
......@@ -152,6 +156,36 @@ private:
{
if (!block)
return;
switch (m_graph.m_form) {
case SSA:
break;
case ThreadedCPS: {
// Clean up variable links for the block. We need to do this before the actual DCE
// because we need to see GetLocals, so we can bypass them in situations where the
// vars-at-tail point to a GetLocal, the GetLocal is dead, but the Phi it points
// to is alive.
for (unsigned phiIndex = 0; phiIndex < block->phis.size(); ++phiIndex) {
if (!block->phis[phiIndex]->shouldGenerate()) {
// FIXME: We could actually free nodes here. Except that it probably
// doesn't matter, since we don't add any nodes after this phase.
// https://bugs.webkit.org/show_bug.cgi?id=126239
block->phis[phiIndex--] = block->phis.last();
block->phis.removeLast();
}
}
cleanVariables(block->variablesAtHead);
cleanVariables(block->variablesAtTail);
break;
}
default:
RELEASE_ASSERT_NOT_REACHED();
return;
}
for (unsigned indexInBlock = block->size(); indexInBlock--;) {
Node* node = block->at(indexInBlock);
......@@ -159,37 +193,23 @@ private:
continue;
switch (node->op()) {
case SetLocal:
case MovHint: {
ASSERT((node->op() == SetLocal) == (m_graph.m_form == ThreadedCPS));
if (node->child1().willNotHaveCheck()) {
// Consider the possibility that UInt32ToNumber is dead but its
// child isn't; if so then we should MovHint the child.
if (!node->child1()->shouldGenerate()
&& permitsOSRBackwardRewiring(node->child1()->op()))
node->child1() = node->child1()->child1();
if (!node->child1()->shouldGenerate()) {
node->setOpAndDefaultFlags(ZombieHint);
node->child1() = Edge();
break;
}
node->setOpAndDefaultFlags(MovHint);
ASSERT(node->child1().useKind() == UntypedUse);
if (!node->child1()->shouldGenerate()) {
node->setOpAndDefaultFlags(ZombieHint);
node->child1() = Edge();
break;
}
node->setOpAndDefaultFlags(MovHintAndCheck);
node->setRefCount(1);
node->setOpAndDefaultFlags(MovHint);
break;
}
case GetLocal:
case SetArgument: {
if (m_graph.m_form == ThreadedCPS) {
// Leave them as not shouldGenerate.
break;
}
case ZombieHint: {
// Currently we assume that DCE runs only once.
RELEASE_ASSERT_NOT_REACHED();
break;
}
default: {
if (node->flags() & NodeHasVarArgs) {
for (unsigned childIdx = node->firstChild(); childIdx < node->firstChild() + node->numChildren(); childIdx++) {
......@@ -228,6 +248,27 @@ private:
}
}
template<typename VariablesVectorType>
void cleanVariables(VariablesVectorType& variables)
{
for (unsigned i = variables.size(); i--;) {
Node* node = variables[i];
if (!node)
continue;
if (node->op() != Phantom && node->shouldGenerate())
continue;
if (node->op() == GetLocal) {
node = node->child1().node();
ASSERT(node->op() == Phi);
if (node->shouldGenerate()) {
variables[i] = node;
continue;
}
}
variables[i] = 0;
}
}
Vector<Node*, 128> m_worklist;
InsertionSet m_insertionSet;
};
......
......@@ -886,7 +886,8 @@ private:
}
case Phantom:
case Identity: {
case Identity:
case Check: {
switch (node->child1().useKind()) {
case NumberUse:
if (node->child1()->shouldSpeculateInt32ForArithmetic())
......@@ -907,9 +908,6 @@ private:
case GetIndexedPropertyStorage:
case GetTypedArrayByteOffset:
case LastNodeType:
case MovHint:
case MovHintAndCheck:
case ZombieHint:
case CheckTierUpInLoop:
case CheckTierUpAtReturn:
case CheckTierUpAndOSREnter:
......@@ -951,7 +949,7 @@ private:
observeUseKindOnNode<StringUse>(node);
}
break;
#if !ASSERT_DISABLED
// Have these no-op cases here to ensure that nobody forgets to add handlers for new opcodes.
case SetArgument:
......@@ -1005,6 +1003,8 @@ private:
case StoreBarrierWithNullCheck:
case FunctionReentryWatchpoint:
case TypedArrayWatchpoint:
case MovHint:
case ZombieHint:
break;
#else
default:
......@@ -1012,7 +1012,8 @@ private:
#endif
}
DFG_NODE_DO_TO_CHILDREN(m_graph, node, observeUntypedEdge);
if (!node->containsMovHint())
DFG_NODE_DO_TO_CHILDREN(m_graph, node, observeUntypedEdge);
}
void observeUntypedEdge(Node*, Edge& edge)
......@@ -1478,7 +1479,7 @@ private:
{
if (isDouble(useKind)) {
if (edge->shouldSpeculateInt32ForArithmetic()) {
injectInt32ToDoubleNode(edge, useKind, m_currentNode->speculationDirection());
injectInt32ToDoubleNode(edge, useKind);
return;
}
......@@ -1491,7 +1492,6 @@ private:
Node* result = m_insertionSet.insertNode(
m_indexInBlock, SpecInt52AsDouble, Int52ToDouble,
m_currentNode->codeOrigin, Edge(edge.node(), NumberUse));
result->setSpeculationDirection(m_currentNode->speculationDirection());
edge = Edge(result, useKind);
return;
}
......@@ -1545,7 +1545,6 @@ private:
Node* result = m_insertionSet.insertNode(
m_indexInBlock, SpecInt52, Int52ToValue,
m_currentNode->codeOrigin, Edge(edge.node(), UntypedUse));
result->setSpeculationDirection(m_currentNode->speculationDirection());
edge = Edge(result, useKind);
return;
}
......@@ -1587,13 +1586,11 @@ private:
edge = newEdge;
}
void injectInt32ToDoubleNode(Edge& edge, UseKind useKind = NumberUse, SpeculationDirection direction = BackwardSpeculation)
void injectInt32ToDoubleNode(Edge& edge, UseKind useKind = NumberUse)
{
Node* result = m_insertionSet.insertNode(
m_indexInBlock, SpecInt52AsDouble, Int32ToDouble,
m_currentNode->codeOrigin, Edge(edge.node(), NumberUse));
if (direction == ForwardSpeculation)
result->mergeFlags(NodeExitsForward);
edge = Edge(result, useKind);
}
......
......@@ -128,7 +128,6 @@ public:
// time and preserve some kind of sanity, if we hoist something that must exit.
//
// Also, we need to remember to:
// - Clear NodeExitsForward for any nodes we hoisted.
// - Update the state-at-tail with the node we hoisted, so future hoist candidates
// know about any type checks we hoisted.
//
......@@ -230,8 +229,6 @@ private:
data.preHeader->insertBeforeLast(node);
node->misc.owner = data.preHeader;
NodeFlags didExitForward = node->flags() & NodeExitsForward;
node->clearFlags(NodeExitsForward);
node->codeOriginForExitTarget = data.preHeader->last()->codeOriginForExitTarget;
// Modify the states at the end of the preHeader of the loop we hoisted to,
......@@ -256,7 +253,6 @@ private:
RELEASE_ASSERT(!(node->flags() & NodeHasVarArgs));
nodeRef = m_graph.addNode(SpecNone, Phantom, node->codeOrigin, node->children);
nodeRef->mergeFlags(didExitForward);
return true;
}
......
......@@ -38,15 +38,13 @@ MinifiedNode MinifiedNode::fromNode(Node* node)
MinifiedNode result;
result.m_id = MinifiedID(node);
result.m_op = node->op();