Changeset 166678 in webkit


Ignore:
Timestamp:
Apr 2, 2014 4:50:25 PM (10 years ago)
Author:
mhahnenberg@apple.com
Message:

CodeBlockSet should be generational
https://bugs.webkit.org/show_bug.cgi?id=127152

Reviewed by Geoffrey Garen.

During EdenCollections we now only visit those CodeBlocks that:
a) Are new since the last collection if they were somehow otherwise reachable.
b) Are reachable from an Executable that is part of the remembered set.

  • bytecode/CodeBlock.cpp:

(JSC::CodeBlock::CodeBlock): Initialize uninitialized variables.
(JSC::CodeBlock::visitAggregate): Move the addition of the weak reference harvester after the
shouldImmediatelyAssumeLivenessDuringScan check since it's redundant if we assume liveness.

  • bytecode/CodeBlock.h:

(JSC::CodeBlock::forEachRelatedCodeBlock): Executes a functor for each CodeBlock reachable from the current CodeBlock (including this).
We use this to clear marks for the CodeBlocks of remembered Executables (see: CodeBlockSet::clearMarksForEdenCollection).
(JSC::CodeBlockSet::mark): Also check the set of new CodeBlocks for memebership when doing conservative scanning.
(JSC::ScriptExecutable::forEachCodeBlock): Executes a functor for each of this Executable's CodeBlocks.

  • heap/CodeBlockSet.cpp:

(JSC::CodeBlockSet::~CodeBlockSet):
(JSC::CodeBlockSet::add):
(JSC::CodeBlockSet::promoteYoungCodeBlocks): Moves all CodeBlocks currently in the set of new CodeBlocks into
the set of old CodeBlocks.
(JSC::CodeBlockSet::clearMarksForFullCollection): Clears the marks for all CodeBlocks.
(JSC::CodeBlockSet::clearMarksForEdenCollection): Clears the marks for CodeBlocks owned by Executables in the
remembered set. When an Executable is added to the remembered set it's typically because we need to do something
with its CodeBlock.
(JSC::CodeBlockSet::clearMarks):
(JSC::CodeBlockSet::deleteUnmarkedAndUnreferenced): Fixpoints over either just the new CodeBlocks or all CodeBlocks
to determine which CodeBlocks are dead and eagerly finalizes/deletes them.
(JSC::CodeBlockSet::remove):
(JSC::CodeBlockSet::traceMarked): Iterate only the currently executing CodeBlocks instead of all CodeBlocks.
(JSC::CodeBlockSet::rememberCurrentlyExecutingCodeBlocks): Clear m_mayBeExecuting for all currently executing
CodeBlocks because we no longer always do this at the beginning of EdenCollections.

  • heap/CodeBlockSet.h:

(JSC::CodeBlockSet::iterate):

  • heap/Heap.cpp:

(JSC::Heap::markRoots):
(JSC::Heap::deleteAllCompiledCode):
(JSC::Heap::deleteUnmarkedCompiledCode):

  • runtime/Executable.cpp:

(JSC::ScriptExecutable::installCode): Write barrier code on installation. We do this due to the following situation:
a) A CodeBlock is created and is compiled on a DFG worker thread.
b) No GC happens.
c) The CodeBlock has finished being compiled and is installed in the Executable.
d) The function never executes before the next GC.
e) The next GC needs needs to visit the new CodeBlock but the Executable won't be revisited unless

it's added to the remembered set.

Location:
trunk/Source/JavaScriptCore
Files:
9 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/ChangeLog

    r166662 r166678  
     12014-04-02  Mark Hahnenberg  <mhahnenberg@apple.com>
     2
     3        CodeBlockSet should be generational
     4        https://bugs.webkit.org/show_bug.cgi?id=127152
     5
     6        Reviewed by Geoffrey Garen.
     7
     8        During EdenCollections we now only visit those CodeBlocks that:
     9        a) Are new since the last collection if they were somehow otherwise reachable.
     10        b) Are reachable from an Executable that is part of the remembered set.
     11
     12        * bytecode/CodeBlock.cpp:
     13        (JSC::CodeBlock::CodeBlock): Initialize uninitialized variables.
     14        (JSC::CodeBlock::visitAggregate): Move the addition of the weak reference harvester after the
     15        shouldImmediatelyAssumeLivenessDuringScan check since it's redundant if we assume liveness.
     16        * bytecode/CodeBlock.h:
     17        (JSC::CodeBlock::forEachRelatedCodeBlock): Executes a functor for each CodeBlock reachable from the current CodeBlock (including this).
     18        We use this to clear marks for the CodeBlocks of remembered Executables (see: CodeBlockSet::clearMarksForEdenCollection).
     19        (JSC::CodeBlockSet::mark): Also check the set of new CodeBlocks for memebership when doing conservative scanning.
     20        (JSC::ScriptExecutable::forEachCodeBlock): Executes a functor for each of this Executable's CodeBlocks.
     21        * heap/CodeBlockSet.cpp:
     22        (JSC::CodeBlockSet::~CodeBlockSet):
     23        (JSC::CodeBlockSet::add):
     24        (JSC::CodeBlockSet::promoteYoungCodeBlocks): Moves all CodeBlocks currently in the set of new CodeBlocks into
     25        the set of old CodeBlocks.
     26        (JSC::CodeBlockSet::clearMarksForFullCollection): Clears the marks for all CodeBlocks.
     27        (JSC::CodeBlockSet::clearMarksForEdenCollection): Clears the marks for CodeBlocks owned by Executables in the
     28        remembered set. When an Executable is added to the remembered set it's typically because we need to do something
     29        with its CodeBlock.
     30        (JSC::CodeBlockSet::clearMarks):
     31        (JSC::CodeBlockSet::deleteUnmarkedAndUnreferenced): Fixpoints over either just the new CodeBlocks or all CodeBlocks
     32        to determine which CodeBlocks are dead and eagerly finalizes/deletes them.
     33        (JSC::CodeBlockSet::remove):
     34        (JSC::CodeBlockSet::traceMarked): Iterate only the currently executing CodeBlocks instead of all CodeBlocks.
     35        (JSC::CodeBlockSet::rememberCurrentlyExecutingCodeBlocks): Clear m_mayBeExecuting for all currently executing
     36        CodeBlocks because we no longer always do this at the beginning of EdenCollections.
     37        * heap/CodeBlockSet.h:
     38        (JSC::CodeBlockSet::iterate):
     39        * heap/Heap.cpp:
     40        (JSC::Heap::markRoots):
     41        (JSC::Heap::deleteAllCompiledCode):
     42        (JSC::Heap::deleteUnmarkedCompiledCode):
     43        * runtime/Executable.cpp:
     44        (JSC::ScriptExecutable::installCode): Write barrier code on installation. We do this due to the following situation:
     45        a) A CodeBlock is created and is compiled on a DFG worker thread.
     46        b) No GC happens.
     47        c) The CodeBlock has finished being compiled and is installed in the Executable.
     48        d) The function never executes before the next GC.
     49        e) The next GC needs needs to visit the new CodeBlock but the Executable won't be revisited unless
     50            it's added to the remembered set.
     51
    1522014-04-02  Mark Lam  <mark.lam@apple.com>
    253
  • trunk/Source/JavaScriptCore/bytecode/CodeBlock.cpp

    r166392 r166678  
    14471447    , m_isStrictMode(other.m_isStrictMode)
    14481448    , m_needsActivation(other.m_needsActivation)
     1449    , m_mayBeExecuting(false)
     1450    , m_visitAggregateHasBeenCalled(false)
    14491451    , m_source(other.m_source)
    14501452    , m_sourceOffset(other.m_sourceOffset)
     
    15041506    , m_isStrictMode(unlinkedCodeBlock->isStrictMode())
    15051507    , m_needsActivation(unlinkedCodeBlock->hasActivationRegister() && unlinkedCodeBlock->codeType() == FunctionCode)
     1508    , m_mayBeExecuting(false)
     1509    , m_visitAggregateHasBeenCalled(false)
    15061510    , m_source(sourceProvider)
    15071511    , m_sourceOffset(sourceOffset)
     
    19381942    visitor.addUnconditionalFinalizer(this);
    19391943   
    1940     // There are two things that we use weak reference harvesters for: DFG fixpoint for
    1941     // jettisoning, and trying to find structures that would be live based on some
    1942     // inline cache. So it makes sense to register them regardless.
    1943     visitor.addWeakReferenceHarvester(this);
    19441944    m_allTransitionsHaveBeenMarked = false;
    19451945   
     
    19521952    }
    19531953   
     1954    // There are two things that we use weak reference harvesters for: DFG fixpoint for
     1955    // jettisoning, and trying to find structures that would be live based on some
     1956    // inline cache. So it makes sense to register them regardless.
     1957    visitor.addWeakReferenceHarvester(this);
     1958
    19541959#if ENABLE(DFG_JIT)
    19551960    // We get here if we're live in the sense that our owner executable is live,
  • trunk/Source/JavaScriptCore/bytecode/CodeBlock.h

    r166178 r166678  
    133133    PassRefPtr<CodeBlock> releaseAlternative() { return m_alternative.release(); }
    134134    void setAlternative(PassRefPtr<CodeBlock> alternative) { m_alternative = alternative; }
     135
     136    template <typename Functor> void forEachRelatedCodeBlock(Functor&& functor)
     137    {
     138        Functor f(std::forward<Functor>(functor));
     139        Vector<CodeBlock*, 4> codeBlocks;
     140        codeBlocks.append(this);
     141
     142        while (!codeBlocks.isEmpty()) {
     143            CodeBlock* currentCodeBlock = codeBlocks.takeLast();
     144            f(currentCodeBlock);
     145
     146            if (CodeBlock* alternative = currentCodeBlock->alternative())
     147                codeBlocks.append(alternative);
     148            if (CodeBlock* osrEntryBlock = currentCodeBlock->specialOSREntryBlockOrNull())
     149                codeBlocks.append(osrEntryBlock);
     150        }
     151    }
    135152   
    136153    CodeSpecializationKind specializationKind() const
     
    12761293    if (value + 1 <= 1)
    12771294        return;
    1278    
    1279     HashSet<CodeBlock*>::iterator iter = m_set.find(static_cast<CodeBlock*>(candidateCodeBlock));
    1280     if (iter == m_set.end())
     1295
     1296    CodeBlock* codeBlock = static_cast<CodeBlock*>(candidateCodeBlock);
     1297    if (!m_oldCodeBlocks.contains(codeBlock) && !m_newCodeBlocks.contains(codeBlock))
    12811298        return;
    1282    
    1283     mark(*iter);
     1299
     1300    mark(codeBlock);
    12841301}
    12851302
     
    12931310   
    12941311    codeBlock->m_mayBeExecuting = true;
     1312    // We might not have cleared the marks for this CodeBlock, but we need to visit it.
     1313    codeBlock->m_visitAggregateHasBeenCalled = false;
    12951314#if ENABLE(GGC)
    12961315    m_currentlyExecuting.append(codeBlock);
     
    12981317}
    12991318
     1319template <typename Functor> inline void ScriptExecutable::forEachCodeBlock(Functor&& functor)
     1320{
     1321    switch (type()) {
     1322    case ProgramExecutableType: {
     1323        jsCast<ProgramExecutable*>(this)->m_programCodeBlock->forEachRelatedCodeBlock(std::forward<Functor>(functor));
     1324        break;
     1325    }
     1326       
     1327    case EvalExecutableType: {
     1328        jsCast<EvalExecutable*>(this)->m_evalCodeBlock->forEachRelatedCodeBlock(std::forward<Functor>(functor));
     1329        break;
     1330    }
     1331       
     1332    case FunctionExecutableType: {
     1333        Functor f(std::forward<Functor>(functor));
     1334        FunctionExecutable* executable = jsCast<FunctionExecutable*>(this);
     1335        if (CodeBlock* codeBlock = executable->m_codeBlockForCall.get())
     1336            codeBlock->forEachRelatedCodeBlock(f);
     1337        if (CodeBlock* codeBlock = executable->m_codeBlockForConstruct.get())
     1338            codeBlock->forEachRelatedCodeBlock(f);
     1339        break;
     1340    }
     1341    default:
     1342        RELEASE_ASSERT_NOT_REACHED();
     1343    }
     1344}
     1345
    13001346} // namespace JSC
    13011347
  • trunk/Source/JavaScriptCore/heap/CodeBlockSet.cpp

    r165109 r166678  
    4242CodeBlockSet::~CodeBlockSet()
    4343{
    44     for (CodeBlock* codeBlock : m_set)
     44    for (CodeBlock* codeBlock : m_oldCodeBlocks)
     45        codeBlock->deref();
     46
     47    for (CodeBlock* codeBlock : m_newCodeBlocks)
    4548        codeBlock->deref();
    4649}
     
    4952{
    5053    CodeBlock* block = codeBlock.leakRef();
    51     bool isNewEntry = m_set.add(block).isNewEntry;
     54    bool isNewEntry = m_newCodeBlocks.add(block).isNewEntry;
    5255    ASSERT_UNUSED(isNewEntry, isNewEntry);
    5356}
    5457
    55 void CodeBlockSet::clearMarks()
     58void CodeBlockSet::promoteYoungCodeBlocks()
    5659{
    57     for (CodeBlock* codeBlock : m_set) {
     60    m_oldCodeBlocks.add(m_newCodeBlocks.begin(), m_newCodeBlocks.end());
     61    m_newCodeBlocks.clear();
     62}
     63
     64void CodeBlockSet::clearMarksForFullCollection()
     65{
     66    for (CodeBlock* codeBlock : m_oldCodeBlocks) {
    5867        codeBlock->m_mayBeExecuting = false;
    5968        codeBlock->m_visitAggregateHasBeenCalled = false;
    6069    }
     70
     71    // We promote after we clear marks on the old generation CodeBlocks because
     72    // none of the young generations CodeBlocks need to be cleared.
     73    promoteYoungCodeBlocks();
    6174}
    6275
    63 void CodeBlockSet::deleteUnmarkedAndUnreferenced()
     76void CodeBlockSet::clearMarksForEdenCollection(const Vector<const JSCell*>& rememberedSet)
    6477{
     78    // This ensures that we will revisit CodeBlocks in remembered Executables even if they were previously marked.
     79    for (const JSCell* cell : rememberedSet) {
     80        ScriptExecutable* executable = const_cast<ScriptExecutable*>(jsDynamicCast<const ScriptExecutable*>(cell));
     81        if (!executable)
     82            continue;
     83        executable->forEachCodeBlock([](CodeBlock* codeBlock) {
     84            codeBlock->m_mayBeExecuting = false;
     85            codeBlock->m_visitAggregateHasBeenCalled = false;
     86        });
     87    }
     88}
     89
     90void CodeBlockSet::deleteUnmarkedAndUnreferenced(HeapOperation collectionType)
     91{
     92    HashSet<CodeBlock*>& set = collectionType == EdenCollection ? m_newCodeBlocks : m_oldCodeBlocks;
     93
    6594    // This needs to be a fixpoint because code blocks that are unmarked may
    6695    // refer to each other. For example, a DFG code block that is owned by
     
    6998    Vector<CodeBlock*, 16> toRemove;
    7099    if (verbose)
    71         dataLog("Fixpointing over unmarked, set size = ", m_set.size(), "...\n");
     100        dataLog("Fixpointing over unmarked, set size = ", set.size(), "...\n");
    72101    for (;;) {
    73         for (CodeBlock* codeBlock : m_set) {
     102        for (CodeBlock* codeBlock : set) {
    74103            if (!codeBlock->hasOneRef())
    75104                continue;
     
    84113            break;
    85114        for (CodeBlock* codeBlock : toRemove)
    86             m_set.remove(codeBlock);
     115            set.remove(codeBlock);
    87116        toRemove.resize(0);
    88117    }
     118
     119    // Any remaining young CodeBlocks are live and need to be promoted to the set of old CodeBlocks.
     120    if (collectionType == EdenCollection)
     121        promoteYoungCodeBlocks();
    89122}
    90123
     
    92125{
    93126    codeBlock->deref();
    94     m_set.remove(codeBlock);
     127    if (m_oldCodeBlocks.contains(codeBlock)) {
     128        m_oldCodeBlocks.remove(codeBlock);
     129        return;
     130    }
     131    ASSERT(m_newCodeBlocks.contains(codeBlock));
     132    m_newCodeBlocks.remove(codeBlock);
    95133}
    96134
     
    98136{
    99137    if (verbose)
    100         dataLog("Tracing ", m_set.size(), " code blocks.\n");
    101     for (CodeBlock* codeBlock : m_set) {
    102         if (!codeBlock->m_mayBeExecuting)
    103             continue;
     138        dataLog("Tracing ", m_currentlyExecuting.size(), " code blocks.\n");
     139    for (CodeBlock* codeBlock : m_currentlyExecuting) {
     140        ASSERT(codeBlock->m_mayBeExecuting);
    104141        codeBlock->visitAggregate(visitor);
    105142    }
     
    109146{
    110147#if ENABLE(GGC)
    111     for (CodeBlock* codeBlock : m_currentlyExecuting)
     148    for (CodeBlock* codeBlock : m_currentlyExecuting) {
    112149        heap->addToRememberedSet(codeBlock->ownerExecutable());
     150        ASSERT(codeBlock->m_mayBeExecuting);
     151    }
    113152    m_currentlyExecuting.clear();
    114153#else
  • trunk/Source/JavaScriptCore/heap/CodeBlockSet.h

    r165546 r166678  
    2828
    2929#include "GCSegmentedArray.h"
     30#include "HeapOperation.h"
    3031#include <wtf/HashSet.h>
    3132#include <wtf/Noncopyable.h>
     
    3839class CodeBlock;
    3940class Heap;
     41class JSCell;
    4042class SlotVisitor;
    4143
     
    5456    void add(PassRefPtr<CodeBlock>);
    5557   
    56     // Clear all mark bits associated with DFG code blocks.
    57     void clearMarks();
    58    
     58    // Clear mark bits for certain CodeBlocks depending on the type of collection.
     59    void clearMarksForEdenCollection(const Vector<const JSCell*>&);
     60
     61    // Clear all mark bits for all CodeBlocks.
     62    void clearMarksForFullCollection();
     63
    5964    // Mark a pointer that may be a CodeBlock that belongs to the set of DFG
    6065    // blocks. This is defined in CodeBlock.h.
     
    6469    // Delete all code blocks that are only referenced by this set (i.e. owned
    6570    // by this set), and that have not been marked.
    66     void deleteUnmarkedAndUnreferenced();
     71    void deleteUnmarkedAndUnreferenced(HeapOperation);
    6772   
    6873    void remove(CodeBlock*);
     
    8186    template<typename Functor> void iterate(Functor& functor)
    8287    {
    83         for (auto& codeBlock : m_set) {
     88        for (auto& codeBlock : m_oldCodeBlocks) {
    8489            bool done = functor(codeBlock);
    8590            if (done)
    86                 break;
     91                return;
     92        }
     93
     94        for (auto& codeBlock : m_newCodeBlocks) {
     95            bool done = functor(codeBlock);
     96            if (done)
     97                return;
    8798        }
    8899    }
    89100
    90101private:
     102    void clearMarksForCodeBlocksInRememberedExecutables(const Vector<const JSCell*>&);
     103    void promoteYoungCodeBlocks();
     104
    91105    // This is not a set of RefPtr<CodeBlock> because we need to be able to find
    92106    // arbitrary bogus pointers. I could have written a thingy that had peek types
    93107    // and all, but that seemed like overkill.
    94     HashSet<CodeBlock* > m_set;
     108    HashSet<CodeBlock*> m_oldCodeBlocks;
     109    HashSet<CodeBlock*> m_newCodeBlocks;
    95110    GCSegmentedArray<CodeBlock*> m_currentlyExecuting;
    96111};
  • trunk/Source/JavaScriptCore/heap/Heap.cpp

    r166544 r166678  
    486486#endif
    487487
    488     m_codeBlocks.clearMarks();
     488#if ENABLE(GGC)
     489    Vector<const JSCell*> rememberedSet(m_slotVisitor.markStack().size());
     490    m_slotVisitor.markStack().fillVector(rememberedSet);
     491#else
     492    Vector<const JSCell*> rememberedSet;
     493#endif
     494
     495    if (m_operationInProgress == EdenCollection)
     496        m_codeBlocks.clearMarksForEdenCollection(rememberedSet);
     497    else
     498        m_codeBlocks.clearMarksForFullCollection();
    489499
    490500    // We gather conservative roots before clearing mark bits because conservative
     
    503513    m_slotVisitor.didStartMarking();
    504514    HeapRootVisitor heapRootVisitor(m_slotVisitor);
    505 
    506 #if ENABLE(GGC)
    507     Vector<const JSCell*> rememberedSet(m_slotVisitor.markStack().size());
    508     m_slotVisitor.markStack().fillVector(rememberedSet);
    509 #else
    510     Vector<const JSCell*> rememberedSet;
    511 #endif
    512515
    513516    {
     
    853856    }
    854857
    855     m_codeBlocks.clearMarks();
    856     m_codeBlocks.deleteUnmarkedAndUnreferenced();
     858    ASSERT(m_operationInProgress == FullCollection);
     859    m_codeBlocks.clearMarksForFullCollection();
     860    m_codeBlocks.deleteUnmarkedAndUnreferenced(FullCollection);
    857861}
    858862
     
    866870}
    867871
    868 void Heap::deleteUnmarkedCompiledCode()
    869 {
    870     GCPHASE(DeleteCodeBlocks);
     872void Heap::clearUnmarkedExecutables()
     873{
     874    GCPHASE(ClearUnmarkedExecutables);
    871875    ExecutableBase* next;
    872876    for (ExecutableBase* current = m_compiledCode.head(); current; current = next) {
     
    880884        m_compiledCode.remove(current);
    881885    }
    882 
    883     m_codeBlocks.deleteUnmarkedAndUnreferenced();
     886}
     887
     888void Heap::deleteUnmarkedCompiledCode()
     889{
     890    GCPHASE(DeleteCodeBlocks);
     891    clearUnmarkedExecutables();
     892    m_codeBlocks.deleteUnmarkedAndUnreferenced(m_operationInProgress);
    884893    m_jitStubRoutines.deleteUnmarkedJettisonedStubRoutines();
    885894}
     
    10121021void Heap::deleteOldCode(double gcStartTime)
    10131022{
     1023    if (m_operationInProgress == EdenCollection)
     1024        return;
     1025
    10141026    GCPHASE(DeleteOldCode);
    10151027    if (gcStartTime - m_lastCodeDiscardTime > minute) {
  • trunk/Source/JavaScriptCore/heap/Heap.h

    r166375 r166678  
    293293    void harvestWeakReferences();
    294294    void finalizeUnconditionalFinalizers();
     295    void clearUnmarkedExecutables();
    295296    void deleteUnmarkedCompiledCode();
    296297    void updateAllocationLimits();
  • trunk/Source/JavaScriptCore/runtime/Executable.cpp

    r166366 r166678  
    179179    if (debugger)
    180180        debugger->registerCodeBlock(genericCodeBlock);
     181
     182    Heap::heap(this)->writeBarrier(this);
    181183}
    182184
  • trunk/Source/JavaScriptCore/runtime/Executable.h

    r164764 r166678  
    430430    }
    431431
     432    template <typename Functor> void forEachCodeBlock(Functor&&);
     433
    432434private:
    433435    JSObject* prepareForExecutionImpl(ExecState*, JSFunction*, JSScope**, CodeSpecializationKind);
Note: See TracChangeset for help on using the changeset viewer.