| Differences between
and this patch
- Source/JavaScriptCore/ChangeLog +95 lines
Lines 1-3 Source/JavaScriptCore/ChangeLog_sec1
1
2016-05-03  Filip Pizlo  <fpizlo@apple.com>
2
3
        References from code to Structures should be stronger than weak
4
        https://bugs.webkit.org/show_bug.cgi?id=157324
5
6
        Reviewed by NOBODY (OOPS!).
7
        
8
        If code refers to a Structure and the Structure dies, then previously we'd kill the code. 
9
        This makes sense because the Structure could be the only thing left referring to some global
10
        object or prototype.
11
12
        But this also causes unnecessary churn. Sometimes there will be a structure that we just
13
        haven't really done anything with recently and so it appears dead. The approach we use
14
        elsewhere in our type inference is that the type that the code uses is general enough to
15
        handle every past execution. Having the GC clear code when some Structure it uses dies means
16
        that we forget that the code used that Structure. We'll either assume that the code is more
17
        monomorphic than it really is (because after GC we patch in some other structure but not the
18
        deleted one, so it looks like we only ever saw the new structure), or we'll assume that it's
19
        crazier than it really is (because we'll remember that there had been some structure that
20
        caused deletion, so we'll assume that deletions might happen in the future, so we'll use a
21
        fully dynamic IC).
22
23
        This change introduces a more nuanced policy a more nuanced policy: if it's cheap to mark a
24
        dead Structure then we should mark it just so that all of the code that refers to it
25
        remembers that there had been this exact Structure in the past. If the code often goes
26
        through different Structures then we already have great mechanisms to realize that the code
27
        is nutty (namely, the PolymorphicAccess size limit). But if the code just does this a
28
        handful of times then remembering this old Structure is probably net good:
29
30
        - It obeys the "handle all past executions" law.
31
        - It preserves the history of the property access, allowing a precise measure of its past
32
          polymorphism.
33
        - It makes the code ready to run fast if the user decides to use that Structure again.
34
          Marking the Structure means it will stay in whatever property transition tables it was in,
35
          so if the program does the same thing it did in the past, it will get this old Structure.
36
37
        It looks like this is a progression in gbemu and it makes gbemu perform more
38
        deterministically. Also, it seems that this makes JetStream run faster.
39
        
40
        Over five in-browser runs of JetStream, here's what we see before and after:
41
        
42
        Geometric Mean:
43
            Before              After
44
            229.23 +- 8.2523    230.70 +- 12.888
45
            232.91 +- 15.638    239.04 +- 13.766
46
            234.79 +- 12.760    236.32 +- 15.562
47
            236.20 +- 23.125    242.02 +- 3.3865
48
            237.22 +- 2.1929    237.23 +- 17.664
49
        
50
        Just gbemu:
51
            Before              After
52
            541.0 +- 135.8      481.7 +- 143.4
53
            518.9 +- 15.65      508.1 +- 136.3
54
            362.5 +- 0.8884     489.7 +- 101.4
55
            470.7 +- 313.3      530.7 +- 11.49
56
            418.7 +- 180.6      537.2 +- 6.514
57
        
58
        Notice that there is plenty of noise before and after, but the noise is now far less severe.
59
        After this change I did not see any runs like "470.7 +- 313.3" where the size of the 
60
        confidence interval (313.3 * 2) is greater than the score (470.7). Also, notice that the
61
        least noisy run before the change also got a lower score than we ever observed after the
62
        change (36.5 +- 0.8884). The noise, and these occasional very low scores, are due to a
63
        pathology where the GC would reset some stubs at an unfortunate time during profiling,
64
        causing the optimizing compiler to make many poor decisions. That pathology doesn't exist
65
        anymore.
66
        
67
        On the other hand, prior to this change it was possible for gbemu to sometimes run sooooper
68
        fast because the GC would cause the profiler to forget gbemu's behavior on the first tick
69
        and focus only on its behavior in subsequent ticks. So, in steady state, we'd optimize gbemu
70
        for its later behavior rather than a combination of its early behavior and later behavior.
71
        We rarely got lucky this way, so it's not fair to view this quirk as a feature.
72
        
73
        * bytecode/CodeBlock.cpp:
74
        (JSC::CodeBlock::propagateTransitions):
75
        * bytecode/PolymorphicAccess.cpp:
76
        (JSC::AccessCase::visitWeak):
77
        (JSC::AccessCase::propagateTransitions):
78
        (JSC::AccessCase::generateWithGuard):
79
        (JSC::PolymorphicAccess::visitWeak):
80
        (JSC::PolymorphicAccess::propagateTransitions):
81
        (JSC::PolymorphicAccess::dump):
82
        * bytecode/PolymorphicAccess.h:
83
        * bytecode/StructureStubInfo.cpp:
84
        (JSC::StructureStubInfo::visitWeakReferences):
85
        (JSC::StructureStubInfo::propagateTransitions):
86
        (JSC::StructureStubInfo::containsPC):
87
        * bytecode/StructureStubInfo.h:
88
        (JSC::StructureStubInfo::considerCaching):
89
        * runtime/Structure.cpp:
90
        (JSC::Structure::visitChildren):
91
        (JSC::Structure::isCheapDuringGC):
92
        (JSC::Structure::markIfCheap):
93
        (JSC::Structure::prototypeChainMayInterceptStoreTo):
94
        * runtime/Structure.h:
95
1
2016-05-03  Michael Saboff  <msaboff@apple.com>
96
2016-05-03  Michael Saboff  <msaboff@apple.com>
2
97
3
        Crash: Array.prototype.slice() and .splice() can call fastSlice() after an array is truncated
98
        Crash: Array.prototype.slice() and .splice() can call fastSlice() after an array is truncated
- Source/JavaScriptCore/bytecode/CodeBlock.cpp -20 / +4 lines
Lines 2616-2647 void CodeBlock::propagateTransitions(Slo Source/JavaScriptCore/bytecode/CodeBlock.cpp_sec1
2616
2616
2617
#if ENABLE(JIT)
2617
#if ENABLE(JIT)
2618
    if (JITCode::isJIT(jitType())) {
2618
    if (JITCode::isJIT(jitType())) {
2619
        for (Bag<StructureStubInfo>::iterator iter = m_stubInfos.begin(); !!iter; ++iter) {
2619
        for (Bag<StructureStubInfo>::iterator iter = m_stubInfos.begin(); !!iter; ++iter)
2620
            StructureStubInfo& stubInfo = **iter;
2620
            allAreMarkedSoFar &= (*iter)->propagateTransitions(visitor);
2621
            if (stubInfo.cacheType != CacheType::Stub)
2622
                continue;
2623
            PolymorphicAccess* list = stubInfo.u.stub;
2624
            JSCell* origin = stubInfo.codeOrigin.codeOriginOwner();
2625
            if (origin && !Heap::isMarked(origin)) {
2626
                allAreMarkedSoFar = false;
2627
                continue;
2628
            }
2629
            for (unsigned j = list->size(); j--;) {
2630
                const AccessCase& access = list->at(j);
2631
                if (access.type() != AccessCase::Transition)
2632
                    continue;
2633
                if (Heap::isMarked(access.structure()))
2634
                    visitor.appendUnbarrieredReadOnlyPointer(access.newStructure());
2635
                else
2636
                    allAreMarkedSoFar = false;
2637
            }
2638
        }
2639
    }
2621
    }
2640
#endif // ENABLE(JIT)
2622
#endif // ENABLE(JIT)
2641
    
2623
    
2642
#if ENABLE(DFG_JIT)
2624
#if ENABLE(DFG_JIT)
2643
    if (JITCode::isOptimizingJIT(jitType())) {
2625
    if (JITCode::isOptimizingJIT(jitType())) {
2644
        DFG::CommonData* dfgCommon = m_jitCode->dfgCommon();
2626
        DFG::CommonData* dfgCommon = m_jitCode->dfgCommon();
2627
        for (auto& weakReference : dfgCommon->weakStructureReferences)
2628
            allAreMarkedSoFar &= weakReference->markIfCheap(visitor);
2645
        
2629
        
2646
        for (unsigned i = 0; i < dfgCommon->transitions.size(); ++i) {
2630
        for (unsigned i = 0; i < dfgCommon->transitions.size(); ++i) {
2647
            if (shouldMarkTransition(dfgCommon->transitions[i])) {
2631
            if (shouldMarkTransition(dfgCommon->transitions[i])) {
- Source/JavaScriptCore/bytecode/PolymorphicAccess.cpp +29 lines
Lines 554-559 bool AccessCase::visitWeak(VM& vm) const Source/JavaScriptCore/bytecode/PolymorphicAccess.cpp_sec1
554
    return true;
554
    return true;
555
}
555
}
556
556
557
bool AccessCase::propagateTransitions(SlotVisitor& visitor) const
558
{
559
    bool result = true;
560
    
561
    if (m_structure)
562
        result &= m_structure->markIfCheap(visitor);
563
    
564
    switch (m_type) {
565
    case Transition:
566
        if (Heap::isMarked(m_structure->previousID()))
567
            visitor.appendUnbarrieredReadOnlyPointer(m_structure.get());
568
        else
569
            result = false;
570
        break;
571
    default:
572
        break;
573
    }
574
    
575
    return result;
576
}
577
557
void AccessCase::generateWithGuard(
578
void AccessCase::generateWithGuard(
558
    AccessGenerationState& state, CCallHelpers::JumpList& fallThrough)
579
    AccessGenerationState& state, CCallHelpers::JumpList& fallThrough)
559
{
580
{
Lines 1487-1492 bool PolymorphicAccess::visitWeak(VM& vm Source/JavaScriptCore/bytecode/PolymorphicAccess.cpp_sec2
1487
    return true;
1508
    return true;
1488
}
1509
}
1489
1510
1511
bool PolymorphicAccess::propagateTransitions(SlotVisitor& visitor) const
1512
{
1513
    bool result = true;
1514
    for (unsigned i = 0; i < size(); ++i)
1515
        result &= at(i).propagateTransitions(visitor);
1516
    return result;
1517
}
1518
1490
void PolymorphicAccess::dump(PrintStream& out) const
1519
void PolymorphicAccess::dump(PrintStream& out) const
1491
{
1520
{
1492
    out.print(RawPointer(this), ":[");
1521
    out.print(RawPointer(this), ":[");
- Source/JavaScriptCore/bytecode/PolymorphicAccess.h +5 lines
Lines 248-253 private: Source/JavaScriptCore/bytecode/PolymorphicAccess.h_sec1
248
    AccessCase();
248
    AccessCase();
249
249
250
    bool visitWeak(VM&) const;
250
    bool visitWeak(VM&) const;
251
    bool propagateTransitions(SlotVisitor&) const;
251
    
252
    
252
    // FIXME: This only exists because of how AccessCase puts post-generation things into itself.
253
    // FIXME: This only exists because of how AccessCase puts post-generation things into itself.
253
    // https://bugs.webkit.org/show_bug.cgi?id=156456
254
    // https://bugs.webkit.org/show_bug.cgi?id=156456
Lines 396-401 public: Source/JavaScriptCore/bytecode/PolymorphicAccess.h_sec2
396
397
397
    // If this returns false then we are requesting a reset of the owning StructureStubInfo.
398
    // If this returns false then we are requesting a reset of the owning StructureStubInfo.
398
    bool visitWeak(VM&) const;
399
    bool visitWeak(VM&) const;
400
    
401
    // This returns true if it has marked everything it will ever marked. This can be used as an
402
    // optimization to then avoid calling this method again during the fixpoint.
403
    bool propagateTransitions(SlotVisitor&) const;
399
404
400
    void aboutToDie();
405
    void aboutToDie();
401
406
- Source/JavaScriptCore/bytecode/StructureStubInfo.cpp -1 / +20 lines
Lines 230-236 void StructureStubInfo::visitWeakReferen Source/JavaScriptCore/bytecode/StructureStubInfo.cpp_sec1
230
{
230
{
231
    VM& vm = *codeBlock->vm();
231
    VM& vm = *codeBlock->vm();
232
    
232
    
233
    bufferedStructures.clear();
233
    bufferedStructures.genericFilter(
234
        [&] (Structure* structure) -> bool {
235
            return Heap::isMarked(structure);
236
        });
234
237
235
    switch (cacheType) {
238
    switch (cacheType) {
236
    case CacheType::GetByIdSelf:
239
    case CacheType::GetByIdSelf:
Lines 250-255 void StructureStubInfo::visitWeakReferen Source/JavaScriptCore/bytecode/StructureStubInfo.cpp_sec2
250
    resetByGC = true;
253
    resetByGC = true;
251
}
254
}
252
255
256
bool StructureStubInfo::propagateTransitions(SlotVisitor& visitor)
257
{
258
    switch (cacheType) {
259
    case CacheType::Unset:
260
        return true;
261
    case CacheType::GetByIdSelf:
262
    case CacheType::PutByIdReplace:
263
        return u.byIdSelf.baseObjectStructure->markIfCheap(visitor);
264
    case CacheType::Stub:
265
        return u.stub->propagateTransitions(visitor);
266
    }
267
    
268
    RELEASE_ASSERT_NOT_REACHED();
269
    return true;
270
}
271
253
bool StructureStubInfo::containsPC(void* pc) const
272
bool StructureStubInfo::containsPC(void* pc) const
254
{
273
{
255
    if (cacheType != CacheType::Stub)
274
    if (cacheType != CacheType::Stub)
- Source/JavaScriptCore/bytecode/StructureStubInfo.h +3 lines
Lines 81-86 public: Source/JavaScriptCore/bytecode/StructureStubInfo.h_sec1
81
    // Check if the stub has weak references that are dead. If it does, then it resets itself,
81
    // Check if the stub has weak references that are dead. If it does, then it resets itself,
82
    // either entirely or just enough to ensure that those dead pointers don't get used anymore.
82
    // either entirely or just enough to ensure that those dead pointers don't get used anymore.
83
    void visitWeakReferences(CodeBlock*);
83
    void visitWeakReferences(CodeBlock*);
84
    
85
    // This returns true if it has marked everything that it will ever mark.
86
    bool propagateTransitions(SlotVisitor&);
84
        
87
        
85
    ALWAYS_INLINE bool considerCaching(Structure* structure)
88
    ALWAYS_INLINE bool considerCaching(Structure* structure)
86
    {
89
    {
- Source/JavaScriptCore/runtime/Structure.cpp +15 lines
Lines 1135-1140 void Structure::visitChildren(JSCell* ce Source/JavaScriptCore/runtime/Structure.cpp_sec1
1135
    visitor.append(&thisObject->m_inferredTypeTable);
1135
    visitor.append(&thisObject->m_inferredTypeTable);
1136
}
1136
}
1137
1137
1138
bool Structure::isCheapDuringGC()
1139
{
1140
    return (!m_globalObject || Heap::isMarked(m_globalObject.get()))
1141
        && (!storedPrototypeObject() || Heap::isMarked(storedPrototypeObject()));
1142
}
1143
1144
bool Structure::markIfCheap(SlotVisitor& visitor)
1145
{
1146
    if (!isCheapDuringGC())
1147
        return Heap::isMarked(this);
1148
    
1149
    visitor.appendUnbarrieredReadOnlyPointer(this);
1150
    return true;
1151
}
1152
1138
bool Structure::prototypeChainMayInterceptStoreTo(VM& vm, PropertyName propertyName)
1153
bool Structure::prototypeChainMayInterceptStoreTo(VM& vm, PropertyName propertyName)
1139
{
1154
{
1140
    if (parseIndex(propertyName))
1155
    if (parseIndex(propertyName))
- Source/JavaScriptCore/runtime/Structure.h +5 lines
Lines 260-265 public: Source/JavaScriptCore/runtime/Structure.h_sec1
260
    StructureChain* prototypeChain(VM&, JSGlobalObject*) const;
260
    StructureChain* prototypeChain(VM&, JSGlobalObject*) const;
261
    StructureChain* prototypeChain(ExecState*) const;
261
    StructureChain* prototypeChain(ExecState*) const;
262
    static void visitChildren(JSCell*, SlotVisitor&);
262
    static void visitChildren(JSCell*, SlotVisitor&);
263
    
264
    bool isCheapDuringGC();
265
    
266
    // Returns true if this structure is now marked.
267
    bool markIfCheap(SlotVisitor&);
263
        
268
        
264
    // Will just the prototype chain intercept this property access?
269
    // Will just the prototype chain intercept this property access?
265
    JS_EXPORT_PRIVATE bool prototypeChainMayInterceptStoreTo(VM&, PropertyName);
270
    JS_EXPORT_PRIVATE bool prototypeChainMayInterceptStoreTo(VM&, PropertyName);

Return to Bug 157324