⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 243176 in webkit


Ignore:
Timestamp:
Mar 19, 2019, 3:53:40 PM (7 years ago)
Author:
sbarati@apple.com
Message:

Prune code after ForceOSRExit
https://bugs.webkit.org/show_bug.cgi?id=195913

Reviewed by Keith Miller.

I removed our original implementation of this in r242989 because
it was not sound. It broke backwards propagation because it removed
uses of a node that backwards propagation relied on to be sound.
Essentially, backwards propagation relies on being able to see uses
that would exist in bytecode to be sound.

The rollout in r242989 was a 1% Speedometer2 regression. This patch
rolls back in the optimization in a sound way.

This patch augments the code we had prior to r242989 to be sound. In
addition to preserving liveness, we now also convert all uses after
the ForceOSRExit to be Phantom. This may pessimize the optimizations
we do in backwards propagation, but it will prevent that phase from
making unsound optimizations.

  • dfg/DFGByteCodeParser.cpp:

(JSC::DFG::ByteCodeParser::addToGraph):
(JSC::DFG::ByteCodeParser::parse):

Location:
trunk/Source/JavaScriptCore
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/ChangeLog

    r243163 r243176  
     12019-03-19  Saam barati  <sbarati@apple.com>
     2
     3        Prune code after ForceOSRExit
     4        https://bugs.webkit.org/show_bug.cgi?id=195913
     5
     6        Reviewed by Keith Miller.
     7
     8        I removed our original implementation of this in r242989 because
     9        it was not sound. It broke backwards propagation because it removed
     10        uses of a node that backwards propagation relied on to be sound.
     11        Essentially, backwards propagation relies on being able to see uses
     12        that would exist in bytecode to be sound.
     13       
     14        The rollout in r242989 was a 1% Speedometer2 regression. This patch
     15        rolls back in the optimization in a sound way.
     16       
     17        This patch augments the code we had prior to r242989 to be sound. In
     18        addition to preserving liveness, we now also convert all uses after
     19        the ForceOSRExit to be Phantom. This may pessimize the optimizations
     20        we do in backwards propagation, but it will prevent that phase from
     21        making unsound optimizations.
     22
     23        * dfg/DFGByteCodeParser.cpp:
     24        (JSC::DFG::ByteCodeParser::addToGraph):
     25        (JSC::DFG::ByteCodeParser::parse):
     26
    1272019-03-19  Michael Catanzaro  <mcatanzaro@igalia.com>
    228
  • trunk/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp

    r242989 r243176  
    716716        VERBOSE_LOG("        appended ", node, " ", Graph::opName(node->op()), "\n");
    717717
     718        m_hasAnyForceOSRExits |= (node->op() == ForceOSRExit);
     719
    718720        m_currentBlock->append(node);
    719721        if (clobbersExitState(m_graph, node))
     
    11771179    const Instruction* m_currentInstruction;
    11781180    bool m_hasDebuggerEnabled;
     1181    bool m_hasAnyForceOSRExits { false };
    11791182};
    11801183
     
    72857288    linkBlocks(inlineStackEntry.m_unlinkedBlocks, inlineStackEntry.m_blockLinkingTargets);
    72867289
     7290    if (m_hasAnyForceOSRExits) {
     7291        BlockSet blocksToIgnore;
     7292        for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
     7293            if (block->isOSRTarget && block->bytecodeBegin == m_graph.m_plan.osrEntryBytecodeIndex()) {
     7294                blocksToIgnore.add(block);
     7295                break;
     7296            }
     7297        }
     7298
     7299        {
     7300            bool isSafeToValidate = false;
     7301            auto postOrder = m_graph.blocksInPostOrder(isSafeToValidate); // This algorithm doesn't rely on the predecessors list, which is not yet built.
     7302            bool changed;
     7303            do {
     7304                changed = false;
     7305                for (BasicBlock* block : postOrder) {
     7306                    for (BasicBlock* successor : block->successors()) {
     7307                        if (blocksToIgnore.contains(successor)) {
     7308                            changed |= blocksToIgnore.add(block);
     7309                            break;
     7310                        }
     7311                    }
     7312                }
     7313            } while (changed);
     7314        }
     7315
     7316        InsertionSet insertionSet(m_graph);
     7317        Operands<VariableAccessData*> mapping(OperandsLike, m_graph.block(0)->variablesAtHead);
     7318
     7319        for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
     7320            if (blocksToIgnore.contains(block))
     7321                continue;
     7322
     7323            mapping.fill(nullptr);
     7324            if (validationEnabled()) {
     7325                // Verify that it's correct to fill mapping with nullptr.
     7326                for (unsigned i = 0; i < block->variablesAtHead.size(); ++i) {
     7327                    Node* node = block->variablesAtHead.at(i);
     7328                    RELEASE_ASSERT(!node);
     7329                }
     7330            }
     7331
     7332            for (unsigned nodeIndex = 0; nodeIndex < block->size(); ++nodeIndex) {
     7333                {
     7334                    Node* node = block->at(nodeIndex);
     7335
     7336                    if (node->hasVariableAccessData(m_graph))
     7337                        mapping.operand(node->local()) = node->variableAccessData();
     7338
     7339                    if (node->op() != ForceOSRExit)
     7340                        continue;
     7341                }
     7342
     7343                NodeOrigin origin = block->at(nodeIndex)->origin;
     7344                RELEASE_ASSERT(origin.exitOK);
     7345
     7346                ++nodeIndex;
     7347
     7348                {
     7349                    if (validationEnabled()) {
     7350                        // This verifies that we don't need to change any of the successors's predecessor
     7351                        // list after planting the Unreachable below. At this point in the bytecode
     7352                        // parser, we haven't linked up the predecessor lists yet.
     7353                        for (BasicBlock* successor : block->successors())
     7354                            RELEASE_ASSERT(successor->predecessors.isEmpty());
     7355                    }
     7356
     7357                    auto insertLivenessPreservingOp = [&] (InlineCallFrame* inlineCallFrame, NodeType op, VirtualRegister operand) {
     7358                        VariableAccessData* variable = mapping.operand(operand);
     7359                        if (!variable) {
     7360                            variable = newVariableAccessData(operand);
     7361                            mapping.operand(operand) = variable;
     7362                        }
     7363
     7364                        VirtualRegister argument = operand - (inlineCallFrame ? inlineCallFrame->stackOffset : 0);
     7365                        if (argument.isArgument() && !argument.isHeader()) {
     7366                            const Vector<ArgumentPosition*>& arguments = m_inlineCallFrameToArgumentPositions.get(inlineCallFrame);
     7367                            arguments[argument.toArgument()]->addVariable(variable);
     7368                        }
     7369                        insertionSet.insertNode(nodeIndex, SpecNone, op, origin, OpInfo(variable));
     7370                    };
     7371                    auto addFlushDirect = [&] (InlineCallFrame* inlineCallFrame, VirtualRegister operand) {
     7372                        insertLivenessPreservingOp(inlineCallFrame, Flush, operand);
     7373                    };
     7374                    auto addPhantomLocalDirect = [&] (InlineCallFrame* inlineCallFrame, VirtualRegister operand) {
     7375                        insertLivenessPreservingOp(inlineCallFrame, PhantomLocal, operand);
     7376                    };
     7377                    flushForTerminalImpl(origin.semantic, addFlushDirect, addPhantomLocalDirect);
     7378                }
     7379
     7380                while (true) {
     7381                    RELEASE_ASSERT(nodeIndex < block->size());
     7382
     7383                    Node* node = block->at(nodeIndex);
     7384
     7385                    node->origin = origin;
     7386                    m_graph.doToChildren(node, [&] (Edge edge) {
     7387                        // We only need to keep data flow edges to nodes defined prior to the ForceOSRExit. The reason
     7388                        // for this is we rely on backwards propagation being able to see the "full" bytecode. To model
     7389                        // this, we preserve uses of a node in a generic way so that backwards propagation can reason
     7390                        // about them. Therefore, we can't remove uses of a node which is defined before the ForceOSRExit
     7391                        // even when we're at a point in the program after the ForceOSRExit, because that would break backwards
     7392                        // propagation's analysis over the uses of a node. However, we don't need this same preservation for
     7393                        // nodes defined after ForceOSRExit, as we've already exitted before those defs.
     7394                        if (edge->hasResult())
     7395                            insertionSet.insertNode(nodeIndex, SpecNone, Phantom, origin, Edge(edge.node(), UntypedUse));
     7396                    });
     7397
     7398                    bool isTerminal = node->isTerminal();
     7399
     7400                    node->removeWithoutChecks();
     7401
     7402                    if (isTerminal) {
     7403                        insertionSet.insertNode(nodeIndex, SpecNone, Unreachable, origin);
     7404                        break;
     7405                    }
     7406
     7407                    ++nodeIndex;
     7408                }
     7409
     7410                insertionSet.execute(block);
     7411
     7412                auto nodeAndIndex = block->findTerminal();
     7413                RELEASE_ASSERT(nodeAndIndex.node->op() == Unreachable);
     7414                block->resize(nodeAndIndex.index + 1);
     7415                break;
     7416            }
     7417        }
     7418    } else if (validationEnabled()) {
     7419        // Ensure our bookkeeping for ForceOSRExit nodes is working.
     7420        for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
     7421            for (Node* node : *block)
     7422                RELEASE_ASSERT(node->op() != ForceOSRExit);
     7423        }
     7424    }
     7425   
    72877426    m_graph.determineReachability();
    72887427    m_graph.killUnreachableBlocks();
Note: See TracChangeset for help on using the changeset viewer.