Ignore:
Timestamp:
May 19, 2016, 2:25:29 PM (9 years ago)
Author:
[email protected]
Message:

DFG::LICMPhase shouldn't hoist type checks unless it knows that the check will succeed at the loop pre-header
https://bugs.webkit.org/show_bug.cgi?id=144527

Reviewed by Saam Barati.

Source/JavaScriptCore:

This adds a control flow equivalence analysis (called ControlEquivalenceAnalysis) based on
dominator analysis over the backwards CFG. Two basic blocks are control flow equivalent if
the execution of one implies that the other one must also execute. It means that the two
blocks' forward and backward dominance are reciprocated: (A dom B and B backdom A) or (B dom
A and A backdom B). LICM now uses it to become more conservative about hoisting checks, if
this has caused problems in the past. If we hoist something that may exit from a block that
was not control equivalent to the pre-header then it's possible that the node's speculation
will fail even though it wouldn't have if it wasn't hoisted. So, we flag these nodes'
origins as being "wasHoisted" and we track all of their exits as "HoistingFailed". LICM will
turn off such speculative hoisting if the CodeBlock from which we are hoisting had the
HoistingFailed exit kind.

Note that this deliberately still allows us to hoist things that may exit even if they are
not control equivalent to the pre-header. This is necessary because the profitability of
hoisting is so huge in all of the cases that we're aware of that it's worth giving it a
shot.

This is neutral on macrobenchmarks since none of the benchmarks we track have a hoistable
operation that would exit only if hoisted. I added microbenchmarks to illustrate the problem
and two of them speed up by ~40% while one of them is neutral (Int52 saves us from having
problems on that program even though LICM previously did the wrong thing).

(JSC::exitKindToString):

  • bytecode/ExitKind.h:
  • dfg/DFGAtTailAbstractState.h:

(JSC::DFG::AtTailAbstractState::operator bool):
(JSC::DFG::AtTailAbstractState::initializeTo):

  • dfg/DFGBackwardsCFG.h: Added.

(JSC::DFG::BackwardsCFG::BackwardsCFG):

  • dfg/DFGBackwardsDominators.h: Added.

(JSC::DFG::BackwardsDominators::BackwardsDominators):

  • dfg/DFGCommon.h:

(JSC::DFG::checkAndSet): Deleted.

  • dfg/DFGControlEquivalenceAnalysis.h: Added.

(JSC::DFG::ControlEquivalenceAnalysis::ControlEquivalenceAnalysis):
(JSC::DFG::ControlEquivalenceAnalysis::dominatesEquivalently):
(JSC::DFG::ControlEquivalenceAnalysis::areEquivalent):

  • dfg/DFGGraph.cpp:

(JSC::DFG::Graph::dump):
(JSC::DFG::Graph::dumpBlockHeader):
(JSC::DFG::Graph::invalidateCFG):
(JSC::DFG::Graph::substituteGetLocal):
(JSC::DFG::Graph::handleAssertionFailure):
(JSC::DFG::Graph::ensureDominators):
(JSC::DFG::Graph::ensurePrePostNumbering):
(JSC::DFG::Graph::ensureNaturalLoops):
(JSC::DFG::Graph::ensureBackwardsCFG):
(JSC::DFG::Graph::ensureBackwardsDominators):
(JSC::DFG::Graph::ensureControlEquivalenceAnalysis):
(JSC::DFG::Graph::methodOfGettingAValueProfileFor):

  • dfg/DFGGraph.h:

(JSC::DFG::Graph::hasDebuggerEnabled):

  • dfg/DFGInPlaceAbstractState.h:

(JSC::DFG::InPlaceAbstractState::operator bool):
(JSC::DFG::InPlaceAbstractState::createValueForNode):
(JSC::DFG::InPlaceAbstractState::forNode):

  • dfg/DFGLICMPhase.cpp:

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

  • dfg/DFGMayExit.cpp:

(JSC::DFG::mayExit):

  • dfg/DFGMayExit.h:
  • dfg/DFGNode.h:
  • dfg/DFGNodeOrigin.cpp:

(JSC::DFG::NodeOrigin::dump):

  • dfg/DFGNodeOrigin.h:

(JSC::DFG::NodeOrigin::takeValidExit):
(JSC::DFG::NodeOrigin::withWasHoisted):
(JSC::DFG::NodeOrigin::forInsertingAfter):

  • dfg/DFGNullAbstractState.h: Added.

(JSC::DFG::NullAbstractState::NullAbstractState):
(JSC::DFG::NullAbstractState::operator bool):
(JSC::DFG::NullAbstractState::forNode):

  • dfg/DFGOSRExit.cpp:

(JSC::DFG::OSRExit::OSRExit):

  • dfg/DFGOSRExitBase.cpp:

(JSC::DFG::OSRExitBase::considerAddingAsFrequentExitSiteSlow):

  • dfg/DFGOSRExitBase.h:

(JSC::DFG::OSRExitBase::OSRExitBase):

  • dfg/DFGTypeCheckHoistingPhase.cpp:

(JSC::DFG::TypeCheckHoistingPhase::run):

  • ftl/FTLOSRExit.cpp:

(JSC::FTL::OSRExitDescriptor::prepareOSRExitHandle):
(JSC::FTL::OSRExit::OSRExit):

  • ftl/FTLOSRExit.h:

Source/WTF:

This adds an adaptor for graphs called BackwardsGraph. The WTF graph framework is based on
passing around a Graph template argument that follows the protocol shared by DFG::CFG,
B3::CFG, and Air::CFG. These graphs always have a single root node but may have many leaf
nodes. This new BackwardsGraph adaptor reverses the graph by creating a synthetic return
node that it uses as the root in the inverted graph. This currently may resort to some
heuristics in programs that have an infinite loop, but other than that, it'll work well in
the general case.

This allows us to say Dominators<BackwardsGraph<some graph type>> as a way of computing
backwards dominators, which then allows us to easily answer control flow equivalence
queries.

  • CMakeLists.txt:
  • WTF.xcodeproj/project.pbxproj:
  • wtf/BackwardsGraph.h: Added.

(WTF::BackwardsGraph::Node::Node):
(WTF::BackwardsGraph::Node::root):
(WTF::BackwardsGraph::Node::operator==):
(WTF::BackwardsGraph::Node::operator!=):
(WTF::BackwardsGraph::Node::operator bool):
(WTF::BackwardsGraph::Node::isRoot):
(WTF::BackwardsGraph::Node::node):
(WTF::BackwardsGraph::Set::Set):
(WTF::BackwardsGraph::Set::add):
(WTF::BackwardsGraph::Set::remove):
(WTF::BackwardsGraph::Set::contains):
(WTF::BackwardsGraph::Set::dump):
(WTF::BackwardsGraph::Map::Map):
(WTF::BackwardsGraph::Map::clear):
(WTF::BackwardsGraph::Map::size):
(WTF::BackwardsGraph::Map::operator[]):
(WTF::BackwardsGraph::BackwardsGraph):
(WTF::BackwardsGraph::root):
(WTF::BackwardsGraph::newMap):
(WTF::BackwardsGraph::successors):
(WTF::BackwardsGraph::predecessors):
(WTF::BackwardsGraph::index):
(WTF::BackwardsGraph::node):
(WTF::BackwardsGraph::numNodes):
(WTF::BackwardsGraph::dump):

  • wtf/Dominators.h:

(WTF::Dominators::Dominators):
(WTF::Dominators::dump):
(WTF::Dominators::LengauerTarjan::computeDepthFirstPreNumbering):

  • wtf/GraphNodeWorklist.h:

(WTF::GraphNodeWith::GraphNodeWith):
(WTF::GraphNodeWith::operator bool):

  • wtf/StdLibExtras.h:

(WTF::callStatelessLambda):
(WTF::checkAndSet):

LayoutTests:

Add tests for LICM hoisting things that would only exit if hoisted.

  • js/regress/licm-dragons-expected.txt: Added.
  • js/regress/licm-dragons-out-of-bounds-expected.txt: Added.
  • js/regress/licm-dragons-out-of-bounds.html: Added.
  • js/regress/licm-dragons-overflow-expected.txt: Added.
  • js/regress/licm-dragons-overflow.html: Added.
  • js/regress/licm-dragons.html: Added.
  • js/regress/script-tests/licm-dragons-out-of-bounds.js: Added.

(foo):

  • js/regress/script-tests/licm-dragons-overflow.js: Added.

(foo):

  • js/regress/script-tests/licm-dragons.js: Added.

(foo):

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/dfg/DFGLICMPhase.cpp

    r198364 r201182  
    11/*
    2  * Copyright (C) 2013-2015 Apple Inc. All rights reserved.
     2 * Copyright (C) 2013-2016 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    3434#include "DFGClobberSet.h"
    3535#include "DFGClobberize.h"
     36#include "DFGControlEquivalenceAnalysis.h"
    3637#include "DFGEdgeDominates.h"
    3738#include "DFGGraph.h"
    3839#include "DFGInsertionSet.h"
     40#include "DFGMayExit.h"
    3941#include "DFGNaturalLoops.h"
    4042#include "DFGPhase.h"
     
    7577        m_graph.ensureDominators();
    7678        m_graph.ensureNaturalLoops();
     79        m_graph.ensureControlEquivalenceAnalysis();
    7780
    7881        if (verbose) {
     
    261264        // https://bugs.webkit.org/show_bug.cgi?id=144525
    262265       
    263         // FIXME: If a node has a type check - even something like a CheckStructure - then we should
    264         // only hoist the node if we know that it will execute on every loop iteration or if we know
    265         // that the type check will always succeed at the loop pre-header through some other means
    266         // (like looking at prediction propagation results). Otherwise, we might make a mistake like
    267         // this:
    268         //
    269         // var o = ...; // sometimes null and sometimes an object with structure S1.
    270         // for (...) {
    271         //     if (o)
    272         //         ... = o.f; // CheckStructure and GetByOffset, which we will currently hoist.
    273         // }
    274         //
    275         // When we encounter such code, we'll hoist the CheckStructure and GetByOffset and then we
    276         // will have a recompile. We'll then end up thinking that the get_by_id needs to be
    277         // polymorphic, which is false.
    278         //
    279         // We can counter this by either having a control flow equivalence check, or by consulting
    280         // prediction propagation to see if the check would always succeed. Prediction propagation
    281         // would not be enough for things like:
    282         //
    283         // var p = ...; // some boolean predicate
    284         // var o = {};
    285         // if (p)
    286         //     o.f = 42;
    287         // for (...) {
    288         //     if (p)
    289         //         ... = o.f;
    290         // }
    291         //
    292         // Prediction propagation can't tell us anything about the structure, and the CheckStructure
    293         // will appear to be hoistable because the loop doesn't clobber structures. The cell check
    294         // in the CheckStructure will be hoistable though, since prediction propagation can tell us
    295         // that o is always SpecFinalObject. In cases like this, control flow equivalence is the
    296         // only effective guard.
    297         //
    298         // https://bugs.webkit.org/show_bug.cgi?id=144527
    299        
    300266        if (readsOverlap(m_graph, node, data.writes)) {
    301267            if (verbose) {
     
    312278                dataLog(
    313279                    "    Not hoisting ", node, " because it isn't safe to execute.\n");
     280            }
     281            return false;
     282        }
     283       
     284        NodeOrigin originalOrigin = node->origin;
     285
     286        // NOTE: We could just use BackwardsDominators here directly, since we already know that the
     287        // preHeader dominates fromBlock. But we wouldn't get anything from being so clever, since
     288        // dominance checks are O(1) and only a few integer compares.
     289        bool addsBlindSpeculation = mayExit(m_graph, node, m_state)
     290            && !m_graph.m_controlEquivalenceAnalysis->dominatesEquivalently(data.preHeader, fromBlock);
     291       
     292        if (addsBlindSpeculation
     293            && m_graph.baselineCodeBlockFor(originalOrigin.semantic)->hasExitSite(FrequentExitSite(HoistingFailed))) {
     294            if (verbose) {
     295                dataLog(
     296                    "    Not hoisting ", node, " because it may exit and the pre-header (",
     297                    *data.preHeader, ") is not control equivalent to the node's original block (",
     298                    *fromBlock, ") and hoisting had previously failed.\n");
    314299            }
    315300            return false;
     
    327312        data.preHeader->insertBeforeTerminal(node);
    328313        node->owner = data.preHeader;
    329         NodeOrigin originalOrigin = node->origin;
    330         node->origin = data.preHeader->terminal()->origin.withSemantic(node->origin.semantic);
     314        NodeOrigin terminalOrigin = data.preHeader->terminal()->origin;
     315        node->origin = terminalOrigin.withSemantic(node->origin.semantic);
     316        node->origin.wasHoisted |= addsBlindSpeculation;
    331317       
    332318        // Modify the states at the end of the preHeader of the loop we hoisted to,
Note: See TracChangeset for help on using the changeset viewer.