Ignore:
Timestamp:
Apr 23, 2019, 7:14:23 PM (6 years ago)
Author:
[email protected]
Message:

LICM incorrectly assumes it'll never insert a node which provably OSR exits
https://bugs.webkit.org/show_bug.cgi?id=196721
<rdar://problem/49556479>

Reviewed by Filip Pizlo.

JSTests:

  • stress/licm-should-handle-if-a-hoist-causes-a-provable-osr-exit.js: Added.

(foo):

Source/JavaScriptCore:

Previously, we assumed LICM could never hoist code that caused us
to provably OSR exit. This is a bad assumption, as we may very well
hoist such code. Obviously hoisting such code is not ideal. We shouldn't
hoist something we provably know will OSR exit. However, this is super rare,
and the phase is written in such a way where it's easier to gracefully
handle this case than to prevent us from hoisting such code.

If we wanted to ensure we never hoisted code that would provably exit, we'd
have to teach the phase to know when it inserted code that provably exits. I
saw two ways to do that:
1: Save and restore the AI state before actually hoisting.
2: Write an analysis that can determine if such a node would exit.

(1) is bad because it costs in memory and compile time. (2) will inevitably
have bugs as running into this condition is rare.

So instead of (1) or (2), I opted to have LICM gracefully handle when
it causes a provable exit. When we encounter this, we mark all blocks
in the loop as !cfaHasVisited and !cfaDidFinish.

  • dfg/DFGLICMPhase.cpp:

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

File:
1 edited

Legend:

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

    r242276 r244579  
    243243       
    244244        m_state.initializeTo(data.preHeader);
     245        ASSERT(m_state.isValid());
    245246        NodeOrigin originalOrigin = node->origin;
    246247        bool canSpeculateBlindly = !m_graph.hasGlobalExitSite(originalOrigin.semantic, HoistingFailed);
     
    264265
    265266        auto updateAbstractState = [&] {
     267            auto invalidate = [&] (const NaturalLoop* loop) {
     268                LoopData& data = m_data[loop->index()];
     269                data.preHeader->cfaDidFinish = false;
     270
     271                for (unsigned bodyIndex = loop->size(); bodyIndex--;) {
     272                    BasicBlock* block = loop->at(bodyIndex);
     273                    if (block != data.preHeader)
     274                        block->cfaHasVisited = false;
     275                    block->cfaDidFinish = false;
     276                }
     277            };
     278
    266279            // We can trust what AI proves about edge proof statuses when hoisting to the preheader.
    267280            m_state.trustEdgeProofs();
    268             for (unsigned i = 0; i < hoistedNodes.size(); ++i)
    269                 m_interpreter.execute(hoistedNodes[i]);
     281            for (unsigned i = 0; i < hoistedNodes.size(); ++i) {
     282                if (!m_interpreter.execute(hoistedNodes[i])) {
     283                    invalidate(loop);
     284                    return;
     285                }
     286            }
     287
    270288            // However, when walking various inner loops below, the proof status of
    271289            // an edge may be trivially true, even if it's not true in the preheader
     
    301319                    continue;
    302320                m_state.initializeTo(subPreHeader);
    303                 for (unsigned i = 0; i < hoistedNodes.size(); ++i)
    304                     m_interpreter.execute(hoistedNodes[i]);
     321                for (unsigned i = 0; i < hoistedNodes.size(); ++i) {
     322                    if (!m_interpreter.execute(hoistedNodes[i])) {
     323                        invalidate(subLoop);
     324                        break;
     325                    }
     326                }
    305327            }
    306328        };
Note: See TracChangeset for help on using the changeset viewer.