Title: [223086] trunk/Source/_javascript_Core
Revision
223086
Author
[email protected]
Date
2017-10-09 16:55:25 -0700 (Mon, 09 Oct 2017)

Log Message

Refactor the inliner to simplify block linking
https://bugs.webkit.org/show_bug.cgi?id=177922

Reviewed by Saam Barati.

The biggest refactor changes the way blocks are linked. In DFGByteCodeParser, most terminals (such as Jump or Branch) jump to nullptr initially, and have
some metadata indicating the bytecode index corresponding to their targets. They are later linked to the right basic block using two fields of InlineStackEntry:
- m_unlinkedBlocks is just a worklist of blocks with a terminal that needs to be linked
- m_linkingTargets is a dictionary from bytecode indices to BasicBlock*
Before refactoring, every block was automatically added to both of these fields, for the InlineStackEntry of whatever function allocated it.
This created a significant number of corner cases, such as blocks allocated in a caller, with a terminal written by an inlined callee and pointing to a block in the callee,
or blocks allocated in an inline callee, with a terminal written by the caller after it returns and pointing to a block in the caller, or blocks with a manually linked
terminal that needs to be taken off m_unlinkedBlocks.
I changed things so that blocks are only added to m_unlinkedBlocks when their terminal gets written (see the LAST_OPCODE macro) making it a lot easier to be in the "right" InlineStackEntry,
that is the one that holds their target in its m_linkingTargets field.

There are a few much smaller refactors in this patch:
- parse() is now of type void insted of bool (it was always returning true)
- The 7 and 8 arguments of handleCall were inlined in its 3 arguments version for readability
- The 9 argument version was cleaned up and simplified
- I made separate allocateBlock routines because the little dance with adoptRef(* new BasicBlock(...)) was being repeated in lots of places, and typos in that were a major source of bugs during other refactorings
- Jumps are now created with explicit addJumpTo() functions, providing some sanity checking through asserts and didLink()
- Blocks are only added to m_unlinkedBlocks if they end in a terminal that linkBlock works with (see LAST_OPCODE)

* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::addToGraph):
(JSC::DFG::ByteCodeParser::handleCall):
(JSC::DFG::ByteCodeParser::refineStatically):
(JSC::DFG::ByteCodeParser::handleRecursiveTailCall):
(JSC::DFG::ByteCodeParser::handleVarargsCall):
(JSC::DFG::ByteCodeParser::inlineCall):
(JSC::DFG::ByteCodeParser::attemptToInlineCall):
(JSC::DFG::ByteCodeParser::handleInlining):
(JSC::DFG::ByteCodeParser::handleIntrinsicCall):
(JSC::DFG::ByteCodeParser::handleConstantInternalFunction):
(JSC::DFG::ByteCodeParser::parseBlock):
(JSC::DFG::ByteCodeParser::parseCodeBlock):
(JSC::DFG::ByteCodeParser::parse):
(JSC::DFG::parse):
(JSC::DFG::ByteCodeParser::cancelLinkingForBlock): Deleted.
* dfg/DFGByteCodeParser.h:
* dfg/DFGPlan.cpp:
(JSC::DFG::Plan::compileInThreadImpl):

Modified Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (223085 => 223086)


--- trunk/Source/_javascript_Core/ChangeLog	2017-10-09 23:45:45 UTC (rev 223085)
+++ trunk/Source/_javascript_Core/ChangeLog	2017-10-09 23:55:25 UTC (rev 223086)
@@ -1,3 +1,49 @@
+2017-10-09  Robin Morisset  <[email protected]>
+
+        Refactor the inliner to simplify block linking
+        https://bugs.webkit.org/show_bug.cgi?id=177922
+
+        Reviewed by Saam Barati.
+
+        The biggest refactor changes the way blocks are linked. In DFGByteCodeParser, most terminals (such as Jump or Branch) jump to nullptr initially, and have
+        some metadata indicating the bytecode index corresponding to their targets. They are later linked to the right basic block using two fields of InlineStackEntry:
+        - m_unlinkedBlocks is just a worklist of blocks with a terminal that needs to be linked
+        - m_linkingTargets is a dictionary from bytecode indices to BasicBlock*
+        Before refactoring, every block was automatically added to both of these fields, for the InlineStackEntry of whatever function allocated it.
+        This created a significant number of corner cases, such as blocks allocated in a caller, with a terminal written by an inlined callee and pointing to a block in the callee,
+        or blocks allocated in an inline callee, with a terminal written by the caller after it returns and pointing to a block in the caller, or blocks with a manually linked
+        terminal that needs to be taken off m_unlinkedBlocks.
+        I changed things so that blocks are only added to m_unlinkedBlocks when their terminal gets written (see the LAST_OPCODE macro) making it a lot easier to be in the "right" InlineStackEntry,
+        that is the one that holds their target in its m_linkingTargets field.
+
+        There are a few much smaller refactors in this patch:
+        - parse() is now of type void insted of bool (it was always returning true)
+        - The 7 and 8 arguments of handleCall were inlined in its 3 arguments version for readability
+        - The 9 argument version was cleaned up and simplified
+        - I made separate allocateBlock routines because the little dance with adoptRef(* new BasicBlock(...)) was being repeated in lots of places, and typos in that were a major source of bugs during other refactorings
+        - Jumps are now created with explicit addJumpTo() functions, providing some sanity checking through asserts and didLink()
+        - Blocks are only added to m_unlinkedBlocks if they end in a terminal that linkBlock works with (see LAST_OPCODE)
+
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::addToGraph):
+        (JSC::DFG::ByteCodeParser::handleCall):
+        (JSC::DFG::ByteCodeParser::refineStatically):
+        (JSC::DFG::ByteCodeParser::handleRecursiveTailCall):
+        (JSC::DFG::ByteCodeParser::handleVarargsCall):
+        (JSC::DFG::ByteCodeParser::inlineCall):
+        (JSC::DFG::ByteCodeParser::attemptToInlineCall):
+        (JSC::DFG::ByteCodeParser::handleInlining):
+        (JSC::DFG::ByteCodeParser::handleIntrinsicCall):
+        (JSC::DFG::ByteCodeParser::handleConstantInternalFunction):
+        (JSC::DFG::ByteCodeParser::parseBlock):
+        (JSC::DFG::ByteCodeParser::parseCodeBlock):
+        (JSC::DFG::ByteCodeParser::parse):
+        (JSC::DFG::parse):
+        (JSC::DFG::ByteCodeParser::cancelLinkingForBlock): Deleted.
+        * dfg/DFGByteCodeParser.h:
+        * dfg/DFGPlan.cpp:
+        (JSC::DFG::Plan::compileInThreadImpl):
+
 2017-10-09  Michael Saboff  <[email protected]>
 
         Implement RegExp Unicode property escapes

Modified: trunk/Source/_javascript_Core/dfg/DFGBasicBlock.h (223085 => 223086)


--- trunk/Source/_javascript_Core/dfg/DFGBasicBlock.h	2017-10-09 23:45:45 UTC (rev 223085)
+++ trunk/Source/_javascript_Core/dfg/DFGBasicBlock.h	2017-10-09 23:55:25 UTC (rev 223086)
@@ -251,22 +251,7 @@
 };
 
 typedef Vector<BasicBlock*, 5> BlockList;
-
-struct UnlinkedBlock {
-    BasicBlock* m_block;
-    bool m_needsNormalLinking;
-    bool m_needsEarlyReturnLinking;
     
-    UnlinkedBlock() { }
-    
-    explicit UnlinkedBlock(BasicBlock* block)
-        : m_block(block)
-        , m_needsNormalLinking(true)
-        , m_needsEarlyReturnLinking(false)
-    {
-    }
-};
-    
 static inline unsigned getBytecodeBeginForBlock(BasicBlock** basicBlock)
 {
     return (*basicBlock)->bytecodeBegin;

Modified: trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp (223085 => 223086)


--- trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp	2017-10-09 23:45:45 UTC (rev 223085)
+++ trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp	2017-10-09 23:55:25 UTC (rev 223086)
@@ -68,21 +68,19 @@
 
 namespace JSC { namespace DFG {
 
-namespace {
-
-#define PARSER_ASSERT(assertion, ...) do {          \
-    if (UNLIKELY(!(assertion))) { \
-        WTFReportAssertionFailure(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, #assertion); \
-        CRASH_WITH_INFO(__VA_ARGS__); \
-    } \
-} while (0)
-
-} // anonymous namespace
-
 namespace DFGByteCodeParserInternal {
+#ifdef NDEBUG
 static const bool verbose = false;
-}
+#else
+static const bool verbose = true;
+#endif
+} // namespace DFGByteCodeParserInternal
 
+#define VERBOSE_LOG(...) do { \
+if (DFGByteCodeParserInternal::verbose && Options::verboseDFGBytecodeParsing()) \
+dataLog(__VA_ARGS__); \
+} while (false)
+
 class ConstantBufferKey {
 public:
     ConstantBufferKey()
@@ -181,7 +179,7 @@
     }
     
     // Parse a full CodeBlock of bytecode.
-    bool parse();
+    void parse();
     
 private:
     struct InlineStackEntry;
@@ -191,6 +189,7 @@
     
     void ensureLocals(unsigned newNumLocals)
     {
+        VERBOSE_LOG("   ensureLocals: trying to raise m_numLocals from ", m_numLocals, " to ", newNumLocals, "\n");
         if (newNumLocals <= m_numLocals)
             return;
         m_numLocals = newNumLocals;
@@ -203,16 +202,23 @@
     bool handleMinMax(int resultOperand, NodeType op, int registerOffset, int argumentCountIncludingThis, const ChecksFunctor& insertChecks);
     
     void refineStatically(CallLinkStatus&, Node* callTarget);
+    // Blocks can either be targetable (i.e. in the m_blockLinkingTargets of one InlineStackEntry) with a well-defined bytecodeBegin,
+    // or they can be untargetable, with bytecodeBegin==UINT_MAX, to be managed manually and not by the linkBlock machinery.
+    // This is used most notably when doing polyvariant inlining (it requires a fair bit of control-flow with no bytecode analog).
+    // It is also used when doing an early return from an inlined callee: it is easier to fix the bytecode index later on if needed
+    // than to move the right index all the way to the treatment of op_ret.
+    BasicBlock* allocateTargetableBlock(unsigned bytecodeIndex);
+    BasicBlock* allocateUntargetableBlock();
+    // An untargetable block can be given a bytecodeIndex to be later managed by linkBlock, but only once, and it can never go in the other direction
+    void makeBlockTargetable(BasicBlock*, unsigned bytecodeIndex);
+    void addJumpTo(BasicBlock*);
+    void addJumpTo(unsigned bytecodeIndex);
     // Handle calls. This resolves issues surrounding inlining and intrinsics.
     enum Terminality { Terminal, NonTerminal };
     Terminality handleCall(
         int result, NodeType op, InlineCallFrame::Kind, unsigned instructionSize,
-        Node* callTarget, int argCount, int registerOffset, CallLinkStatus,
+        Node* callTarget, int argumentCountIncludingThis, int registerOffset, CallLinkStatus,
         SpeculatedType prediction);
-    Terminality handleCall(
-        int result, NodeType op, CallMode, unsigned instructionSize,
-        Node* callTarget, int argCount, int registerOffset, CallLinkStatus);
-    Terminality handleCall(int result, NodeType op, CallMode, unsigned instructionSize, int callee, int argCount, int registerOffset);
     Terminality handleCall(Instruction* pc, NodeType op, CallMode);
     Terminality handleVarargsCall(Instruction* pc, NodeType op, CallMode);
     void emitFunctionChecks(CallVariant, Node* callTarget, VirtualRegister thisArgumnt);
@@ -221,11 +227,10 @@
     unsigned inliningCost(CallVariant, int argumentCountIncludingThis, InlineCallFrame::Kind); // Return UINT_MAX if it's not an inlining candidate. By convention, intrinsics have a cost of 1.
     // Handle inlining. Return true if it succeeded, false if we need to plant a call.
     bool handleInlining(Node* callTargetNode, int resultOperand, const CallLinkStatus&, int registerOffset, VirtualRegister thisArgument, VirtualRegister argumentsArgument, unsigned argumentsOffset, int argumentCountIncludingThis, unsigned nextOffset, NodeType callOp, InlineCallFrame::Kind, SpeculatedType prediction);
-    enum CallerLinkability { CallerDoesNormalLinking, CallerLinksManually };
     template<typename ChecksFunctor>
-    bool attemptToInlineCall(Node* callTargetNode, int resultOperand, CallVariant, int registerOffset, int argumentCountIncludingThis, unsigned nextOffset, InlineCallFrame::Kind, CallerLinkability, SpeculatedType prediction, unsigned& inliningBalance, const ChecksFunctor& insertChecks);
+    bool attemptToInlineCall(Node* callTargetNode, int resultOperand, CallVariant, int registerOffset, int argumentCountIncludingThis, unsigned nextOffset, InlineCallFrame::Kind, SpeculatedType prediction, unsigned& inliningBalance, const ChecksFunctor& insertChecks);
     template<typename ChecksFunctor>
-    void inlineCall(Node* callTargetNode, int resultOperand, CallVariant, int registerOffset, int argumentCountIncludingThis, unsigned nextOffset, InlineCallFrame::Kind, CallerLinkability, const ChecksFunctor& insertChecks);
+    void inlineCall(Node* callTargetNode, int resultOperand, CallVariant, int registerOffset, int argumentCountIncludingThis, unsigned nextOffset, InlineCallFrame::Kind, const ChecksFunctor& insertChecks);
     void cancelLinkingForBlock(InlineStackEntry*, BasicBlock*); // Only works when the given block is the last one to have been added for that inline stack entry.
     // Handle intrinsic functions. Return true if it succeeded, false if we need to plant a call.
     template<typename ChecksFunctor>
@@ -301,7 +306,7 @@
     bool parseBlock(unsigned limit);
     // Link block successors.
     void linkBlock(BasicBlock*, Vector<BasicBlock*>& possibleTargets);
-    void linkBlocks(Vector<UnlinkedBlock>& unlinkedBlocks, Vector<BasicBlock*>& possibleTargets);
+    void linkBlocks(Vector<BasicBlock*>& unlinkedBlocks, Vector<BasicBlock*>& possibleTargets);
     
     VariableAccessData* newVariableAccessData(VirtualRegister operand)
     {
@@ -752,8 +757,7 @@
     
     Node* addToGraph(Node* node)
     {
-        if (Options::verboseDFGBytecodeParsing())
-            dataLog("        appended ", node, " ", Graph::opName(node->op()), "\n");
+        VERBOSE_LOG("        appended ", node, " ", Graph::opName(node->op()), "\n");
         m_currentBlock->append(node);
         if (clobbersExitState(m_graph, node))
             m_exitOK = false;
@@ -1115,33 +1119,24 @@
         Vector<unsigned> m_constantBufferRemap;
         Vector<unsigned> m_switchRemap;
         
-        // Blocks introduced by this code block, which need successor linking.
-        // May include up to one basic block that includes the continuation after
-        // the callsite in the caller. These must be appended in the order that they
-        // are created, but their bytecodeBegin values need not be in order as they
-        // are ignored.
-        Vector<UnlinkedBlock> m_unlinkedBlocks;
+        // These are blocks whose terminal is a Jump, Branch or Switch, and whose target has not yet been linked.
+        // Their terminal instead refers to a bytecode index, and the right BB can be found in m_blockLinkingTargets.
+        Vector<BasicBlock*> m_unlinkedBlocks;
         
         // Potential block linking targets. Must be sorted by bytecodeBegin, and
         // cannot have two blocks that have the same bytecodeBegin.
         Vector<BasicBlock*> m_blockLinkingTargets;
 
-        // If the callsite's basic block was split into two, then this will be
-        // the head of the callsite block. It needs its successors linked to the
-        // m_unlinkedBlocks, but not the other way around: there's no way for
-        // any blocks in m_unlinkedBlocks to jump back into this block.
-        BasicBlock* m_callsiteBlockHead;
-        
-        // Does the callsite block head need linking? This is typically true
-        // but will be false for the machine code block's inline stack entry
-        // (since that one is not inlined) and for cases where an inline callee
-        // did the linking for us.
-        bool m_callsiteBlockHeadNeedsLinking;
-        
+        // This is set by op_enter in parseBlock(), and indicates the first block of the function.
+        BasicBlock* m_entryBlock;
+
+        // Optional: a continuation block for returns to jump to. It is set by early returns if it does not exist.
+        BasicBlock* m_continuationBlock;
+
         VirtualRegister m_returnValue;
         
         // Speculations about variable types collected from the profiled code block,
-        // which are based on OSR exit profiles that past DFG compilatins of this
+        // which are based on OSR exit profiles that past DFG compilations of this
         // code block had gathered.
         LazyOperandValueProfileParser m_lazyOperands;
         
@@ -1149,13 +1144,6 @@
         StubInfoMap m_stubInfos;
         ByValInfoMap m_byValInfos;
         
-        // Did we see any returns? We need to handle the (uncommon but necessary)
-        // case where a procedure that does not return was inlined.
-        bool m_didReturn;
-        
-        // Did we have any early returns?
-        bool m_didEarlyReturn;
-        
         // Pointers to the argument position trackers for this slice of code.
         Vector<ArgumentPosition*> m_argumentPositions;
         
@@ -1165,7 +1153,6 @@
             ByteCodeParser*,
             CodeBlock*,
             CodeBlock* profiledBlock,
-            BasicBlock* callsiteBlockHead,
             JSFunction* callee, // Null if this is a closure call.
             VirtualRegister returnValueVR,
             VirtualRegister inlineCallFrameStart,
@@ -1203,7 +1190,7 @@
             , m_value(value)
             , m_setMode(setMode)
         {
-            PARSER_ASSERT(operand.isValid());
+            RELEASE_ASSERT(operand.isValid());
         }
         
         Node* execute(ByteCodeParser* parser)
@@ -1224,67 +1211,72 @@
     bool m_hasDebuggerEnabled;
 };
 
-// The idiom:
-//     if (true) { ...; goto label; } else label: continue
-// Allows using NEXT_OPCODE as a statement, even in unbraced if+else, while containing a `continue`.
-// The more common idiom:
-//     do { ...; } while (false)
-// Doesn't allow using `continue`.
-#define NEXT_OPCODE(name) \
-    if (true) { \
-        m_currentIndex += OPCODE_LENGTH(name); \
-        goto WTF_CONCAT(NEXT_OPCODE_, __LINE__); /* Need a unique label: usable more than once per function. */ \
-    } else \
-    WTF_CONCAT(NEXT_OPCODE_, __LINE__): \
-        continue
+BasicBlock* ByteCodeParser::allocateTargetableBlock(unsigned bytecodeIndex)
+{
+    ASSERT(bytecodeIndex != UINT_MAX);
+    Ref<BasicBlock> block = adoptRef(*new BasicBlock(bytecodeIndex, m_numArguments, m_numLocals, 1));
+    BasicBlock* blockPtr = block.ptr();
+    m_inlineStackTop->m_blockLinkingTargets.append(blockPtr);
+    m_graph.appendBlock(WTFMove(block));
+    return blockPtr;
+}
 
-// Chain expressions with comma-operator so LAST_OPCODE can be used as a statement.
-#define LAST_OPCODE(name) \
-    return \
-        m_currentIndex += OPCODE_LENGTH(name), \
-        m_exitOK = false, \
-        shouldContinueParsing
+BasicBlock* ByteCodeParser::allocateUntargetableBlock()
+{
+    Ref<BasicBlock> block = adoptRef(*new BasicBlock(UINT_MAX, m_numArguments, m_numLocals, 1));
+    BasicBlock* blockPtr = block.ptr();
+    m_graph.appendBlock(WTFMove(block));
+    return blockPtr;
+}
 
-ByteCodeParser::Terminality ByteCodeParser::handleCall(Instruction* pc, NodeType op, CallMode callMode)
+void ByteCodeParser::makeBlockTargetable(BasicBlock* block, unsigned bytecodeIndex)
 {
-    ASSERT(OPCODE_LENGTH(op_call) == OPCODE_LENGTH(op_construct));
-    ASSERT(OPCODE_LENGTH(op_call) == OPCODE_LENGTH(op_tail_call));
-    return handleCall(
-        pc[1].u.operand, op, callMode, OPCODE_LENGTH(op_call),
-        pc[2].u.operand, pc[3].u.operand, -pc[4].u.operand);
+    ASSERT(block->bytecodeBegin == UINT_MAX);
+    block->bytecodeBegin = bytecodeIndex;
+    m_inlineStackTop->m_blockLinkingTargets.append(block);
 }
 
-ByteCodeParser::Terminality ByteCodeParser::handleCall(
-    int result, NodeType op, CallMode callMode, unsigned instructionSize,
-    int callee, int argumentCountIncludingThis, int registerOffset)
+void ByteCodeParser::addJumpTo(BasicBlock* block)
 {
-    Node* callTarget = get(VirtualRegister(callee));
+    ASSERT(!m_currentBlock->terminal());
+    Node* jumpNode = addToGraph(Jump);
+    jumpNode->targetBlock() = block;
+    m_currentBlock->didLink();
+}
+
+void ByteCodeParser::addJumpTo(unsigned bytecodeIndex)
+{
+    ASSERT(!m_currentBlock->terminal());
+    addToGraph(Jump, OpInfo(bytecodeIndex));
+    m_inlineStackTop->m_unlinkedBlocks.append(m_currentBlock);
+}
+
+ByteCodeParser::Terminality ByteCodeParser::handleCall(Instruction* pc, NodeType op, CallMode callMode)
+{
+    static_assert(OPCODE_LENGTH(op_call) == OPCODE_LENGTH(op_construct),
+        "op_call, op_tail_call and op_construct should always have the same length");
+    static_assert(OPCODE_LENGTH(op_call) == OPCODE_LENGTH(op_tail_call),
+        "op_call, op_tail_call and op_construct should always have the same length");
     
+    int result = pc[1].u.operand;
+    Node* callTarget = get(VirtualRegister(pc[2].u.operand));
+    int argumentCountIncludingThis = pc[3].u.operand;
+    int registerOffset = -pc[4].u.operand;
+
     CallLinkStatus callLinkStatus = CallLinkStatus::computeFor(
         m_inlineStackTop->m_profiledBlock, currentCodeOrigin(),
         m_inlineStackTop->m_callLinkInfos, m_callContextMap);
-    
-    return handleCall(
-        result, op, callMode, instructionSize, callTarget,
-        argumentCountIncludingThis, registerOffset, callLinkStatus);
+
+    InlineCallFrame::Kind kind = InlineCallFrame::kindFor(callMode);
+
+    return handleCall(result, op, kind, OPCODE_LENGTH(op_call), callTarget,
+        argumentCountIncludingThis, registerOffset, callLinkStatus, getPrediction());
 }
-    
-ByteCodeParser::Terminality ByteCodeParser::handleCall(
-    int result, NodeType op, CallMode callMode, unsigned instructionSize,
-    Node* callTarget, int argumentCountIncludingThis, int registerOffset,
-    CallLinkStatus callLinkStatus)
-{
-    return handleCall(
-        result, op, InlineCallFrame::kindFor(callMode), instructionSize, callTarget, argumentCountIncludingThis,
-        registerOffset, callLinkStatus, getPrediction());
-}
 
 void ByteCodeParser::refineStatically(CallLinkStatus& callLinkStatus, Node* callTarget)
 {
-    if (callTarget->isCellConstant()) {
+    if (callTarget->isCellConstant())
         callLinkStatus.setProvenConstantCallee(CallVariant(callTarget->asCell()));
-        return;
-    }
 }
 
 ByteCodeParser::Terminality ByteCodeParser::handleCall(
@@ -1296,39 +1288,37 @@
 
     refineStatically(callLinkStatus, callTarget);
     
-    if (Options::verboseDFGBytecodeParsing())
-        dataLog("    Handling call at ", currentCodeOrigin(), ": ", callLinkStatus, "\n");
+    VERBOSE_LOG("    Handling call at ", currentCodeOrigin(), ": ", callLinkStatus, "\n");
     
-    if (!callLinkStatus.canOptimize()) {
-        // Oddly, this conflates calls that haven't executed with calls that behaved sufficiently polymorphically
-        // that we cannot optimize them.
+    // We first check that we have profiling information about this call, and that it did not behave too polymorphically.
+    if (callLinkStatus.canOptimize()) {
+        VirtualRegister thisArgument = virtualRegisterForArgument(0, registerOffset);
 
-        Node* callNode = addCall(result, op, nullptr, callTarget, argumentCountIncludingThis, registerOffset, prediction);
-        if (callNode->op() == TailCall)
-            return Terminal;
-        ASSERT(callNode->op() != TailCallVarargs && callNode->op() != TailCallForwardVarargs);
-        return NonTerminal;
+        // Inlining is quite complex, and managed by a pipeline of functions:
+        // handle(Varargs)Call -> handleInlining -> attemptToInlineCall -> inlineCall
+        // - handleCall and handleVarargsCall deal with the case where no inlining happens, and do some sanity checks on their arguments
+        // - handleInlining checks whether the call is polymorphic, and if so is responsible for inserting a switch on the callee
+        // - attemptToInlineCall deals with special cases such as intrinsics, it also checks the inlining heuristic (through inliningCost)
+        // - inlineCall finally does the actual inlining, after a complicated procedure to setup the stack correctly
+        unsigned nextOffset = m_currentIndex + instructionSize;
+        if (handleInlining(callTarget, result, callLinkStatus, registerOffset, thisArgument, VirtualRegister(), 0, argumentCountIncludingThis, nextOffset, op, kind, prediction)) {
+            if (UNLIKELY(m_graph.compilation()))
+                m_graph.compilation()->noticeInlinedCall();
+            return NonTerminal;
+        }
     }
     
-    unsigned nextOffset = m_currentIndex + instructionSize;
-    
-    if (handleInlining(callTarget, result, callLinkStatus, registerOffset, virtualRegisterForArgument(0, registerOffset), VirtualRegister(), 0, argumentCountIncludingThis, nextOffset, op, kind, prediction)) {
-        if (UNLIKELY(m_graph.compilation()))
-            m_graph.compilation()->noticeInlinedCall();
-        return NonTerminal;
-    }
-    
     Node* callNode = addCall(result, op, nullptr, callTarget, argumentCountIncludingThis, registerOffset, prediction);
-    if (callNode->op() == TailCall)
-        return Terminal;
     ASSERT(callNode->op() != TailCallVarargs && callNode->op() != TailCallForwardVarargs);
-    return NonTerminal;
+    return callNode->op() == TailCall ? Terminal : NonTerminal;
 }
 
 ByteCodeParser::Terminality ByteCodeParser::handleVarargsCall(Instruction* pc, NodeType op, CallMode callMode)
 {
-    ASSERT(OPCODE_LENGTH(op_call_varargs) == OPCODE_LENGTH(op_construct_varargs));
-    ASSERT(OPCODE_LENGTH(op_call_varargs) == OPCODE_LENGTH(op_tail_call_varargs));
+    static_assert(OPCODE_LENGTH(op_call_varargs) == OPCODE_LENGTH(op_construct_varargs),
+        "op_call_varargs, op_tail_call_varargs and op_construct_varargs should always have the same length");
+    static_assert(OPCODE_LENGTH(op_call_varargs) == OPCODE_LENGTH(op_tail_call_varargs),
+        "op_call_varargs, op_tail_call_varargs and op_construct_varargs should always have the same length");
 
     int result = pc[1].u.operand;
     int callee = pc[2].u.operand;
@@ -1346,8 +1336,7 @@
         m_inlineStackTop->m_callLinkInfos, m_callContextMap);
     refineStatically(callLinkStatus, callTarget);
     
-    if (Options::verboseDFGBytecodeParsing())
-        dataLog("    Varargs call link status at ", currentCodeOrigin(), ": ", callLinkStatus, "\n");
+    VERBOSE_LOG("    Varargs call link status at ", currentCodeOrigin(), ": ", callLinkStatus, "\n");
     
     if (callLinkStatus.canOptimize()
         && handleInlining(callTarget, result, callLinkStatus, firstFreeReg, VirtualRegister(thisReg), VirtualRegister(arguments), firstVarArgOffset, 0, m_currentIndex + OPCODE_LENGTH(op_call_varargs), op, InlineCallFrame::varargsKindFor(callMode), prediction)) {
@@ -1426,19 +1415,16 @@
 {
     CallMode callMode = InlineCallFrame::callModeFor(kind);
     CodeSpecializationKind specializationKind = specializationKindFor(callMode);
-    if (DFGByteCodeParserInternal::verbose)
-        dataLog("Considering inlining ", callee, " into ", currentCodeOrigin(), "\n");
+    VERBOSE_LOG("Considering inlining ", callee, " into ", currentCodeOrigin(), "\n");
     
     if (m_hasDebuggerEnabled) {
-        if (DFGByteCodeParserInternal::verbose)
-            dataLog("    Failing because the debugger is in use.\n");
+        VERBOSE_LOG("    Failing because the debugger is in use.\n");
         return UINT_MAX;
     }
 
     FunctionExecutable* executable = callee.functionExecutable();
     if (!executable) {
-        if (DFGByteCodeParserInternal::verbose)
-            dataLog("    Failing because there is no function executable.\n");
+        VERBOSE_LOG("    Failing because there is no function executable.\n");
         return UINT_MAX;
     }
     
@@ -1451,15 +1437,13 @@
     // it's a rare case because we expect that any hot callees would have already been compiled.
     CodeBlock* codeBlock = executable->baselineCodeBlockFor(specializationKind);
     if (!codeBlock) {
-        if (DFGByteCodeParserInternal::verbose)
-            dataLog("    Failing because no code block available.\n");
+        VERBOSE_LOG("    Failing because no code block available.\n");
         return UINT_MAX;
     }
 
     if (!Options::useArityFixupInlining()) {
         if (codeBlock->numParameters() > argumentCountIncludingThis) {
-            if (DFGByteCodeParserInternal::verbose)
-                dataLog("    Failing because of arity mismatch.\n");
+            VERBOSE_LOG("    Failing because of arity mismatch.\n");
             return UINT_MAX;
         }
     }
@@ -1466,18 +1450,15 @@
 
     CapabilityLevel capabilityLevel = inlineFunctionForCapabilityLevel(
         codeBlock, specializationKind, callee.isClosureCall());
-    if (DFGByteCodeParserInternal::verbose) {
-        dataLog("    Call mode: ", callMode, "\n");
-        dataLog("    Is closure call: ", callee.isClosureCall(), "\n");
-        dataLog("    Capability level: ", capabilityLevel, "\n");
-        dataLog("    Might inline function: ", mightInlineFunctionFor(codeBlock, specializationKind), "\n");
-        dataLog("    Might compile function: ", mightCompileFunctionFor(codeBlock, specializationKind), "\n");
-        dataLog("    Is supported for inlining: ", isSupportedForInlining(codeBlock), "\n");
-        dataLog("    Is inlining candidate: ", codeBlock->ownerScriptExecutable()->isInliningCandidate(), "\n");
-    }
+    VERBOSE_LOG("    Call mode: ", callMode, "\n");
+    VERBOSE_LOG("    Is closure call: ", callee.isClosureCall(), "\n");
+    VERBOSE_LOG("    Capability level: ", capabilityLevel, "\n");
+    VERBOSE_LOG("    Might inline function: ", mightInlineFunctionFor(codeBlock, specializationKind), "\n");
+    VERBOSE_LOG("    Might compile function: ", mightCompileFunctionFor(codeBlock, specializationKind), "\n");
+    VERBOSE_LOG("    Is supported for inlining: ", isSupportedForInlining(codeBlock), "\n");
+    VERBOSE_LOG("    Is inlining candidate: ", codeBlock->ownerScriptExecutable()->isInliningCandidate(), "\n");
     if (!canInline(capabilityLevel)) {
-        if (DFGByteCodeParserInternal::verbose)
-            dataLog("    Failing because the function is not inlineable.\n");
+        VERBOSE_LOG("    Failing because the function is not inlineable.\n");
         return UINT_MAX;
     }
     
@@ -1486,8 +1467,7 @@
     // purpose of unsetting SABI.
     if (!isSmallEnoughToInlineCodeInto(m_codeBlock)) {
         codeBlock->m_shouldAlwaysBeInlined = false;
-        if (DFGByteCodeParserInternal::verbose)
-            dataLog("    Failing because the caller is too large.\n");
+        VERBOSE_LOG("    Failing because the caller is too large.\n");
         return UINT_MAX;
     }
     
@@ -1510,8 +1490,7 @@
     for (InlineStackEntry* entry = m_inlineStackTop; entry; entry = entry->m_caller) {
         ++depth;
         if (depth >= Options::maximumInliningDepth()) {
-            if (DFGByteCodeParserInternal::verbose)
-                dataLog("    Failing because depth exceeded.\n");
+            VERBOSE_LOG("    Failing because depth exceeded.\n");
             return UINT_MAX;
         }
         
@@ -1518,15 +1497,13 @@
         if (entry->executable() == executable) {
             ++recursion;
             if (recursion >= Options::maximumInliningRecursion()) {
-                if (DFGByteCodeParserInternal::verbose)
-                    dataLog("    Failing because recursion detected.\n");
+                VERBOSE_LOG("    Failing because recursion detected.\n");
                 return UINT_MAX;
             }
         }
     }
     
-    if (DFGByteCodeParserInternal::verbose)
-        dataLog("    Inlining should be possible.\n");
+    VERBOSE_LOG("    Inlining should be possible.\n");
     
     // It might be possible to inline.
     return codeBlock->instructionCount();
@@ -1533,7 +1510,7 @@
 }
 
 template<typename ChecksFunctor>
-void ByteCodeParser::inlineCall(Node* callTargetNode, int resultOperand, CallVariant callee, int registerOffset, int argumentCountIncludingThis, unsigned nextOffset, InlineCallFrame::Kind kind, CallerLinkability callerLinkability, const ChecksFunctor& insertChecks)
+void ByteCodeParser::inlineCall(Node* callTargetNode, int resultOperand, CallVariant callee, int registerOffset, int argumentCountIncludingThis, unsigned nextOffset, InlineCallFrame::Kind kind, const ChecksFunctor& insertChecks)
 {
     CodeSpecializationKind specializationKind = InlineCallFrame::specializationKindFor(kind);
     
@@ -1578,8 +1555,6 @@
         calleeVariable->mergeShouldNeverUnbox(true);
     }
 
-    Vector<std::pair<Node*, VirtualRegister>> delayedAritySets;
-
     if (arityFixupCount) {
         // Note: we do arity fixup in two phases:
         // 1. We get all the values we need and MovHint them to the expected locals.
@@ -1630,10 +1605,9 @@
         // our callee's frame. We emit an ExitOK below from the callee's CodeOrigin.
     }
 
-    InlineStackEntry inlineStackEntry(
-        this, codeBlock, codeBlock, m_graph.lastBlock(), callee.function(), resultReg,
+    InlineStackEntry inlineStackEntry(this, codeBlock, codeBlock, callee.function(), resultReg,
         (VirtualRegister)inlineCallFrameStart, argumentCountIncludingThis, kind);
-    
+
     // This is where the actual inlining really happens.
     unsigned oldIndex = m_currentIndex;
     m_currentIndex = 0;
@@ -1649,11 +1623,11 @@
     inlineVariableData.argumentPositionStart = argumentPositionStart;
     inlineVariableData.calleeVariable = 0;
     
-    PARSER_ASSERT(
+    RELEASE_ASSERT(
         m_inlineStackTop->m_inlineCallFrame->isClosureCall
         == callee.isClosureCall());
     if (callee.isClosureCall()) {
-        PARSER_ASSERT(calleeVariable);
+        RELEASE_ASSERT(calleeVariable);
         inlineVariableData.calleeVariable = calleeVariable;
     }
     
@@ -1664,109 +1638,27 @@
     
     m_currentIndex = oldIndex;
     m_exitOK = false;
+
+    linkBlocks(inlineStackEntry.m_unlinkedBlocks, inlineStackEntry.m_blockLinkingTargets);
     
-    // If the inlined code created some new basic blocks, then we have linking to do.
-    if (inlineStackEntry.m_callsiteBlockHead != m_graph.lastBlock()) {
-        
-        ASSERT(!inlineStackEntry.m_unlinkedBlocks.isEmpty());
-        if (inlineStackEntry.m_callsiteBlockHeadNeedsLinking)
-            linkBlock(inlineStackEntry.m_callsiteBlockHead, inlineStackEntry.m_blockLinkingTargets);
-        else
-            ASSERT(inlineStackEntry.m_callsiteBlockHead->isLinked);
-        
-        if (callerLinkability == CallerDoesNormalLinking)
-            cancelLinkingForBlock(inlineStackEntry.m_caller, inlineStackEntry.m_callsiteBlockHead);
-        
-        linkBlocks(inlineStackEntry.m_unlinkedBlocks, inlineStackEntry.m_blockLinkingTargets);
-    } else
-        ASSERT(inlineStackEntry.m_unlinkedBlocks.isEmpty());
-    
-    BasicBlock* lastBlock = m_graph.lastBlock();
-    // If there was a return, but no early returns, then we're done. We allow parsing of
-    // the caller to continue in whatever basic block we're in right now.
-    if (!inlineStackEntry.m_didEarlyReturn && inlineStackEntry.m_didReturn) {
-        if (Options::verboseDFGBytecodeParsing())
-            dataLog("    Allowing parsing to continue in last inlined block.\n");
-        
-        ASSERT(lastBlock->isEmpty() || !lastBlock->terminal());
-        
-        // If we created new blocks then the last block needs linking, but in the
-        // caller. It doesn't need to be linked to, but it needs outgoing links.
-        if (!inlineStackEntry.m_unlinkedBlocks.isEmpty()) {
-            // For debugging purposes, set the bytecodeBegin. Note that this doesn't matter
-            // for release builds because this block will never serve as a potential target
-            // in the linker's binary search.
-            if (Options::verboseDFGBytecodeParsing())
-                dataLog("        Repurposing last block from ", lastBlock->bytecodeBegin, " to ", m_currentIndex, "\n");
-            lastBlock->bytecodeBegin = m_currentIndex;
-            if (callerLinkability == CallerDoesNormalLinking) {
-                if (DFGByteCodeParserInternal::verbose)
-                    dataLog("Adding unlinked block ", RawPointer(m_graph.lastBlock()), " (one return)\n");
-                m_inlineStackTop->m_caller->m_unlinkedBlocks.append(UnlinkedBlock(m_graph.lastBlock()));
-            }
-        }
-        
-        m_currentBlock = m_graph.lastBlock();
-        return;
+    // If the callee returned at least once, it prepared a continuation block for us.
+    if (inlineStackEntry.m_continuationBlock)
+        m_currentBlock = inlineStackEntry.m_continuationBlock;
+    else {
+        // We are in the case where the callee never returns (for example it loops forever).
+        // In that case, all blocks should end in a terminal.
+        ASSERT(m_graph.lastBlock()->terminal());
+        // We then allocate a new block to continue in.
+        m_currentBlock = allocateTargetableBlock(nextOffset);
     }
+    ASSERT(m_currentBlock);
+    ASSERT(!m_currentBlock->terminal());
 
-    if (Options::verboseDFGBytecodeParsing())
-        dataLog("    Creating new block after inlining.\n");
-
-    // If we get to this point then all blocks must end in some sort of terminals.
-    ASSERT(lastBlock->terminal());
-
-    // Need to create a new basic block for the continuation at the caller.
-    Ref<BasicBlock> block = adoptRef(*new BasicBlock(nextOffset, m_numArguments, m_numLocals, 1));
-
-    // Link the early returns to the basic block we're about to create.
-    for (size_t i = 0; i < inlineStackEntry.m_unlinkedBlocks.size(); ++i) {
-        if (!inlineStackEntry.m_unlinkedBlocks[i].m_needsEarlyReturnLinking)
-            continue;
-        BasicBlock* blockToLink = inlineStackEntry.m_unlinkedBlocks[i].m_block;
-        ASSERT(!blockToLink->isLinked);
-        Node* node = blockToLink->terminal();
-        ASSERT(node->op() == Jump);
-        ASSERT(!node->targetBlock());
-        node->targetBlock() = block.ptr();
-        inlineStackEntry.m_unlinkedBlocks[i].m_needsEarlyReturnLinking = false;
-        if (DFGByteCodeParserInternal::verbose)
-            dataLog("Marking ", RawPointer(blockToLink), " as linked (jumps to return)\n");
-        blockToLink->didLink();
-    }
-    
-    m_currentBlock = block.ptr();
-    ASSERT(m_inlineStackTop->m_caller->m_blockLinkingTargets.isEmpty() || m_inlineStackTop->m_caller->m_blockLinkingTargets.last()->bytecodeBegin < nextOffset);
-    if (DFGByteCodeParserInternal::verbose)
-        dataLog("Adding unlinked block ", RawPointer(block.ptr()), " (many returns)\n");
-    if (callerLinkability == CallerDoesNormalLinking) {
-        m_inlineStackTop->m_caller->m_unlinkedBlocks.append(UnlinkedBlock(block.ptr()));
-        m_inlineStackTop->m_caller->m_blockLinkingTargets.append(block.ptr());
-    }
-    m_graph.appendBlock(WTFMove(block));
     prepareToParseBlock();
 }
 
-void ByteCodeParser::cancelLinkingForBlock(InlineStackEntry* inlineStackEntry, BasicBlock* block)
-{
-    // It's possible that the callsite block head is not owned by the caller.
-    if (!inlineStackEntry->m_unlinkedBlocks.isEmpty()) {
-        // It's definitely owned by the caller, because the caller created new blocks.
-        // Assert that this all adds up.
-        ASSERT_UNUSED(block, inlineStackEntry->m_unlinkedBlocks.last().m_block == block);
-        ASSERT(inlineStackEntry->m_unlinkedBlocks.last().m_needsNormalLinking);
-        inlineStackEntry->m_unlinkedBlocks.last().m_needsNormalLinking = false;
-    } else {
-        // It's definitely not owned by the caller. Tell the caller that he does not
-        // need to link his callsite block head, because we did it for him.
-        ASSERT(inlineStackEntry->m_callsiteBlockHeadNeedsLinking);
-        ASSERT_UNUSED(block, inlineStackEntry->m_callsiteBlockHead == block);
-        inlineStackEntry->m_callsiteBlockHeadNeedsLinking = false;
-    }
-}
-
 template<typename ChecksFunctor>
-bool ByteCodeParser::attemptToInlineCall(Node* callTargetNode, int resultOperand, CallVariant callee, int registerOffset, int argumentCountIncludingThis, unsigned nextOffset, InlineCallFrame::Kind kind, CallerLinkability callerLinkability, SpeculatedType prediction, unsigned& inliningBalance, const ChecksFunctor& insertChecks)
+bool ByteCodeParser::attemptToInlineCall(Node* callTargetNode, int resultOperand, CallVariant callee, int registerOffset, int argumentCountIncludingThis, unsigned nextOffset, InlineCallFrame::Kind kind, SpeculatedType prediction, unsigned& inliningBalance, const ChecksFunctor& insertChecks)
 {
     CodeSpecializationKind specializationKind = InlineCallFrame::specializationKindFor(kind);
     
@@ -1773,8 +1665,7 @@
     if (!inliningBalance)
         return false;
     
-    if (DFGByteCodeParserInternal::verbose)
-        dataLog("    Considering callee ", callee, "\n");
+    VERBOSE_LOG("    Considering callee ", callee, "\n");
     
     // Intrinsics and internal functions can only be inlined if we're not doing varargs. This is because
     // we currently don't have any way of getting profiling information for arguments to non-JS varargs
@@ -1793,13 +1684,13 @@
     
         if (InternalFunction* function = callee.internalFunction()) {
             if (handleConstantInternalFunction(callTargetNode, resultOperand, function, registerOffset, argumentCountIncludingThis, specializationKind, prediction, insertChecksWithAccounting)) {
-                PARSER_ASSERT(didInsertChecks);
+                RELEASE_ASSERT(didInsertChecks);
                 addToGraph(Phantom, callTargetNode);
                 emitArgumentPhantoms(registerOffset, argumentCountIncludingThis);
                 inliningBalance--;
                 return true;
             }
-            PARSER_ASSERT(!didInsertChecks);
+            RELEASE_ASSERT(!didInsertChecks);
             return false;
         }
     
@@ -1806,7 +1697,7 @@
         Intrinsic intrinsic = callee.intrinsicFor(specializationKind);
         if (intrinsic != NoIntrinsic) {
             if (handleIntrinsicCall(callTargetNode, resultOperand, intrinsic, registerOffset, argumentCountIncludingThis, prediction, insertChecksWithAccounting)) {
-                PARSER_ASSERT(didInsertChecks);
+                RELEASE_ASSERT(didInsertChecks);
                 addToGraph(Phantom, callTargetNode);
                 emitArgumentPhantoms(registerOffset, argumentCountIncludingThis);
                 inliningBalance--;
@@ -1813,7 +1704,7 @@
                 return true;
             }
 
-            PARSER_ASSERT(!didInsertChecks);
+            RELEASE_ASSERT(!didInsertChecks);
             // We might still try to inline the Intrinsic because it might be a builtin JS function.
         }
 
@@ -1820,13 +1711,13 @@
         if (Options::useDOMJIT()) {
             if (const DOMJIT::Signature* signature = callee.signatureFor(specializationKind)) {
                 if (handleDOMJITCall(callTargetNode, resultOperand, signature, registerOffset, argumentCountIncludingThis, prediction, insertChecksWithAccounting)) {
-                    PARSER_ASSERT(didInsertChecks);
+                    RELEASE_ASSERT(didInsertChecks);
                     addToGraph(Phantom, callTargetNode);
                     emitArgumentPhantoms(registerOffset, argumentCountIncludingThis);
                     inliningBalance--;
                     return true;
                 }
-                PARSER_ASSERT(!didInsertChecks);
+                RELEASE_ASSERT(!didInsertChecks);
             }
         }
     }
@@ -1836,7 +1727,7 @@
         return false;
 
     Instruction* savedCurrentInstruction = m_currentInstruction;
-    inlineCall(callTargetNode, resultOperand, callee, registerOffset, argumentCountIncludingThis, nextOffset, kind, callerLinkability, insertChecks);
+    inlineCall(callTargetNode, resultOperand, callee, registerOffset, argumentCountIncludingThis, nextOffset, kind, insertChecks);
     inliningBalance -= myInliningCost;
     m_currentInstruction = savedCurrentInstruction;
     return true;
@@ -1848,22 +1739,17 @@
     VirtualRegister argumentsArgument, unsigned argumentsOffset, int argumentCountIncludingThis,
     unsigned nextOffset, NodeType callOp, InlineCallFrame::Kind kind, SpeculatedType prediction)
 {
-    if (DFGByteCodeParserInternal::verbose) {
-        dataLog("Handling inlining...\n");
-        dataLog("Stack: ", currentCodeOrigin(), "\n");
-    }
+    VERBOSE_LOG("Handling inlining...\nStack: ", currentCodeOrigin(), "\n");
     CodeSpecializationKind specializationKind = InlineCallFrame::specializationKindFor(kind);
     
     if (!callLinkStatus.size()) {
-        if (DFGByteCodeParserInternal::verbose)
-            dataLog("Bailing inlining.\n");
+        VERBOSE_LOG("Bailing inlining.\n");
         return false;
     }
     
     if (InlineCallFrame::isVarargs(kind)
         && callLinkStatus.maxNumArguments() > Options::maximumVarargsForInlining()) {
-        if (DFGByteCodeParserInternal::verbose)
-            dataLog("Bailing inlining because of varargs.\n");
+        VERBOSE_LOG("Bailing inlining because of varargs.\n");
         return false;
     }
         
@@ -1908,7 +1794,7 @@
         
         bool result = attemptToInlineCall(
             callTargetNode, resultOperand, callLinkStatus[0], registerOffset,
-            argumentCountIncludingThis, nextOffset, kind, CallerDoesNormalLinking, prediction,
+            argumentCountIncludingThis, nextOffset, kind, prediction,
             inliningBalance, [&] (CodeBlock* codeBlock) {
                 emitFunctionChecks(callLinkStatus[0], callTargetNode, thisArgument);
 
@@ -1981,11 +1867,7 @@
                     }
                 }
             });
-        if (DFGByteCodeParserInternal::verbose) {
-            dataLog("Done inlining (simple).\n");
-            dataLog("Stack: ", currentCodeOrigin(), "\n");
-            dataLog("Result: ", result, "\n");
-        }
+        VERBOSE_LOG("Done inlining (simple).\nStack: ", currentCodeOrigin(), "\nResult: ", result, "\n");
         return result;
     }
     
@@ -1997,10 +1879,7 @@
     // also.
     if (!isFTL(m_graph.m_plan.mode) || !Options::usePolymorphicCallInlining()
         || InlineCallFrame::isVarargs(kind)) {
-        if (DFGByteCodeParserInternal::verbose) {
-            dataLog("Bailing inlining (hard).\n");
-            dataLog("Stack: ", currentCodeOrigin(), "\n");
-        }
+        VERBOSE_LOG("Bailing inlining (hard).\nStack: ", currentCodeOrigin(), "\n");
         return false;
     }
     
@@ -2009,15 +1888,10 @@
     // it has no idea.
     if (!Options::usePolymorphicCallInliningForNonStubStatus()
         && !callLinkStatus.isBasedOnStub()) {
-        if (DFGByteCodeParserInternal::verbose) {
-            dataLog("Bailing inlining (non-stub polymorphism).\n");
-            dataLog("Stack: ", currentCodeOrigin(), "\n");
-        }
+        VERBOSE_LOG("Bailing inlining (non-stub polymorphism).\nStack: ", currentCodeOrigin(), "\n");
         return false;
     }
-    
-    unsigned oldOffset = m_currentIndex;
-    
+
     bool allAreClosureCalls = true;
     bool allAreDirectCalls = true;
     for (unsigned i = callLinkStatus.size(); i--;) {
@@ -2026,7 +1900,7 @@
         else
             allAreClosureCalls = false;
     }
-    
+
     Node* thingToSwitchOn;
     if (allAreDirectCalls)
         thingToSwitchOn = callTargetNode;
@@ -2037,30 +1911,22 @@
         // where it would be beneficial. It might be best to handle these cases as if all calls were
         // closure calls.
         // https://bugs.webkit.org/show_bug.cgi?id=136020
-        if (DFGByteCodeParserInternal::verbose) {
-            dataLog("Bailing inlining (mix).\n");
-            dataLog("Stack: ", currentCodeOrigin(), "\n");
-        }
+        VERBOSE_LOG("Bailing inlining (mix).\nStack: ", currentCodeOrigin(), "\n");
         return false;
     }
-    
-    if (DFGByteCodeParserInternal::verbose) {
-        dataLog("Doing hard inlining...\n");
-        dataLog("Stack: ", currentCodeOrigin(), "\n");
-    }
-    
+
+    VERBOSE_LOG("Doing hard inlining...\nStack: ", currentCodeOrigin(), "\n");
+
     int registerOffset = registerOffsetOrFirstFreeReg;
-    
+
     // This makes me wish that we were in SSA all the time. We need to pick a variable into which to
     // store the callee so that it will be accessible to all of the blocks we're about to create. We
     // get away with doing an immediate-set here because we wouldn't have performed any side effects
     // yet.
-    if (DFGByteCodeParserInternal::verbose)
-        dataLog("Register offset: ", registerOffset);
+    VERBOSE_LOG("Register offset: ", registerOffset);
     VirtualRegister calleeReg(registerOffset + CallFrameSlot::callee);
     calleeReg = m_inlineStackTop->remapOperand(calleeReg);
-    if (DFGByteCodeParserInternal::verbose)
-        dataLog("Callee is going to be ", calleeReg, "\n");
+    VERBOSE_LOG("Callee is going to be ", calleeReg, "\n");
     setDirect(calleeReg, callTargetNode, ImmediateSetWithFlush);
 
     // It's OK to exit right now, even though we set some locals. That's because those locals are not
@@ -2071,13 +1937,8 @@
     SwitchData& data = ""
     data.kind = SwitchCell;
     addToGraph(Switch, OpInfo(&data), thingToSwitchOn);
+    m_currentBlock->didLink();
     
-    BasicBlock* originBlock = m_currentBlock;
-    if (DFGByteCodeParserInternal::verbose)
-        dataLog("Marking ", RawPointer(originBlock), " as linked (origin of poly inline)\n");
-    originBlock->didLink();
-    cancelLinkingForBlock(m_inlineStackTop, originBlock);
-    
     // Each inlined callee will have a landing block that it returns at. They should all have jumps
     // to the continuation block, which we create last.
     Vector<BasicBlock*> landingBlocks;
@@ -2085,14 +1946,13 @@
     // We may force this true if we give up on inlining any of the edges.
     bool couldTakeSlowPath = callLinkStatus.couldTakeSlowPath();
     
-    if (DFGByteCodeParserInternal::verbose)
-        dataLog("About to loop over functions at ", currentCodeOrigin(), ".\n");
-    
+    VERBOSE_LOG("About to loop over functions at ", currentCodeOrigin(), ".\n");
+
+    unsigned oldOffset = m_currentIndex;
     for (unsigned i = 0; i < callLinkStatus.size(); ++i) {
         m_currentIndex = oldOffset;
-        Ref<BasicBlock> block = adoptRef(*new BasicBlock(UINT_MAX, m_numArguments, m_numLocals, 1));
-        m_currentBlock = block.ptr();
-        m_graph.appendBlock(block.copyRef());
+        BasicBlock* calleeEntryBlock = allocateUntargetableBlock();
+        m_currentBlock = calleeEntryBlock;
         prepareToParseBlock();
         
         Node* myCallTargetNode = getDirect(calleeReg);
@@ -2099,17 +1959,17 @@
         
         bool inliningResult = attemptToInlineCall(
             myCallTargetNode, resultOperand, callLinkStatus[i], registerOffset,
-            argumentCountIncludingThis, nextOffset, kind, CallerLinksManually, prediction,
+            argumentCountIncludingThis, nextOffset, kind, prediction,
             inliningBalance, [&] (CodeBlock*) { });
         
         if (!inliningResult) {
             // That failed so we let the block die. Nothing interesting should have been added to
             // the block. We also give up on inlining any of the (less frequent) callees.
-            ASSERT(m_currentBlock == block.ptr());
-            ASSERT(m_graph.m_blocks.last() == block.ptr());
-            m_graph.killBlockAndItsContents(block.ptr());
+            ASSERT(m_graph.m_blocks.last() == m_currentBlock);
+            m_graph.killBlockAndItsContents(m_currentBlock);
             m_graph.m_blocks.removeLast();
-            
+            VERBOSE_LOG("Inlining of a poly call failed, we will have to go through a slow path\n");
+
             // The fact that inlining failed means we need a slow path.
             couldTakeSlowPath = true;
             break;
@@ -2122,7 +1982,7 @@
             ASSERT(allAreClosureCalls);
             thingToCaseOn = callLinkStatus[i].executable();
         }
-        data.cases.append(SwitchCase(m_graph.freeze(thingToCaseOn), block.ptr()));
+        data.cases.append(SwitchCase(m_graph.freeze(thingToCaseOn), calleeEntryBlock));
         m_currentIndex = nextOffset;
         m_exitOK = true;
         processSetLocalQueue(); // This only comes into play for intrinsics, since normal inlined code will leave an empty queue.
@@ -2129,33 +1989,32 @@
         if (Node* terminal = m_currentBlock->terminal())
             ASSERT_UNUSED(terminal, terminal->op() == TailCall || terminal->op() == TailCallVarargs || terminal->op() == TailCallForwardVarargs);
         else {
+            // FIXME: https://bugs.webkit.org/show_bug.cgi?id=177926 we can avoid this indirection in the case of normal inlined call
+            // by passing the continuation block all the way into inlineStackEntry->m_continuationBlock in inlineCall.
+            // It is not trivial because of the SetLocalQueue managed by intrinsics.
             addToGraph(Jump);
             landingBlocks.append(m_currentBlock);
         }
-        if (DFGByteCodeParserInternal::verbose)
-            dataLog("Marking ", RawPointer(m_currentBlock), " as linked (tail of poly inlinee)\n");
+        VERBOSE_LOG("Marking ", RawPointer(m_currentBlock), " as linked (tail of poly inlinee)\n");
         m_currentBlock->didLink();
 
-        if (DFGByteCodeParserInternal::verbose)
-            dataLog("Finished inlining ", callLinkStatus[i], " at ", currentCodeOrigin(), ".\n");
+        VERBOSE_LOG("Finished inlining ", callLinkStatus[i], " at ", currentCodeOrigin(), ".\n");
     }
     
-    Ref<BasicBlock> slowPathBlock = adoptRef(
-        *new BasicBlock(UINT_MAX, m_numArguments, m_numLocals, 1));
+    BasicBlock* slowPathBlock = allocateUntargetableBlock();
     m_currentIndex = oldOffset;
     m_exitOK = true;
-    data.fallThrough = BranchTarget(slowPathBlock.ptr());
-    m_graph.appendBlock(slowPathBlock.copyRef());
-    if (DFGByteCodeParserInternal::verbose)
-        dataLog("Marking ", RawPointer(slowPathBlock.ptr()), " as linked (slow path block)\n");
+    data.fallThrough = BranchTarget(slowPathBlock);
+    VERBOSE_LOG("Marking ", RawPointer(slowPathBlock), " as linked (slow path block)\n");
     slowPathBlock->didLink();
     prepareToParseBlock();
-    m_currentBlock = slowPathBlock.ptr();
+    m_currentBlock = slowPathBlock;
     Node* myCallTargetNode = getDirect(calleeReg);
     if (couldTakeSlowPath) {
         addCall(
             resultOperand, callOp, nullptr, myCallTargetNode, argumentCountIncludingThis,
             registerOffset, prediction);
+        VERBOSE_LOG("We added a call in the slow path\n");
     } else {
         addToGraph(CheckBadCell);
         addToGraph(Phantom, myCallTargetNode);
@@ -2162,6 +2021,7 @@
         emitArgumentPhantoms(registerOffset, argumentCountIncludingThis);
         
         set(VirtualRegister(resultOperand), addToGraph(BottomValue));
+        VERBOSE_LOG("coultTakeSlowPath was wrong\n");
     }
 
     m_currentIndex = nextOffset;
@@ -2173,26 +2033,22 @@
         addToGraph(Jump);
         landingBlocks.append(m_currentBlock);
     }
-    
-    Ref<BasicBlock> continuationBlock = adoptRef(
-        *new BasicBlock(UINT_MAX, m_numArguments, m_numLocals, 1));
-    m_graph.appendBlock(continuationBlock.copyRef());
-    if (DFGByteCodeParserInternal::verbose)
-        dataLog("Adding unlinked block ", RawPointer(continuationBlock.ptr()), " (continuation)\n");
-    m_inlineStackTop->m_unlinkedBlocks.append(UnlinkedBlock(continuationBlock.ptr()));
+
+    // Continuation block
+    m_currentBlock = allocateUntargetableBlock();
+    VERBOSE_LOG("Adding untargetable block ", RawPointer(m_currentBlock), " (continuation)\n");
     prepareToParseBlock();
-    m_currentBlock = continuationBlock.ptr();
+
+    for (auto &landingBlock : landingBlocks) {
+        landingBlock->terminal()->targetBlock() = m_currentBlock;
+        landingBlock->didLink();
+        VERBOSE_LOG("We linked ", RawPointer(m_currentBlock), " (landing block) to the continuation block \n");
+    }
     
-    for (unsigned i = landingBlocks.size(); i--;)
-        landingBlocks[i]->terminal()->targetBlock() = continuationBlock.ptr();
-    
     m_currentIndex = oldOffset;
     m_exitOK = true;
     
-    if (DFGByteCodeParserInternal::verbose) {
-        dataLog("Done inlining (hard).\n");
-        dataLog("Stack: ", currentCodeOrigin(), "\n");
-    }
+    VERBOSE_LOG("Done inlining (hard).\nStack: ", currentCodeOrigin(), "\n");
     return true;
 }
 
@@ -2229,8 +2085,7 @@
 template<typename ChecksFunctor>
 bool ByteCodeParser::handleIntrinsicCall(Node* callee, int resultOperand, Intrinsic intrinsic, int registerOffset, int argumentCountIncludingThis, SpeculatedType prediction, const ChecksFunctor& insertChecks)
 {
-    if (Options::verboseDFGBytecodeParsing())
-        dataLog("       The intrinsic is ", intrinsic, "\n");
+    VERBOSE_LOG("       The intrinsic is ", intrinsic, "\n");
     
     // It so happens that the code below doesn't handle the invalid result case. We could fix that, but
     // it would only benefit intrinsics called as setters, like if you do:
@@ -3323,8 +3178,7 @@
     Node* callTargetNode, int resultOperand, InternalFunction* function, int registerOffset,
     int argumentCountIncludingThis, CodeSpecializationKind kind, SpeculatedType prediction, const ChecksFunctor& insertChecks)
 {
-    if (Options::verboseDFGBytecodeParsing())
-        dataLog("    Handling constant internal function ", JSValue(function), "\n");
+    VERBOSE_LOG("    Handling constant internal function ", JSValue(function), "\n");
     
     // It so happens that the code below assumes that the result operand is valid. It's extremely
     // unlikely that the result operand would be invalid - you'd have to call this via a setter call.
@@ -3541,14 +3395,13 @@
 
 GetByOffsetMethod ByteCodeParser::planLoad(const ObjectPropertyCondition& condition)
 {
-    if (DFGByteCodeParserInternal::verbose)
-        dataLog("Planning a load: ", condition, "\n");
+    VERBOSE_LOG("Planning a load: ", condition, "\n");
     
     // We might promote this to Equivalence, and a later DFG pass might also do such promotion
     // even if we fail, but for simplicity this cannot be asked to load an equivalence condition.
     // None of the clients of this method will request a load of an Equivalence condition anyway,
     // and supporting it would complicate the heuristics below.
-    PARSER_ASSERT(condition.kind() == PropertyCondition::Presence);
+    RELEASE_ASSERT(condition.kind() == PropertyCondition::Presence);
     
     // Here's the ranking of how to handle this, from most preferred to least preferred:
     //
@@ -3651,14 +3504,13 @@
 
 GetByOffsetMethod ByteCodeParser::planLoad(const ObjectPropertyConditionSet& conditionSet)
 {
-    if (DFGByteCodeParserInternal::verbose)
-        dataLog("conditionSet = ", conditionSet, "\n");
+    VERBOSE_LOG("conditionSet = ", conditionSet, "\n");
     
     GetByOffsetMethod result;
     for (const ObjectPropertyCondition& condition : conditionSet) {
         switch (condition.kind()) {
         case PropertyCondition::Presence:
-            PARSER_ASSERT(!result); // Should only see exactly one of these.
+            RELEASE_ASSERT(!result); // Should only see exactly one of these.
             result = planLoad(condition);
             if (!result)
                 return GetByOffsetMethod();
@@ -3833,7 +3685,7 @@
 
 Node* ByteCodeParser::store(Node* base, unsigned identifier, const PutByIdVariant& variant, Node* value)
 {
-    PARSER_ASSERT(variant.kind() == PutByIdVariant::Replace);
+    RELEASE_ASSERT(variant.kind() == PutByIdVariant::Replace);
 
     checkPresenceLike(base, m_graph.identifiers()[identifier], variant.offset(), variant.structure());
     return handlePutByOffset(base, identifier, variant.offset(), variant.requiredType(), value);
@@ -4220,6 +4072,42 @@
     return static_cast<uint64_t>(identifierNumber) | (static_cast<uint64_t>(getPutInfo) << 32);
 }
 
+// The idiom:
+//     if (true) { ...; goto label; } else label: continue
+// Allows using NEXT_OPCODE as a statement, even in unbraced if+else, while containing a `continue`.
+// The more common idiom:
+//     do { ...; } while (false)
+// Doesn't allow using `continue`.
+#define NEXT_OPCODE(name) \
+    if (true) { \
+        m_currentIndex += OPCODE_LENGTH(name); \
+        goto WTF_CONCAT(NEXT_OPCODE_, __LINE__); /* Need a unique label: usable more than once per function. */ \
+    } else \
+        WTF_CONCAT(NEXT_OPCODE_, __LINE__): \
+    continue
+
+#define LAST_OPCODE_LINKED(name) \
+    return \
+        m_currentIndex += OPCODE_LENGTH(name), \
+        m_exitOK = false, \
+        shouldContinueParsing
+
+#define LAST_OPCODE(name) \
+    do { \
+        if (m_currentBlock->terminal()) { \
+            switch (m_currentBlock->terminal()->op()) { \
+            case Jump: \
+            case Branch: \
+            case Switch: \
+                ASSERT(!m_currentBlock->isLinked); \
+                m_inlineStackTop->m_unlinkedBlocks.append(m_currentBlock); \
+                break;\
+            default: break; \
+            } \
+        } \
+        LAST_OPCODE_LINKED(name); \
+    } while (false)
+
 bool ByteCodeParser::parseBlock(unsigned limit)
 {
     bool shouldContinueParsing = true;
@@ -4232,7 +4120,7 @@
     // opposed to using a value we set explicitly.
     if (m_currentBlock == m_graph.block(0) && !inlineCallFrame()) {
         auto addResult = m_graph.m_rootToArguments.add(m_currentBlock, ArgumentsVector());
-        PARSER_ASSERT(addResult.isNewEntry);
+        RELEASE_ASSERT(addResult.isNewEntry);
         ArgumentsVector& entrypointArguments = addResult.iterator->value;
         entrypointArguments.resize(m_numArguments);
 
@@ -4254,7 +4142,7 @@
     }
 
     while (true) {
-        // We're staring a new bytecode instruction. Hence, we once again have a place that we can exit
+        // We're staring at a new bytecode instruction. So we once again have a place that we can exit
         // to.
         m_exitOK = true;
         
@@ -4269,11 +4157,9 @@
             // logic relies on every bytecode resulting in one or more nodes, which would
             // be true anyway except for op_loop_hint, which emits a Phantom to force this
             // to be true.
-            // We also don't insert a jump if the block already has a terminal,
-            // which could happen after a tail call.
-            ASSERT(m_currentBlock->isEmpty() || !m_currentBlock->terminal());
+
             if (!m_currentBlock->isEmpty())
-                addToGraph(Jump, OpInfo(m_currentIndex));
+                addJumpTo(m_currentIndex);
             return shouldContinueParsing;
         }
         
@@ -4282,8 +4168,7 @@
         m_currentInstruction = currentInstruction; // Some methods want to use this, and we'd rather not thread it through calls.
         OpcodeID opcodeID = Interpreter::getOpcodeID(currentInstruction->u.opcode);
         
-        if (Options::verboseDFGBytecodeParsing())
-            dataLog("    parsing ", currentCodeOrigin(), ": ", opcodeID, "\n");
+        VERBOSE_LOG("    parsing ", currentCodeOrigin(), ": ", opcodeID, "\n");
         
         if (UNLIKELY(m_graph.compilation())) {
             addToGraph(CountExecution, OpInfo(m_graph.compilation()->executionCounterFor(
@@ -4299,6 +4184,9 @@
             // Initialize all locals to undefined.
             for (int i = 0; i < m_inlineStackTop->m_codeBlock->m_numVars; ++i)
                 set(virtualRegisterForLocal(i), undefined, ImmediateNakedSet);
+
+            m_inlineStackTop->m_entryBlock = m_currentBlock;
+
             NEXT_OPCODE(op_enter);
         }
             
@@ -5276,33 +5164,38 @@
 
         case op_ret:
             ASSERT(!m_currentBlock->terminal());
-            if (inlineCallFrame()) {
+            if (!inlineCallFrame()) {
+                // Simple case: we are just producing a return
+                addToGraph(Return, get(VirtualRegister(currentInstruction[1].u.operand)));
                 flushForReturn();
-                if (m_inlineStackTop->m_returnValue.isValid())
-                    setDirect(m_inlineStackTop->m_returnValue, get(VirtualRegister(currentInstruction[1].u.operand)), ImmediateSetWithFlush);
-                m_inlineStackTop->m_didReturn = true;
-                if (m_inlineStackTop->m_unlinkedBlocks.isEmpty()) {
-                    // If we're returning from the first block, then we're done parsing.
-                    ASSERT(m_inlineStackTop->m_callsiteBlockHead == m_graph.lastBlock());
+                LAST_OPCODE(op_ret);
+            }
+
+            flushForReturn();
+            if (m_inlineStackTop->m_returnValue.isValid())
+                setDirect(m_inlineStackTop->m_returnValue, get(VirtualRegister(currentInstruction[1].u.operand)), ImmediateSetWithFlush);
+
+            if (!m_inlineStackTop->m_continuationBlock && m_currentIndex + OPCODE_LENGTH(op_ret) != m_inlineStackTop->m_codeBlock->instructions().size()) {
+                if (m_inlineStackTop->m_entryBlock == m_currentBlock) {
+                    // This is a rather weird case: we are returning from the first block in the function, so all the rest of the function is dead code.
+                    // So we treat this as a normal return instead of an early return, and signal to skip the rest of the function.
+                    // FIXME https://bugs.webkit.org/show_bug.cgi?id=177925: evaluate whether that optimization is useful, it seems like a really weird corner case.
                     shouldContinueParsing = false;
-                    LAST_OPCODE(op_ret);
                 } else {
-                    // If inlining created blocks, and we're doing a return, then we need some
-                    // special linking.
-                    ASSERT(m_inlineStackTop->m_unlinkedBlocks.last().m_block == m_graph.lastBlock());
-                    m_inlineStackTop->m_unlinkedBlocks.last().m_needsNormalLinking = false;
+                    // This is an early return from an inlined function and we do not have a continuation block, so we must allocate one.
+                    // It is untargetable, because we do not know the appropriate index.
+                    // If this block turns out to be a jump target, parseCodeBlock will fix its bytecodeIndex before putting it in m_blockLinkingTargets
+                    m_inlineStackTop->m_continuationBlock = allocateUntargetableBlock();
                 }
-                if (m_currentIndex + OPCODE_LENGTH(op_ret) != m_inlineStackTop->m_codeBlock->instructions().size() || m_inlineStackTop->m_didEarlyReturn) {
-                    ASSERT(m_currentIndex + OPCODE_LENGTH(op_ret) <= m_inlineStackTop->m_codeBlock->instructions().size());
-                    addToGraph(Jump, OpInfo(0));
-                    m_inlineStackTop->m_unlinkedBlocks.last().m_needsEarlyReturnLinking = true;
-                    m_inlineStackTop->m_didEarlyReturn = true;
-                }
-                LAST_OPCODE(op_ret);
             }
-            addToGraph(Return, get(VirtualRegister(currentInstruction[1].u.operand)));
-            flushForReturn();
-            LAST_OPCODE(op_ret);
+
+            if (m_inlineStackTop->m_continuationBlock)
+                addJumpTo(m_inlineStackTop->m_continuationBlock);
+            else {
+                // We are returning from an inlined function, and do not need to jump anywhere, so we just keep the current block
+                m_inlineStackTop->m_continuationBlock = m_currentBlock;
+            }
+            LAST_OPCODE_LINKED(op_ret);
             
         case op_end:
             ASSERT(!inlineCallFrame());
@@ -5334,7 +5227,7 @@
                 NEXT_OPCODE(op_catch);
             }
 
-            PARSER_ASSERT(!m_currentBlock->size());
+            RELEASE_ASSERT(!m_currentBlock->size());
 
             ValueProfileAndOperandBuffer* buffer = static_cast<ValueProfileAndOperandBuffer*>(currentInstruction[3].u.pointer);
 
@@ -5359,8 +5252,8 @@
                     if (operand.isLocal())
                         localPredictions.append(prediction);
                     else {
-                        PARSER_ASSERT(operand.isArgument());
-                        PARSER_ASSERT(static_cast<uint32_t>(operand.toArgument()) < argumentPredictions.size());
+                        RELEASE_ASSERT(operand.isArgument());
+                        RELEASE_ASSERT(static_cast<uint32_t>(operand.toArgument()) < argumentPredictions.size());
                         if (validationEnabled())
                             seenArguments.add(operand.toArgument());
                         argumentPredictions[operand.toArgument()] = prediction;
@@ -5369,7 +5262,7 @@
 
                 if (validationEnabled()) {
                     for (unsigned argument = 0; argument < m_numArguments; ++argument)
-                        PARSER_ASSERT(seenArguments.contains(argument));
+                        RELEASE_ASSERT(seenArguments.contains(argument));
                 }
             }
 
@@ -5410,7 +5303,7 @@
 
             {
                 auto addResult = m_graph.m_rootToArguments.add(m_currentBlock, ArgumentsVector());
-                PARSER_ASSERT(addResult.isNewEntry);
+                RELEASE_ASSERT(addResult.isNewEntry);
                 ArgumentsVector& entrypointArguments = addResult.iterator->value;
                 entrypointArguments.resize(m_numArguments);
 
@@ -5450,7 +5343,7 @@
             Terminality terminality = handleCall(currentInstruction, TailCall, CallMode::Tail);
             ASSERT_WITH_MESSAGE(m_currentInstruction == currentInstruction, "handleCall, which may have inlined the callee, trashed m_currentInstruction");
             // If the call is terminal then we should not parse any further bytecodes as the TailCall will exit the function.
-            // If the call is not terminal, however, then we want the subsequent op_ret/op_jump to update metadata and clean
+            // If the call is not terminal, however, then we want the subsequent op_ret/op_jmp to update metadata and clean
             // things up.
             if (terminality == NonTerminal)
                 NEXT_OPCODE(op_tail_call);
@@ -5474,7 +5367,7 @@
             Terminality terminality = handleVarargsCall(currentInstruction, TailCallVarargs, CallMode::Tail);
             ASSERT_WITH_MESSAGE(m_currentInstruction == currentInstruction, "handleVarargsCall, which may have inlined the callee, trashed m_currentInstruction");
             // If the call is terminal then we should not parse any further bytecodes as the TailCall will exit the function.
-            // If the call is not terminal, however, then we want the subsequent op_ret/op_jump to update metadata and clean
+            // If the call is not terminal, however, then we want the subsequent op_ret/op_jmp to update metadata and clean
             // things up.
             if (terminality == NonTerminal)
                 NEXT_OPCODE(op_tail_call_varargs);
@@ -5490,12 +5383,12 @@
             Terminality terminality = handleVarargsCall(currentInstruction, TailCallForwardVarargs, CallMode::Tail);
             ASSERT_WITH_MESSAGE(m_currentInstruction == currentInstruction, "handleVarargsCall, which may have inlined the callee, trashed m_currentInstruction");
             // If the call is terminal then we should not parse any further bytecodes as the TailCall will exit the function.
-            // If the call is not terminal, however, then we want the subsequent op_ret/op_jump to update metadata and clean
+            // If the call is not terminal, however, then we want the subsequent op_ret/op_jmp to update metadata and clean
             // things up.
             if (terminality == NonTerminal)
-                NEXT_OPCODE(op_tail_call);
+                NEXT_OPCODE(op_tail_call_forward_arguments);
             else
-                LAST_OPCODE(op_tail_call);
+                LAST_OPCODE(op_tail_call_forward_arguments);
         }
             
         case op_construct_varargs: {
@@ -5555,8 +5448,8 @@
             case GlobalLexicalVar:
             case GlobalLexicalVarWithVarInjectionChecks: {
                 JSScope* constantScope = JSScope::constantScopeForCodeBlock(resolveType, m_inlineStackTop->m_codeBlock);
-                PARSER_ASSERT(constantScope);
-                PARSER_ASSERT(static_cast<JSScope*>(currentInstruction[6].u.pointer) == constantScope);
+                RELEASE_ASSERT(constantScope);
+                RELEASE_ASSERT(static_cast<JSScope*>(currentInstruction[6].u.pointer) == constantScope);
                 set(VirtualRegister(dst), weakJSConstant(constantScope));
                 addToGraph(Phantom, get(VirtualRegister(scope)));
                 break;
@@ -5897,7 +5790,7 @@
             // Baseline->DFG OSR jumps between loop hints. The DFG assumes that Baseline->DFG
             // OSR can only happen at basic block boundaries. Assert that these two statements
             // are compatible.
-            PARSER_ASSERT(m_currentIndex == blockBegin);
+            RELEASE_ASSERT(m_currentIndex == blockBegin);
             
             // We never do OSR into an inlined code block. That could not happen, since OSR
             // looks up the code block that is the replacement for the baseline JIT code
@@ -6244,25 +6137,18 @@
     }
         
     default:
-        break;
+        RELEASE_ASSERT_NOT_REACHED();
     }
     
-    if (DFGByteCodeParserInternal::verbose)
-        dataLog("Marking ", RawPointer(block), " as linked (actually did linking)\n");
+    VERBOSE_LOG("Marking ", RawPointer(block), " as linked (actually did linking)\n");
     block->didLink();
 }
 
-void ByteCodeParser::linkBlocks(Vector<UnlinkedBlock>& unlinkedBlocks, Vector<BasicBlock*>& possibleTargets)
+void ByteCodeParser::linkBlocks(Vector<BasicBlock*>& unlinkedBlocks, Vector<BasicBlock*>& possibleTargets)
 {
     for (size_t i = 0; i < unlinkedBlocks.size(); ++i) {
-        if (DFGByteCodeParserInternal::verbose)
-            dataLog("Attempting to link ", RawPointer(unlinkedBlocks[i].m_block), "\n");
-        if (unlinkedBlocks[i].m_needsNormalLinking) {
-            if (DFGByteCodeParserInternal::verbose)
-                dataLog("    Does need normal linking.\n");
-            linkBlock(unlinkedBlocks[i].m_block, possibleTargets);
-            unlinkedBlocks[i].m_needsNormalLinking = false;
-        }
+        VERBOSE_LOG("Attempting to link ", RawPointer(unlinkedBlocks[i]), "\n");
+        linkBlock(unlinkedBlocks[i], possibleTargets);
     }
 }
 
@@ -6270,7 +6156,6 @@
     ByteCodeParser* byteCodeParser,
     CodeBlock* codeBlock,
     CodeBlock* profiledBlock,
-    BasicBlock* callsiteBlockHead,
     JSFunction* callee, // Null if this is a closure call.
     VirtualRegister returnValueVR,
     VirtualRegister inlineCallFrameStart,
@@ -6279,10 +6164,8 @@
     : m_byteCodeParser(byteCodeParser)
     , m_codeBlock(codeBlock)
     , m_profiledBlock(profiledBlock)
-    , m_callsiteBlockHead(callsiteBlockHead)
+    , m_continuationBlock(nullptr)
     , m_returnValue(returnValueVR)
-    , m_didReturn(false)
-    , m_didEarlyReturn(false)
     , m_caller(byteCodeParser->m_inlineStackTop)
 {
     {
@@ -6312,7 +6195,6 @@
         // Inline case.
         ASSERT(codeBlock != byteCodeParser->m_codeBlock);
         ASSERT(inlineCallFrameStart.isValid());
-        ASSERT(callsiteBlockHead);
         
         m_inlineCallFrame = byteCodeParser->m_graph.m_plan.inlineCallFrames->add();
 
@@ -6357,7 +6239,6 @@
             m_switchRemap[i] = byteCodeParser->m_codeBlock->numberOfSwitchJumpTables();
             byteCodeParser->m_codeBlock->addSwitchJumpTable() = codeBlock->switchJumpTable(i);
         }
-        m_callsiteBlockHeadNeedsLinking = true;
     } else {
         // Machine code block case.
         ASSERT(codeBlock == byteCodeParser->m_codeBlock);
@@ -6364,7 +6245,6 @@
         ASSERT(!callee);
         ASSERT(!returnValueVR.isValid());
         ASSERT(!inlineCallFrameStart.isValid());
-        ASSERT(!callsiteBlockHead);
 
         m_inlineCallFrame = 0;
 
@@ -6377,7 +6257,6 @@
             m_constantBufferRemap[i] = i;
         for (size_t i = 0; i < codeBlock->numberOfSwitchJumpTables(); ++i)
             m_switchRemap[i] = i;
-        m_callsiteBlockHeadNeedsLinking = false;
     }
     
     byteCodeParser->m_inlineStackTop = this;
@@ -6432,47 +6311,20 @@
 
         // Loop until we reach the current limit (i.e. next jump target).
         do {
+            // There may already be a currentBlock in two cases:
+            // - we may have just entered the loop for the first time
+            // - we may have just returned from an inlined callee that had some early returns and
+            //   so allocated a continuation block, and the instruction after the call is a jump target.
+            // In both cases, we want to keep using it.
             if (!m_currentBlock) {
-                // Check if we can use the last block.
-                if (m_graph.numBlocks() && m_graph.lastBlock()->isEmpty()) {
-                    // This must be a block belonging to us.
-                    ASSERT(m_inlineStackTop->m_unlinkedBlocks.last().m_block == m_graph.lastBlock());
-                    // Either the block is linkable or it isn't. If it's linkable then it's the last
-                    // block in the blockLinkingTargets list. If it's not then the last block will
-                    // have a lower bytecode index that the one we're about to give to this block.
-                    if (m_inlineStackTop->m_blockLinkingTargets.isEmpty() || m_inlineStackTop->m_blockLinkingTargets.last()->bytecodeBegin != m_currentIndex) {
-                        // Make the block linkable.
-                        ASSERT(m_inlineStackTop->m_blockLinkingTargets.isEmpty() || m_inlineStackTop->m_blockLinkingTargets.last()->bytecodeBegin < m_currentIndex);
-                        m_inlineStackTop->m_blockLinkingTargets.append(m_graph.lastBlock());
-                    }
-                    // Change its bytecode begin and continue.
-                    m_currentBlock = m_graph.lastBlock();
-                    m_currentBlock->bytecodeBegin = m_currentIndex;
-                } else {
-                    Ref<BasicBlock> block = adoptRef(*new BasicBlock(m_currentIndex, m_numArguments, m_numLocals, 1));
-                    m_currentBlock = block.ptr();
-                    // This assertion checks two things:
-                    // 1) If the bytecodeBegin is greater than currentIndex, then something has gone
-                    //    horribly wrong. So, we're probably generating incorrect code.
-                    // 2) If the bytecodeBegin is equal to the currentIndex, then we failed to do
-                    //    a peephole coalescing of this block in the if statement above. So, we're
-                    //    generating suboptimal code and leaving more work for the CFG simplifier.
-                    if (!m_inlineStackTop->m_unlinkedBlocks.isEmpty()) {
-                        unsigned lastBegin =
-                            m_inlineStackTop->m_unlinkedBlocks.last().m_block->bytecodeBegin;
-                        ASSERT_UNUSED(
-                            lastBegin, lastBegin == UINT_MAX || lastBegin < m_currentIndex);
-                    }
-                    m_inlineStackTop->m_unlinkedBlocks.append(UnlinkedBlock(block.ptr()));
-                    m_inlineStackTop->m_blockLinkingTargets.append(block.ptr());
-                    // The first block is definitely an OSR target.
-                    if (!m_graph.numBlocks()) {
-                        block->isOSRTarget = true;
-                        m_graph.m_roots.append(block.ptr());
-                    }
-                    m_graph.appendBlock(WTFMove(block));
-                    prepareToParseBlock();
+                m_currentBlock = allocateTargetableBlock(m_currentIndex);
+
+                // The first block is definitely an OSR target.
+                if (m_graph.numBlocks() == 1) {
+                    m_currentBlock->isOSRTarget = true;
+                    m_graph.m_roots.append(m_currentBlock);
                 }
+                prepareToParseBlock();
             }
 
             bool shouldContinueParsing = parseBlock(limit);
@@ -6479,21 +6331,22 @@
 
             // We should not have gone beyond the limit.
             ASSERT(m_currentIndex <= limit);
-            
-            // We should have planted a terminal, or we just gave up because
-            // we realized that the jump target information is imprecise, or we
-            // are at the end of an inline function, or we realized that we
-            // should stop parsing because there was a return in the first
-            // basic block.
-            ASSERT(m_currentBlock->isEmpty() || m_currentBlock->terminal() || (m_currentIndex == codeBlock->instructions().size() && inlineCallFrame()) || !shouldContinueParsing);
 
             if (!shouldContinueParsing) {
-                if (Options::verboseDFGBytecodeParsing())
-                    dataLog("Done parsing ", *codeBlock, "\n");
+                VERBOSE_LOG("Done parsing ", *codeBlock, "\n");
                 return;
             }
-            
-            m_currentBlock = nullptr;
+
+            if (m_currentBlock->isEmpty()) {
+                // This case only happens if the last instruction was an inlined call with early returns
+                // or polymorphic (creating an empty continuation block),
+                // and then we hit the limit before putting anything in the continuation block.
+                ASSERT(m_currentIndex == limit);
+                makeBlockTargetable(m_currentBlock, m_currentIndex);
+            } else {
+                ASSERT(m_currentBlock->terminal() || (m_currentIndex == codeBlock->instructions().size() && inlineCallFrame()));
+                m_currentBlock = nullptr;
+            }
         } while (m_currentIndex < limit);
     }
 
@@ -6500,17 +6353,15 @@
     // Should have reached the end of the instructions.
     ASSERT(m_currentIndex == codeBlock->instructions().size());
     
-    if (Options::verboseDFGBytecodeParsing())
-        dataLog("Done parsing ", *codeBlock, " (fell off end)\n");
+    VERBOSE_LOG("Done parsing ", *codeBlock, " (fell off end)\n");
 }
 
-bool ByteCodeParser::parse()
+void ByteCodeParser::parse()
 {
     // Set during construction.
     ASSERT(!m_currentIndex);
     
-    if (Options::verboseDFGBytecodeParsing())
-        dataLog("Parsing ", *m_codeBlock, "\n");
+    VERBOSE_LOG("Parsing ", *m_codeBlock, "\n");
     
     m_dfgCodeBlock = m_graph.m_plan.profiledDFGCodeBlock;
     if (isFTL(m_graph.m_plan.mode) && m_dfgCodeBlock
@@ -6522,7 +6373,7 @@
     }
     
     InlineStackEntry inlineStackEntry(
-        this, m_codeBlock, m_profiledBlock, 0, 0, VirtualRegister(), VirtualRegister(),
+        this, m_codeBlock, m_profiledBlock, 0, VirtualRegister(), VirtualRegister(),
         m_codeBlock->numParameters(), InlineCallFrame::Call);
     
     parseCodeBlock();
@@ -6529,6 +6380,8 @@
     linkBlocks(inlineStackEntry.m_unlinkedBlocks, inlineStackEntry.m_blockLinkingTargets);
 
     m_graph.determineReachability();
+    if (Options::verboseDFGBytecodeParsing())
+        m_graph.dump();
     m_graph.killUnreachableBlocks();
 
     for (BlockIndex blockIndex = m_graph.numBlocks(); blockIndex--;) {
@@ -6543,13 +6396,11 @@
 
     m_graph.m_localVars = m_numLocals;
     m_graph.m_parameterSlots = m_parameterSlots;
-
-    return true;
 }
 
-bool parse(Graph& graph)
+void parse(Graph& graph)
 {
-    return ByteCodeParser(graph).parse();
+    ByteCodeParser(graph).parse();
 }
 
 } } // namespace JSC::DFG

Modified: trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.h (223085 => 223086)


--- trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.h	2017-10-09 23:45:45 UTC (rev 223085)
+++ trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.h	2017-10-09 23:55:25 UTC (rev 223086)
@@ -31,7 +31,7 @@
 
 class Graph;
 
-bool parse(Graph&);
+void parse(Graph&);
 
 } } // namespace JSC::DFG
 

Modified: trunk/Source/_javascript_Core/dfg/DFGGraph.cpp (223085 => 223086)


--- trunk/Source/_javascript_Core/dfg/DFGGraph.cpp	2017-10-09 23:45:45 UTC (rev 223085)
+++ trunk/Source/_javascript_Core/dfg/DFGGraph.cpp	2017-10-09 23:55:25 UTC (rev 223086)
@@ -817,7 +817,8 @@
             continue;
         if (block->isReachable)
             continue;
-        
+
+        dataLogIf(Options::verboseDFGBytecodeParsing(), "Basic block #", blockIndex, " was killed because it was unreachable\n");
         killBlockAndItsContents(block);
     }
 }

Modified: trunk/Source/_javascript_Core/dfg/DFGPlan.cpp (223085 => 223086)


--- trunk/Source/_javascript_Core/dfg/DFGPlan.cpp	2017-10-09 23:45:45 UTC (rev 223085)
+++ trunk/Source/_javascript_Core/dfg/DFGPlan.cpp	2017-10-09 23:55:25 UTC (rev 223086)
@@ -247,11 +247,7 @@
     }
     
     Graph dfg(*vm, *this);
-    
-    if (!parse(dfg)) {
-        finalizer = std::make_unique<FailedFinalizer>(*this);
-        return FailPath;
-    }
+    parse(dfg);
 
     codeBlock->setCalleeSaveRegisters(RegisterSet::dfgCalleeSaveRegisters());
 
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to