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:
Anton Bikineev 2022-11-18 14:50:44 +01:00 committed by V8 LUCI CQ
parent 94d83955c4
commit cd731db4b3
11 changed files with 228 additions and 5 deletions

View File

@ -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",

View File

@ -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",

View File

@ -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 ---

View File

@ -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

View File

@ -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

View 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

View 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_

View File

@ -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>

View File

@ -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;

View File

@ -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.

View File

@ -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