Use IsoCellSets to track Executables with clearable code.
authormark.lam@apple.com <mark.lam@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 21 Jun 2018 15:09:40 +0000 (15:09 +0000)
committermark.lam@apple.com <mark.lam@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 21 Jun 2018 15:09:40 +0000 (15:09 +0000)
https://bugs.webkit.org/show_bug.cgi?id=186877

Reviewed by Filip Pizlo.

Here’s an example of the results that this fix may yield:
1. The workload: load cnn.com, wait for it to fully load, scroll down and up.
2. Statistics on memory touched and memory freed by VM::deleteAllCode():

   Visiting Executables:
                                                Old             New
   Number of objects visited:                   70897           14264
   Number of objects with deletable code:       14264 (20.1%)   14264 (100%)
   Number of memory pages visited:              3224            1602
   Number of memory pages with deletable code:  1602 (49.7%)    1602 (100%)

   Visitng UnlinkedFunctionExecutables:
                                                Old             New
   Number of objects visited:                   105454          17231
   Number of objects with deletable code:       42319 (20.1%)   17231 (100%) **
   Number of memory pages visited:              4796            1349
   Number of memory pages with deletable code:  4013 (83.7%)    1349 (100%)

** The number of objects differ because the old code only visit unlinked
   executables indirectly via linked executables, whereas the new behavior visit
   all unlinked executables with deletable code directly.  This means:

   a. we used to not visit unlinked executables that have not been linked yet
      i.e. deleteAllCode() may not delete all code (especially code that is not
      used).
   b. we had to visit all linked executables to check if they of type
      FunctionExecutable, before going on to visit their unlinked executable, and
      this includes the ones that do not have deletable code.  This means that we
      would touch more memory in the process.

   Both of these these issues are now fixed with the new code.

This code was tested with manually inserted instrumentation to track the above
statistics.  It is not feasible to write an automated test for this without
leaving a lot of invasive instrumentation in the code.

* bytecode/UnlinkedFunctionExecutable.cpp:
(JSC::UnlinkedFunctionExecutable::unlinkedCodeBlockFor):
* bytecode/UnlinkedFunctionExecutable.h:
* heap/CodeBlockSetInlines.h:
(JSC::CodeBlockSet::iterateViaSubspaces):
* heap/Heap.cpp:
(JSC::Heap::deleteAllCodeBlocks):
(JSC::Heap::deleteAllUnlinkedCodeBlocks):
(JSC::Heap::deleteUnmarkedCompiledCode):
(JSC::Heap::clearUnmarkedExecutables): Deleted.
(JSC::Heap::addExecutable): Deleted.
* heap/Heap.h:
* runtime/DirectEvalExecutable.h:

* runtime/ExecutableBase.cpp:
(JSC::ExecutableBase::hasClearableCode const):
- this is written based on the implementation of ExecutableBase::clearCode().

* runtime/ExecutableBase.h:
* runtime/FunctionExecutable.h:
* runtime/IndirectEvalExecutable.h:
* runtime/ModuleProgramExecutable.h:
* runtime/ProgramExecutable.h:
* runtime/ScriptExecutable.cpp:
(JSC::ScriptExecutable::clearCode):
(JSC::ScriptExecutable::installCode):
* runtime/ScriptExecutable.h:
(JSC::ScriptExecutable::finishCreation):
* runtime/VM.cpp:
(JSC::VM::VM):
* runtime/VM.h:
(JSC::VM::ScriptExecutableSpaceAndSet::ScriptExecutableSpaceAndSet):
(JSC::VM::ScriptExecutableSpaceAndSet::clearableCodeSetFor):
(JSC::VM::forEachScriptExecutableSpace):
(JSC::VM::UnlinkedFunctionExecutableSpaceAndSet::UnlinkedFunctionExecutableSpaceAndSet):
(JSC::VM::UnlinkedFunctionExecutableSpaceAndSet::clearableCodeSetFor):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@233039 268f45cc-cd09-0410-ab3c-d52691b4dbfc

17 files changed:
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/bytecode/UnlinkedFunctionExecutable.cpp
Source/JavaScriptCore/bytecode/UnlinkedFunctionExecutable.h
Source/JavaScriptCore/heap/CodeBlockSetInlines.h
Source/JavaScriptCore/heap/Heap.cpp
Source/JavaScriptCore/heap/Heap.h
Source/JavaScriptCore/runtime/DirectEvalExecutable.h
Source/JavaScriptCore/runtime/ExecutableBase.cpp
Source/JavaScriptCore/runtime/ExecutableBase.h
Source/JavaScriptCore/runtime/FunctionExecutable.h
Source/JavaScriptCore/runtime/IndirectEvalExecutable.h
Source/JavaScriptCore/runtime/ModuleProgramExecutable.h
Source/JavaScriptCore/runtime/ProgramExecutable.h
Source/JavaScriptCore/runtime/ScriptExecutable.cpp
Source/JavaScriptCore/runtime/ScriptExecutable.h
Source/JavaScriptCore/runtime/VM.cpp
Source/JavaScriptCore/runtime/VM.h

index 05e0596..8838036 100644 (file)
@@ -1,3 +1,83 @@
+2018-06-21  Mark Lam  <mark.lam@apple.com>
+
+        Use IsoCellSets to track Executables with clearable code.
+        https://bugs.webkit.org/show_bug.cgi?id=186877
+
+        Reviewed by Filip Pizlo.
+
+        Here’s an example of the results that this fix may yield: 
+        1. The workload: load cnn.com, wait for it to fully load, scroll down and up.
+        2. Statistics on memory touched and memory freed by VM::deleteAllCode():
+
+           Visiting Executables:
+                                                        Old             New
+           Number of objects visited:                   70897           14264
+           Number of objects with deletable code:       14264 (20.1%)   14264 (100%)
+           Number of memory pages visited:              3224            1602
+           Number of memory pages with deletable code:  1602 (49.7%)    1602 (100%)
+
+           Visitng UnlinkedFunctionExecutables:
+                                                        Old             New
+           Number of objects visited:                   105454          17231
+           Number of objects with deletable code:       42319 (20.1%)   17231 (100%) **
+           Number of memory pages visited:              4796            1349
+           Number of memory pages with deletable code:  4013 (83.7%)    1349 (100%)
+
+        ** The number of objects differ because the old code only visit unlinked
+           executables indirectly via linked executables, whereas the new behavior visit
+           all unlinked executables with deletable code directly.  This means:
+
+           a. we used to not visit unlinked executables that have not been linked yet
+              i.e. deleteAllCode() may not delete all code (especially code that is not
+              used).
+           b. we had to visit all linked executables to check if they of type
+              FunctionExecutable, before going on to visit their unlinked executable, and
+              this includes the ones that do not have deletable code.  This means that we
+              would touch more memory in the process.
+
+           Both of these these issues are now fixed with the new code.
+
+        This code was tested with manually inserted instrumentation to track the above
+        statistics.  It is not feasible to write an automated test for this without
+        leaving a lot of invasive instrumentation in the code.
+
+        * bytecode/UnlinkedFunctionExecutable.cpp:
+        (JSC::UnlinkedFunctionExecutable::unlinkedCodeBlockFor):
+        * bytecode/UnlinkedFunctionExecutable.h:
+        * heap/CodeBlockSetInlines.h:
+        (JSC::CodeBlockSet::iterateViaSubspaces):
+        * heap/Heap.cpp:
+        (JSC::Heap::deleteAllCodeBlocks):
+        (JSC::Heap::deleteAllUnlinkedCodeBlocks):
+        (JSC::Heap::deleteUnmarkedCompiledCode):
+        (JSC::Heap::clearUnmarkedExecutables): Deleted.
+        (JSC::Heap::addExecutable): Deleted.
+        * heap/Heap.h:
+        * runtime/DirectEvalExecutable.h:
+
+        * runtime/ExecutableBase.cpp:
+        (JSC::ExecutableBase::hasClearableCode const):
+        - this is written based on the implementation of ExecutableBase::clearCode().
+
+        * runtime/ExecutableBase.h:
+        * runtime/FunctionExecutable.h:
+        * runtime/IndirectEvalExecutable.h:
+        * runtime/ModuleProgramExecutable.h:
+        * runtime/ProgramExecutable.h:
+        * runtime/ScriptExecutable.cpp:
+        (JSC::ScriptExecutable::clearCode):
+        (JSC::ScriptExecutable::installCode):
+        * runtime/ScriptExecutable.h:
+        (JSC::ScriptExecutable::finishCreation):
+        * runtime/VM.cpp:
+        (JSC::VM::VM):
+        * runtime/VM.h:
+        (JSC::VM::ScriptExecutableSpaceAndSet::ScriptExecutableSpaceAndSet):
+        (JSC::VM::ScriptExecutableSpaceAndSet::clearableCodeSetFor):
+        (JSC::VM::forEachScriptExecutableSpace):
+        (JSC::VM::UnlinkedFunctionExecutableSpaceAndSet::UnlinkedFunctionExecutableSpaceAndSet):
+        (JSC::VM::UnlinkedFunctionExecutableSpaceAndSet::clearableCodeSetFor):
+
 2018-06-21  Zan Dobersek  <zdobersek@igalia.com>
 
         [GTK] WebDriver: allow applying host-specific TLS certificates for automated sessions
index e7fc761..e540a24 100644 (file)
@@ -33,6 +33,7 @@
 #include "Debugger.h"
 #include "ExecutableInfo.h"
 #include "FunctionOverrides.h"
+#include "IsoCellSetInlines.h"
 #include "JSCInlines.h"
 #include "Parser.h"
 #include "SourceProvider.h"
@@ -224,6 +225,7 @@ UnlinkedFunctionCodeBlock* UnlinkedFunctionExecutable::unlinkedCodeBlockFor(
         m_unlinkedCodeBlockForConstruct.set(vm, this, result);
         break;
     }
+    vm.unlinkedFunctionExecutableSpace.clearableCodeSet.add(this);
     return result;
 }
 
index 0abdc05..f9e8939 100644 (file)
@@ -58,6 +58,12 @@ public:
     typedef JSCell Base;
     static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal;
 
+    template<typename CellType>
+    static IsoSubspace* subspaceFor(VM& vm)
+    {
+        return &vm.unlinkedFunctionExecutableSpace.space;
+    }
+
     static UnlinkedFunctionExecutable* create(VM* vm, const SourceCode& source, FunctionMetadataNode* node, UnlinkedFunctionKind unlinkedFunctionKind, ConstructAbility constructAbility, JSParserScriptMode scriptMode, VariableEnvironment& parentScopeTDZVariables, DerivedContextType derivedContextType, bool isBuiltinDefaultClassConstructor = false)
     {
         UnlinkedFunctionExecutable* instance = new (NotNull, allocateCell<UnlinkedFunctionExecutable>(vm->heap))
@@ -105,10 +111,11 @@ public:
 
     JS_EXPORT_PRIVATE FunctionExecutable* link(VM&, const SourceCode& parentSource, std::optional<int> overrideLineNumber = std::nullopt, Intrinsic = NoIntrinsic);
 
-    void clearCode()
+    void clearCode(VM& vm)
     {
         m_unlinkedCodeBlockForCall.clear();
         m_unlinkedCodeBlockForConstruct.clear();
+        vm.unlinkedFunctionExecutableSpace.clearableCodeSet.remove(this);
     }
 
     void recordParse(CodeFeatures features, bool hasCapturedVariables)
index d61bab4..5ddbf49 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016-2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2016-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -59,8 +59,8 @@ template<typename Functor>
 void CodeBlockSet::iterateViaSubspaces(VM& vm, const Functor& functor)
 {
     vm.forEachCodeBlockSpace(
-        [&] (IsoSubspace& space) {
-            space.forEachLiveCell(
+        [&] (auto& spaceAndSet) {
+            spaceAndSet.space.forEachLiveCell(
                 [&] (HeapCell* cell, HeapCell::Kind) {
                     functor(jsCast<CodeBlock*>(static_cast<JSCell*>(cell)));
                 });
index 88e12db..1a35a75 100644 (file)
@@ -884,18 +884,27 @@ void Heap::deleteAllCodeBlocks(DeleteAllCodeEffort effort)
 {
     if (m_collectionScope && effort == DeleteAllCodeIfNotCollecting)
         return;
-    
+
+    VM& vm = *m_vm;
     PreventCollectionScope preventCollectionScope(*this);
     
     // If JavaScript is running, it's not safe to delete all JavaScript code, since
     // we'll end up returning to deleted code.
-    RELEASE_ASSERT(!m_vm->entryScope);
+    RELEASE_ASSERT(!vm.entryScope);
     RELEASE_ASSERT(!m_collectionScope);
 
     completeAllJITPlans();
 
-    for (ExecutableBase* executable : m_executables)
-        executable->clearCode();
+    vm.forEachScriptExecutableSpace(
+        [&] (auto& spaceAndSet) {
+            HeapIterationScope heapIterationScope(*this);
+            auto& clearableCodeSet = spaceAndSet.clearableCodeSet;
+            clearableCodeSet.forEachLiveCell(
+                [&] (HeapCell* cell, HeapCell::Kind) {
+                    ScriptExecutable* executable = static_cast<ScriptExecutable*>(cell);
+                    executable->clearCode(clearableCodeSet);
+                });
+        });
 
 #if ENABLE(WEBASSEMBLY)
     {
@@ -905,10 +914,10 @@ void Heap::deleteAllCodeBlocks(DeleteAllCodeEffort effort)
         // points into a CodeBlock that could be dead. The IC will still succeed because
         // it uses a callee check, but then it will call into dead code.
         HeapIterationScope heapIterationScope(*this);
-        m_vm->webAssemblyCodeBlockSpace.forEachLiveCell([&] (HeapCell* cell, HeapCell::Kind kind) {
+        vm.webAssemblyCodeBlockSpace.forEachLiveCell([&] (HeapCell* cell, HeapCell::Kind kind) {
             ASSERT_UNUSED(kind, kind == HeapCell::Kind::JSCell);
             JSWebAssemblyCodeBlock* codeBlock = static_cast<JSWebAssemblyCodeBlock*>(cell);
-            codeBlock->clearJSCallICs(*m_vm);
+            codeBlock->clearJSCallICs(vm);
         });
     }
 #endif
@@ -918,38 +927,23 @@ void Heap::deleteAllUnlinkedCodeBlocks(DeleteAllCodeEffort effort)
 {
     if (m_collectionScope && effort == DeleteAllCodeIfNotCollecting)
         return;
-    
+
+    VM& vm = *m_vm;
     PreventCollectionScope preventCollectionScope(*this);
 
     RELEASE_ASSERT(!m_collectionScope);
-    
-    for (ExecutableBase* current : m_executables) {
-        if (!current->isFunctionExecutable())
-            continue;
-        static_cast<FunctionExecutable*>(current)->unlinkedExecutable()->clearCode();
-    }
-}
 
-void Heap::clearUnmarkedExecutables()
-{
-    for (unsigned i = m_executables.size(); i--;) {
-        ExecutableBase* current = m_executables[i];
-        if (isMarked(current))
-            continue;
-
-        // Eagerly dereference the Executable's JITCode in order to run watchpoint
-        // destructors. Otherwise, watchpoints might fire for deleted CodeBlocks.
-        current->clearCode();
-        std::swap(m_executables[i], m_executables.last());
-        m_executables.removeLast();
-    }
-
-    m_executables.shrinkToFit();
+    HeapIterationScope heapIterationScope(*this);
+    vm.unlinkedFunctionExecutableSpace.clearableCodeSet.forEachLiveCell(
+        [&] (HeapCell* cell, HeapCell::Kind) {
+            UnlinkedFunctionExecutable* executable = static_cast<UnlinkedFunctionExecutable*>(cell);
+            executable->clearCode(vm);
+        });
 }
 
 void Heap::deleteUnmarkedCompiledCode()
 {
-    clearUnmarkedExecutables();
+    vm()->forEachScriptExecutableSpace([] (auto& space) { space.space.sweep(); });
     vm()->forEachCodeBlockSpace([] (auto& space) { space.space.sweep(); }); // Sweeping must occur before deleting stubs, otherwise the stubs might still think they're alive as they get deleted.
     m_jitStubRoutines->deleteUnmarkedJettisonedStubRoutines();
 }
@@ -2380,11 +2374,6 @@ void Heap::FinalizerOwner::finalize(Handle<Unknown> handle, void* context)
     WeakSet::deallocate(WeakImpl::asWeakImpl(slot));
 }
 
-void Heap::addExecutable(ExecutableBase* executable)
-{
-    m_executables.append(executable);
-}
-
 void Heap::collectNowFullIfNotDoneRecently(Synchronousness synchronousness)
 {
     if (!m_fullActivityCallback) {
index c1e6314..ecc86b1 100644 (file)
@@ -55,7 +55,6 @@ class CollectingScope;
 class ConservativeRoots;
 class GCDeferralContext;
 class EdenGCActivityCallback;
-class ExecutableBase;
 class FullGCActivityCallback;
 class GCActivityCallback;
 class GCAwareJITStubRoutine;
@@ -165,7 +164,6 @@ public:
     
     typedef void (*Finalizer)(JSCell*);
     JS_EXPORT_PRIVATE void addFinalizer(JSCell*, Finalizer);
-    void addExecutable(ExecutableBase*);
 
     void notifyIsSafeToCollect();
     bool isSafeToCollect() const { return m_isSafeToCollect; }
@@ -509,8 +507,7 @@ private:
     void finalizeMarkedUnconditionalFinalizers(CellSet&);
 
     void finalizeUnconditionalFinalizers();
-    
-    void clearUnmarkedExecutables();
+
     void deleteUnmarkedCompiledCode();
     JS_EXPORT_PRIVATE void addToRememberedSet(const JSCell*);
     void updateAllocationLimits();
@@ -628,8 +625,6 @@ private:
     Seconds m_lastFullGCLength;
     Seconds m_lastEdenGCLength;
 
-    Vector<ExecutableBase*> m_executables;
-
     Vector<WeakBlock*> m_logicallyEmptyWeakBlocks;
     size_t m_indexOfNextLogicallyEmptyWeakBlockToSweep { WTF::notFound };
     
index 74182fd..3206708 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016-2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2016-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -34,7 +34,7 @@ public:
     template<typename CellType>
     static IsoSubspace* subspaceFor(VM& vm)
     {
-        return &vm.directEvalExecutableSpace;
+        return &vm.directEvalExecutableSpace.space;
     }
 
     static DirectEvalExecutable* create(ExecState*, const SourceCode&, bool isInStrictContext, DerivedContextType, bool isArrowFunctionContext, EvalContextType, const VariableEnvironment*);
index 4937b48..df43130 100644 (file)
@@ -49,6 +49,43 @@ void ExecutableBase::destroy(JSCell* cell)
     static_cast<ExecutableBase*>(cell)->ExecutableBase::~ExecutableBase();
 }
 
+bool ExecutableBase::hasClearableCode() const
+{
+    VM& vm = *this->vm();
+
+#if ENABLE(JIT)
+    if (m_jitCodeForCall
+        || m_jitCodeForConstruct
+        || m_jitCodeForCallWithArityCheck
+        || m_jitCodeForConstructWithArityCheck)
+        return true;
+#endif
+
+    if (structure(vm)->classInfo() == FunctionExecutable::info()) {
+        auto* executable = static_cast<const FunctionExecutable*>(this);
+        if (executable->m_codeBlockForCall || executable->m_codeBlockForConstruct)
+            return true;
+
+    } else if (structure(vm)->classInfo() == EvalExecutable::info()) {
+        auto* executable = static_cast<const EvalExecutable*>(this);
+        if (executable->m_evalCodeBlock || executable->m_unlinkedEvalCodeBlock)
+            return true;
+
+    } else if (structure(vm)->classInfo() == ProgramExecutable::info()) {
+        auto* executable = static_cast<const ProgramExecutable*>(this);
+        if (executable->m_programCodeBlock || executable->m_unlinkedProgramCodeBlock)
+            return true;
+
+    } else if (structure(vm)->classInfo() == ModuleProgramExecutable::info()) {
+        auto* executable = static_cast<const ModuleProgramExecutable*>(this);
+        if (executable->m_moduleProgramCodeBlock
+            || executable->m_unlinkedModuleProgramCodeBlock
+            || executable->m_moduleEnvironmentSymbolTable)
+            return true;
+    }
+    return false;
+}
+
 void ExecutableBase::clearCode()
 {
 #if ENABLE(JIT)
index 09466a5..c719ffb 100644 (file)
@@ -114,7 +114,8 @@ public:
     }
 
     static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue proto) { return Structure::create(vm, globalObject, proto, TypeInfo(CellType, StructureFlags), info()); }
-        
+
+    bool hasClearableCode() const;
     void clearCode();
 
     DECLARE_EXPORT_INFO;
index 646fac0..30b4b84 100644 (file)
@@ -42,7 +42,7 @@ public:
     template<typename CellType>
     static IsoSubspace* subspaceFor(VM& vm)
     {
-        return &vm.functionExecutableSpace;
+        return &vm.functionExecutableSpace.space;
     }
 
     static FunctionExecutable* create(
index f64601c..d868d93 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016-2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2016-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -34,7 +34,7 @@ public:
     template<typename CellType>
     static IsoSubspace* subspaceFor(VM& vm)
     {
-        return &vm.indirectEvalExecutableSpace;
+        return &vm.indirectEvalExecutableSpace.space;
     }
 
     static IndirectEvalExecutable* create(ExecState*, const SourceCode&, bool isInStrictContext, DerivedContextType, bool isArrowFunctionContext, EvalContextType);
index a1fe864..f5171d4 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2009-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -39,7 +39,7 @@ public:
     template<typename CellType>
     static IsoSubspace* subspaceFor(VM& vm)
     {
-        return &vm.moduleProgramExecutableSpace;
+        return &vm.moduleProgramExecutableSpace.space;
     }
 
     static ModuleProgramExecutable* create(ExecState*, const SourceCode&);
index 9837c88..12bebba 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2009-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -39,7 +39,7 @@ public:
     template<typename CellType>
     static IsoSubspace* subspaceFor(VM& vm)
     {
-        return &vm.programExecutableSpace;
+        return &vm.programExecutableSpace.space;
     }
 
     static ProgramExecutable* create(ExecState* exec, const SourceCode& source)
index 390cdde..ca23df9 100644 (file)
@@ -30,6 +30,7 @@
 #include "Debugger.h"
 #include "EvalCodeBlock.h"
 #include "FunctionCodeBlock.h"
+#include "IsoCellSetInlines.h"
 #include "JIT.h"
 #include "JSCInlines.h"
 #include "LLIntEntrypoint.h"
@@ -70,6 +71,13 @@ void ScriptExecutable::destroy(JSCell* cell)
     static_cast<ScriptExecutable*>(cell)->ScriptExecutable::~ScriptExecutable();
 }
 
+void ScriptExecutable::clearCode(IsoCellSet& clearableCodeSet)
+{
+    Base::clearCode();
+    ASSERT(&VM::ScriptExecutableSpaceAndSet::clearableCodeSetFor(*subspace()) == &clearableCodeSet);
+    clearableCodeSet.remove(this);
+}
+
 void ScriptExecutable::installCode(CodeBlock* codeBlock)
 {
     installCode(*codeBlock->vm(), codeBlock, codeBlock->codeType(), codeBlock->specializationKind());
@@ -147,6 +155,12 @@ void ScriptExecutable::installCode(VM& vm, CodeBlock* genericCodeBlock, CodeType
         break;
     }
 
+    auto& clearableCodeSet = VM::ScriptExecutableSpaceAndSet::clearableCodeSetFor(*subspace());
+    if (hasClearableCode())
+        clearableCodeSet.add(this);
+    else
+        clearableCodeSet.remove(this);
+
     if (genericCodeBlock) {
         RELEASE_ASSERT(genericCodeBlock->ownerExecutable() == this);
         RELEASE_ASSERT(JITCode::isExecutableScript(genericCodeBlock->jitType()));
index edc9b11..8d891bd 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009, 2010, 2013-2016 Apple Inc. All rights reserved.
+ * Copyright (C) 2009-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -29,6 +29,8 @@
 
 namespace JSC {
 
+class IsoCellSet;
+
 class ScriptExecutable : public ExecutableBase {
 public:
     typedef ExecutableBase Base;
@@ -94,6 +96,8 @@ public:
     CodeBlock* newCodeBlockFor(CodeSpecializationKind, JSFunction*, JSScope*, JSObject*& exception);
     CodeBlock* newReplacementCodeBlockFor(CodeSpecializationKind);
 
+    void clearCode(IsoCellSet&);
+
     // This function has an interesting GC story. Callers of this function are asking us to create a CodeBlock
     // that is not jettisoned before this function returns. Callers are essentially asking for a strong reference
     // to the CodeBlock. Because the Executable may be allocating the CodeBlock, we require callers to pass in
@@ -113,7 +117,6 @@ protected:
     void finishCreation(VM& vm)
     {
         Base::finishCreation(vm);
-        vm.heap.addExecutable(this); // Balanced by Heap::deleteUnmarkedCompiledCode().
 
 #if ENABLE(CODEBLOCK_SAMPLING)
         if (SamplingTool* sampler = vm.interpreter->sampler())
index 9eb3f0d..f019955 100644 (file)
@@ -278,13 +278,10 @@ VM::VM(VMType vmType, HeapType heapType)
     , boundFunctionSpace ISO_SUBSPACE_INIT(heap, cellJSValueOOBHeapCellType.get(), JSBoundFunction)
     , callbackFunctionSpace ISO_SUBSPACE_INIT(heap, destructibleObjectHeapCellType.get(), JSCallbackFunction)
     , customGetterSetterFunctionSpace ISO_SUBSPACE_INIT(heap, cellJSValueOOBHeapCellType.get(), JSCustomGetterSetterFunction)
-    , directEvalExecutableSpace ISO_SUBSPACE_INIT(heap, destructibleCellHeapCellType.get(), DirectEvalExecutable)
     , errorConstructorSpace ISO_SUBSPACE_INIT(heap, destructibleObjectHeapCellType.get(), ErrorConstructor)
     , executableToCodeBlockEdgeSpace ISO_SUBSPACE_INIT(heap, cellDangerousBitsHeapCellType.get(), ExecutableToCodeBlockEdge)
-    , functionExecutableSpace ISO_SUBSPACE_INIT(heap, destructibleCellHeapCellType.get(), FunctionExecutable)
     , functionSpace ISO_SUBSPACE_INIT(heap, cellJSValueOOBHeapCellType.get(), JSFunction)
     , generatorFunctionSpace ISO_SUBSPACE_INIT(heap, cellJSValueOOBHeapCellType.get(), JSGeneratorFunction)
-    , indirectEvalExecutableSpace ISO_SUBSPACE_INIT(heap, destructibleCellHeapCellType.get(), IndirectEvalExecutable)
     , inferredTypeSpace ISO_SUBSPACE_INIT(heap, destructibleCellHeapCellType.get(), InferredType)
     , inferredValueSpace ISO_SUBSPACE_INIT(heap, destructibleCellHeapCellType.get(), InferredValue)
     , internalFunctionSpace ISO_SUBSPACE_INIT(heap, destructibleObjectHeapCellType.get(), InternalFunction)
@@ -294,14 +291,12 @@ VM::VM(VMType vmType, HeapType heapType)
     , intlNumberFormatConstructorSpace ISO_SUBSPACE_INIT(heap, destructibleObjectHeapCellType.get(), IntlNumberFormatConstructor)
     , intlPluralRulesConstructorSpace ISO_SUBSPACE_INIT(heap, destructibleObjectHeapCellType.get(), IntlPluralRulesConstructor)
 #endif
-    , moduleProgramExecutableSpace ISO_SUBSPACE_INIT(heap, destructibleCellHeapCellType.get(), ModuleProgramExecutable)
     , nativeErrorConstructorSpace ISO_SUBSPACE_INIT(heap, destructibleObjectHeapCellType.get(), NativeErrorConstructor)
     , nativeExecutableSpace ISO_SUBSPACE_INIT(heap, destructibleCellHeapCellType.get(), NativeExecutable)
     , nativeStdFunctionSpace ISO_SUBSPACE_INIT(heap, cellJSValueOOBHeapCellType.get(), JSNativeStdFunction)
 #if JSC_OBJC_API_ENABLED
     , objCCallbackFunctionSpace ISO_SUBSPACE_INIT(heap, destructibleObjectHeapCellType.get(), ObjCCallbackFunction)
 #endif
-    , programExecutableSpace ISO_SUBSPACE_INIT(heap, destructibleCellHeapCellType.get(), ProgramExecutable)
     , propertyTableSpace ISO_SUBSPACE_INIT(heap, destructibleCellHeapCellType.get(), PropertyTable)
     , proxyRevokeSpace ISO_SUBSPACE_INIT(heap, destructibleObjectHeapCellType.get(), ProxyRevoke)
     , regExpConstructorSpace ISO_SUBSPACE_INIT(heap, destructibleObjectHeapCellType.get(), RegExpConstructor)
@@ -324,6 +319,12 @@ VM::VM(VMType vmType, HeapType heapType)
     , functionCodeBlockSpace ISO_SUBSPACE_INIT(heap, destructibleCellHeapCellType.get(), FunctionCodeBlock)
     , moduleProgramCodeBlockSpace ISO_SUBSPACE_INIT(heap, destructibleCellHeapCellType.get(), ModuleProgramCodeBlock)
     , programCodeBlockSpace ISO_SUBSPACE_INIT(heap, destructibleCellHeapCellType.get(), ProgramCodeBlock)
+    , directEvalExecutableSpace ISO_SUBSPACE_INIT(heap, destructibleCellHeapCellType.get(), DirectEvalExecutable)
+    , functionExecutableSpace ISO_SUBSPACE_INIT(heap, destructibleCellHeapCellType.get(), FunctionExecutable)
+    , indirectEvalExecutableSpace ISO_SUBSPACE_INIT(heap, destructibleCellHeapCellType.get(), IndirectEvalExecutable)
+    , moduleProgramExecutableSpace ISO_SUBSPACE_INIT(heap, destructibleCellHeapCellType.get(), ModuleProgramExecutable)
+    , programExecutableSpace ISO_SUBSPACE_INIT(heap, destructibleCellHeapCellType.get(), ProgramExecutable)
+    , unlinkedFunctionExecutableSpace ISO_SUBSPACE_INIT(heap, destructibleCellHeapCellType.get(), UnlinkedFunctionExecutable)
     , vmType(vmType)
     , clientData(0)
     , topEntryFrame(nullptr)
index 586ffa3..cb0d353 100644 (file)
@@ -352,13 +352,10 @@ public:
     IsoSubspace boundFunctionSpace;
     IsoSubspace callbackFunctionSpace;
     IsoSubspace customGetterSetterFunctionSpace;
-    IsoSubspace directEvalExecutableSpace;
     IsoSubspace errorConstructorSpace;
     IsoSubspace executableToCodeBlockEdgeSpace;
-    IsoSubspace functionExecutableSpace;
     IsoSubspace functionSpace;
     IsoSubspace generatorFunctionSpace;
-    IsoSubspace indirectEvalExecutableSpace;
     IsoSubspace inferredTypeSpace;
     IsoSubspace inferredValueSpace;
     IsoSubspace internalFunctionSpace;
@@ -368,14 +365,12 @@ public:
     IsoSubspace intlNumberFormatConstructorSpace;
     IsoSubspace intlPluralRulesConstructorSpace;
 #endif
-    IsoSubspace moduleProgramExecutableSpace;
     IsoSubspace nativeErrorConstructorSpace;
     IsoSubspace nativeExecutableSpace;
     IsoSubspace nativeStdFunctionSpace;
 #if JSC_OBJC_API_ENABLED
     IsoSubspace objCCallbackFunctionSpace;
 #endif
-    IsoSubspace programExecutableSpace;
     IsoSubspace propertyTableSpace;
     IsoSubspace proxyRevokeSpace;
     IsoSubspace regExpConstructorSpace;
@@ -431,7 +426,63 @@ public:
         func(moduleProgramCodeBlockSpace);
         func(programCodeBlockSpace);
     }
-    
+
+    struct ScriptExecutableSpaceAndSet {
+        IsoSubspace space;
+        IsoCellSet clearableCodeSet;
+
+        template<typename... Arguments>
+        ScriptExecutableSpaceAndSet(Arguments&&... arguments)
+            : space(std::forward<Arguments>(arguments)...)
+            , clearableCodeSet(space)
+        { }
+
+        static IsoCellSet& clearableCodeSetFor(Subspace& space)
+        {
+            return *bitwise_cast<IsoCellSet*>(
+                bitwise_cast<char*>(&space) -
+                OBJECT_OFFSETOF(ScriptExecutableSpaceAndSet, space) +
+                OBJECT_OFFSETOF(ScriptExecutableSpaceAndSet, clearableCodeSet));
+        }
+    };
+
+    ScriptExecutableSpaceAndSet directEvalExecutableSpace;
+    ScriptExecutableSpaceAndSet functionExecutableSpace;
+    ScriptExecutableSpaceAndSet indirectEvalExecutableSpace;
+    ScriptExecutableSpaceAndSet moduleProgramExecutableSpace;
+    ScriptExecutableSpaceAndSet programExecutableSpace;
+
+    template<typename Func>
+    void forEachScriptExecutableSpace(const Func& func)
+    {
+        func(directEvalExecutableSpace);
+        func(functionExecutableSpace);
+        func(indirectEvalExecutableSpace);
+        func(moduleProgramExecutableSpace);
+        func(programExecutableSpace);
+    }
+
+    struct UnlinkedFunctionExecutableSpaceAndSet {
+        IsoSubspace space;
+        IsoCellSet clearableCodeSet;
+
+        template<typename... Arguments>
+        UnlinkedFunctionExecutableSpaceAndSet(Arguments&&... arguments)
+            : space(std::forward<Arguments>(arguments)...)
+            , clearableCodeSet(space)
+        { }
+        
+        static IsoCellSet& clearableCodeSetFor(Subspace& space)
+        {
+            return *bitwise_cast<IsoCellSet*>(
+                bitwise_cast<char*>(&space) -
+                OBJECT_OFFSETOF(UnlinkedFunctionExecutableSpaceAndSet, space) +
+                OBJECT_OFFSETOF(UnlinkedFunctionExecutableSpaceAndSet, clearableCodeSet));
+        }
+    };
+
+    UnlinkedFunctionExecutableSpaceAndSet unlinkedFunctionExecutableSpace;
+
     VMType vmType;
     ClientData* clientData;
     EntryFrame* topEntryFrame;