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.


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@166678 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/heap/CodeBlockSet.cpp b/Source/JavaScriptCore/heap/CodeBlockSet.cpp
index 5655098..57ff34f 100644
--- a/Source/JavaScriptCore/heap/CodeBlockSet.cpp
+++ b/Source/JavaScriptCore/heap/CodeBlockSet.cpp
@@ -41,36 +41,65 @@
 
 CodeBlockSet::~CodeBlockSet()
 {
-    for (CodeBlock* codeBlock : m_set)
+    for (CodeBlock* codeBlock : m_oldCodeBlocks)
+        codeBlock->deref();
+
+    for (CodeBlock* codeBlock : m_newCodeBlocks)
         codeBlock->deref();
 }
 
 void CodeBlockSet::add(PassRefPtr<CodeBlock> codeBlock)
 {
     CodeBlock* block = codeBlock.leakRef();
-    bool isNewEntry = m_set.add(block).isNewEntry;
+    bool isNewEntry = m_newCodeBlocks.add(block).isNewEntry;
     ASSERT_UNUSED(isNewEntry, isNewEntry);
 }
 
-void CodeBlockSet::clearMarks()
+void CodeBlockSet::promoteYoungCodeBlocks()
 {
-    for (CodeBlock* codeBlock : m_set) {
+    m_oldCodeBlocks.add(m_newCodeBlocks.begin(), m_newCodeBlocks.end());
+    m_newCodeBlocks.clear();
+}
+
+void CodeBlockSet::clearMarksForFullCollection()
+{
+    for (CodeBlock* codeBlock : m_oldCodeBlocks) {
         codeBlock->m_mayBeExecuting = false;
         codeBlock->m_visitAggregateHasBeenCalled = false;
     }
+
+    // We promote after we clear marks on the old generation CodeBlocks because
+    // none of the young generations CodeBlocks need to be cleared.
+    promoteYoungCodeBlocks();
 }
 
-void CodeBlockSet::deleteUnmarkedAndUnreferenced()
+void CodeBlockSet::clearMarksForEdenCollection(const Vector<const JSCell*>& rememberedSet)
 {
+    // This ensures that we will revisit CodeBlocks in remembered Executables even if they were previously marked.
+    for (const JSCell* cell : rememberedSet) {
+        ScriptExecutable* executable = const_cast<ScriptExecutable*>(jsDynamicCast<const ScriptExecutable*>(cell));
+        if (!executable)
+            continue;
+        executable->forEachCodeBlock([](CodeBlock* codeBlock) {
+            codeBlock->m_mayBeExecuting = false;
+            codeBlock->m_visitAggregateHasBeenCalled = false;
+        });
+    }
+}
+
+void CodeBlockSet::deleteUnmarkedAndUnreferenced(HeapOperation collectionType)
+{
+    HashSet<CodeBlock*>& set = collectionType == EdenCollection ? m_newCodeBlocks : m_oldCodeBlocks;
+
     // This needs to be a fixpoint because code blocks that are unmarked may
     // refer to each other. For example, a DFG code block that is owned by
     // the GC may refer to an FTL for-entry code block that is also owned by
     // the GC.
     Vector<CodeBlock*, 16> toRemove;
     if (verbose)
-        dataLog("Fixpointing over unmarked, set size = ", m_set.size(), "...\n");
+        dataLog("Fixpointing over unmarked, set size = ", set.size(), "...\n");
     for (;;) {
-        for (CodeBlock* codeBlock : m_set) {
+        for (CodeBlock* codeBlock : set) {
             if (!codeBlock->hasOneRef())
                 continue;
             if (codeBlock->m_mayBeExecuting)
@@ -83,24 +112,32 @@
         if (toRemove.isEmpty())
             break;
         for (CodeBlock* codeBlock : toRemove)
-            m_set.remove(codeBlock);
+            set.remove(codeBlock);
         toRemove.resize(0);
     }
+
+    // Any remaining young CodeBlocks are live and need to be promoted to the set of old CodeBlocks.
+    if (collectionType == EdenCollection)
+        promoteYoungCodeBlocks();
 }
 
 void CodeBlockSet::remove(CodeBlock* codeBlock)
 {
     codeBlock->deref();
-    m_set.remove(codeBlock);
+    if (m_oldCodeBlocks.contains(codeBlock)) {
+        m_oldCodeBlocks.remove(codeBlock);
+        return;
+    }
+    ASSERT(m_newCodeBlocks.contains(codeBlock));
+    m_newCodeBlocks.remove(codeBlock);
 }
 
 void CodeBlockSet::traceMarked(SlotVisitor& visitor)
 {
     if (verbose)
-        dataLog("Tracing ", m_set.size(), " code blocks.\n");
-    for (CodeBlock* codeBlock : m_set) {
-        if (!codeBlock->m_mayBeExecuting)
-            continue;
+        dataLog("Tracing ", m_currentlyExecuting.size(), " code blocks.\n");
+    for (CodeBlock* codeBlock : m_currentlyExecuting) {
+        ASSERT(codeBlock->m_mayBeExecuting);
         codeBlock->visitAggregate(visitor);
     }
 }
@@ -108,8 +145,10 @@
 void CodeBlockSet::rememberCurrentlyExecutingCodeBlocks(Heap* heap)
 {
 #if ENABLE(GGC)
-    for (CodeBlock* codeBlock : m_currentlyExecuting)
+    for (CodeBlock* codeBlock : m_currentlyExecuting) {
         heap->addToRememberedSet(codeBlock->ownerExecutable());
+        ASSERT(codeBlock->m_mayBeExecuting);
+    }
     m_currentlyExecuting.clear();
 #else
     UNUSED_PARAM(heap);