From 938181c5b3205827ae2cf18a5a254e08e0369353 Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Tue, 1 Sep 2026 03:46:43 +0000 Subject: [PATCH 1/3] [JSC] CodeBlock aging: no clock reads on the compile/sweep paths Stamp BaselineJITCode::m_ownerWentAwayAt with the end time of the last collection (the CodeBlock died in it; Heap already records it) instead of ApproximateTime::now() in every ~CodeBlock, drop the now() in the BaselineJITCode initializer (an entry always has an owner until a CodeBlock dies and stamps it), and compare against the current collection's start time in releaseUnusedSharedBaselineCode. --- Source/JavaScriptCore/bytecode/CodeBlock.cpp | 2 +- Source/JavaScriptCore/heap/Heap.cpp | 2 +- Source/JavaScriptCore/heap/Heap.h | 1 + Source/JavaScriptCore/jit/BaselineJITCode.h | 8 ++++---- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Source/JavaScriptCore/bytecode/CodeBlock.cpp b/Source/JavaScriptCore/bytecode/CodeBlock.cpp index 36980cd9bc7b..1c44d1e17f26 100644 --- a/Source/JavaScriptCore/bytecode/CodeBlock.cpp +++ b/Source/JavaScriptCore/bytecode/CodeBlock.cpp @@ -905,7 +905,7 @@ CodeBlock::~CodeBlock() #if ENABLE(JIT) && USE(BUN_JSC_ADDITIONS) if (jitType() == JITType::BaselineJIT) - static_cast(m_jitCode.get())->m_ownerWentAwayAt = ApproximateTime::now(); + static_cast(m_jitCode.get())->m_ownerWentAwayAt = vm.heap.lastGCEndTime(); #endif if (JITCode::isBaselineCode(jitType())) { diff --git a/Source/JavaScriptCore/heap/Heap.cpp b/Source/JavaScriptCore/heap/Heap.cpp index f4596abb7baf..9d8c4ff2704a 100644 --- a/Source/JavaScriptCore/heap/Heap.cpp +++ b/Source/JavaScriptCore/heap/Heap.cpp @@ -1244,7 +1244,7 @@ void Heap::releaseUnusedSharedBaselineCode() #if ENABLE(JIT) && USE(BUN_JSC_ADDITIONS) if (!Options::useBaselineJITCodeSharing() || !Options::useExecutionCountForCodeBlockAging()) return; - ApproximateTime cutoff = ApproximateTime::now() - CodeBlock::timeToLive(JITType::BaselineJIT) * Options::codeBlockAgingLeaseMultiplier(); + MonotonicTime cutoff = m_currentGCStartTime - CodeBlock::timeToLive(JITType::BaselineJIT) * Options::codeBlockAgingLeaseMultiplier(); // End phase: the world is stopped and allocation already is, so no HeapIterationScope. auto visit = [&] (HeapCell* cell, HeapCell::Kind) { auto& code = static_cast(cell)->m_unlinkedBaselineCode; diff --git a/Source/JavaScriptCore/heap/Heap.h b/Source/JavaScriptCore/heap/Heap.h index 479e4b9b498d..b7fd559375f2 100644 --- a/Source/JavaScriptCore/heap/Heap.h +++ b/Source/JavaScriptCore/heap/Heap.h @@ -393,6 +393,7 @@ class Heap { MutatorState mutatorState() const { return m_mutatorState; } std::optional collectionScope() const { return m_collectionScope; } std::optional lastCollectionScope() const { return m_lastCollectionScope; } + MonotonicTime lastGCEndTime() const { return m_lastGCEndTime; } bool hasHeapAccess() const { return m_worldState.load() & hasAccessBit; } bool worldIsStopped() const { return m_worldIsStopped; } bool worldIsRunning() const { return !worldIsStopped(); } diff --git a/Source/JavaScriptCore/jit/BaselineJITCode.h b/Source/JavaScriptCore/jit/BaselineJITCode.h index 097b15a6feb5..f3f4b23299ce 100644 --- a/Source/JavaScriptCore/jit/BaselineJITCode.h +++ b/Source/JavaScriptCore/jit/BaselineJITCode.h @@ -27,7 +27,7 @@ #include "CallLinkInfo.h" #include "JITCode.h" -#include +#include #include "JITCodeMap.h" #include "PropertyInlineCache.h" #include @@ -107,9 +107,9 @@ class BaselineJITCode : public DirectJITCode, public MathICHolder { JITCodeMap m_jitCodeMap; JITConstantPool m_constantPool; std::unique_ptr m_pcToCodeOriginMap; - // When a CodeBlock running this code last went away; UnlinkedCodeBlock's cached copy is released once no CodeBlock - // has used it for a while (Heap::releaseUnusedSharedBaselineCode). - ApproximateTime m_ownerWentAwayAt { ApproximateTime::now() }; + // End time of the collection in which a CodeBlock running this code last died; UnlinkedCodeBlock's cached copy is + // released once no CodeBlock has used it for a while (Heap::releaseUnusedSharedBaselineCode). + MonotonicTime m_ownerWentAwayAt; private: // The percentage of ValueProfiles that had some profiling data in them. double m_livenessRate { 0 }; From 65db4a58f6bc9049a157c0f7bae86812ac2f7991 Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Tue, 1 Sep 2026 03:55:01 +0000 Subject: [PATCH 2/3] Stamp with the latest GC boundary (end of the last collection or start of the current one) so a CodeBlock destroyed during a collection's end phase cannot look a cycle older than it is --- Source/JavaScriptCore/bytecode/CodeBlock.cpp | 2 +- Source/JavaScriptCore/heap/Heap.h | 3 ++- Source/JavaScriptCore/jit/BaselineJITCode.h | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Source/JavaScriptCore/bytecode/CodeBlock.cpp b/Source/JavaScriptCore/bytecode/CodeBlock.cpp index 1c44d1e17f26..8896f2349b39 100644 --- a/Source/JavaScriptCore/bytecode/CodeBlock.cpp +++ b/Source/JavaScriptCore/bytecode/CodeBlock.cpp @@ -905,7 +905,7 @@ CodeBlock::~CodeBlock() #if ENABLE(JIT) && USE(BUN_JSC_ADDITIONS) if (jitType() == JITType::BaselineJIT) - static_cast(m_jitCode.get())->m_ownerWentAwayAt = vm.heap.lastGCEndTime(); + static_cast(m_jitCode.get())->m_ownerWentAwayAt = vm.heap.lastGCBoundaryTime(); #endif if (JITCode::isBaselineCode(jitType())) { diff --git a/Source/JavaScriptCore/heap/Heap.h b/Source/JavaScriptCore/heap/Heap.h index b7fd559375f2..b517984f5636 100644 --- a/Source/JavaScriptCore/heap/Heap.h +++ b/Source/JavaScriptCore/heap/Heap.h @@ -393,7 +393,8 @@ class Heap { MutatorState mutatorState() const { return m_mutatorState; } std::optional collectionScope() const { return m_collectionScope; } std::optional lastCollectionScope() const { return m_lastCollectionScope; } - MonotonicTime lastGCEndTime() const { return m_lastGCEndTime; } + // The most recent collection boundary: the end of the last one, or the start of the one in progress. + MonotonicTime lastGCBoundaryTime() const { return std::max(m_lastGCEndTime, m_currentGCStartTime); } bool hasHeapAccess() const { return m_worldState.load() & hasAccessBit; } bool worldIsStopped() const { return m_worldIsStopped; } bool worldIsRunning() const { return !worldIsStopped(); } diff --git a/Source/JavaScriptCore/jit/BaselineJITCode.h b/Source/JavaScriptCore/jit/BaselineJITCode.h index f3f4b23299ce..bf016435d313 100644 --- a/Source/JavaScriptCore/jit/BaselineJITCode.h +++ b/Source/JavaScriptCore/jit/BaselineJITCode.h @@ -107,7 +107,7 @@ class BaselineJITCode : public DirectJITCode, public MathICHolder { JITCodeMap m_jitCodeMap; JITConstantPool m_constantPool; std::unique_ptr m_pcToCodeOriginMap; - // End time of the collection in which a CodeBlock running this code last died; UnlinkedCodeBlock's cached copy is + // The collection in which a CodeBlock running this code last died (Heap::lastGCBoundaryTime); UnlinkedCodeBlock's cached copy is // released once no CodeBlock has used it for a while (Heap::releaseUnusedSharedBaselineCode). MonotonicTime m_ownerWentAwayAt; private: From 54ca0eba918c61491b3a3e7063ca93b56f287cac Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Tue, 1 Sep 2026 04:30:05 +0000 Subject: [PATCH 3/3] Guard the new member and accessor with USE(BUN_JSC_ADDITIONS) like their users --- Source/JavaScriptCore/heap/Heap.h | 2 ++ Source/JavaScriptCore/jit/BaselineJITCode.h | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Source/JavaScriptCore/heap/Heap.h b/Source/JavaScriptCore/heap/Heap.h index b517984f5636..0d3d10e94047 100644 --- a/Source/JavaScriptCore/heap/Heap.h +++ b/Source/JavaScriptCore/heap/Heap.h @@ -393,8 +393,10 @@ class Heap { MutatorState mutatorState() const { return m_mutatorState; } std::optional collectionScope() const { return m_collectionScope; } std::optional lastCollectionScope() const { return m_lastCollectionScope; } +#if USE(BUN_JSC_ADDITIONS) // The most recent collection boundary: the end of the last one, or the start of the one in progress. MonotonicTime lastGCBoundaryTime() const { return std::max(m_lastGCEndTime, m_currentGCStartTime); } +#endif bool hasHeapAccess() const { return m_worldState.load() & hasAccessBit; } bool worldIsStopped() const { return m_worldIsStopped; } bool worldIsRunning() const { return !worldIsStopped(); } diff --git a/Source/JavaScriptCore/jit/BaselineJITCode.h b/Source/JavaScriptCore/jit/BaselineJITCode.h index bf016435d313..264389f64e5c 100644 --- a/Source/JavaScriptCore/jit/BaselineJITCode.h +++ b/Source/JavaScriptCore/jit/BaselineJITCode.h @@ -107,9 +107,11 @@ class BaselineJITCode : public DirectJITCode, public MathICHolder { JITCodeMap m_jitCodeMap; JITConstantPool m_constantPool; std::unique_ptr m_pcToCodeOriginMap; - // The collection in which a CodeBlock running this code last died (Heap::lastGCBoundaryTime); UnlinkedCodeBlock's cached copy is - // released once no CodeBlock has used it for a while (Heap::releaseUnusedSharedBaselineCode). +#if USE(BUN_JSC_ADDITIONS) + // The collection in which a CodeBlock running this code last died (Heap::lastGCBoundaryTime); UnlinkedCodeBlock's + // cached copy is released once no CodeBlock has used it for a while (Heap::releaseUnusedSharedBaselineCode). MonotonicTime m_ownerWentAwayAt; +#endif private: // The percentage of ValueProfiles that had some profiling data in them. double m_livenessRate { 0 };