Changeset 240364 in webkit


Ignore:
Timestamp:
Jan 23, 2019 4:17:15 PM (5 years ago)
Author:
ysuzuki@apple.com
Message:

[DFG] AvailabilityMap::pruneByLiveness should make non-live operands Availability::unavailable instead of Availability()
https://bugs.webkit.org/show_bug.cgi?id=193711
<rdar://problem/47250262>

Reviewed by Saam Barati.

JSTests:

  • stress/availability-was-cleared-when-locals-are-not-live.js: Added.

(shouldBe):
(foo):
(bar):
(baz):

Source/JavaScriptCore:

When pruning OSR Availability based on bytecode liveness, we accidentally clear the Availability (making it DeadFlush) instead of
making it Availability::unavailable() (Making it ConflictingFlush). In OSRAvailabilityAnalysisPhase, we perform forward analysis.
We first clear all the availability of basic blocks DeadFlush, which is an empty set. And then, we set operands in the root block
ConflictingFlush. In this forward analysis, DeadFlush is BOTTOM, and ConflictingFlush is TOP. Then, we propagate information by
merging availability until we reach to the fixed-point. As an optimization, we perform "pruning" of the availability in the head
of the basic blocks. We remove availabilities of operands which are not live in the bytecode liveness at the head of the basic block.
The problem is, when removing availabilities, we set DeadFlush for them instead of ConflictingFlush. Basically, it means that we set
BOTTOM (an empty set) instead of TOP. Let's consider the following simple example. We have 6 basic blocks, and they are connected
as follows.

BB0 -> BB1 -> BB2 -> BB4

| \
v > BB3 /

BB5

And consider about loc1 in FTL, which is required to be recovered in BB4's OSR exit.

BB0 does nothing

head: loc1 is dead
tail: loc1 is dead

BB1 has MovHint @1, loc1

head: loc1 is dead
tail: loc1 is live

BB2 does nothing

head: loc1 is live
tail: loc1 is live

BB3 has PutStack @1, loc1

head: loc1 is live
tail: loc1 is live

BB4 has OSR exit using loc1

head: loc1 is live
tail: loc1 is live (in bytecode)

BB5 does nothing

head: loc1 is dead
tail: loc1 is dead

In our OSR Availability analysis, we always prune loc1 result in BB1's head since its head says "loc1 is dead".
But at that time, we clear the availability for loc1, which makes it DeadFlush, instead of making it ConflictingFlush.

So, the flush format of loc1 in each tail of BB is like this.

BB0

ConflictingFlush (because all the local operands are initialized with ConflictingFlush)

BB1

DeadFlush+@1 (pruning clears it)

BB2

DeadFlush+@1 (since it is propagated from BB1)

BB3

FlushedJSValue+@1 with loc1 (since it has PutStack)

BB4

FlushedJSValue+@1 with loc1 (since MERGE(DeadFlush, FlushedJSValue) = FlushedJSValue)

BB5

DeadFlush (pruning clears it)

Then, if we go the path BB0->BB1->BB2->BB4, we read the value from the stack while it is not flushed.
The correct fix is making availability "unavailable" when pruning based on bytecode liveness.

  • dfg/DFGAvailabilityMap.cpp:

(JSC::DFG::AvailabilityMap::pruneByLiveness): When pruning availability, we first set all the operands Availability::unavailable(),
and copy the calculated value from the current availability map.

  • dfg/DFGOSRAvailabilityAnalysisPhase.cpp:

(JSC::DFG::OSRAvailabilityAnalysisPhase::run): Add logging things for debugging.

Location:
trunk
Files:
1 added
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/JSTests/ChangeLog

    r240329 r240364  
     12019-01-23  Yusuke Suzuki  <ysuzuki@apple.com>
     2
     3        [DFG] AvailabilityMap::pruneByLiveness should make non-live operands Availability::unavailable instead of Availability()
     4        https://bugs.webkit.org/show_bug.cgi?id=193711
     5        <rdar://problem/47250262>
     6
     7        Reviewed by Saam Barati.
     8
     9        * stress/availability-was-cleared-when-locals-are-not-live.js: Added.
     10        (shouldBe):
     11        (foo):
     12        (bar):
     13        (baz):
     14
    1152019-01-22  Yusuke Suzuki  <ysuzuki@apple.com>
    216
  • trunk/Source/JavaScriptCore/ChangeLog

    r240335 r240364  
     12019-01-23  Yusuke Suzuki  <ysuzuki@apple.com>
     2
     3        [DFG] AvailabilityMap::pruneByLiveness should make non-live operands Availability::unavailable instead of Availability()
     4        https://bugs.webkit.org/show_bug.cgi?id=193711
     5        <rdar://problem/47250262>
     6
     7        Reviewed by Saam Barati.
     8
     9        When pruning OSR Availability based on bytecode liveness, we accidentally clear the Availability (making it DeadFlush) instead of
     10        making it Availability::unavailable() (Making it ConflictingFlush). In OSRAvailabilityAnalysisPhase, we perform forward analysis.
     11        We first clear all the availability of basic blocks DeadFlush, which is an empty set. And then, we set operands in the root block
     12        ConflictingFlush. In this forward analysis, DeadFlush is BOTTOM, and ConflictingFlush is TOP. Then, we propagate information by
     13        merging availability until we reach to the fixed-point. As an optimization, we perform "pruning" of the availability in the head
     14        of the basic blocks. We remove availabilities of operands which are not live in the bytecode liveness at the head of the basic block.
     15        The problem is, when removing availabilities, we set DeadFlush for them instead of ConflictingFlush. Basically, it means that we set
     16        BOTTOM (an empty set) instead of TOP. Let's consider the following simple example. We have 6 basic blocks, and they are connected
     17        as follows.
     18
     19            BB0 -> BB1 -> BB2 -> BB4
     20             |        \        ^
     21             v          > BB3 /
     22            BB5
     23
     24        And consider about loc1 in FTL, which is required to be recovered in BB4's OSR exit.
     25
     26            BB0 does nothing
     27                head: loc1 is dead
     28                tail: loc1 is dead
     29
     30            BB1 has MovHint @1, loc1
     31                head: loc1 is dead
     32                tail: loc1 is live
     33
     34            BB2 does nothing
     35                head: loc1 is live
     36                tail: loc1 is live
     37
     38            BB3 has PutStack @1, loc1
     39                head: loc1 is live
     40                tail: loc1 is live
     41
     42            BB4 has OSR exit using loc1
     43                head: loc1 is live
     44                tail: loc1 is live (in bytecode)
     45
     46            BB5 does nothing
     47                head: loc1 is dead
     48                tail: loc1 is dead
     49
     50        In our OSR Availability analysis, we always prune loc1 result in BB1's head since its head says "loc1 is dead".
     51        But at that time, we clear the availability for loc1, which makes it DeadFlush, instead of making it ConflictingFlush.
     52
     53        So, the flush format of loc1 in each tail of BB is like this.
     54
     55            BB0
     56                ConflictingFlush (because all the local operands are initialized with ConflictingFlush)
     57            BB1
     58                DeadFlush+@1 (pruning clears it)
     59            BB2
     60                DeadFlush+@1 (since it is propagated from BB1)
     61            BB3
     62                FlushedJSValue+@1 with loc1 (since it has PutStack)
     63            BB4
     64                FlushedJSValue+@1 with loc1 (since MERGE(DeadFlush, FlushedJSValue) = FlushedJSValue)
     65            BB5
     66                DeadFlush (pruning clears it)
     67
     68        Then, if we go the path BB0->BB1->BB2->BB4, we read the value from the stack while it is not flushed.
     69        The correct fix is making availability "unavailable" when pruning based on bytecode liveness.
     70
     71        * dfg/DFGAvailabilityMap.cpp:
     72        (JSC::DFG::AvailabilityMap::pruneByLiveness): When pruning availability, we first set all the operands Availability::unavailable(),
     73        and copy the calculated value from the current availability map.
     74        * dfg/DFGOSRAvailabilityAnalysisPhase.cpp:
     75        (JSC::DFG::OSRAvailabilityAnalysisPhase::run): Add logging things for debugging.
     76
    1772019-01-23  David Kilzer  <ddkilzer@apple.com>
    278
  • trunk/Source/JavaScriptCore/dfg/DFGAvailabilityMap.cpp

    r219502 r240364  
    6666void AvailabilityMap::pruneByLiveness(Graph& graph, CodeOrigin where)
    6767{
    68     Operands<Availability> localsCopy(OperandsLike, m_locals);
     68    Operands<Availability> localsCopy(m_locals.numberOfArguments(), m_locals.numberOfLocals(), Availability::unavailable());
    6969    graph.forAllLiveInBytecode(
    7070        where,
  • trunk/Source/JavaScriptCore/dfg/DFGOSRAvailabilityAnalysisPhase.cpp

    r226033 r240364  
    3636
    3737namespace JSC { namespace DFG {
     38namespace DFGOSRAvailabilityAnalysisPhaseInternal {
     39static constexpr bool verbose = false;
     40}
    3841
    3942class OSRAvailabilityAnalysisPhase : public Phase {
     
    6467        // This could be made more efficient by processing blocks in reverse postorder.
    6568       
     69        auto dumpAvailability = [] (BasicBlock* block) {
     70            dataLogLn(block->ssa->availabilityAtHead);
     71            dataLogLn(block->ssa->availabilityAtTail);
     72        };
     73
     74        auto dumpBytecodeLivenessAtHead = [&] (BasicBlock* block) {
     75            dataLog("Live: ");
     76            m_graph.forAllLiveInBytecode(
     77                block->at(0)->origin.forExit,
     78                [&] (VirtualRegister reg) {
     79                    dataLog(reg, " ");
     80                });
     81            dataLogLn("");
     82        };
     83
    6684        LocalOSRAvailabilityCalculator calculator(m_graph);
    6785        bool changed;
     
    7492                    continue;
    7593               
     94                if (DFGOSRAvailabilityAnalysisPhaseInternal::verbose) {
     95                    dataLogLn("Before changing Block #", block->index);
     96                    dumpAvailability(block);
     97                }
    7698                calculator.beginBlock(block);
    7799               
     
    84106                block->ssa->availabilityAtTail = calculator.m_availability;
    85107                changed = true;
     108
     109                if (DFGOSRAvailabilityAnalysisPhaseInternal::verbose) {
     110                    dataLogLn("After changing Block #", block->index);
     111                    dumpAvailability(block);
     112                }
    86113
    87114                for (unsigned successorIndex = block->numSuccessors(); successorIndex--;) {
     
    94121                    successor->ssa->availabilityAtHead.pruneByLiveness(
    95122                        m_graph, successor->at(0)->origin.forExit);
     123                    if (DFGOSRAvailabilityAnalysisPhaseInternal::verbose) {
     124                        dataLogLn("After pruning Block #", successor->index);
     125                        dumpAvailability(successor);
     126                        dumpBytecodeLivenessAtHead(successor);
     127                    }
    96128                }
    97129            }
Note: See TracChangeset for help on using the changeset viewer.