Changeset 232075 in webkit


Ignore:
Timestamp:
May 22, 2018 12:47:39 PM (6 years ago)
Author:
sbarati@apple.com
Message:

DFG::LICMPhase should attempt to hoist edge type checks if hoisting the whole node fails
https://bugs.webkit.org/show_bug.cgi?id=144525

Reviewed by Filip Pizlo.

This patch teaches LICM to fall back to hoisting a node's type checks when
hoisting the entire node fails.

This patch follow the same principles we use when deciding to hoist nodes in general:

  • If the pre header is control equivalent to where the current check is, we

go ahead and hoist the check.

  • Otherwise, if hoisting hasn't failed before, we go ahead and gamble and

hoist the check. If hoisting failed in the past, we will not hoist the check.

  • dfg/DFGLICMPhase.cpp:

(JSC::DFG::LICMPhase::attemptHoist):

  • dfg/DFGUseKind.h:

(JSC::DFG::checkMayCrashIfInputIsEmpty):

Location:
trunk/Source/JavaScriptCore
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/ChangeLog

    r232074 r232075  
     12018-05-22  Saam Barati  <sbarati@apple.com>
     2
     3        DFG::LICMPhase should attempt to hoist edge type checks if hoisting the whole node fails
     4        https://bugs.webkit.org/show_bug.cgi?id=144525
     5
     6        Reviewed by Filip Pizlo.
     7
     8        This patch teaches LICM to fall back to hoisting a node's type checks when
     9        hoisting the entire node fails.
     10       
     11        This patch follow the same principles we use when deciding to hoist nodes in general:
     12        - If the pre header is control equivalent to where the current check is, we
     13        go ahead and hoist the check.
     14        - Otherwise, if hoisting hasn't failed before, we go ahead and gamble and
     15        hoist the check. If hoisting failed in the past, we will not hoist the check.
     16
     17        * dfg/DFGLICMPhase.cpp:
     18        (JSC::DFG::LICMPhase::attemptHoist):
     19        * dfg/DFGUseKind.h:
     20        (JSC::DFG::checkMayCrashIfInputIsEmpty):
     21
    1222018-05-21  Filip Pizlo  <fpizlo@apple.com>
    223
  • trunk/Source/JavaScriptCore/dfg/DFGLICMPhase.cpp

    r232000 r232075  
    211211            for (unsigned nodeIndex = 0; nodeIndex < block->size(); ++nodeIndex) {
    212212                Node*& nodeRef = block->at(nodeIndex);
    213                 if (doesWrites(m_graph, nodeRef)) {
    214                     if (verbose)
    215                         dataLog("    Not hoisting ", nodeRef, " because it writes things.\n");
    216                     continue;
    217                 }
    218 
    219213                for (unsigned stackIndex = loopStack.size(); stackIndex--;)
    220214                    changed |= attemptHoist(block, nodeRef, loopStack[stackIndex]);
     
    243237        }
    244238       
     239        m_state.initializeTo(data.preHeader);
     240        NodeOrigin originalOrigin = node->origin;
     241        bool canSpeculateBlindly = !m_graph.hasGlobalExitSite(originalOrigin.semantic, HoistingFailed);
     242
     243        // NOTE: We could just use BackwardsDominators here directly, since we already know that the
     244        // preHeader dominates fromBlock. But we wouldn't get anything from being so clever, since
     245        // dominance checks are O(1) and only a few integer compares.
     246        bool isControlEquivalent = m_graph.m_controlEquivalenceAnalysis->dominatesEquivalently(data.preHeader, fromBlock);
     247
     248        bool addsBlindSpeculation = !isControlEquivalent;
     249        NodeOrigin terminalOrigin = data.preHeader->terminal()->origin;
     250        Vector<Node*, 2> hoistedNodes; // This is sorted in the program order they will appear in the basic block we're hoisting to.
     251
     252        auto insertHoistedNode = [&] (Node* node) {
     253            data.preHeader->insertBeforeTerminal(node);
     254            node->owner = data.preHeader;
     255            node->origin = terminalOrigin.withSemantic(node->origin.semantic);
     256            node->origin.wasHoisted |= addsBlindSpeculation;
     257            hoistedNodes.append(node);
     258        };
     259
     260        auto updateAbstractState = [&] {
     261            // We can trust what AI proves about edge proof statuses when hoisting to the preheader.
     262            m_state.trustEdgeProofs();
     263            for (unsigned i = 0; i < hoistedNodes.size(); ++i)
     264                m_interpreter.execute(hoistedNodes[i]);
     265            // However, when walking various inner loops below, the proof status of
     266            // an edge may be trivially true, even if it's not true in the preheader
     267            // we hoist to. We don't allow the below node executions to change the
     268            // state of edge proofs. An example of where a proof is trivially true
     269            // is if we have two loops, L1 and L2, where L2 is nested inside L1. The
     270            // header for L1 dominates L2. We hoist a Check from L1's header into L1's
     271            // preheader. However, inside L2's preheader, we can't trust that AI will
     272            // tell us this edge is proven. It's proven in L2's preheader because L2
     273            // is dominated by L1's header. However, the edge is not guaranteed to be
     274            // proven inside L1's preheader.
     275            m_state.dontTrustEdgeProofs();
     276
     277            // Modify the states at the end of the preHeader of the loop we hoisted to,
     278            // and all pre-headers inside the loop. This isn't a stability bottleneck right now
     279            // because most loops are small and most blocks belong to few loops.
     280            for (unsigned bodyIndex = loop->size(); bodyIndex--;) {
     281                BasicBlock* subBlock = loop->at(bodyIndex);
     282                const NaturalLoop* subLoop = m_graph.m_ssaNaturalLoops->headerOf(subBlock);
     283                if (!subLoop)
     284                    continue;
     285                BasicBlock* subPreHeader = m_data[subLoop->index()].preHeader;
     286                // We may not have given this loop a pre-header because either it didn't have exitOK
     287                // or the header had multiple predecessors that it did not dominate. In that case the
     288                // loop wouldn't be a hoisting candidate anyway, so we don't have to do anything.
     289                if (!subPreHeader)
     290                    continue;
     291                // The pre-header's tail may be unreachable, in which case we have nothing to do.
     292                if (!subPreHeader->cfaDidFinish)
     293                    continue;
     294                // We handled this above.
     295                if (subPreHeader == data.preHeader)
     296                    continue;
     297                m_state.initializeTo(subPreHeader);
     298                for (unsigned i = 0; i < hoistedNodes.size(); ++i)
     299                    m_interpreter.execute(hoistedNodes[i]);
     300            }
     301        };
     302       
     303        auto tryHoistChecks = [&] {
     304            if (addsBlindSpeculation && !canSpeculateBlindly)
     305                return false;
     306
     307            ASSERT(hoistedNodes.isEmpty());
     308
     309            Vector<Edge, 3> checks;
     310            m_graph.doToChildren(node, [&] (Edge edge) {
     311                if (!m_graph.m_ssaDominators->dominates(edge.node()->owner, data.preHeader))
     312                    return;
     313
     314                if (!edge.willHaveCheck())
     315                    return;
     316
     317                if ((m_state.forNode(edge).m_type & SpecEmpty) && checkMayCrashIfInputIsEmpty(edge.useKind())) {
     318                    if (!canSpeculateBlindly)
     319                        return;
     320                    Node* checkNotEmpty = m_graph.addNode(CheckNotEmpty, originalOrigin, Edge(edge.node(), UntypedUse));
     321                    insertHoistedNode(checkNotEmpty);
     322                }
     323
     324                checks.append(edge);
     325            });
     326
     327            if (checks.isEmpty())
     328                return false;
     329
     330            AdjacencyList children;
     331            NodeType checkOp = Check;
     332            if (checks.size() <= AdjacencyList::Size) {
     333                children = AdjacencyList(AdjacencyList::Fixed);
     334                for (unsigned i = 0; i < checks.size(); ++i)
     335                    children.setChild(i, checks[i]);
     336            } else {
     337                checkOp = CheckVarargs;
     338                unsigned firstChild = m_graph.m_varArgChildren.size();
     339                for (Edge edge : checks)
     340                    m_graph.m_varArgChildren.append(edge);
     341                children = AdjacencyList(AdjacencyList::Variable, firstChild, checks.size());
     342            }
     343
     344            Node* check = m_graph.addNode(checkOp, originalOrigin, children);
     345            insertHoistedNode(check);
     346            updateAbstractState();
     347
     348            if (verbose)
     349                dataLogLn("    Hoisted some checks from ", node, " and created a new Check ", check, ". Hoisted from ", *fromBlock, " to ", *data.preHeader);
     350
     351            return true;
     352        };
     353
    245354        if (!edgesDominate(m_graph, node, data.preHeader)) {
    246355            if (verbose) {
     
    248357                    "    Not hoisting ", node, " because it isn't loop invariant.\n");
    249358            }
    250             return false;
    251         }
    252        
    253         // FIXME: At this point if the hoisting of the full node fails but the node has type checks,
    254         // we could still hoist just the checks.
    255         // https://bugs.webkit.org/show_bug.cgi?id=144525
    256        
     359            return tryHoistChecks();
     360        }
     361
     362        if (doesWrites(m_graph, node)) {
     363            if (verbose)
     364                dataLog("    Not hoisting ", node, " because it writes things.\n");
     365            return tryHoistChecks();
     366        }
     367
     368        // It's not safe to consult the AbstractState inside mayExit until we prove all edges
     369        // dominate the pre-header we're hoisting to. We are more conservative above when assigning
     370        // to this variable since we hadn't yet proven all edges dominate the pre-header. Above, we
     371        // just assume mayExit is true. We refine that here since we can now consult the AbstractState.
     372        addsBlindSpeculation = mayExit(m_graph, node, m_state) && !isControlEquivalent;
     373
    257374        if (readsOverlap(m_graph, node, data.writes)) {
    258375            if (verbose) {
     
    261378                    " because it reads things that the loop writes.\n");
    262379            }
    263             return false;
    264         }
    265        
    266         m_state.initializeTo(data.preHeader);
    267 
    268         NodeOrigin originalOrigin = node->origin;
    269         bool canSpeculateBlindly = !m_graph.hasGlobalExitSite(originalOrigin.semantic, HoistingFailed);
    270 
    271         // NOTE: We could just use BackwardsDominators here directly, since we already know that the
    272         // preHeader dominates fromBlock. But we wouldn't get anything from being so clever, since
    273         // dominance checks are O(1) and only a few integer compares.
    274         bool addsBlindSpeculation = mayExit(m_graph, node, m_state)
    275             && !m_graph.m_controlEquivalenceAnalysis->dominatesEquivalently(data.preHeader, fromBlock);
     380            return tryHoistChecks();
     381        }
    276382       
    277383        if (addsBlindSpeculation && !canSpeculateBlindly) {
     
    282388                    *fromBlock, ") and hoisting had previously failed.\n");
    283389            }
    284             return false;
    285         }
    286        
    287         // For abstract interpretation, these are in the reverse order that they appear in this
    288         // vector.
    289         Vector<Node*, 2> hoistedNodesReverse;
    290         hoistedNodesReverse.append(node);
    291 
    292         NodeOrigin terminalOrigin = data.preHeader->terminal()->origin;
    293        
    294         auto insertHoistedNode = [&] (Node* node) {
    295             data.preHeader->insertBeforeTerminal(node);
    296             node->owner = data.preHeader;
    297             node->origin = terminalOrigin.withSemantic(node->origin.semantic);
    298             node->origin.wasHoisted |= addsBlindSpeculation;
    299         };
     390            return tryHoistChecks();
     391        }
    300392       
    301393        if (!safeToExecute(m_state, m_graph, node)) {
     
    316408                        Node* check = m_graph.addNode(CheckNotEmpty, originalOrigin, Edge(edge.node(), UntypedUse));
    317409                        insertHoistedNode(check);
    318                         hoistedNodesReverse.append(check);
    319410                    });
    320411            } else {
     
    323414                        "    Not hoisting ", node, " because it isn't safe to execute.\n");
    324415                }
    325                 return false;
     416                return tryHoistChecks();
    326417            }
    327418        }
     
    334425
    335426        insertHoistedNode(node);
    336        
    337         // We can trust what AI proves about edge proof statuses when hoisting to the preheader.
    338         m_state.trustEdgeProofs();
    339         m_state.initializeTo(data.preHeader);
    340         for (unsigned i = hoistedNodesReverse.size(); i--;)
    341             m_interpreter.execute(hoistedNodesReverse[i]);
    342         // However, when walking various inner loops below, the proof status of
    343         // an edge may be trivially true, even if it's not true in the preheader
    344         // we hoist to. We don't allow the below node executions to change the
    345         // state of edge proofs. An example of where a proof is trivially true
    346         // is if we have two loops, L1 and L2, where L2 is nested inside L1. The
    347         // header for L1 dominates L2. We hoist a Check from L1's header into L1's
    348         // preheader. However, inside L2's preheader, we can't trust that AI will
    349         // tell us this edge is proven. It's proven in L2's preheader because L2
    350         // is dominated by L1's header. However, the edge is not guaranteed to be
    351         // proven inside L1's preheader.
    352         m_state.dontTrustEdgeProofs();
    353 
    354         // Modify the states at the end of the preHeader of the loop we hoisted to,
    355         // and all pre-headers inside the loop. This isn't a stability bottleneck right now
    356         // because most loops are small and most blocks belong to few loops.
    357         for (unsigned bodyIndex = loop->size(); bodyIndex--;) {
    358             BasicBlock* subBlock = loop->at(bodyIndex);
    359             const NaturalLoop* subLoop = m_graph.m_ssaNaturalLoops->headerOf(subBlock);
    360             if (!subLoop)
    361                 continue;
    362             BasicBlock* subPreHeader = m_data[subLoop->index()].preHeader;
    363             // We may not have given this loop a pre-header because either it didn't have exitOK
    364             // or the header had multiple predecessors that it did not dominate. In that case the
    365             // loop wouldn't be a hoisting candidate anyway, so we don't have to do anything.
    366             if (!subPreHeader)
    367                 continue;
    368             // The pre-header's tail may be unreachable, in which case we have nothing to do.
    369             if (!subPreHeader->cfaDidFinish)
    370                 continue;
    371             // We handled this above.
    372             if (subPreHeader == data.preHeader)
    373                 continue;
    374             m_state.initializeTo(subPreHeader);
    375             for (unsigned i = hoistedNodesReverse.size(); i--;)
    376                 m_interpreter.execute(hoistedNodesReverse[i]);
    377         }
     427        updateAbstractState();
    378428
    379429        if (node->flags() & NodeHasVarArgs)
  • trunk/Source/JavaScriptCore/dfg/DFGUseKind.h

    r230516 r232075  
    300300}
    301301
     302inline bool checkMayCrashIfInputIsEmpty(UseKind kind)
     303{
     304#if USE(JSVALUE64)
     305    switch (kind) {
     306    case UntypedUse:
     307    case Int32Use:
     308    case KnownInt32Use:
     309    case AnyIntUse:
     310    case NumberUse:
     311    case BooleanUse:
     312    case KnownBooleanUse:
     313    case CellUse:
     314    case KnownCellUse:
     315    case CellOrOtherUse:
     316    case OtherUse:
     317    case MiscUse:
     318    case NotCellUse:
     319        return false;
     320    default:
     321        return true;
     322    }
     323#else
     324    UNUSED_PARAM(kind);
     325    return true;
     326#endif
     327}
     328
    302329} } // namespace JSC::DFG
    303330
Note: See TracChangeset for help on using the changeset viewer.