unified-young-gen: Implement V8->Oilpan remembered set
The CL implements an old-V8-to-young-Oilpan remembered set together with a generational barrier. Bug: v8:13475 Change-Id: I5f09f7c6db397f2a49cb0c47fd758a1604af4e83 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4030433 Reviewed-by: Omer Katz <omerkatz@chromium.org> Commit-Queue: Anton Bikineev <bikineev@chromium.org> Reviewed-by: Michael Lippautz <mlippautz@chromium.org> Cr-Commit-Position: refs/heads/main@{#84365}
This commit is contained in:
parent
94d83955c4
commit
cd731db4b3
@ -1429,6 +1429,8 @@ filegroup(
|
||||
"src/heap/cppgc-js/cpp-marking-state-inl.h",
|
||||
"src/heap/cppgc-js/cpp-snapshot.cc",
|
||||
"src/heap/cppgc-js/cpp-snapshot.h",
|
||||
"src/heap/cppgc-js/cross-heap-remembered-set.cc",
|
||||
"src/heap/cppgc-js/cross-heap-remembered-set.h",
|
||||
"src/heap/cppgc-js/unified-heap-marking-state.cc",
|
||||
"src/heap/cppgc-js/unified-heap-marking-state.h",
|
||||
"src/heap/cppgc-js/unified-heap-marking-state-inl.h",
|
||||
|
2
BUILD.gn
2
BUILD.gn
@ -3063,6 +3063,7 @@ v8_header_set("v8_internal_headers") {
|
||||
"src/heap/cppgc-js/cpp-marking-state-inl.h",
|
||||
"src/heap/cppgc-js/cpp-marking-state.h",
|
||||
"src/heap/cppgc-js/cpp-snapshot.h",
|
||||
"src/heap/cppgc-js/cross-heap-remembered-set.h",
|
||||
"src/heap/cppgc-js/unified-heap-marking-state-inl.h",
|
||||
"src/heap/cppgc-js/unified-heap-marking-state.h",
|
||||
"src/heap/cppgc-js/unified-heap-marking-verifier.h",
|
||||
@ -4477,6 +4478,7 @@ v8_source_set("v8_base_without_compiler") {
|
||||
"src/heap/concurrent-marking.cc",
|
||||
"src/heap/cppgc-js/cpp-heap.cc",
|
||||
"src/heap/cppgc-js/cpp-snapshot.cc",
|
||||
"src/heap/cppgc-js/cross-heap-remembered-set.cc",
|
||||
"src/heap/cppgc-js/unified-heap-marking-state.cc",
|
||||
"src/heap/cppgc-js/unified-heap-marking-verifier.cc",
|
||||
"src/heap/cppgc-js/unified-heap-marking-visitor.cc",
|
||||
|
@ -6144,7 +6144,8 @@ void v8::Object::SetAlignedPointerInInternalField(int index, void* value) {
|
||||
.store_aligned_pointer(obj->GetIsolate(), value),
|
||||
location, "Unaligned pointer");
|
||||
DCHECK_EQ(value, GetAlignedPointerFromInternalField(index));
|
||||
internal::WriteBarrier::MarkingFromInternalFields(i::JSObject::cast(*obj));
|
||||
internal::WriteBarrier::CombinedBarrierFromInternalFields(
|
||||
i::JSObject::cast(*obj), value);
|
||||
}
|
||||
|
||||
void v8::Object::SetAlignedPointerInInternalFields(int argc, int indices[],
|
||||
@ -6167,7 +6168,8 @@ void v8::Object::SetAlignedPointerInInternalFields(int argc, int indices[],
|
||||
location, "Unaligned pointer");
|
||||
DCHECK_EQ(value, GetAlignedPointerFromInternalField(index));
|
||||
}
|
||||
internal::WriteBarrier::MarkingFromInternalFields(js_obj);
|
||||
internal::WriteBarrier::CombinedBarrierFromInternalFields(js_obj, argc,
|
||||
values);
|
||||
}
|
||||
|
||||
// --- E n v i r o n m e n t ---
|
||||
|
@ -465,6 +465,7 @@ CppHeap::CppHeap(
|
||||
marking_support, sweeping_support, *this),
|
||||
minor_gc_heap_growing_(
|
||||
std::make_unique<MinorGCHeapGrowing>(*stats_collector())),
|
||||
cross_heap_remembered_set_(*this),
|
||||
wrapper_descriptor_(wrapper_descriptor) {
|
||||
CHECK_NE(WrapperDescriptor::kUnknownEmbedderId,
|
||||
wrapper_descriptor_.embedder_id_for_garbage_collected);
|
||||
@ -802,6 +803,9 @@ void CppHeap::TraceEpilogue() {
|
||||
|
||||
#if defined(CPPGC_YOUNG_GENERATION)
|
||||
ResetRememberedSet();
|
||||
// We can reset the remembered set on each GC because surviving Oilpan objects
|
||||
// are immediately considered old.
|
||||
ResetCrossHeapRememberedSet();
|
||||
#endif // defined(CPPGC_YOUNG_GENERATION)
|
||||
|
||||
{
|
||||
@ -1064,5 +1068,14 @@ void CppHeap::StartIncrementalGarbageCollection(cppgc::internal::GCConfig) {
|
||||
}
|
||||
size_t CppHeap::epoch() const { UNIMPLEMENTED(); }
|
||||
|
||||
void CppHeap::ResetCrossHeapRememberedSet() {
|
||||
if (!generational_gc_supported()) {
|
||||
DCHECK(cross_heap_remembered_set_.IsEmpty());
|
||||
return;
|
||||
}
|
||||
DCHECK(isolate_);
|
||||
cross_heap_remembered_set_.Reset(*isolate_);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -16,10 +16,12 @@ static_assert(
|
||||
#include "src/base/flags.h"
|
||||
#include "src/base/macros.h"
|
||||
#include "src/base/optional.h"
|
||||
#include "src/heap/cppgc-js/cross-heap-remembered-set.h"
|
||||
#include "src/heap/cppgc/heap-base.h"
|
||||
#include "src/heap/cppgc/marker.h"
|
||||
#include "src/heap/cppgc/stats-collector.h"
|
||||
#include "src/logging/metrics.h"
|
||||
#include "src/objects/js-objects.h"
|
||||
|
||||
namespace v8 {
|
||||
|
||||
@ -171,6 +173,12 @@ class V8_EXPORT_PRIVATE CppHeap final
|
||||
void StartIncrementalGarbageCollection(cppgc::internal::GCConfig) override;
|
||||
size_t epoch() const override;
|
||||
|
||||
V8_INLINE void RememberCrossHeapReferenceIfNeeded(
|
||||
v8::internal::JSObject host_obj, void* value);
|
||||
template <typename F>
|
||||
inline void VisitCrossHeapRememberedSetIfNeeded(F f);
|
||||
void ResetCrossHeapRememberedSet();
|
||||
|
||||
private:
|
||||
void ReduceGCCapabilititesFromFlags();
|
||||
|
||||
@ -198,6 +206,7 @@ class V8_EXPORT_PRIVATE CppHeap final
|
||||
GarbageCollectionFlags current_gc_flags_;
|
||||
|
||||
std::unique_ptr<MinorGCHeapGrowing> minor_gc_heap_growing_;
|
||||
CrossHeapRememberedSet cross_heap_remembered_set_;
|
||||
|
||||
std::unique_ptr<cppgc::internal::Sweeper::SweepingOnMutatorThreadObserver>
|
||||
sweeping_on_mutator_thread_observer_;
|
||||
@ -216,6 +225,21 @@ class V8_EXPORT_PRIVATE CppHeap final
|
||||
friend class MetricRecorderAdapter;
|
||||
};
|
||||
|
||||
void CppHeap::RememberCrossHeapReferenceIfNeeded(
|
||||
v8::internal::JSObject host_obj, void* value) {
|
||||
if (!generational_gc_supported()) return;
|
||||
DCHECK(isolate_);
|
||||
cross_heap_remembered_set_.RememberReferenceIfNeeded(*isolate_, host_obj,
|
||||
value);
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
void CppHeap::VisitCrossHeapRememberedSetIfNeeded(F f) {
|
||||
if (!generational_gc_supported()) return;
|
||||
DCHECK(isolate_);
|
||||
cross_heap_remembered_set_.Visit(*isolate_, std::move(f));
|
||||
}
|
||||
|
||||
DEFINE_OPERATORS_FOR_FLAGS(CppHeap::GarbageCollectionFlags)
|
||||
|
||||
} // namespace internal
|
||||
|
36
src/heap/cppgc-js/cross-heap-remembered-set.cc
Normal file
36
src/heap/cppgc-js/cross-heap-remembered-set.cc
Normal file
@ -0,0 +1,36 @@
|
||||
// Copyright 2022 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/heap/cppgc-js/cross-heap-remembered-set.h"
|
||||
|
||||
#include "src/api/api-inl.h"
|
||||
#include "src/handles/global-handles-inl.h"
|
||||
#include "src/heap/cppgc/heap-page.h"
|
||||
|
||||
namespace v8::internal {
|
||||
|
||||
void CrossHeapRememberedSet::RememberReferenceIfNeeded(Isolate& isolate,
|
||||
JSObject host_obj,
|
||||
void* cppgc_object) {
|
||||
DCHECK_NOT_NULL(cppgc_object);
|
||||
// Any in-cage pointer must point to a vaild, not freed cppgc object.
|
||||
auto* page =
|
||||
cppgc::internal::BasePage::FromInnerAddress(&heap_base_, cppgc_object);
|
||||
// TODO(v8:13475): Better filter with on-cage check.
|
||||
if (!page) return;
|
||||
auto& value_hoh = page->ObjectHeaderFromInnerAddress(cppgc_object);
|
||||
if (!value_hoh.IsYoung()) return;
|
||||
remembered_v8_to_cppgc_references_.push_back(
|
||||
isolate.global_handles()->Create(host_obj));
|
||||
}
|
||||
|
||||
void CrossHeapRememberedSet::Reset(Isolate& isolate) {
|
||||
for (auto& h : remembered_v8_to_cppgc_references_) {
|
||||
isolate.global_handles()->Destroy(h.location());
|
||||
}
|
||||
remembered_v8_to_cppgc_references_.clear();
|
||||
remembered_v8_to_cppgc_references_.shrink_to_fit();
|
||||
}
|
||||
|
||||
} // namespace v8::internal
|
54
src/heap/cppgc-js/cross-heap-remembered-set.h
Normal file
54
src/heap/cppgc-js/cross-heap-remembered-set.h
Normal file
@ -0,0 +1,54 @@
|
||||
// Copyright 2022 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef V8_HEAP_CPPGC_JS_CROSS_HEAP_REMEMBERED_SET_H_
|
||||
#define V8_HEAP_CPPGC_JS_CROSS_HEAP_REMEMBERED_SET_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "src/base/macros.h"
|
||||
#include "src/handles/handles.h"
|
||||
#include "src/objects/js-objects.h"
|
||||
|
||||
namespace cppgc::internal {
|
||||
class HeapBase;
|
||||
}
|
||||
|
||||
namespace v8::internal {
|
||||
|
||||
// The class is used to remember V8 to Oilpan references.
|
||||
class V8_EXPORT_PRIVATE CrossHeapRememberedSet final {
|
||||
public:
|
||||
explicit CrossHeapRememberedSet(cppgc::internal::HeapBase& heap_base)
|
||||
: heap_base_(heap_base) {}
|
||||
|
||||
CrossHeapRememberedSet(const CrossHeapRememberedSet&) = delete;
|
||||
CrossHeapRememberedSet(CrossHeapRememberedSet&&) = delete;
|
||||
|
||||
void RememberReferenceIfNeeded(Isolate& isolate, JSObject host_obj,
|
||||
void* cppgc_object);
|
||||
void Reset(Isolate& isolate);
|
||||
|
||||
template <typename F>
|
||||
void Visit(Isolate&, F);
|
||||
|
||||
bool IsEmpty() const { return remembered_v8_to_cppgc_references_.empty(); }
|
||||
|
||||
private:
|
||||
cppgc::internal::HeapBase& heap_base_;
|
||||
// The vector keeps handles to remembered V8 objects that have outgoing
|
||||
// references to the cppgc heap. Plese note that the handles are global.
|
||||
std::vector<Handle<JSObject>> remembered_v8_to_cppgc_references_;
|
||||
};
|
||||
|
||||
template <typename F>
|
||||
void CrossHeapRememberedSet::Visit(Isolate& isolate, F f) {
|
||||
for (auto& obj : remembered_v8_to_cppgc_references_) {
|
||||
f(*obj);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace v8::internal
|
||||
|
||||
#endif // V8_HEAP_CPPGC_JS_CROSS_HEAP_REMEMBERED_SET_H_
|
@ -10,6 +10,7 @@
|
||||
|
||||
#include "src/common/code-memory-access-inl.h"
|
||||
#include "src/common/globals.h"
|
||||
#include "src/heap/cppgc-js/cpp-heap.h"
|
||||
#include "src/heap/heap-write-barrier.h"
|
||||
#include "src/heap/marking-barrier.h"
|
||||
#include "src/objects/code.h"
|
||||
@ -327,9 +328,19 @@ void WriteBarrier::MarkingFromGlobalHandle(Object value) {
|
||||
}
|
||||
|
||||
// static
|
||||
void WriteBarrier::MarkingFromInternalFields(JSObject host) {
|
||||
void WriteBarrier::CombinedBarrierFromInternalFields(JSObject host,
|
||||
void* value) {
|
||||
CombinedBarrierFromInternalFields(host, 1, &value);
|
||||
}
|
||||
|
||||
// static
|
||||
void WriteBarrier::CombinedBarrierFromInternalFields(JSObject host, size_t argc,
|
||||
void** values) {
|
||||
if (V8_ENABLE_THIRD_PARTY_HEAP_BOOL) return;
|
||||
if (!IsMarking(host)) return;
|
||||
if (V8_LIKELY(!IsMarking(host))) {
|
||||
GenerationalBarrierFromInternalFields(host, argc, values);
|
||||
return;
|
||||
}
|
||||
MarkingBarrier* marking_barrier = CurrentMarkingBarrier(host);
|
||||
if (marking_barrier->is_minor()) {
|
||||
// TODO(v8:13012): We do not currently mark Oilpan objects while MinorMC is
|
||||
@ -340,6 +351,27 @@ void WriteBarrier::MarkingFromInternalFields(JSObject host) {
|
||||
MarkingSlowFromInternalFields(marking_barrier->heap(), host);
|
||||
}
|
||||
|
||||
// static
|
||||
void WriteBarrier::GenerationalBarrierFromInternalFields(JSObject host,
|
||||
void* value) {
|
||||
GenerationalBarrierFromInternalFields(host, 1, &value);
|
||||
}
|
||||
|
||||
// static
|
||||
void WriteBarrier::GenerationalBarrierFromInternalFields(JSObject host,
|
||||
size_t argc,
|
||||
void** values) {
|
||||
auto* memory_chunk = MemoryChunk::FromHeapObject(host);
|
||||
if (V8_LIKELY(memory_chunk->InYoungGeneration())) return;
|
||||
auto* cpp_heap = memory_chunk->heap()->cpp_heap();
|
||||
if (!cpp_heap) return;
|
||||
for (size_t i = 0; i < argc; ++i) {
|
||||
if (!values[i]) continue;
|
||||
v8::internal::CppHeap::From(cpp_heap)->RememberCrossHeapReferenceIfNeeded(
|
||||
host, values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ENABLE_SLOW_DCHECKS
|
||||
// static
|
||||
template <typename T>
|
||||
|
@ -66,7 +66,12 @@ class V8_EXPORT_PRIVATE WriteBarrier {
|
||||
|
||||
// Invoked from global handles where no host object is available.
|
||||
static inline void MarkingFromGlobalHandle(Object value);
|
||||
static inline void MarkingFromInternalFields(JSObject host);
|
||||
|
||||
static inline void CombinedBarrierFromInternalFields(JSObject host,
|
||||
void* value);
|
||||
static inline void CombinedBarrierFromInternalFields(JSObject host,
|
||||
size_t argc,
|
||||
void** values);
|
||||
|
||||
static MarkingBarrier* SetForThread(MarkingBarrier*);
|
||||
|
||||
@ -90,6 +95,12 @@ class V8_EXPORT_PRIVATE WriteBarrier {
|
||||
static void MarkingSlowFromGlobalHandle(HeapObject value);
|
||||
static void MarkingSlowFromInternalFields(Heap* heap, JSObject host);
|
||||
|
||||
static inline void GenerationalBarrierFromInternalFields(JSObject host,
|
||||
void* value);
|
||||
static inline void GenerationalBarrierFromInternalFields(JSObject host,
|
||||
size_t argc,
|
||||
void** values);
|
||||
|
||||
static void SharedSlow(Code host, RelocInfo*, HeapObject value);
|
||||
|
||||
friend class Heap;
|
||||
|
@ -557,6 +557,22 @@ bool MarkCompactCollector::StartCompaction(StartCompactionMode mode) {
|
||||
return compacting_;
|
||||
}
|
||||
|
||||
namespace {
|
||||
void VisitObjectWithEmbedderFields(JSObject object,
|
||||
MarkingWorklists::Local& worklist) {
|
||||
DCHECK(object.IsJSApiObject() || object.IsJSArrayBuffer() ||
|
||||
object.IsJSDataView() || object.IsJSTypedArray());
|
||||
DCHECK(!Heap::InYoungGeneration(object));
|
||||
|
||||
MarkingWorklists::Local::WrapperSnapshot wrapper_snapshot;
|
||||
const bool valid_snapshot =
|
||||
worklist.ExtractWrapper(object.map(), object, wrapper_snapshot);
|
||||
DCHECK(valid_snapshot);
|
||||
USE(valid_snapshot);
|
||||
worklist.PushExtractedWrapper(wrapper_snapshot);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void MarkCompactCollector::StartMarking() {
|
||||
std::vector<Address> contexts =
|
||||
heap()->memory_measurement()->StartProcessing();
|
||||
@ -6255,6 +6271,11 @@ void MinorMarkCompactCollector::MarkRootSetInParallel(
|
||||
isolate()->global_handles()->IterateYoungStrongAndDependentRoots(
|
||||
root_visitor);
|
||||
isolate()->traced_handles()->IterateYoungRoots(root_visitor);
|
||||
if (auto* cpp_heap = CppHeap::From(heap_->cpp_heap())) {
|
||||
cpp_heap->VisitCrossHeapRememberedSetIfNeeded([this](JSObject obj) {
|
||||
VisitObjectWithEmbedderFields(obj, *local_marking_worklists());
|
||||
});
|
||||
}
|
||||
|
||||
if (!was_marked_incrementally) {
|
||||
// Create items for each page.
|
||||
|
@ -128,6 +128,32 @@ TEST_F(YoungUnifiedHeapTest, FindingCppGCToV8Reference) {
|
||||
EXPECT_TRUE(local->IsObject());
|
||||
}
|
||||
|
||||
TEST_F(YoungUnifiedHeapTest, GenerationalBarrierV8ToCppGCReference) {
|
||||
if (i::v8_flags.single_generation) return;
|
||||
|
||||
v8::HandleScope scope(v8_isolate());
|
||||
v8::Local<v8::Context> context = v8::Context::New(v8_isolate());
|
||||
v8::Context::Scope context_scope(context);
|
||||
|
||||
v8::Local<v8::Object> api_object =
|
||||
WrapperHelper::CreateWrapper(context, nullptr, nullptr);
|
||||
auto handle_api_object =
|
||||
v8::Utils::OpenHandle(*v8::Local<v8::Object>::Cast(api_object));
|
||||
|
||||
EXPECT_TRUE(Heap::InYoungGeneration(*handle_api_object));
|
||||
CollectAllAvailableGarbage();
|
||||
EXPECT_EQ(0u, Wrappable::destructor_callcount);
|
||||
EXPECT_FALSE(Heap::InYoungGeneration(*handle_api_object));
|
||||
|
||||
auto* wrappable = cppgc::MakeGarbageCollected<Wrappable>(allocation_handle());
|
||||
uint16_t type_info = WrapperHelper::kTracedEmbedderId;
|
||||
WrapperHelper::SetWrappableConnection(api_object, &type_info, wrappable);
|
||||
|
||||
Wrappable::destructor_callcount = 0;
|
||||
CollectYoungGarbageWithoutEmbedderStack(cppgc::Heap::SweepingType::kAtomic);
|
||||
EXPECT_EQ(0u, Wrappable::destructor_callcount);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user