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
diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog
index d4cbd4f..e56fe9b 100644
--- a/Source/JavaScriptCore/ChangeLog
+++ b/Source/JavaScriptCore/ChangeLog
@@ -1,3 +1,190 @@
+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.
diff --git a/Source/JavaScriptCore/bytecode/CodeBlock.cpp b/Source/JavaScriptCore/bytecode/CodeBlock.cpp
index d5e7da0..08f3838 100644
--- a/Source/JavaScriptCore/bytecode/CodeBlock.cpp
+++ b/Source/JavaScriptCore/bytecode/CodeBlock.cpp
@@ -135,6 +135,7 @@
 
     if (codeType() == FunctionCode)
         out.print(specializationKind());
+    out.print(", ", instructionCount());
     if (this->jitType() == JITCode::BaselineJIT && m_shouldAlwaysBeInlined)
         out.print(" (SABI)");
     if (ownerExecutable()->neverInline())
diff --git a/Source/JavaScriptCore/bytecode/CodeBlock.h b/Source/JavaScriptCore/bytecode/CodeBlock.h
index 99f1804..7ddf040 100644
--- a/Source/JavaScriptCore/bytecode/CodeBlock.h
+++ b/Source/JavaScriptCore/bytecode/CodeBlock.h
@@ -244,7 +244,7 @@
 
     bool usesOpcode(OpcodeID);
 
-    unsigned instructionCount() { return m_instructions.size(); }
+    unsigned instructionCount() const { return m_instructions.size(); }
 
     int argumentIndexAfterCapture(size_t argument);
     
diff --git a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
index 63ed0df..465d1db 100644
--- a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
+++ b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
@@ -195,10 +195,10 @@
         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 @@
         break;
             
     case Phantom:
+    case Check:
     case CountExecution:
     case CheckTierUpInLoop:
     case CheckTierUpAtReturn:
diff --git a/Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp b/Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp
index dc0c3fd..9366031 100644
--- a/Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp
@@ -333,6 +333,11 @@
                     // 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;
diff --git a/Source/JavaScriptCore/dfg/DFGArrayifySlowPathGenerator.h b/Source/JavaScriptCore/dfg/DFGArrayifySlowPathGenerator.h
index 51c0c6a..201ca47 100644
--- a/Source/JavaScriptCore/dfg/DFGArrayifySlowPathGenerator.h
+++ b/Source/JavaScriptCore/dfg/DFGArrayifySlowPathGenerator.h
@@ -63,13 +63,13 @@
             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:
diff --git a/Source/JavaScriptCore/dfg/DFGBackwardsPropagationPhase.cpp b/Source/JavaScriptCore/dfg/DFGBackwardsPropagationPhase.cpp
index 2c4991e..86693a1 100644
--- a/Source/JavaScriptCore/dfg/DFGBackwardsPropagationPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGBackwardsPropagationPhase.cpp
@@ -187,6 +187,10 @@
             break;
         }
             
+        case MovHint:
+        case Check:
+            break;
+            
         case BitAnd:
         case BitOr:
         case BitXor:
diff --git a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
index 79a2f49..a31851b 100644
--- a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
+++ b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
@@ -232,15 +232,19 @@
         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 @@
     };
     
     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 @@
         == 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 @@
     }
 
     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 @@
         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 @@
             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.
diff --git a/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp b/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp
index 0b04fc3..c27e110 100644
--- a/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp
@@ -1051,7 +1051,7 @@
         if (!node)
             return;
         ASSERT(node->mustGenerate());
-        node->setOpAndDefaultNonExitFlags(phantomType);
+        node->setOpAndDefaultFlags(phantomType);
         if (phantomType == Phantom)
             eliminateIrrelevantPhantomChildren(node);
         
diff --git a/Source/JavaScriptCore/dfg/DFGClobberize.h b/Source/JavaScriptCore/dfg/DFGClobberize.h
index f4e3780..df97f4f 100644
--- a/Source/JavaScriptCore/dfg/DFGClobberize.h
+++ b/Source/JavaScriptCore/dfg/DFGClobberize.h
@@ -129,9 +129,9 @@
     case ConstantStoragePointer:
     case UInt32ToNumber:
     case DoubleAsInt32:
+    case Check:
         return;
         
-    case MovHintAndCheck:
     case MovHint:
     case ZombieHint:
     case Upsilon:
diff --git a/Source/JavaScriptCore/dfg/DFGCommon.h b/Source/JavaScriptCore/dfg/DFGCommon.h
index aab6f04..7b4b1db 100644
--- a/Source/JavaScriptCore/dfg/DFGCommon.h
+++ b/Source/JavaScriptCore/dfg/DFGCommon.h
@@ -182,8 +182,6 @@
 
 enum OperandSpeculationMode { AutomaticOperandSpeculation, ManualOperandSpeculation };
 
-enum SpeculationDirection { ForwardSpeculation, BackwardSpeculation };
-
 enum ProofStatus { NeedsCheck, IsProved };
 
 inline bool isProved(ProofStatus proofStatus)
diff --git a/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp b/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
index 9530a20..8354081 100644
--- a/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
@@ -110,12 +110,8 @@
                     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);
diff --git a/Source/JavaScriptCore/dfg/DFGDCEPhase.cpp b/Source/JavaScriptCore/dfg/DFGDCEPhase.cpp
index 0c455e4..b590f08 100644
--- a/Source/JavaScriptCore/dfg/DFGDCEPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGDCEPhase.cpp
@@ -113,8 +113,12 @@
             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 @@
     {
         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 @@
                 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 @@
         }
     }
     
+    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;
 };
diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
index e516545..03778c3 100644
--- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
@@ -886,7 +886,8 @@
         }
 
         case Phantom:
-        case Identity: {
+        case Identity:
+        case Check: {
             switch (node->child1().useKind()) {
             case NumberUse:
                 if (node->child1()->shouldSpeculateInt32ForArithmetic())
@@ -907,9 +908,6 @@
         case GetIndexedPropertyStorage:
         case GetTypedArrayByteOffset:
         case LastNodeType:
-        case MovHint:
-        case MovHintAndCheck:
-        case ZombieHint:
         case CheckTierUpInLoop:
         case CheckTierUpAtReturn:
         case CheckTierUpAndOSREnter:
@@ -951,7 +949,7 @@
                 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 @@
         case StoreBarrierWithNullCheck:
         case FunctionReentryWatchpoint:
         case TypedArrayWatchpoint:
+        case MovHint:
+        case ZombieHint:
             break;
 #else
         default:
@@ -1012,7 +1012,8 @@
 #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 @@
     {
         if (isDouble(useKind)) {
             if (edge->shouldSpeculateInt32ForArithmetic()) {
-                injectInt32ToDoubleNode(edge, useKind, m_currentNode->speculationDirection());
+                injectInt32ToDoubleNode(edge, useKind);
                 return;
             }
             
@@ -1491,7 +1492,6 @@
                 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 @@
             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 @@
         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);
     }
diff --git a/Source/JavaScriptCore/dfg/DFGLICMPhase.cpp b/Source/JavaScriptCore/dfg/DFGLICMPhase.cpp
index f7e7026..6465130 100644
--- a/Source/JavaScriptCore/dfg/DFGLICMPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGLICMPhase.cpp
@@ -128,7 +128,6 @@
         //   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 @@
         
         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 @@
         RELEASE_ASSERT(!(node->flags() & NodeHasVarArgs));
         
         nodeRef = m_graph.addNode(SpecNone, Phantom, node->codeOrigin, node->children);
-        nodeRef->mergeFlags(didExitForward);
         
         return true;
     }
diff --git a/Source/JavaScriptCore/dfg/DFGMinifiedNode.cpp b/Source/JavaScriptCore/dfg/DFGMinifiedNode.cpp
index 5fec771..802cb29 100644
--- a/Source/JavaScriptCore/dfg/DFGMinifiedNode.cpp
+++ b/Source/JavaScriptCore/dfg/DFGMinifiedNode.cpp
@@ -38,15 +38,13 @@
     MinifiedNode result;
     result.m_id = MinifiedID(node);
     result.m_op = node->op();
-    if (hasChild(node->op()))
-        result.m_childOrInfo = MinifiedID(node->child1().node()).m_id;
-    else if (hasConstantNumber(node->op()))
-        result.m_childOrInfo = node->constantNumber();
+    if (hasConstantNumber(node->op()))
+        result.m_info = node->constantNumber();
     else if (hasWeakConstant(node->op()))
-        result.m_childOrInfo = bitwise_cast<uintptr_t>(node->weakConstant());
+        result.m_info = bitwise_cast<uintptr_t>(node->weakConstant());
     else {
         ASSERT(node->op() == PhantomArguments);
-        result.m_childOrInfo = 0;
+        result.m_info = 0;
     }
     return result;
 }
diff --git a/Source/JavaScriptCore/dfg/DFGMinifiedNode.h b/Source/JavaScriptCore/dfg/DFGMinifiedNode.h
index 1571371..afea6ae 100644
--- a/Source/JavaScriptCore/dfg/DFGMinifiedNode.h
+++ b/Source/JavaScriptCore/dfg/DFGMinifiedNode.h
@@ -43,16 +43,9 @@
     switch (type) {
     case JSConstant:
     case WeakJSConstant:
-    case ValueToInt32:
-    case Int32ToDouble:
-    case UInt32ToNumber:
-    case DoubleAsInt32:
     case PhantomArguments:
-    case Int52ToValue:
-    case Int52ToDouble:
         return true;
     default:
-        ASSERT(!permitsOSRBackwardRewiring(type) && !permitsOSRForwardRewiring(type));
         return false;
     }
 }
@@ -66,14 +59,6 @@
     MinifiedID id() const { return m_id; }
     NodeType op() const { return m_op; }
     
-    bool hasChild1() const { return hasChild(m_op); }
-    
-    MinifiedID child1() const
-    {
-        ASSERT(hasChild(m_op));
-        return MinifiedID::fromBits(m_childOrInfo);
-    }
-    
     bool hasConstant() const { return hasConstantNumber() || hasWeakConstant(); }
     
     bool hasConstantNumber() const { return hasConstantNumber(m_op); }
@@ -81,7 +66,7 @@
     unsigned constantNumber() const
     {
         ASSERT(hasConstantNumber(m_op));
-        return m_childOrInfo;
+        return m_info;
     }
     
     bool hasWeakConstant() const { return hasWeakConstant(m_op); }
@@ -89,7 +74,7 @@
     JSCell* weakConstant() const
     {
         ASSERT(hasWeakConstant(m_op));
-        return bitwise_cast<JSCell*>(m_childOrInfo);
+        return bitwise_cast<JSCell*>(m_info);
     }
     
     static MinifiedID getID(MinifiedNode* node) { return node->id(); }
@@ -99,20 +84,6 @@
     }
     
 private:
-    static bool hasChild(NodeType type)
-    {
-        switch (type) {
-        case ValueToInt32:
-        case Int32ToDouble:
-        case UInt32ToNumber:
-        case DoubleAsInt32:
-        case Int52ToDouble:
-        case Int52ToValue:
-            return true;
-        default:
-            return false;
-        }
-    }
     static bool hasConstantNumber(NodeType type)
     {
         return type == JSConstant;
@@ -123,7 +94,7 @@
     }
     
     MinifiedID m_id;
-    uintptr_t m_childOrInfo; // Nodes in the minified graph have only one child each.
+    uintptr_t m_info;
     NodeType m_op;
 };
 
diff --git a/Source/JavaScriptCore/dfg/DFGNode.cpp b/Source/JavaScriptCore/dfg/DFGNode.cpp
index 12e7a67..bf43f29 100644
--- a/Source/JavaScriptCore/dfg/DFGNode.cpp
+++ b/Source/JavaScriptCore/dfg/DFGNode.cpp
@@ -46,9 +46,6 @@
     case GetLocal:
     case GetArgument:
     case SetLocal:
-    case MovHint:
-    case MovHintAndCheck:
-    case ZombieHint:
     case SetArgument:
     case Flush:
     case PhantomLocal:
diff --git a/Source/JavaScriptCore/dfg/DFGNode.h b/Source/JavaScriptCore/dfg/DFGNode.h
index 5fe539c..a8d7e69 100644
--- a/Source/JavaScriptCore/dfg/DFGNode.h
+++ b/Source/JavaScriptCore/dfg/DFGNode.h
@@ -274,59 +274,27 @@
         return filterFlags(~flags);
     }
     
-    SpeculationDirection speculationDirection()
-    {
-        if (flags() & NodeExitsForward)
-            return ForwardSpeculation;
-        return BackwardSpeculation;
-    }
-    
-    void setSpeculationDirection(SpeculationDirection direction)
-    {
-        switch (direction) {
-        case ForwardSpeculation:
-            mergeFlags(NodeExitsForward);
-            return;
-        case BackwardSpeculation:
-            clearFlags(NodeExitsForward);
-            return;
-        }
-        RELEASE_ASSERT_NOT_REACHED();
-    }
-    
     void setOpAndDefaultFlags(NodeType op)
     {
         m_op = op;
         m_flags = defaultFlags(op);
     }
 
-    void setOpAndDefaultNonExitFlags(NodeType op)
-    {
-        ASSERT(!(m_flags & NodeHasVarArgs));
-        setOpAndDefaultNonExitFlagsUnchecked(op);
-    }
-
-    void setOpAndDefaultNonExitFlagsUnchecked(NodeType op)
-    {
-        m_op = op;
-        m_flags = (defaultFlags(op) & ~NodeExitsForward) | (m_flags & NodeExitsForward);
-    }
-
     void convertToPhantom()
     {
-        setOpAndDefaultNonExitFlags(Phantom);
+        setOpAndDefaultFlags(Phantom);
     }
 
     void convertToPhantomUnchecked()
     {
-        setOpAndDefaultNonExitFlagsUnchecked(Phantom);
+        setOpAndDefaultFlags(Phantom);
     }
 
     void convertToIdentity()
     {
         RELEASE_ASSERT(child1());
         RELEASE_ASSERT(!child2());
-        setOpAndDefaultNonExitFlags(Identity);
+        setOpAndDefaultFlags(Identity);
     }
 
     bool mustGenerate()
@@ -531,9 +499,7 @@
     bool containsMovHint()
     {
         switch (op()) {
-        case SetLocal:
         case MovHint:
-        case MovHintAndCheck:
         case ZombieHint:
             return true;
         default:
@@ -567,6 +533,8 @@
         switch (op()) {
         case GetLocalUnlinked:
         case ExtractOSREntryLocal:
+        case MovHint:
+        case ZombieHint:
             return true;
         default:
             return false;
@@ -1190,11 +1158,6 @@
         case SetLocal:
         case MovHint:
         case ZombieHint:
-        case MovHintAndCheck:
-        case Int32ToDouble:
-        case ValueToInt32:
-        case UInt32ToNumber:
-        case DoubleAsInt32:
         case PhantomArguments:
             return true;
         case Phantom:
diff --git a/Source/JavaScriptCore/dfg/DFGNodeFlags.cpp b/Source/JavaScriptCore/dfg/DFGNodeFlags.cpp
index 7d40886..396ca61 100644
--- a/Source/JavaScriptCore/dfg/DFGNodeFlags.cpp
+++ b/Source/JavaScriptCore/dfg/DFGNodeFlags.cpp
@@ -96,9 +96,6 @@
     if (!(flags & NodeDoesNotExit))
         out.print(comma, "CanExit");
     
-    if (flags & NodeExitsForward)
-        out.print(comma, "NodeExitsForward");
-    
     CString string = out.toCString();
     if (!string.length())
         actualOut.print("<empty>");
diff --git a/Source/JavaScriptCore/dfg/DFGNodeFlags.h b/Source/JavaScriptCore/dfg/DFGNodeFlags.h
index 3f2f8d1..d68f058 100644
--- a/Source/JavaScriptCore/dfg/DFGNodeFlags.h
+++ b/Source/JavaScriptCore/dfg/DFGNodeFlags.h
@@ -68,9 +68,7 @@
 
 #define NodeRelevantToOSR                0x4000
 
-#define NodeExitsForward                 0x8000
-
-#define NodeIsStaticConstant            0x10000 // Used only by the parser, to determine if a constant arose statically and hence could be folded at parse-time.
+#define NodeIsStaticConstant             0x8000 // Used only by the parser, to determine if a constant arose statically and hence could be folded at parse-time.
 
 typedef uint32_t NodeFlags;
 
diff --git a/Source/JavaScriptCore/dfg/DFGNodeType.h b/Source/JavaScriptCore/dfg/DFGNodeType.h
index c5713ec..30687a1 100644
--- a/Source/JavaScriptCore/dfg/DFGNodeType.h
+++ b/Source/JavaScriptCore/dfg/DFGNodeType.h
@@ -57,12 +57,12 @@
     /* Any two nodes that are part of the same Phi graph will share the same */\
     /* VariableAccessData, and thus will share predictions. */\
     macro(GetLocal, NodeResultJS) \
-    macro(SetLocal, NodeExitsForward) \
-    macro(MovHintAndCheck, NodeMustGenerate | NodeExitsForward) \
+    macro(SetLocal, 0) \
     macro(MovHint, NodeDoesNotExit) \
     macro(ZombieHint, NodeDoesNotExit) \
     macro(GetArgument, NodeResultJS | NodeMustGenerate) \
     macro(Phantom, NodeMustGenerate) \
+    macro(Check, 0) /* Used if we want just a type check but not liveness. DCE eithers kills this or converts it to Phantom. */\
     macro(Upsilon, NodeDoesNotExit | NodeRelevantToOSR) \
     macro(Phi, NodeDoesNotExit | NodeRelevantToOSR) \
     macro(Flush, NodeMustGenerate | NodeDoesNotExit) \
@@ -318,80 +318,6 @@
     }
 }
 
-inline bool permitsOSRBackwardRewiring(NodeType op)
-{
-    switch (op) {
-    case Identity:
-        RELEASE_ASSERT_NOT_REACHED();
-        return true;
-    case UInt32ToNumber:
-    case Int52ToValue:
-    case Int52ToDouble:
-        // These are the only node where we do:
-        //
-        //     b: UInt32ToNumber(@a)
-        //     c: SetLocal(@b)
-        //
-        // and then also have some uses of @a without Phantom'ing @b.
-        return true;
-    default:
-        return false;
-    }
-}
-
-// Returns the priority with which we should select the given node for forward
-// rewiring. Higher is better. Zero means that the node is not useful for rewiring.
-// By convention, we use 100 to mean that the node is totally equivalent to its
-// input with no information loss.
-inline unsigned forwardRewiringSelectionScore(NodeType op)
-{
-    switch (op) {
-    case Identity:
-        // We shouldn't see these by the time we get to OSR even though it clearly
-        // is a perfect identity function.
-        RELEASE_ASSERT_NOT_REACHED();
-        return 100;
-        
-    case DoubleAsInt32:
-        // This speculates that the incoming double is convertible to an int32. So
-        // its result is totally equivalent.
-        return 100;
-        
-    case Int32ToDouble:
-        // This converts an int32 to a double, but that loses a bit of information.
-        // OTOH it's still an equivalent number.
-        return 75;
-        
-    case UInt32ToNumber:
-        // It's completely fine to use this for OSR exit, since the uint32 isn't
-        // actually representable in bytecode.
-        return 100;
-
-    case ValueToInt32:
-        // This loses information. Only use it if there are no better alternatives.
-        return 25;
-        
-    case Int52ToValue:
-        // Loses no information. It just boxes the value, which is what OSR wants
-        // to do anyway.
-        return 100;
-        
-    case Int52ToDouble:
-        // This is like Int32ToDouble; we can use it because it gives a semantically
-        // equivalent value but that value may be an int32 in a double, so we'd
-        // rather not if we can avoid it.
-        return 75;
-        
-    default:
-        return 0;
-    }
-}
-
-inline bool permitsOSRForwardRewiring(NodeType op)
-{
-    return forwardRewiringSelectionScore(op) > 0;
-}
-
 } } // namespace JSC::DFG
 
 #endif // ENABLE(DFG_JIT)
diff --git a/Source/JavaScriptCore/dfg/DFGOSRAvailabilityAnalysisPhase.cpp b/Source/JavaScriptCore/dfg/DFGOSRAvailabilityAnalysisPhase.cpp
index 705408e..a649635 100644
--- a/Source/JavaScriptCore/dfg/DFGOSRAvailabilityAnalysisPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGOSRAvailabilityAnalysisPhase.cpp
@@ -103,17 +103,15 @@
                         break;
                     }
                         
-                    case MovHint:
-                    case MovHintAndCheck: {
-                        VariableAccessData* variable = node->variableAccessData();
-                        availability.operand(variable->local()) =
+                    case MovHint: {
+                        availability.operand(node->unlinkedLocal()) =
                             Availability(node->child1().node());
                         break;
                     }
                         
                     case ZombieHint: {
-                        VariableAccessData* variable = node->variableAccessData();
-                        availability.operand(variable->local()) = Availability::unavailable();
+                        availability.operand(node->unlinkedLocal()) =
+                            Availability::unavailable();
                         break;
                     }
                         
diff --git a/Source/JavaScriptCore/dfg/DFGOSREntrypointCreationPhase.cpp b/Source/JavaScriptCore/dfg/DFGOSREntrypointCreationPhase.cpp
index f5d7274..4f82d15 100644
--- a/Source/JavaScriptCore/dfg/DFGOSREntrypointCreationPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGOSREntrypointCreationPhase.cpp
@@ -104,17 +104,9 @@
                 m_graph, variable->prediction(), ExtractOSREntryLocal, codeOrigin,
                 OpInfo(variable->local().offset()));
             
-            // Create a MovHint. We can't use MovHint's directly at this stage of
-            // compilation, so we cook one up by creating a new VariableAccessData
-            // that isn't unified with any of the others. This ensures that this
-            // SetLocal will turn into a MovHint and will not have any type checks.
-            m_graph.m_variableAccessData.append(
-                VariableAccessData(variable->local(), variable->isCaptured()));
-            VariableAccessData* newVariable = &m_graph.m_variableAccessData.last();
-            Node* setLocal = newRoot->appendNode(
-                m_graph, SpecNone, SetLocal, codeOrigin, OpInfo(newVariable),
+            newRoot->appendNode(
+                m_graph, SpecNone, MovHint, codeOrigin, OpInfo(variable->local().offset()),
                 Edge(locals[local]));
-            setLocal->setSpeculationDirection(BackwardSpeculation);
         }
         for (int local = 0; local < baseline->m_numCalleeRegisters; ++local) {
             Node* previousHead = target->variablesAtHead.local(local);
@@ -122,9 +114,8 @@
                 continue;
             VariableAccessData* variable = previousHead->variableAccessData();
             Node* node = locals[local];
-            Node* setLocal = newRoot->appendNode(
+            newRoot->appendNode(
                 m_graph, SpecNone, SetLocal, codeOrigin, OpInfo(variable), Edge(node));
-            setLocal->setSpeculationDirection(BackwardSpeculation);
         }
         
         newRoot->appendNode(
diff --git a/Source/JavaScriptCore/dfg/DFGOSRExit.cpp b/Source/JavaScriptCore/dfg/DFGOSRExit.cpp
index 2975ed4..538a85a 100644
--- a/Source/JavaScriptCore/dfg/DFGOSRExit.cpp
+++ b/Source/JavaScriptCore/dfg/DFGOSRExit.cpp
@@ -68,26 +68,6 @@
     m_patchableCodeOffset = linkBuffer.offsetOf(label);
 }
 
-void OSRExit::convertToForward(BasicBlock* block, Node* currentNode, unsigned nodeIndex, const ValueRecovery& valueRecovery)
-{
-    Node* node;
-    Node* lastMovHint;
-    if (!doSearchForForwardConversion(block, currentNode, nodeIndex, !!valueRecovery, node, lastMovHint))
-        return;
-
-    ASSERT(node->codeOrigin != currentNode->codeOrigin);
-    
-    m_codeOrigin = node->codeOrigin;
-    
-    if (!valueRecovery)
-        return;
-    
-    ASSERT(lastMovHint);
-    ASSERT(lastMovHint->child1() == currentNode);
-    m_valueRecoveryOverride = adoptRef(
-        new ValueRecoveryOverride(lastMovHint->local(), valueRecovery));
-}
-
 } } // namespace JSC::DFG
 
 #endif // ENABLE(DFG_JIT)
diff --git a/Source/JavaScriptCore/dfg/DFGOSRExit.h b/Source/JavaScriptCore/dfg/DFGOSRExit.h
index 716bdd2..d40efe4 100644
--- a/Source/JavaScriptCore/dfg/DFGOSRExit.h
+++ b/Source/JavaScriptCore/dfg/DFGOSRExit.h
@@ -101,8 +101,6 @@
     MacroAssembler::Jump getPatchableCodeOffsetAsJump() const;
     CodeLocationJump codeLocationForRepatch(CodeBlock*) const;
     void correctJump(LinkBuffer&);
-    
-    void convertToForward(BasicBlock*, Node*, unsigned nodeIndex, const ValueRecovery&);
 
     unsigned m_streamIndex;
     
diff --git a/Source/JavaScriptCore/dfg/DFGOSRExitBase.cpp b/Source/JavaScriptCore/dfg/DFGOSRExitBase.cpp
index 762830a..ebfd27f 100644
--- a/Source/JavaScriptCore/dfg/DFGOSRExitBase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGOSRExitBase.cpp
@@ -46,45 +46,6 @@
             FrequentExitSite(m_codeOriginForExitProfile.bytecodeIndex, m_kind));
 }
 
-bool OSRExitBase::doSearchForForwardConversion(
-    BasicBlock* block, Node* currentNode, unsigned nodeIndex, bool hasValueRecovery,
-    Node*& node, Node*& lastMovHint)
-{
-    // Check that either the current node is a SetLocal, or the preceding node was a
-    // SetLocal with the same code origin, or that we've provided a valueRecovery.
-    if (!ASSERT_DISABLED
-        && !hasValueRecovery
-        && !currentNode->containsMovHint()) {
-        Node* setLocal = block->at(nodeIndex - 1);
-        ASSERT_UNUSED(setLocal, setLocal->containsMovHint());
-        ASSERT_UNUSED(setLocal, setLocal->codeOriginForExitTarget == currentNode->codeOriginForExitTarget);
-    }
-    
-    // Find the first node for the next bytecode instruction. Also track the last mov hint
-    // on this node.
-    unsigned indexInBlock = nodeIndex + 1;
-    node = 0;
-    lastMovHint = 0;
-    for (;;) {
-        if (indexInBlock == block->size()) {
-            // This is an inline return. Give up and do a backwards speculation. This is safe
-            // because an inline return has its own bytecode index and it's always safe to
-            // reexecute that bytecode.
-            ASSERT(node->op() == Jump);
-            return false;
-        }
-        node = block->at(indexInBlock);
-        if (node->containsMovHint() && node->child1() == currentNode)
-            lastMovHint = node;
-        if (node->codeOriginForExitTarget != currentNode->codeOriginForExitTarget)
-            break;
-        indexInBlock++;
-    }
-    
-    ASSERT(node->codeOriginForExitTarget != currentNode->codeOriginForExitTarget);
-    return true;
-}
-
 } } // namespace JSC::DFG
 
 #endif // ENABLE(DFG_JIT)
diff --git a/Source/JavaScriptCore/dfg/DFGOSRExitBase.h b/Source/JavaScriptCore/dfg/DFGOSRExitBase.h
index c2ee7d4..ee1d69d 100644
--- a/Source/JavaScriptCore/dfg/DFGOSRExitBase.h
+++ b/Source/JavaScriptCore/dfg/DFGOSRExitBase.h
@@ -64,11 +64,6 @@
             return false;
         return considerAddingAsFrequentExitSiteSlow(profiledCodeBlock);
     }
-    
-    // Returns true if the forward conversion is really needed.
-    bool doSearchForForwardConversion(
-        BasicBlock*, Node* currentNode, unsigned nodeIndex, bool hasValueRecovery,
-        Node*& nextBCNode, Node*& lastMovHint);
 
 private:
     bool considerAddingAsFrequentExitSiteSlow(CodeBlock* profiledCodeBlock);
diff --git a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
index a2825aa..d1c5551 100644
--- a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
@@ -504,9 +504,6 @@
         case CheckArray:
         case Arrayify:
         case ArrayifyToStructure:
-        case MovHint:
-        case MovHintAndCheck:
-        case ZombieHint:
         case CheckTierUpInLoop:
         case CheckTierUpAtReturn:
         case CheckTierUpAndOSREnter:
@@ -577,6 +574,7 @@
         case VarInjectionWatchpoint:
         case AllocationProfileWatchpoint:
         case Phantom:
+        case Check:
         case PutGlobalVar:
         case CheckWatchdogTimer:
         case Unreachable:
@@ -585,6 +583,8 @@
         case FunctionReentryWatchpoint:
         case TypedArrayWatchpoint:
         case ConstantStoragePointer:
+        case MovHint:
+        case ZombieHint:
             break;
             
         // This gets ignored because it already has a prediction.
@@ -744,6 +744,10 @@
             break;
         }
             
+        case MovHint:
+            // Ignore these since they have no effect on in-DFG execution.
+            break;
+            
         default:
             m_graph.voteChildren(node, VoteValue);
             break;
diff --git a/Source/JavaScriptCore/dfg/DFGSSAConversionPhase.cpp b/Source/JavaScriptCore/dfg/DFGSSAConversionPhase.cpp
index 1300bc5..57fc095 100644
--- a/Source/JavaScriptCore/dfg/DFGSSAConversionPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSSAConversionPhase.cpp
@@ -186,8 +186,8 @@
                             // the value was already on the stack.
                         } else {
                             m_insertionSet.insertNode(
-                                0, SpecNone, MovHint, CodeOrigin(), OpInfo(variable),
-                                Edge(node));
+                                0, SpecNone, MovHint, CodeOrigin(),
+                                OpInfo(variable->local().offset()), Edge(node));
                         }
                     }
                 }
@@ -269,7 +269,7 @@
         // - GetLocal over uncaptured variables die and get replaced with references
         //   to the node specified by variablesAtHead.
         // - SetLocal gets NodeMustGenerate if it's flushed, or turns into a
-        //   MovHint otherwise.
+        //   Check otherwise.
         // - Flush loses its children but remains, because we want to know when a
         //   flushed SetLocal's value is no longer needed. This also makes it simpler
         //   to reason about the format of a local, since we can just do a backwards
@@ -308,7 +308,7 @@
                     if (variable->isCaptured() || m_flushedLocalOps.contains(node))
                         node->mergeFlags(NodeMustGenerate);
                     else
-                        node->setOpAndDefaultFlags(MovHint);
+                        node->setOpAndDefaultFlags(Check);
                     node->misc.replacement = node->child1().node(); // Only for Upsilons.
                     break;
                 }
diff --git a/Source/JavaScriptCore/dfg/DFGSafeToExecute.h b/Source/JavaScriptCore/dfg/DFGSafeToExecute.h
index bbc8bef..7595876 100644
--- a/Source/JavaScriptCore/dfg/DFGSafeToExecute.h
+++ b/Source/JavaScriptCore/dfg/DFGSafeToExecute.h
@@ -118,7 +118,6 @@
     case GetCallee:
     case GetLocal:
     case SetLocal:
-    case MovHintAndCheck:
     case MovHint:
     case ZombieHint:
     case GetArgument:
@@ -250,6 +249,7 @@
     case TypedArrayWatchpoint:
     case CheckInBounds:
     case ConstantStoragePointer:
+    case Check:
         return true;
         
     case GetByVal:
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
index 24e6405..74cad85 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
@@ -106,7 +106,7 @@
             structure, numElements)));
 }
 
-void SpeculativeJIT::backwardSpeculationCheck(ExitKind kind, JSValueSource jsValueSource, Node* node, MacroAssembler::Jump jumpToFail)
+void SpeculativeJIT::speculationCheck(ExitKind kind, JSValueSource jsValueSource, Node* node, MacroAssembler::Jump jumpToFail)
 {
     if (!m_compileOkay)
         return;
@@ -115,7 +115,7 @@
     m_jit.jitCode()->appendOSRExit(OSRExit(kind, jsValueSource, m_jit.graph().methodOfGettingAValueProfileFor(node), this, m_stream->size()));
 }
 
-void SpeculativeJIT::backwardSpeculationCheck(ExitKind kind, JSValueSource jsValueSource, Node* node, const MacroAssembler::JumpList& jumpsToFail)
+void SpeculativeJIT::speculationCheck(ExitKind kind, JSValueSource jsValueSource, Node* node, const MacroAssembler::JumpList& jumpsToFail)
 {
     if (!m_compileOkay)
         return;
@@ -124,22 +124,7 @@
     m_jit.jitCode()->appendOSRExit(OSRExit(kind, jsValueSource, m_jit.graph().methodOfGettingAValueProfileFor(node), this, m_stream->size()));
 }
 
-void SpeculativeJIT::speculationCheck(ExitKind kind, JSValueSource jsValueSource, Node* node, MacroAssembler::Jump jumpToFail)
-{
-    if (!m_compileOkay)
-        return;
-    backwardSpeculationCheck(kind, jsValueSource, node, jumpToFail);
-    if (m_speculationDirection == ForwardSpeculation)
-        convertLastOSRExitToForward();
-}
-
-void SpeculativeJIT::speculationCheck(ExitKind kind, JSValueSource jsValueSource, Edge nodeUse, MacroAssembler::Jump jumpToFail)
-{
-    ASSERT(m_isCheckingArgumentTypes || m_canExit);
-    speculationCheck(kind, jsValueSource, nodeUse.node(), jumpToFail);
-}
-
-OSRExitJumpPlaceholder SpeculativeJIT::backwardSpeculationCheck(ExitKind kind, JSValueSource jsValueSource, Node* node)
+OSRExitJumpPlaceholder SpeculativeJIT::speculationCheck(ExitKind kind, JSValueSource jsValueSource, Node* node)
 {
     if (!m_compileOkay)
         return OSRExitJumpPlaceholder();
@@ -150,19 +135,16 @@
     return OSRExitJumpPlaceholder(index);
 }
 
-OSRExitJumpPlaceholder SpeculativeJIT::backwardSpeculationCheck(ExitKind kind, JSValueSource jsValueSource, Edge nodeUse)
+OSRExitJumpPlaceholder SpeculativeJIT::speculationCheck(ExitKind kind, JSValueSource jsValueSource, Edge nodeUse)
 {
     ASSERT(m_isCheckingArgumentTypes || m_canExit);
-    return backwardSpeculationCheck(kind, jsValueSource, nodeUse.node());
+    return speculationCheck(kind, jsValueSource, nodeUse.node());
 }
 
-void SpeculativeJIT::speculationCheck(ExitKind kind, JSValueSource jsValueSource, Node* node, const MacroAssembler::JumpList& jumpsToFail)
+void SpeculativeJIT::speculationCheck(ExitKind kind, JSValueSource jsValueSource, Edge nodeUse, MacroAssembler::Jump jumpToFail)
 {
-    if (!m_compileOkay)
-        return;
-    backwardSpeculationCheck(kind, jsValueSource, node, jumpsToFail);
-    if (m_speculationDirection == ForwardSpeculation)
-        convertLastOSRExitToForward();
+    ASSERT(m_isCheckingArgumentTypes || m_canExit);
+    speculationCheck(kind, jsValueSource, nodeUse.node(), jumpToFail);
 }
 
 void SpeculativeJIT::speculationCheck(ExitKind kind, JSValueSource jsValueSource, Edge nodeUse, const MacroAssembler::JumpList& jumpsToFail)
@@ -171,7 +153,7 @@
     speculationCheck(kind, jsValueSource, nodeUse.node(), jumpsToFail);
 }
 
-void SpeculativeJIT::backwardSpeculationCheck(ExitKind kind, JSValueSource jsValueSource, Node* node, MacroAssembler::Jump jumpToFail, const SpeculationRecovery& recovery)
+void SpeculativeJIT::speculationCheck(ExitKind kind, JSValueSource jsValueSource, Node* node, MacroAssembler::Jump jumpToFail, const SpeculationRecovery& recovery)
 {
     if (!m_compileOkay)
         return;
@@ -181,24 +163,10 @@
     m_jit.jitCode()->appendOSRExit(OSRExit(kind, jsValueSource, m_jit.graph().methodOfGettingAValueProfileFor(node), this, m_stream->size(), recoveryIndex));
 }
 
-void SpeculativeJIT::backwardSpeculationCheck(ExitKind kind, JSValueSource jsValueSource, Edge nodeUse, MacroAssembler::Jump jumpToFail, const SpeculationRecovery& recovery)
+void SpeculativeJIT::speculationCheck(ExitKind kind, JSValueSource jsValueSource, Edge nodeUse, MacroAssembler::Jump jumpToFail, const SpeculationRecovery& recovery)
 {
     ASSERT(m_isCheckingArgumentTypes || m_canExit);
-    backwardSpeculationCheck(kind, jsValueSource, nodeUse.node(), jumpToFail, recovery);
-}
-
-void SpeculativeJIT::speculationCheck(ExitKind kind, JSValueSource jsValueSource, Node* node, MacroAssembler::Jump jumpToFail, const SpeculationRecovery& recovery)
-{
-    if (!m_compileOkay)
-        return;
-    backwardSpeculationCheck(kind, jsValueSource, node, jumpToFail, recovery);
-    if (m_speculationDirection == ForwardSpeculation)
-        convertLastOSRExitToForward();
-}
-
-void SpeculativeJIT::speculationCheck(ExitKind kind, JSValueSource jsValueSource, Edge edge, MacroAssembler::Jump jumpToFail, const SpeculationRecovery& recovery)
-{
-    speculationCheck(kind, jsValueSource, edge.node(), jumpToFail, recovery);
+    speculationCheck(kind, jsValueSource, nodeUse.node(), jumpToFail, recovery);
 }
 
 void SpeculativeJIT::emitInvalidationPoint(Node* node)
@@ -206,7 +174,6 @@
     if (!m_compileOkay)
         return;
     ASSERT(m_canExit);
-    ASSERT(m_speculationDirection == BackwardSpeculation);
     OSRExitCompilationInfo& info = m_jit.appendExitInfo(JITCompiler::JumpList());
     m_jit.jitCode()->appendOSRExit(OSRExit(
         UncountableInvalidation, JSValueSource(),
@@ -217,26 +184,6 @@
     noResult(node);
 }
 
-void SpeculativeJIT::convertLastOSRExitToForward(const ValueRecovery& valueRecovery)
-{
-    m_jit.jitCode()->lastOSRExit().convertToForward(
-        m_block, m_currentNode, m_indexInBlock, valueRecovery);
-}
-
-void SpeculativeJIT::forwardSpeculationCheck(ExitKind kind, JSValueSource jsValueSource, Node* node, MacroAssembler::Jump jumpToFail, const ValueRecovery& valueRecovery)
-{
-    ASSERT(m_isCheckingArgumentTypes || m_canExit);
-    backwardSpeculationCheck(kind, jsValueSource, node, jumpToFail);
-    convertLastOSRExitToForward(valueRecovery);
-}
-
-void SpeculativeJIT::forwardSpeculationCheck(ExitKind kind, JSValueSource jsValueSource, Node* node, const MacroAssembler::JumpList& jumpsToFail, const ValueRecovery& valueRecovery)
-{
-    ASSERT(m_isCheckingArgumentTypes || m_canExit);
-    backwardSpeculationCheck(kind, jsValueSource, node, jumpsToFail);
-    convertLastOSRExitToForward(valueRecovery);
-}
-
 void SpeculativeJIT::terminateSpeculativeExecution(ExitKind kind, JSValueRegs jsValueRegs, Node* node)
 {
     ASSERT(m_isCheckingArgumentTypes || m_canExit);
@@ -252,24 +199,11 @@
     terminateSpeculativeExecution(kind, jsValueRegs, nodeUse.node());
 }
 
-void SpeculativeJIT::backwardTypeCheck(JSValueSource source, Edge edge, SpeculatedType typesPassedThrough, MacroAssembler::Jump jumpToFail)
+void SpeculativeJIT::typeCheck(JSValueSource source, Edge edge, SpeculatedType typesPassedThrough, MacroAssembler::Jump jumpToFail)
 {
     ASSERT(needsTypeCheck(edge, typesPassedThrough));
     m_interpreter.filter(edge, typesPassedThrough);
-    backwardSpeculationCheck(BadType, source, edge.node(), jumpToFail);
-}
-
-void SpeculativeJIT::typeCheck(JSValueSource source, Edge edge, SpeculatedType typesPassedThrough, MacroAssembler::Jump jumpToFail)
-{
-    backwardTypeCheck(source, edge, typesPassedThrough, jumpToFail);
-    if (m_speculationDirection == ForwardSpeculation)
-        convertLastOSRExitToForward();
-}
-
-void SpeculativeJIT::forwardTypeCheck(JSValueSource source, Edge edge, SpeculatedType typesPassedThrough, MacroAssembler::Jump jumpToFail, const ValueRecovery& valueRecovery)
-{
-    backwardTypeCheck(source, edge, typesPassedThrough, jumpToFail);
-    convertLastOSRExitToForward(valueRecovery);
+    speculationCheck(BadType, source, edge.node(), jumpToFail);
 }
 
 RegisterSet SpeculativeJIT::usedRegisters()
@@ -1380,14 +1314,7 @@
     Node* child = node->child1().node();
     noticeOSRBirth(child);
     
-    m_stream->appendAndLog(VariableEvent::movHint(MinifiedID(child), node->local()));
-}
-
-void SpeculativeJIT::compileMovHintAndCheck(Node* node)
-{
-    compileMovHint(node);
-    speculate(node, node->child1());
-    noResult(node);
+    m_stream->appendAndLog(VariableEvent::movHint(MinifiedID(child), node->unlinkedLocal()));
 }
 
 void SpeculativeJIT::bail()
@@ -1482,7 +1409,7 @@
                 break;
                 
             case ZombieHint: {
-                recordSetLocal(DataFormatDead);
+                recordSetLocal(m_currentNode->unlinkedLocal(), VirtualRegister(), DataFormatDead);
                 break;
             }
 
@@ -1501,8 +1428,6 @@
                 dataLog("\n");
             }
             
-            m_speculationDirection = (m_currentNode->flags() & NodeExitsForward) ? ForwardSpeculation : BackwardSpeculation;
-            
             compile(m_currentNode);
 
 #if ENABLE(DFG_REGISTER_ALLOCATION_VALIDATION)
@@ -1540,18 +1465,19 @@
 {
     ASSERT(!m_currentNode);
     m_isCheckingArgumentTypes = true;
-    m_speculationDirection = BackwardSpeculation;
     m_codeOriginForExitTarget = CodeOrigin(0);
     m_codeOriginForExitProfile = CodeOrigin(0);
 
     for (int i = 0; i < m_jit.codeBlock()->numParameters(); ++i) {
         Node* node = m_jit.graph().m_arguments[i];
-        ASSERT(node->op() == SetArgument);
-        if (!node->shouldGenerate()) {
+        if (!node) {
             // The argument is dead. We don't do any checks for such arguments.
             continue;
         }
         
+        ASSERT(node->op() == SetArgument);
+        ASSERT(node->shouldGenerate());
+
         VariableAccessData* variableAccessData = node->variableAccessData();
         FlushFormat format = variableAccessData->flushFormat();
         
@@ -2167,16 +2093,9 @@
         MacroAssembler::AboveOrEqual, op1GPR, GPRInfo::tagTypeNumberRegister);
     
     if (needsTypeCheck(node->child1(), SpecFullNumber)) {
-        if (node->flags() & NodeExitsForward) {
-            forwardTypeCheck(
-                JSValueRegs(op1GPR), node->child1(), SpecFullNumber,
-                m_jit.branchTest64(MacroAssembler::Zero, op1GPR, GPRInfo::tagTypeNumberRegister),
-                ValueRecovery::inGPR(op1GPR, DataFormatJS));
-        } else {
-            backwardTypeCheck(
-                JSValueRegs(op1GPR), node->child1(), SpecFullNumber,
-                m_jit.branchTest64(MacroAssembler::Zero, op1GPR, GPRInfo::tagTypeNumberRegister));
-        }
+        typeCheck(
+            JSValueRegs(op1GPR), node->child1(), SpecFullNumber,
+            m_jit.branchTest64(MacroAssembler::Zero, op1GPR, GPRInfo::tagTypeNumberRegister));
     }
     
     m_jit.move(op1GPR, tempGPR);
@@ -2198,16 +2117,9 @@
         MacroAssembler::Equal, op1TagGPR, TrustedImm32(JSValue::Int32Tag));
     
     if (needsTypeCheck(node->child1(), SpecFullNumber)) {
-        if (node->flags() & NodeExitsForward) {
-            forwardTypeCheck(
-                JSValueRegs(op1TagGPR, op1PayloadGPR), node->child1(), SpecFullNumber,
-                m_jit.branch32(MacroAssembler::AboveOrEqual, op1TagGPR, TrustedImm32(JSValue::LowestTag)),
-                ValueRecovery::inPair(op1TagGPR, op1PayloadGPR));
-        } else {
-            backwardTypeCheck(
-                JSValueRegs(op1TagGPR, op1PayloadGPR), node->child1(), SpecFullNumber,
-                m_jit.branch32(MacroAssembler::AboveOrEqual, op1TagGPR, TrustedImm32(JSValue::LowestTag)));
-        }
+        typeCheck(
+            JSValueRegs(op1TagGPR, op1PayloadGPR), node->child1(), SpecFullNumber,
+            m_jit.branch32(MacroAssembler::AboveOrEqual, op1TagGPR, TrustedImm32(JSValue::LowestTag)));
     }
     
     unboxDouble(op1TagGPR, op1PayloadGPR, resultFPR, tempFPR);
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
index 6f0b841..13612cb 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
@@ -697,8 +697,12 @@
         // Check that no intervening nodes will be generated.
         for (unsigned index = m_indexInBlock + 1; index < m_block->size() - 1; ++index) {
             Node* node = m_block->at(index);
-            if (node->shouldGenerate())
-                return UINT_MAX;
+            if (!node->shouldGenerate())
+                continue;
+            // Check if it's a Phantom that can be safely ignored.
+            if (node->op() == Phantom && !node->child1())
+                continue;
+            return UINT_MAX;
         }
 
         // Check if the lastNode is a branch on this node.
@@ -2143,43 +2147,28 @@
     JITCompiler::Jump convertToDouble(JSValueOperand&, FPRReg result);
 #endif
     
-    // Add a backward speculation check.
-    void backwardSpeculationCheck(ExitKind, JSValueSource, Node*, MacroAssembler::Jump jumpToFail);
-    void backwardSpeculationCheck(ExitKind, JSValueSource, Node*, const MacroAssembler::JumpList& jumpsToFail);
-
-    // Add a speculation check without additional recovery.
+    // Add a speculation check.
     void speculationCheck(ExitKind, JSValueSource, Node*, MacroAssembler::Jump jumpToFail);
-    void speculationCheck(ExitKind, JSValueSource, Edge, MacroAssembler::Jump jumpToFail);
-    // Add a speculation check without additional recovery, and with a promise to supply a jump later.
-    OSRExitJumpPlaceholder backwardSpeculationCheck(ExitKind, JSValueSource, Node*);
-    OSRExitJumpPlaceholder backwardSpeculationCheck(ExitKind, JSValueSource, Edge);
-    // Add a set of speculation checks without additional recovery.
     void speculationCheck(ExitKind, JSValueSource, Node*, const MacroAssembler::JumpList& jumpsToFail);
+
+    // Add a speculation check without additional recovery, and with a promise to supply a jump later.
+    OSRExitJumpPlaceholder speculationCheck(ExitKind, JSValueSource, Node*);
+    OSRExitJumpPlaceholder speculationCheck(ExitKind, JSValueSource, Edge);
+    void speculationCheck(ExitKind, JSValueSource, Edge, MacroAssembler::Jump jumpToFail);
     void speculationCheck(ExitKind, JSValueSource, Edge, const MacroAssembler::JumpList& jumpsToFail);
     // Add a speculation check with additional recovery.
-    void backwardSpeculationCheck(ExitKind, JSValueSource, Node*, MacroAssembler::Jump jumpToFail, const SpeculationRecovery&);
-    void backwardSpeculationCheck(ExitKind, JSValueSource, Edge, MacroAssembler::Jump jumpToFail, const SpeculationRecovery&);
+    void speculationCheck(ExitKind, JSValueSource, Node*, MacroAssembler::Jump jumpToFail, const SpeculationRecovery&);
+    void speculationCheck(ExitKind, JSValueSource, Edge, MacroAssembler::Jump jumpToFail, const SpeculationRecovery&);
     
     void emitInvalidationPoint(Node*);
     
-    // It is generally a good idea to not use this directly.
-    void convertLastOSRExitToForward(const ValueRecovery& = ValueRecovery());
-    
-    // Note: not specifying the valueRecovery argument (leaving it as ValueRecovery()) implies
-    // that you've ensured that there exists a MovHint prior to your use of forwardSpeculationCheck().
-    void forwardSpeculationCheck(ExitKind, JSValueSource, Node*, MacroAssembler::Jump jumpToFail, const ValueRecovery& = ValueRecovery());
-    void forwardSpeculationCheck(ExitKind, JSValueSource, Node*, const MacroAssembler::JumpList& jumpsToFail, const ValueRecovery& = ValueRecovery());
-    void speculationCheck(ExitKind, JSValueSource, Node*, MacroAssembler::Jump jumpToFail, const SpeculationRecovery&);
-    void speculationCheck(ExitKind, JSValueSource, Edge, MacroAssembler::Jump jumpToFail, const SpeculationRecovery&);
     // Called when we statically determine that a speculation will fail.
     void terminateSpeculativeExecution(ExitKind, JSValueRegs, Node*);
     void terminateSpeculativeExecution(ExitKind, JSValueRegs, Edge);
     
     // Helpers for performing type checks on an edge stored in the given registers.
     bool needsTypeCheck(Edge edge, SpeculatedType typesPassedThrough) { return m_interpreter.needsTypeCheck(edge, typesPassedThrough); }
-    void backwardTypeCheck(JSValueSource, Edge, SpeculatedType typesPassedThrough, MacroAssembler::Jump jumpToFail);
     void typeCheck(JSValueSource, Edge, SpeculatedType typesPassedThrough, MacroAssembler::Jump jumpToFail);
-    void forwardTypeCheck(JSValueSource, Edge, SpeculatedType typesPassedThrough, MacroAssembler::Jump jumpToFail, const ValueRecovery&);
 
     void speculateInt32(Edge);
     void speculateMachineInt(Edge);
@@ -2252,7 +2241,6 @@
     // The current node being generated.
     BasicBlock* m_block;
     Node* m_currentNode;
-    SpeculationDirection m_speculationDirection;
     bool m_canExit;
     unsigned m_indexInBlock;
     // Virtual and physical register maps.
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
index 4920289..31a1550 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
@@ -1921,24 +1921,14 @@
         break;
     }
 
-    case MovHintAndCheck: {
-        compileMovHintAndCheck(node);
-        break;
-    }
-        
     case MovHint:
-    case ZombieHint: {
+    case ZombieHint:
+    case Check: {
         RELEASE_ASSERT_NOT_REACHED();
         break;
     }
 
     case SetLocal: {
-        // SetLocal doubles as a hint as to where a node will be stored and
-        // as a speculation point. So before we speculate make sure that we
-        // know where the child of this node needs to go in the virtual
-        // stack.
-        compileMovHint(node);
-        
         switch (node->variableAccessData()->flushFormat()) {
         case FlushedDouble: {
             SpeculateDoubleOperand value(this, node->child1());
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
index 85d5834..af1010c 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
@@ -2236,24 +2236,14 @@
         break;
     }
         
-    case MovHintAndCheck: {
-        compileMovHintAndCheck(node);
-        break;
-    }
-        
     case MovHint:
-    case ZombieHint: {
+    case ZombieHint:
+    case Check: {
         RELEASE_ASSERT_NOT_REACHED();
         break;
     }
 
     case SetLocal: {
-        // SetLocal doubles as a hint as to where a node will be stored and
-        // as a speculation point. So before we speculate make sure that we
-        // know where the child of this node needs to go in the virtual
-        // stack.
-        compileMovHint(node);
-        
         switch (node->variableAccessData()->flushFormat()) {
         case FlushedDouble: {
             SpeculateDoubleOperand value(this, node->child1());
@@ -2304,7 +2294,6 @@
             JSValueOperand value(this, node->child1());
             m_jit.store64(value.gpr(), JITCompiler::addressFor(node->machineLocal()));
             noResult(node);
-            
             recordSetLocal(DataFormatJS);
             break;
         }
diff --git a/Source/JavaScriptCore/dfg/DFGTypeCheckHoistingPhase.cpp b/Source/JavaScriptCore/dfg/DFGTypeCheckHoistingPhase.cpp
index 9483602..5625ef4 100644
--- a/Source/JavaScriptCore/dfg/DFGTypeCheckHoistingPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGTypeCheckHoistingPhase.cpp
@@ -163,33 +163,22 @@
                     if (!iter->value.m_structure && !iter->value.m_arrayModeIsValid)
                         break;
 
-                    // First insert a dead SetLocal to tell OSR that the child's value should
-                    // be dropped into this bytecode variable if the CheckStructure decides
-                    // to exit.
-                    
                     CodeOrigin codeOrigin = node->codeOrigin;
                     Edge child1 = node->child1();
                     
-                    insertionSet.insertNode(
-                        indexInBlock, SpecNone, SetLocal, codeOrigin, OpInfo(variable), child1);
-
-                    // Use NodeExitsForward to indicate that we should exit to the next
-                    // bytecode instruction rather than reexecuting the current one.
-                    Node* newNode = 0;
                     if (iter->value.m_structure) {
-                        newNode = insertionSet.insertNode(
+                        insertionSet.insertNode(
                             indexInBlock, SpecNone, CheckStructure, codeOrigin,
                             OpInfo(m_graph.addStructureSet(iter->value.m_structure)),
                             Edge(child1.node(), CellUse));
                     } else if (iter->value.m_arrayModeIsValid) {
                         ASSERT(iter->value.m_arrayModeHoistingOkay);
-                        newNode = insertionSet.insertNode(
+                        insertionSet.insertNode(
                             indexInBlock, SpecNone, CheckArray, codeOrigin,
                             OpInfo(iter->value.m_arrayMode.asWord()),
                             Edge(child1.node(), CellUse));
                     } else
                         RELEASE_ASSERT_NOT_REACHED();
-                    newNode->mergeFlags(NodeExitsForward);
                     changed = true;
                     break;
                 }
@@ -228,9 +217,6 @@
                 switch (node->op()) {
                 case CheckStructure:
                 case StructureTransitionWatchpoint: {
-                    // We currently rely on the fact that we're the only ones who would
-                    // insert these nodes with NodeExitsForward.
-                    RELEASE_ASSERT(!(node->flags() & NodeExitsForward));
                     Node* child = node->child1().node();
                     if (child->op() != GetLocal)
                         break;
@@ -257,6 +243,7 @@
                 case GetIndexedPropertyStorage:
                 case GetTypedArrayByteOffset:
                 case Phantom:
+                case MovHint:
                     // Don't count these uses.
                     break;
                     
@@ -329,9 +316,6 @@
                 Node* node = block->at(indexInBlock);
                 switch (node->op()) {
                 case CheckArray: {
-                    // We currently rely on the fact that we're the only ones who would
-                    // insert these nodes with NodeExitsForward.
-                    RELEASE_ASSERT(!(node->flags() & NodeExitsForward));
                     Node* child = node->child1().node();
                     if (child->op() != GetLocal)
                         break;
@@ -357,6 +341,7 @@
                 case GetArrayLength:
                 case GetIndexedPropertyStorage:
                 case Phantom:
+                case MovHint:
                     // Don't count these uses.
                     break;
                     
diff --git a/Source/JavaScriptCore/dfg/DFGValidate.cpp b/Source/JavaScriptCore/dfg/DFGValidate.cpp
index a50c07f..a0c3cce 100644
--- a/Source/JavaScriptCore/dfg/DFGValidate.cpp
+++ b/Source/JavaScriptCore/dfg/DFGValidate.cpp
@@ -245,10 +245,7 @@
                         edge->op() == SetLocal
                         || edge->op() == SetArgument
                         || edge->op() == Flush
-                        || edge->op() == Phi
-                        || edge->op() == ZombieHint
-                        || edge->op() == MovHint
-                        || edge->op() == MovHintAndCheck);
+                        || edge->op() == Phi);
                     
                     if (phisInThisBlock.contains(edge.node()))
                         continue;
@@ -257,9 +254,6 @@
                         VALIDATE(
                             (node, edge),
                             edge->op() == SetLocal
-                            || edge->op() == ZombieHint
-                            || edge->op() == MovHint
-                            || edge->op() == MovHintAndCheck
                             || edge->op() == SetArgument
                             || edge->op() == Flush);
                         
@@ -292,9 +286,6 @@
                         VALIDATE(
                             (local, block->predecessors[k], prevNode),
                             prevNode->op() == SetLocal
-                            || prevNode->op() == MovHint
-                            || prevNode->op() == MovHintAndCheck
-                            || prevNode->op() == ZombieHint
                             || prevNode->op() == SetArgument
                             || prevNode->op() == Phi);
                         if (prevNode == edge.node()) {
diff --git a/Source/JavaScriptCore/dfg/DFGVariableAccessData.h b/Source/JavaScriptCore/dfg/DFGVariableAccessData.h
index aa67407..5f83aea 100644
--- a/Source/JavaScriptCore/dfg/DFGVariableAccessData.h
+++ b/Source/JavaScriptCore/dfg/DFGVariableAccessData.h
@@ -74,6 +74,7 @@
         , m_structureCheckHoistingFailed(false)
         , m_checkArrayHoistingFailed(false)
         , m_isProfitableToUnbox(false)
+        , m_isLoadedFrom(false)
         , m_doubleFormatState(EmptyDoubleFormatState)
     {
         clearVotes();
diff --git a/Source/JavaScriptCore/dfg/DFGVariableEventStream.cpp b/Source/JavaScriptCore/dfg/DFGVariableEventStream.cpp
index 5becbb2..98e08f6 100644
--- a/Source/JavaScriptCore/dfg/DFGVariableEventStream.cpp
+++ b/Source/JavaScriptCore/dfg/DFGVariableEventStream.cpp
@@ -192,70 +192,8 @@
         
         MinifiedGenerationInfo info = generationInfos.get(source.id());
         if (info.format == DataFormatNone) {
-            // Try to see if there is an alternate node that would contain the value we want.
-            //
-            // Backward rewiring refers to:
-            //
-            //     a: Something(...)
-            //     b: Id(@a) // some identity function
-            //     c: SetLocal(@b)
-            //
-            // Where we find @b being dead, but @a is still alive.
-            //
-            // Forward rewiring refers to:
-            //
-            //     a: Something(...)
-            //     b: SetLocal(@a)
-            //     c: Id(@a) // some identity function
-            //
-            // Where we find @a being dead, but @b is still alive.
-            
-            bool found = false;
-            
-            if (node && permitsOSRBackwardRewiring(node->op())) {
-                MinifiedID id = node->child1();
-                if (tryToSetConstantRecovery(valueRecoveries[i], codeBlock, graph.at(id)))
-                    continue;
-                info = generationInfos.get(id);
-                if (info.format != DataFormatNone)
-                    found = true;
-            }
-            
-            if (!found) {
-                MinifiedID bestID;
-                unsigned bestScore = 0;
-                
-                HashMap<MinifiedID, MinifiedGenerationInfo>::iterator iter = generationInfos.begin();
-                HashMap<MinifiedID, MinifiedGenerationInfo>::iterator end = generationInfos.end();
-                for (; iter != end; ++iter) {
-                    MinifiedID id = iter->key;
-                    node = graph.at(id);
-                    if (!node)
-                        continue;
-                    if (!node->hasChild1())
-                        continue;
-                    if (node->child1() != source.id())
-                        continue;
-                    if (iter->value.format == DataFormatNone)
-                        continue;
-                    unsigned myScore = forwardRewiringSelectionScore(node->op());
-                    if (myScore <= bestScore)
-                        continue;
-                    bestID = id;
-                    bestScore = myScore;
-                }
-                
-                if (!!bestID) {
-                    info = generationInfos.get(bestID);
-                    ASSERT(info.format != DataFormatNone);
-                    found = true;
-                }
-            }
-            
-            if (!found) {
-                valueRecoveries[i] = ValueRecovery::constant(jsUndefined());
-                continue;
-            }
+            valueRecoveries[i] = ValueRecovery::constant(jsUndefined());
+            continue;
         }
         
         ASSERT(info.format != DataFormatNone);
diff --git a/Source/JavaScriptCore/ftl/FTLCapabilities.cpp b/Source/JavaScriptCore/ftl/FTLCapabilities.cpp
index e01df4e..d7d4365 100644
--- a/Source/JavaScriptCore/ftl/FTLCapabilities.cpp
+++ b/Source/JavaScriptCore/ftl/FTLCapabilities.cpp
@@ -42,7 +42,6 @@
     case WeakJSConstant:
     case GetLocal:
     case SetLocal:
-    case MovHintAndCheck:
     case MovHint:
     case ZombieHint:
     case Phantom:
@@ -111,6 +110,7 @@
     case LogicalNot:
     case CheckInBounds:
     case ConstantStoragePointer:
+    case Check:
         // These are OK.
         break;
     case GetById:
diff --git a/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp b/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
index 6965ce3..a0aa879 100644
--- a/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
+++ b/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
@@ -228,8 +228,6 @@
         
         bool shouldExecuteEffects = m_interpreter.startExecuting(m_node);
         
-        m_direction = (m_node->flags() & NodeExitsForward) ? ForwardSpeculation : BackwardSpeculation;
-        
         switch (m_node->op()) {
         case Upsilon:
             compileUpsilon();
@@ -260,9 +258,6 @@
         case ZombieHint:
             compileZombieHint();
             break;
-        case MovHintAndCheck:
-            compileMovHintAndCheck();
-            break;
         case Phantom:
             compilePhantom();
             break;
@@ -657,15 +652,15 @@
 
         switch (useKindFor(variable->flushFormat())) {
         case Int32Use:
-            speculateBackward(BadType, jsValueValue(jsValue), m_node, isNotInt32(jsValue));
+            speculate(BadType, jsValueValue(jsValue), m_node, isNotInt32(jsValue));
             setInt32(unboxInt32(jsValue));
             break;
         case CellUse:
-            speculateBackward(BadType, jsValueValue(jsValue), m_node, isNotCell(jsValue));
+            speculate(BadType, jsValueValue(jsValue), m_node, isNotCell(jsValue));
             setJSValue(jsValue);
             break;
         case BooleanUse:
-            speculateBackward(BadType, jsValueValue(jsValue), m_node, isNotBoolean(jsValue));
+            speculate(BadType, jsValueValue(jsValue), m_node, isNotBoolean(jsValue));
             setBoolean(unboxBoolean(jsValue));
             break;
         case UntypedUse:
@@ -701,8 +696,6 @@
     
     void compileSetLocal()
     {
-        observeMovHint(m_node);
-        
         VariableAccessData* variable = m_node->variableAccessData();
         switch (variable->flushFormat()) {
         case FlushedJSValue: {
@@ -753,19 +746,16 @@
     
     void compileMovHint()
     {
-        observeMovHint(m_node);
+        ASSERT(m_node->containsMovHint());
+        ASSERT(m_node->op() != ZombieHint);
+        
+        VirtualRegister operand = m_node->unlinkedLocal();
+        m_availability.operand(operand) = Availability(m_node->child1().node());
     }
     
     void compileZombieHint()
     {
-        VariableAccessData* data = m_node->variableAccessData();
-        m_availability.operand(data->local()) = Availability::unavailable();
-    }
-    
-    void compileMovHintAndCheck()
-    {
-        observeMovHint(m_node);
-        speculate(m_node->child1());
+        m_availability.operand(m_node->unlinkedLocal()) = Availability::unavailable();
     }
     
     void compilePhantom()
@@ -1177,40 +1167,7 @@
     
     void compileInt32ToDouble()
     {
-        if (!m_interpreter.needsTypeCheck(m_node->child1(), SpecFullNumber)
-            || m_node->speculationDirection() == BackwardSpeculation) {
-            setDouble(lowDouble(m_node->child1()));
-            return;
-        }
-        
-        LValue boxedValue = lowJSValue(m_node->child1(), ManualOperandSpeculation);
-        
-        LBasicBlock intCase = FTL_NEW_BLOCK(m_out, ("Double unboxing int case"));
-        LBasicBlock doubleCase = FTL_NEW_BLOCK(m_out, ("Double unboxing double case"));
-        LBasicBlock continuation = FTL_NEW_BLOCK(m_out, ("Double unboxing continuation"));
-        
-        m_out.branch(isNotInt32(boxedValue), doubleCase, intCase);
-        
-        LBasicBlock lastNext = m_out.appendTo(intCase, doubleCase);
-        
-        ValueFromBlock intToDouble = m_out.anchor(
-            m_out.intToDouble(unboxInt32(boxedValue)));
-        m_out.jump(continuation);
-        
-        m_out.appendTo(doubleCase, continuation);
-
-        forwardTypeCheck(
-            jsValueValue(boxedValue), m_node->child1(), SpecFullNumber,
-            isCellOrMisc(boxedValue), jsValueValue(boxedValue));
-        
-        ValueFromBlock unboxedDouble = m_out.anchor(unboxDouble(boxedValue));
-        m_out.jump(continuation);
-        
-        m_out.appendTo(continuation, lastNext);
-        
-        LValue result = m_out.phi(m_out.doubleType, intToDouble, unboxedDouble);
-        
-        setDouble(result);
+        setDouble(lowDouble(m_node->child1()));
     }
     
     void compileCheckStructure()
@@ -3196,26 +3153,10 @@
         return m_out.phi(m_out.int32, fastResult, slowResult);
     }
     
-    void speculateBackward(
-        ExitKind kind, FormattedValue lowValue, Node* highValue, LValue failCondition)
-    {
-        appendOSRExit(
-            kind, lowValue, highValue, failCondition, BackwardSpeculation, FormattedValue());
-    }
-    
-    void speculateForward(
-        ExitKind kind, FormattedValue lowValue, Node* highValue, LValue failCondition,
-        const FormattedValue& recovery)
-    {
-        appendOSRExit(
-            kind, lowValue, highValue, failCondition, ForwardSpeculation, recovery);
-    }
-    
     void speculate(
         ExitKind kind, FormattedValue lowValue, Node* highValue, LValue failCondition)
     {
-        appendOSRExit(
-            kind, lowValue, highValue, failCondition, m_direction, FormattedValue());
+        appendOSRExit(kind, lowValue, highValue, failCondition);
     }
     
     void terminate(ExitKind kind)
@@ -3223,41 +3164,21 @@
         speculate(kind, noValue(), 0, m_out.booleanTrue);
     }
     
-    void backwardTypeCheck(
-        FormattedValue lowValue, Edge highValue, SpeculatedType typesPassedThrough,
-        LValue failCondition)
-    {
-        appendTypeCheck(
-            lowValue, highValue, typesPassedThrough, failCondition, BackwardSpeculation,
-            FormattedValue());
-    }
-    
-    void forwardTypeCheck(
-        FormattedValue lowValue, Edge highValue, SpeculatedType typesPassedThrough,
-        LValue failCondition, const FormattedValue& recovery)
-    {
-        appendTypeCheck(
-            lowValue, highValue, typesPassedThrough, failCondition, ForwardSpeculation,
-            recovery);
-    }
-    
     void typeCheck(
         FormattedValue lowValue, Edge highValue, SpeculatedType typesPassedThrough,
         LValue failCondition)
     {
-        appendTypeCheck(
-            lowValue, highValue, typesPassedThrough, failCondition, m_direction,
-            FormattedValue());
+        appendTypeCheck(lowValue, highValue, typesPassedThrough, failCondition);
     }
     
     void appendTypeCheck(
         FormattedValue lowValue, Edge highValue, SpeculatedType typesPassedThrough,
-        LValue failCondition, SpeculationDirection direction, FormattedValue recovery)
+        LValue failCondition)
     {
         if (!m_interpreter.needsTypeCheck(highValue, typesPassedThrough))
             return;
         ASSERT(mayHaveTypeCheck(highValue.useKind()));
-        appendOSRExit(BadType, lowValue, highValue.node(), failCondition, direction, recovery);
+        appendOSRExit(BadType, lowValue, highValue.node(), failCondition);
         m_interpreter.filter(highValue, typesPassedThrough);
     }
     
@@ -4092,8 +4013,7 @@
     }
     
     void appendOSRExit(
-        ExitKind kind, FormattedValue lowValue, Node* highValue, LValue failCondition,
-        SpeculationDirection direction, FormattedValue recovery)
+        ExitKind kind, FormattedValue lowValue, Node* highValue, LValue failCondition)
     {
         if (verboseCompilationEnabled())
             dataLog("    OSR exit #", m_ftlState.jitCode->osrExit.size(), " with availability: ", m_availability, "\n");
@@ -4118,38 +4038,20 @@
         
         lastNext = m_out.appendTo(failCase, continuation);
         
-        emitOSRExitCall(exit, lowValue, direction, recovery);
+        emitOSRExitCall(exit, lowValue);
         
         m_out.unreachable();
         
         m_out.appendTo(continuation, lastNext);
     }
     
-    void emitOSRExitCall(
-        OSRExit& exit, FormattedValue lowValue, SpeculationDirection direction,
-        FormattedValue recovery)
+    void emitOSRExitCall(OSRExit& exit, FormattedValue lowValue)
     {
         ExitArgumentList arguments;
         
         CodeOrigin codeOrigin = exit.m_codeOrigin;
         
-        if (direction == BackwardSpeculation)
-            buildExitArguments(exit, arguments, lowValue, codeOrigin);
-        else {
-            ASSERT(direction == ForwardSpeculation);
-            if (!recovery) {
-                for (unsigned nodeIndex = m_nodeIndex; nodeIndex < m_highBlock->size(); ++nodeIndex) {
-                    Node* node = m_highBlock->at(nodeIndex);
-                    if (node->codeOriginForExitTarget == codeOrigin)
-                        continue;
-                    codeOrigin = node->codeOriginForExitTarget;
-                    break;
-                }
-            }
-            
-            buildExitArguments(exit, arguments, lowValue, codeOrigin);
-            exit.convertToForward(m_highBlock, m_node, m_nodeIndex, recovery, arguments);
-        }
+        buildExitArguments(exit, arguments, lowValue, codeOrigin);
         
         callStackmap(exit, arguments);
     }
@@ -4309,16 +4211,6 @@
         arguments.append(value);
     }
     
-    void observeMovHint(Node* node)
-    {
-        ASSERT(node->containsMovHint());
-        ASSERT(node->op() != ZombieHint);
-        
-        VirtualRegister operand = node->local();
-        
-        m_availability.operand(operand) = Availability(node->child1().node());
-    }
-    
     void setInt32(Node* node, LValue value)
     {
         m_int32Values.set(node, LoweredNodeValue(value, m_highBlock));
@@ -4489,7 +4381,6 @@
     CodeOrigin m_codeOriginForExitProfile;
     unsigned m_nodeIndex;
     Node* m_node;
-    SpeculationDirection m_direction;
     
     uint32_t m_stackmapIDs;
 };
diff --git a/Source/JavaScriptCore/ftl/FTLOSRExit.cpp b/Source/JavaScriptCore/ftl/FTLOSRExit.cpp
index 3fbe7db..00ea014 100644
--- a/Source/JavaScriptCore/ftl/FTLOSRExit.cpp
+++ b/Source/JavaScriptCore/ftl/FTLOSRExit.cpp
@@ -61,41 +61,6 @@
         m_patchableCodeOffset);
 }
 
-void OSRExit::convertToForward(
-    BasicBlock* block, Node* currentNode, unsigned nodeIndex,
-    const FormattedValue &value, ExitArgumentList& arguments)
-{
-    Node* node;
-    Node* lastMovHint;
-    if (!doSearchForForwardConversion(block, currentNode, nodeIndex, !!value, node, lastMovHint))
-        return;
-
-    ASSERT(node->codeOrigin != currentNode->codeOrigin);
-    
-    m_codeOrigin = node->codeOrigin;
-    
-    if (!value)
-        return;
-    
-    VirtualRegister overriddenOperand = lastMovHint->local();
-    
-    // Is the value for this operand being passed as an argument to the exit, or is
-    // it something else? If it's an argument already, then replace that argument;
-    // otherwise add another argument.
-    if (m_values.operand(overriddenOperand).isArgument()) {
-        ExitArgument exitArgument = m_values.operand(overriddenOperand).exitArgument();
-        arguments[exitArgument.argument()] = value.value();
-        m_values.operand(overriddenOperand) = ExitValue::exitArgument(
-            exitArgument.withFormat(value.format()));
-        return;
-    }
-    
-    unsigned argument = arguments.size();
-    arguments.append(value.value());
-    m_values.operand(overriddenOperand) = ExitValue::exitArgument(
-        ExitArgument(value.format(), argument));
-}
-
 } } // namespace JSC::FTL
 
 #endif // ENABLE(FTL_JIT)
diff --git a/Source/JavaScriptCore/ftl/FTLOSRExit.h b/Source/JavaScriptCore/ftl/FTLOSRExit.h
index 1460f4f..4e479eb 100644
--- a/Source/JavaScriptCore/ftl/FTLOSRExit.h
+++ b/Source/JavaScriptCore/ftl/FTLOSRExit.h
@@ -166,10 +166,6 @@
     uint32_t m_stackmapID;
     
     CodeLocationJump codeLocationForRepatch(CodeBlock* ftlCodeBlock) const;
-    
-    void convertToForward(
-        DFG::BasicBlock*, DFG::Node* currentNode, unsigned nodeIndex,
-        const FormattedValue&, ExitArgumentList& arguments);
 };
 
 } } // namespace JSC::FTL
diff --git a/Source/JavaScriptCore/tests/stress/dead-int32-to-double.js b/Source/JavaScriptCore/tests/stress/dead-int32-to-double.js
new file mode 100644
index 0000000..0713fd6
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/dead-int32-to-double.js
@@ -0,0 +1,16 @@
+function foo(int, o) {
+    var x = int;
+    o.f = x;
+    for (var i = 0; i < 100; ++i)
+        x += 0.5;
+}
+
+noInline(foo);
+
+for (var i = 0; i < 100; ++i)
+    foo(42, {});
+
+var o = {g: 43};
+foo(47, o);
+if (o.f != 47)
+    throw "Error: o.f is " + o.f;
diff --git a/Source/JavaScriptCore/tests/stress/dead-uint32-to-number.js b/Source/JavaScriptCore/tests/stress/dead-uint32-to-number.js
new file mode 100644
index 0000000..4e39878
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/dead-uint32-to-number.js
@@ -0,0 +1,16 @@
+function foo(a, o) {
+    var x = a >>> 0;
+    o.f = x | 0;
+    for (var i = 0; i < 100; ++i)
+        x++;
+}
+
+noInline(foo);
+
+for (var i = 0; i < 100; ++i)
+    foo(42, {});
+
+var o = {g: 43};
+foo(47, o);
+if (o.f != 47)
+    throw "Error: o.f is " + o.f;