| Differences between
and this patch
- Source/JavaScriptCore/ChangeLog +47 lines
Lines 1-3 Source/JavaScriptCore/ChangeLog_sec1
1
2015-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 NOBODY (OOPS!).
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
1
2015-03-15  Joseph Pecoraro  <pecoraro@apple.com>
48
2015-03-15  Joseph Pecoraro  <pecoraro@apple.com>
2
49
3
        Web Inspector: Better handle displaying -0
50
        Web Inspector: Better handle displaying -0
- Source/JavaScriptCore/dfg/DFGPutStackSinkingPhase.cpp -38 / +47 lines
Lines 221-226 public: Source/JavaScriptCore/dfg/DFGPutStackSinkingPhase.cpp_sec1
221
                        continue;
221
                        continue;
222
                    }
222
                    }
223
                    
223
                    
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
                    
224
                    auto escapeHandler = [&] (VirtualRegister operand) {
230
                    auto escapeHandler = [&] (VirtualRegister operand) {
225
                        if (operand.isHeader())
231
                        if (operand.isHeader())
226
                            return;
232
                            return;
Lines 390-395 public: Source/JavaScriptCore/dfg/DFGPutStackSinkingPhase.cpp_sec2
390
                    deferred.operand(node->unlinkedLocal()) = ConflictingFlush;
396
                    deferred.operand(node->unlinkedLocal()) = ConflictingFlush;
391
                    break;
397
                    break;
392
                }
398
                }
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
                }
393
                
422
                
394
                default: {
423
                default: {
395
                    auto escapeHandler = [&] (VirtualRegister operand) {
424
                    auto escapeHandler = [&] (VirtualRegister operand) {
Lines 418-433 public: Source/JavaScriptCore/dfg/DFGPutStackSinkingPhase.cpp_sec3
418
                    preciseLocalClobberize(
447
                    preciseLocalClobberize(
419
                        m_graph, node, escapeHandler, escapeHandler,
448
                        m_graph, node, escapeHandler, escapeHandler,
420
                        [&] (VirtualRegister, Node*) { });
449
                        [&] (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
                    }
431
                    break;
450
                    break;
432
                } }
451
                } }
433
            }
452
            }
Lines 444-477 public: Source/JavaScriptCore/dfg/DFGPutStackSinkingPhase.cpp_sec4
444
                    FlushFormat format = deferredAtHead[successorBlock].operand(operand);
463
                    FlushFormat format = deferredAtHead[successorBlock].operand(operand);
445
                    DFG_ASSERT(m_graph, nullptr, isConcrete(format));
464
                    DFG_ASSERT(m_graph, nullptr, isConcrete(format));
446
                    UseKind useKind = useKindFor(format);
465
                    UseKind useKind = useKindFor(format);
447
                    Node* incoming = mapping.operand(operand);
466
                    
448
                    if (!incoming) {
467
                    // We need to get a value for the stack slot. This phase doesn't really have a
449
                        // This can totally happen, see tests/stress/put-local-conservative.js.
468
                    // good way of determining if a stack location got clobbered. It just knows if
450
                        // This arises because deferral and liveness are both conservative.
469
                    // there is a deferral. The lack of a deferral might mean that a PutStack or
451
                        // Conservative liveness means that a load from a *different* closure
470
                    // GetStack had never happened, or it might mean that the value was read, or
452
                        // variable may lead us to believe that our local is live. Conservative
471
                    // that it was written. It's OK for us to make some bad decisions here, since
453
                        // deferral may lead us to believe that the local doesn't have a top deferral
472
                    // GCSE will clean it up anyway.
454
                        // because someone has done something that would have forced it to be
473
                    Node* incoming;
455
                        // materialized. The basic pattern is:
474
                    if (isConcrete(deferred.operand(operand))) {
456
                        //
475
                        incoming = mapping.operand(operand);
457
                        // GetClosureVar(loc42) // loc42's deferral is now bottom
476
                        DFG_ASSERT(m_graph, phiNode, incoming);
458
                        // if (predicate1)
477
                    } else {
459
                        //     PutClosureVar(loc42) // prevent GCSE of our GetClosureVar's
478
                        // Issue a GetStack to get the value. This might introduce some redundancy
460
                        // if (predicate2)
479
                        // into the code, but if it's bad enough, GCSE will clean it up.
461
                        //     PutStack(loc42) // we now have a concrete deferral
480
                        incoming = insertionSet.insertNode(
462
                        // // we still have the concrete deferral because we merged with bottom
481
                            upsilonInsertionPoint, SpecNone, GetStack, upsilonOrigin,
463
                        // GetClosureVar(loc42) // force materialization
482
                            OpInfo(m_graph.m_stackAccessData.add(operand, format)));
464
                        //
483
                        incoming->setResult(resultFor(format));
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();
475
                    }
484
                    }
476
                    
485
                    
477
                    insertionSet.insertNode(
486
                    insertionSet.insertNode(
- Source/JavaScriptCore/tests/stress/get-stack-identity-due-to-sinking.js +18 lines
Line 0 Source/JavaScriptCore/tests/stress/get-stack-identity-due-to-sinking.js_sec1
1
function foo(p, a) {
2
    if (p) {
3
        var tmp = arguments;
4
    }
5
    return a;
6
}
7
8
function bar(p, a) {
9
    return foo(p, a);
10
}
11
12
noInline(bar);
13
14
for (var i = 0; i < 10000; ++i) {
15
    var result = bar(false, 42);
16
    if (result != 42)
17
        throw "Error: bad result: " + result;
18
}
- Source/JavaScriptCore/tests/stress/get-stack-mapping-with-dead-get-stack.js +27 lines
Line 0 Source/JavaScriptCore/tests/stress/get-stack-mapping-with-dead-get-stack.js_sec1
1
function bar() {
2
    if (foo.arguments[0] === void 0)
3
        throw "Error: foo.arguments[0] should not be undefined but is."
4
}
5
6
noInline(bar);
7
8
function foo(a, p) {
9
    var tmp = a;
10
    effectful42();
11
    for (var i = 0; i < 10; ++i) {
12
        bar();
13
        a = i;
14
    }
15
    if (p) {
16
        var tmp = arguments;
17
    }
18
    return a;
19
}
20
21
noInline(foo);
22
23
for (var i = 0; i < 10000; ++i) {
24
    var result = foo(0, false);
25
    if (result != 9)
26
        throw "Error: bad result: " + result;
27
}
- Source/JavaScriptCore/tests/stress/get-stack-mapping.js +26 lines
Line 0 Source/JavaScriptCore/tests/stress/get-stack-mapping.js_sec1
1
function bar() {
2
    if (foo.arguments[0] === void 0)
3
        throw "Error: foo.arguments[0] should not be undefined but is."
4
}
5
6
noInline(bar);
7
8
function foo(a, p) {
9
    effectful42();
10
    for (var i = 0; i < 10; ++i) {
11
        bar();
12
        a = i;
13
    }
14
    if (p) {
15
        var tmp = arguments;
16
    }
17
    return a;
18
}
19
20
noInline(foo);
21
22
for (var i = 0; i < 10000; ++i) {
23
    var result = foo(0, false);
24
    if (result != 9)
25
        throw "Error: bad result: " + result;
26
}
- Source/JavaScriptCore/tests/stress/weird-put-stack-varargs.js +26 lines
Line 0 Source/JavaScriptCore/tests/stress/weird-put-stack-varargs.js_sec1
1
function baz() {
2
    if (!foo.arguments[1])
3
        throw "Error: foo.arguments[1] should be truthy but is falsy: " + foo.arguments[1];
4
}
5
6
noInline(baz);
7
8
function foo(a, b) {
9
    if (a)
10
        b = 42;
11
    baz();
12
}
13
14
function fuzz(a, b) {
15
    return a + b;
16
}
17
18
function bar(array1, array2) {
19
    fuzz.apply(this, array1);
20
    foo.apply(this, array2);
21
}
22
23
noInline(bar);
24
25
for (var i = 0; i < 100000; ++i)
26
    bar([false, false], [false, true]);

Return to Bug 141624