⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 181563 in webkit


Ignore:
Timestamp:
Mar 16, 2015, 10:39:07 AM (11 years ago)
Author:
fpizlo@apple.com
Message:

DFG::PutStackSinkingPhase should eliminate GetStacks that have an obviously known source, and emit GetStacks when the stack's value is needed and none is deferred
https://bugs.webkit.org/show_bug.cgi?id=141624

Reviewed by Geoffrey Garen.

Not eliminating GetStacks was an obvious omission from the original PutStackSinkingPhase.
Previously, we would treat GetStacks conservatively and assume that the stack slot
escaped. That's pretty dumb, since a GetStack is a local load of the stack. This change
makes GetStack a no-op from the standpoint of this phase's deferral analysis. At the end
we either keep the GetStack (if there was no concrete deferral) or we replace it with an
identity over the value that would have been stored by the deferred PutStack. Note that
this might be a Phi that the phase creates, so this is strictly stronger than what GCSE
could do.

But this change revealed the fact that this phase never correctly handled side effects in
case that we had done a GetStack, then a side-effect, and then found ourselves wanting the
value on the stack due to (for example) a Phi on a deferred PutStack and that GetStack.
Basically, it's only correct to use the SSA converter's incoming value mapping if we have
a concrete deferral - since anything but a concrete deferral may imply that the value has
been clobbered.

This has no performance change. I believe that the bug was previously benign because we
have so few operations that clobber the stack anymore, and most of those get used in a
very idiomatic way. The GetStack elimination will be very useful for the varargs
simplification that is part of bug 141174.

This includes a test for the case that Speedometer hit, plus tests for the other cases I
thought of once I realized the deeper issue.

  • dfg/DFGPutStackSinkingPhase.cpp:
  • tests/stress/get-stack-identity-due-to-sinking.js: Added.

(foo):
(bar):

  • tests/stress/get-stack-mapping-with-dead-get-stack.js: Added.

(bar):
(foo):

  • tests/stress/get-stack-mapping.js: Added.

(bar):
(foo):

  • tests/stress/weird-put-stack-varargs.js: Added.

(baz):
(foo):
(fuzz):
(bar):

Location:
trunk/Source/JavaScriptCore
Files:
4 added
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/ChangeLog

    r181556 r181563  
     12015-03-15  Filip Pizlo  <fpizlo@apple.com>
     2
     3        DFG::PutStackSinkingPhase should eliminate GetStacks that have an obviously known source, and emit GetStacks when the stack's value is needed and none is deferred
     4        https://bugs.webkit.org/show_bug.cgi?id=141624
     5
     6        Reviewed by Geoffrey Garen.
     7
     8        Not eliminating GetStacks was an obvious omission from the original PutStackSinkingPhase.
     9        Previously, we would treat GetStacks conservatively and assume that the stack slot
     10        escaped. That's pretty dumb, since a GetStack is a local load of the stack. This change
     11        makes GetStack a no-op from the standpoint of this phase's deferral analysis. At the end
     12        we either keep the GetStack (if there was no concrete deferral) or we replace it with an
     13        identity over the value that would have been stored by the deferred PutStack. Note that
     14        this might be a Phi that the phase creates, so this is strictly stronger than what GCSE
     15        could do.
     16       
     17        But this change revealed the fact that this phase never correctly handled side effects in
     18        case that we had done a GetStack, then a side-effect, and then found ourselves wanting the
     19        value on the stack due to (for example) a Phi on a deferred PutStack and that GetStack.
     20        Basically, it's only correct to use the SSA converter's incoming value mapping if we have
     21        a concrete deferral - since anything but a concrete deferral may imply that the value has
     22        been clobbered.
     23       
     24        This has no performance change. I believe that the bug was previously benign because we
     25        have so few operations that clobber the stack anymore, and most of those get used in a
     26        very idiomatic way. The GetStack elimination will be very useful for the varargs
     27        simplification that is part of bug 141174.
     28       
     29        This includes a test for the case that Speedometer hit, plus tests for the other cases I
     30        thought of once I realized the deeper issue.
     31
     32        * dfg/DFGPutStackSinkingPhase.cpp:
     33        * tests/stress/get-stack-identity-due-to-sinking.js: Added.
     34        (foo):
     35        (bar):
     36        * tests/stress/get-stack-mapping-with-dead-get-stack.js: Added.
     37        (bar):
     38        (foo):
     39        * tests/stress/get-stack-mapping.js: Added.
     40        (bar):
     41        (foo):
     42        * tests/stress/weird-put-stack-varargs.js: Added.
     43        (baz):
     44        (foo):
     45        (fuzz):
     46        (bar):
     47
    1482015-03-16  Joseph Pecoraro  <pecoraro@apple.com>
    249
  • trunk/Source/JavaScriptCore/dfg/DFGPutStackSinkingPhase.cpp

    r181498 r181563  
    222222                    }
    223223                   
     224                    if (node->op() == GetStack) {
     225                        // A GetStack doesn't affect anything, since we know which local we are reading
     226                        // from.
     227                        continue;
     228                    }
     229                   
    224230                    auto escapeHandler = [&] (VirtualRegister operand) {
    225231                        if (operand.isHeader())
     
    391397                    break;
    392398                }
     399                   
     400                case GetStack: {
     401                    StackAccessData* data = node->stackAccessData();
     402                    FlushFormat format = deferred.operand(data->local);
     403                    if (!isConcrete(format)) {
     404                        // This means there is no deferral. No deferral means that the most
     405                        // authoritative value for this stack slot is what is stored in the stack. So,
     406                        // keep the GetStack.
     407                        mapping.operand(data->local) = node;
     408                        break;
     409                    }
     410                   
     411                    // We have a concrete deferral, which means a PutStack that hasn't executed yet. It
     412                    // would have stored a value with a certain format. That format must match our
     413                    // format. But more importantly, we can simply use the value that the PutStack would
     414                    // have stored and get rid of the GetStack.
     415                    DFG_ASSERT(m_graph, node, format == data->format);
     416                   
     417                    Node* incoming = mapping.operand(data->local);
     418                    node->child1() = incoming->defaultEdge();
     419                    node->convertToIdentity();
     420                    break;
     421                }
    393422               
    394423                default: {
     
    419448                        m_graph, node, escapeHandler, escapeHandler,
    420449                        [&] (VirtualRegister, Node*) { });
    421                    
    422                     // If we're a GetStack, then we also create a mapping.
    423                     // FIXME: We should be able to just eliminate such GetLocals, when we know
    424                     // what their incoming value will be.
    425                     // https://bugs.webkit.org/show_bug.cgi?id=141624
    426                     if (node->op() == GetStack) {
    427                         StackAccessData* data = node->stackAccessData();
    428                         VirtualRegister operand = data->local;
    429                         mapping.operand(operand) = node;
    430                     }
    431450                    break;
    432451                } }
     
    445464                    DFG_ASSERT(m_graph, nullptr, isConcrete(format));
    446465                    UseKind useKind = useKindFor(format);
    447                     Node* incoming = mapping.operand(operand);
    448                     if (!incoming) {
    449                         // This can totally happen, see tests/stress/put-local-conservative.js.
    450                         // This arises because deferral and liveness are both conservative.
    451                         // Conservative liveness means that a load from a *different* closure
    452                         // variable may lead us to believe that our local is live. Conservative
    453                         // deferral may lead us to believe that the local doesn't have a top deferral
    454                         // because someone has done something that would have forced it to be
    455                         // materialized. The basic pattern is:
    456                         //
    457                         // GetClosureVar(loc42) // loc42's deferral is now bottom
    458                         // if (predicate1)
    459                         //     PutClosureVar(loc42) // prevent GCSE of our GetClosureVar's
    460                         // if (predicate2)
    461                         //     PutStack(loc42) // we now have a concrete deferral
    462                         // // we still have the concrete deferral because we merged with bottom
    463                         // GetClosureVar(loc42) // force materialization
    464                         //
    465                         // We will have a Phi with no incoming value form the basic block that
    466                         // bypassed the PutStack.
    467                        
    468                         // Note: we sort of could have used the equivalent of LLVM's undef here. The
    469                         // point is that it's OK to just leave random bits in the local if we're
    470                         // coming down this path. But, we don't have a way of saying that in our IR
    471                         // right now and anyway it probably doesn't matter that much.
    472                        
    473                         incoming = insertionSet.insertBottomConstantForUse(
    474                             upsilonInsertionPoint, upsilonOrigin, useKind).node();
     466                   
     467                    // We need to get a value for the stack slot. This phase doesn't really have a
     468                    // good way of determining if a stack location got clobbered. It just knows if
     469                    // there is a deferral. The lack of a deferral might mean that a PutStack or
     470                    // GetStack had never happened, or it might mean that the value was read, or
     471                    // that it was written. It's OK for us to make some bad decisions here, since
     472                    // GCSE will clean it up anyway.
     473                    Node* incoming;
     474                    if (isConcrete(deferred.operand(operand))) {
     475                        incoming = mapping.operand(operand);
     476                        DFG_ASSERT(m_graph, phiNode, incoming);
     477                    } else {
     478                        // Issue a GetStack to get the value. This might introduce some redundancy
     479                        // into the code, but if it's bad enough, GCSE will clean it up.
     480                        incoming = insertionSet.insertNode(
     481                            upsilonInsertionPoint, SpecNone, GetStack, upsilonOrigin,
     482                            OpInfo(m_graph.m_stackAccessData.add(operand, format)));
     483                        incoming->setResult(resultFor(format));
    475484                    }
    476485                   
Note: See TracChangeset for help on using the changeset viewer.