Ignore:
Timestamp:
Aug 27, 2020, 10:10:18 AM (5 years ago)
Author:
[email protected]
Message:

OSR availability validation should run for any node with exitOK
https://bugs.webkit.org/show_bug.cgi?id=215672

Reviewed by Saam Barati.

Currently we only validate OSR exit availability if a node would
say mayExit(graph, node) != DoesNotExit and the node is marked
as exitOK. However, it would be perfectly valid to insert a node
that exits anywhere we have a node marked exitOK. So with this
patch we now validate all places where it would ever be possible
to OSR exit.

Relaxing our criteria revealed a number of bugs however. Which I
will describe below in, IMO, increasing complexity/subtly.

First, we currently don't mark arity fixup during inlining as not
exitOK. However, since our arity code says its code origin is
OpEnter, we assume arity fixup has already happened.

Second, OpGetScope, should not mark its first argument as used
since it's not actually used. This is problematic because we could
have a loop where OpGetScope is the first bytecode, namely when
doing tail recursive inlining. If we were in that position, there
could be a local that was used at a merge point at the loop
backedge that had two MovHint defs from both predecessors. In DFG
IR this would look like:

BB#1:
@1: MovHint(Undefined, loc1)
...
Jump(#2)

BB#2:
... loc1 is live here in bytecode
@2: MovHint(@scopeObject, loc1)
@3: SetLocal(@scopeObject, loc1)
Branch(#3, #4)
#4 is the successor of the tail call loop

BB#3:
@4 MovHint(Undefined, loc1)
...
Jump(#2)

When we do CPS conversion the MovHints at @1 and @4 will be seen
as different variables (there's no GetLocal). Then, after, during
SSA conversion we won't insert a phi connecting them, making the
argument to OpGetScope, in this case loc1, unrecoverable there are
conflicting nodes and the value isn't saved on the stack.

There were also issues with MovHintRemoval Phase but rather than
fix them we opted to just remove the phase as it didn't show any
performance impact. I'll describe the issues I found below for
completeness, however.

Third, MovHint removal phase had a bug where it would not mark
sections where a zombied MovHint has yet to be killed as not
exitOK. So in theory another phase could come along and insert an
exiting node there.

Fourth, MovHint removal phase had a second bug where a MovHint
that was not killed in the current block would be zombied, which
is wrong for SSA. It's wrong because the MovHinted value could
still be live for OSR exit in a successor block.

Lastly, this patch adds some new verbose options as well as the ability to
dump a DFG::BasicBlock without dereferencing it.

  • bytecode/BytecodeUseDef.cpp:

(JSC::computeUsesForBytecodeIndexImpl):

  • dfg/DFGBasicBlock.cpp:

(WTF::printInternal):

  • dfg/DFGBasicBlock.h:
  • dfg/DFGByteCodeParser.cpp:

(JSC::DFG::ByteCodeParser::inlineCall):

  • dfg/DFGCPSRethreadingPhase.cpp:

(JSC::DFG::CPSRethreadingPhase::propagatePhis):

  • dfg/DFGEpoch.h:

(JSC::DFG::Epoch::operator bool const):

  • dfg/DFGOSRAvailabilityAnalysisPhase.cpp:

(JSC::DFG::OSRAvailabilityAnalysisPhase::run):

  • dfg/DFGSSACalculator.cpp:

(JSC::DFG::SSACalculator::dump const):

File:
1 edited

Legend:

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

    r266095 r266242  
    135135                for (unsigned nodeIndex = 0; nodeIndex < block->size(); ++nodeIndex) {
    136136                    Node* node = block->at(nodeIndex);
    137                     // FIXME: The mayExit status of a node doesn't seem like it should mean we don't need to have everything available.
    138                     if (mayExit(m_graph, node) != DoesNotExit && node->origin.exitOK) {
     137                    if (node->origin.exitOK) {
    139138                        // If we're allowed to exit here, the heap must be in a state
    140139                        // where exiting wouldn't crash. These particular fields are
     
    144143
    145144                        CodeOrigin exitOrigin = node->origin.forExit;
    146                         AvailabilityMap& availabilityMap = calculator.m_availability;
     145                        // FIXME: availabilityMap seems like it should be able to be a reference to the calculator's map. https://bugs.webkit.org/show_bug.cgi?id=215675
     146                        AvailabilityMap availabilityMap = calculator.m_availability;
    147147                        availabilityMap.pruneByLiveness(m_graph, exitOrigin);
    148148
Note: See TracChangeset for help on using the changeset viewer.