bool run()
    {
        RELEASE_ASSERT(m_graph.m_plan.mode == DFGMode);
        
        if (!Options::useFTLJIT())
            return false;
        
        if (m_graph.m_profiledBlock->m_didFailFTLCompilation)
            return false;

        if (!Options::bytecodeRangeToFTLCompile().isInRange(m_graph.m_profiledBlock->instructionCount()))
            return false;

#if ENABLE(FTL_JIT)
        FTL::CapabilityLevel level = FTL::canCompile(m_graph);
        if (level == FTL::CannotCompile)
            return false;
        
        if (!Options::useOSREntryToFTL())
            level = FTL::CanCompile;

        // First we find all the loops that contain a LoopHint for which we cannot OSR enter.
        // We use that information to decide if we need CheckTierUpAndOSREnter or CheckTierUpWithNestedTriggerAndOSREnter.
        m_graph.ensureNaturalLoops();
        NaturalLoops& naturalLoops = *m_graph.m_naturalLoops;

        HashSet<const NaturalLoop*> loopsContainingLoopHintWithoutOSREnter = findLoopsContainingLoopHintWithoutOSREnter(naturalLoops, level);

        bool canTierUpAndOSREnter = false;
        
        InsertionSet insertionSet(m_graph);
        for (BlockIndex blockIndex = m_graph.numBlocks(); blockIndex--;) {
            BasicBlock* block = m_graph.block(blockIndex);
            if (!block)
                continue;
            
            for (unsigned nodeIndex = 0; nodeIndex < block->size(); ++nodeIndex) {
                Node* node = block->at(nodeIndex);
                if (node->op() != LoopHint)
                    continue;

                NodeOrigin origin = node->origin;
                if (canOSREnterAtLoopHint(level, block, nodeIndex)) {
                    canTierUpAndOSREnter = true;
                    const NaturalLoop* loop = naturalLoops.innerMostLoopOf(block);
                    if (loop && loopsContainingLoopHintWithoutOSREnter.contains(loop))
                        insertionSet.insertNode(nodeIndex + 1, SpecNone, CheckTierUpWithNestedTriggerAndOSREnter, origin);
                    else
                        insertionSet.insertNode(nodeIndex + 1, SpecNone, CheckTierUpAndOSREnter, origin);
                } else
                    insertionSet.insertNode(nodeIndex + 1, SpecNone, CheckTierUpInLoop, origin);
                break;
            }
            
            NodeAndIndex terminal = block->findTerminal();
            if (terminal.node->isFunctionTerminal()) {
                insertionSet.insertNode(
                    terminal.index, SpecNone, CheckTierUpAtReturn, terminal.node->origin);
            }
            
            insertionSet.execute(block);
        }

        m_graph.m_plan.canTierUpAndOSREnter = canTierUpAndOSREnter;
        m_graph.m_plan.willTryToTierUp = true;
        return true;
#else // ENABLE(FTL_JIT)
        RELEASE_ASSERT_NOT_REACHED();
        return false;
#endif // ENABLE(FTL_JIT)
    }
    bool run()
    {
        RELEASE_ASSERT(m_graph.m_plan.mode == DFGMode);

        if (!Options::useFTLJIT())
            return false;

        if (m_graph.m_profiledBlock->m_didFailFTLCompilation)
            return false;

#if ENABLE(FTL_JIT)
        FTL::CapabilityLevel level = FTL::canCompile(m_graph);
        if (level == FTL::CannotCompile)
            return false;

        if (!Options::enableOSREntryToFTL())
            level = FTL::CanCompile;

        InsertionSet insertionSet(m_graph);
        for (BlockIndex blockIndex = m_graph.numBlocks(); blockIndex--;) {
            BasicBlock* block = m_graph.block(blockIndex);
            if (!block)
                continue;

            for (unsigned nodeIndex = 0; nodeIndex < block->size(); ++nodeIndex) {
                Node* node = block->at(nodeIndex);
                if (node->op() != LoopHint)
                    continue;

                // We only put OSR checks for the first LoopHint in the block. Note that
                // more than one LoopHint could happen in cases where we did a lot of CFG
                // simplification in the bytecode parser, but it should be very rare.

                NodeOrigin origin = node->origin;

                if (level != FTL::CanCompileAndOSREnter || origin.semantic.inlineCallFrame) {
                    insertionSet.insertNode(
                        nodeIndex + 1, SpecNone, CheckTierUpInLoop, origin);
                    break;
                }

                bool isAtTop = true;
                for (unsigned subNodeIndex = nodeIndex; subNodeIndex--;) {
                    if (!block->at(subNodeIndex)->isSemanticallySkippable()) {
                        isAtTop = false;
                        break;
                    }
                }

                if (!isAtTop) {
                    insertionSet.insertNode(
                        nodeIndex + 1, SpecNone, CheckTierUpInLoop, origin);
                    break;
                }

                insertionSet.insertNode(
                    nodeIndex + 1, SpecNone, CheckTierUpAndOSREnter, origin);
                break;
            }

            NodeAndIndex terminal = block->findTerminal();
            if (terminal.node->op() == Return) {
                insertionSet.insertNode(
                    terminal.index, SpecNone, CheckTierUpAtReturn, terminal.node->origin);
            }

            insertionSet.execute(block);
        }

        m_graph.m_plan.willTryToTierUp = true;
        return true;
#else // ENABLE(FTL_JIT)
        RELEASE_ASSERT_NOT_REACHED();
        return false;
#endif // ENABLE(FTL_JIT)
    }
    bool run()
    {
        RELEASE_ASSERT(m_graph.m_plan.mode == DFGMode);
        
        if (!Options::useFTLJIT())
            return false;
        
        if (m_graph.m_profiledBlock->m_didFailFTLCompilation)
            return false;

        if (!Options::bytecodeRangeToFTLCompile().isInRange(m_graph.m_profiledBlock->instructionCount()))
            return false;

#if ENABLE(FTL_JIT)
        FTL::CapabilityLevel level = FTL::canCompile(m_graph);
        if (level == FTL::CannotCompile)
            return false;
        
        if (!Options::useOSREntryToFTL())
            level = FTL::CanCompile;

        m_graph.ensureNaturalLoops();
        NaturalLoops& naturalLoops = *m_graph.m_naturalLoops;
        HashMap<const NaturalLoop*, unsigned> naturalLoopToLoopHint = buildNaturalLoopToLoopHintMap(naturalLoops);

        HashMap<unsigned, LoopHintDescriptor> tierUpHierarchy;

        InsertionSet insertionSet(m_graph);
        for (BlockIndex blockIndex = m_graph.numBlocks(); blockIndex--;) {
            BasicBlock* block = m_graph.block(blockIndex);
            if (!block)
                continue;

            for (unsigned nodeIndex = 0; nodeIndex < block->size(); ++nodeIndex) {
                Node* node = block->at(nodeIndex);
                if (node->op() != LoopHint)
                    continue;

                NodeOrigin origin = node->origin;
                bool canOSREnter = canOSREnterAtLoopHint(level, block, nodeIndex);

                NodeType tierUpType = CheckTierUpAndOSREnter;
                if (!canOSREnter)
                    tierUpType = CheckTierUpInLoop;
                insertionSet.insertNode(nodeIndex + 1, SpecNone, tierUpType, origin);

                unsigned bytecodeIndex = origin.semantic.bytecodeIndex;
                if (canOSREnter)
                    m_graph.m_plan.tierUpAndOSREnterBytecodes.append(bytecodeIndex);

                if (const NaturalLoop* loop = naturalLoops.innerMostLoopOf(block)) {
                    LoopHintDescriptor descriptor;
                    descriptor.canOSREnter = canOSREnter;

                    const NaturalLoop* outerLoop = loop;
                    while ((outerLoop = naturalLoops.innerMostOuterLoop(*outerLoop))) {
                        auto it = naturalLoopToLoopHint.find(outerLoop);
                        if (it != naturalLoopToLoopHint.end())
                            descriptor.osrEntryCandidates.append(it->value);
                    }
                    if (!descriptor.osrEntryCandidates.isEmpty())
                        tierUpHierarchy.add(bytecodeIndex, WTFMove(descriptor));
                }
                break;
            }

            NodeAndIndex terminal = block->findTerminal();
            if (terminal.node->isFunctionTerminal()) {
                insertionSet.insertNode(
                    terminal.index, SpecNone, CheckTierUpAtReturn, terminal.node->origin);
            }

            insertionSet.execute(block);
        }

        // Add all the candidates that can be OSR Entered.
        for (auto entry : tierUpHierarchy) {
            Vector<unsigned> tierUpCandidates;
            for (unsigned bytecodeIndex : entry.value.osrEntryCandidates) {
                auto descriptorIt = tierUpHierarchy.find(bytecodeIndex);
                if (descriptorIt != tierUpHierarchy.end()
                    && descriptorIt->value.canOSREnter)
                    tierUpCandidates.append(bytecodeIndex);
            }

            if (!tierUpCandidates.isEmpty())
                m_graph.m_plan.tierUpInLoopHierarchy.add(entry.key, WTFMove(tierUpCandidates));
        }
        m_graph.m_plan.willTryToTierUp = true;
        return true;
#else // ENABLE(FTL_JIT)
        RELEASE_ASSERT_NOT_REACHED();
        return false;
#endif // ENABLE(FTL_JIT)
    }