cppgc: Implement slim write barrier

Introduces a slim write barrier for Oilpan behind
`cppgc_enable_slim_write_barrier` that is enabled by default. The slim
write barrier only performs a single approximate global check for
whether the write barrier is needed and delegates all other checks to
a slow path call. This is beneficial in configurations that do not need
many checks for the barrier overall, i.e., configurations without
young generation. Young generation is off by default which is why this
approach is beneficial.

On Speedometer the write barrier is hit 75M times with a fast bailout
of 99.3%. Progression on Speedometer2 is somewhere around 0.2-0.5%.

The resulting code embedded in another function is only 34 bytes
compared to 128 bytes before. See attached bug for detailed assembly
snippet.

Change-Id: I6869513186e7a26104c46f1f2ac2cfa855689f64
Bug: chromium:1406464
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4152488
Reviewed-by: Anton Bikineev <bikineev@chromium.org>
Commit-Queue: Michael Lippautz <mlippautz@chromium.org>
Cr-Commit-Position: refs/heads/main@{#85232}
This commit is contained in:
Michael Lippautz 2023-01-11 14:12:51 +01:00 committed by V8 LUCI CQ
parent 080e281820
commit 24ca73004e
6 changed files with 61 additions and 0 deletions

View File

@ -68,6 +68,7 @@ load(":bazel/v8-non-pointer-compression.bzl", "v8_binary_non_pointer_compression
# v8_enable_sandbox
# cppgc_enable_caged_heap
# cppgc_enable_check_assignments_in_prefinalizers
# cppgc_enable_slim_write_barrier
# cppgc_enable_object_names
# cppgc_enable_pointer_compression
# cppgc_enable_verify_heap

View File

@ -840,6 +840,7 @@ config("v8_header_features") {
external_cppgc_defines = [
"CPPGC_SUPPORTS_OBJECT_NAMES",
"CPPGC_CAGED_HEAP",
"CPPGC_SLIM_WRITE_BARRIER",
"CPPGC_YOUNG_GENERATION",
"CPPGC_POINTER_COMPRESSION",
]
@ -869,6 +870,9 @@ if (cppgc_enable_pointer_compression) {
if (cppgc_enable_2gb_cage) {
enabled_external_cppgc_defines += [ "CPPGC_2GB_CAGE" ]
}
if (cppgc_enable_slim_write_barrier) {
enabled_external_cppgc_defines += [ "CPPGC_SLIM_WRITE_BARRIER" ]
}
disabled_external_cppgc_defines =
external_cppgc_defines - enabled_external_cppgc_defines

View File

@ -106,6 +106,12 @@ declare_args() {
# Enable young generation in cppgc.
cppgc_enable_young_generation = false
# Enables a slim write barrier that only performs a single check in the fast
# path and delegates all further checks to a slow path call. This is fast
# in a setting with few slow-path checks, i.e., with disabled young generation
# GC.
cppgc_enable_slim_write_barrier = true
# Enable pointer compression in cppgc.
cppgc_enable_pointer_compression = false

View File

@ -34,18 +34,28 @@ struct DijkstraWriteBarrierPolicy {
}
V8_INLINE static void AssigningBarrier(const void* slot, const void* value) {
#ifdef CPPGC_SLIM_WRITE_BARRIER
if (V8_UNLIKELY(WriteBarrier::IsEnabled()))
WriteBarrier::CombinedWriteBarrierSlow(slot);
#else // !CPPGC_SLIM_WRITE_BARRIER
WriteBarrier::Params params;
const WriteBarrier::Type type =
WriteBarrier::GetWriteBarrierType(slot, value, params);
WriteBarrier(type, params, slot, value);
#endif // !CPPGC_SLIM_WRITE_BARRIER
}
V8_INLINE static void AssigningBarrier(const void* slot,
MemberStorage storage) {
#ifdef CPPGC_SLIM_WRITE_BARRIER
if (V8_UNLIKELY(WriteBarrier::IsEnabled()))
WriteBarrier::CombinedWriteBarrierSlow(slot);
#else // !CPPGC_SLIM_WRITE_BARRIER
WriteBarrier::Params params;
const WriteBarrier::Type type =
WriteBarrier::GetWriteBarrierType(slot, storage, params);
WriteBarrier(type, params, slot, storage.Load());
#endif // !CPPGC_SLIM_WRITE_BARRIER
}
private:

View File

@ -79,6 +79,13 @@ class V8_EXPORT WriteBarrier final {
// Returns the required write barrier for a given `value`.
static V8_INLINE Type GetWriteBarrierType(const void* value, Params& params);
#ifdef CPPGC_SLIM_WRITE_BARRIER
// A write barrier that combines `GenerationalBarrier()` and
// `DijkstraMarkingBarrier()`. We only pass a single parameter here to clobber
// as few registers as possible.
static V8_NOINLINE void CombinedWriteBarrierSlow(const void* slot);
#endif // CPPGC_SLIM_WRITE_BARRIER
static V8_INLINE void DijkstraMarkingBarrier(const Params& params,
const void* object);
static V8_INLINE void DijkstraMarkingBarrierRange(

View File

@ -5,6 +5,7 @@
#include "src/heap/cppgc/write-barrier.h"
#include "include/cppgc/heap-consistency.h"
#include "include/cppgc/internal/member-storage.h"
#include "include/cppgc/internal/pointer-policies.h"
#include "src/heap/cppgc/globals.h"
#include "src/heap/cppgc/heap-object-header.h"
@ -222,5 +223,37 @@ bool YoungGenerationEnabler::IsEnabled() {
#endif // defined(CPPGC_YOUNG_GENERATION)
#ifdef CPPGC_SLIM_WRITE_BARRIER
// static
void WriteBarrier::CombinedWriteBarrierSlow(const void* slot) {
#if defined(CPPGC_POINTER_COMPRESSION)
using MemberStorage = CompressedPointer;
#else // !defined(CPPGC_POINTER_COMPRESSION)
using MemberStorage = RawPointer;
#endif // !defined(CPPGC_POINTER_COMPRESSION)
const auto* storage = reinterpret_cast<const MemberStorage*>(slot);
const void* value = storage->Load();
WriteBarrier::Params params;
const WriteBarrier::Type type =
WriteBarrier::GetWriteBarrierType(slot, value, params);
switch (type) {
case WriteBarrier::Type::kGenerational:
WriteBarrier::GenerationalBarrier<
WriteBarrier::GenerationalBarrierType::kPreciseSlot>(params, slot);
break;
case WriteBarrier::Type::kMarking:
WriteBarrier::DijkstraMarkingBarrier(params, value);
break;
case WriteBarrier::Type::kNone:
// The fast checks are approximate and may trigger spuriously if any heap
// has marking in progress. `GetWriteBarrierType()` above is exact which
// is the reason we could still observe a bailout here.
break;
}
}
#endif // CPPGC_SLIM_WRITE_BARRIER
} // namespace internal
} // namespace cppgc