Commit 509ee760 authored by Michael Lippautz's avatar Michael Lippautz Committed by V8 LUCI CQ

cppgc: Avoid decompression for Member write barriers

Thread through compressed pointer into write barrier to allow to delay
compression after checking whether a write barrier is actually needed.

Change-Id: If7e6cbb69a57cc9aeeb551c11f685bace4e56c4c
Bug: chromium:1325007
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3769826
Commit-Queue: Michael Lippautz <mlippautz@chromium.org>
Reviewed-by: 's avatarAnton Bikineev <bikineev@chromium.org>
Cr-Commit-Position: refs/heads/main@{#81816}
parent c66a8017
...@@ -470,6 +470,7 @@ filegroup( ...@@ -470,6 +470,7 @@ filegroup(
"include/cppgc/internal/finalizer-trait.h", "include/cppgc/internal/finalizer-trait.h",
"include/cppgc/internal/gc-info.h", "include/cppgc/internal/gc-info.h",
"include/cppgc/internal/logging.h", "include/cppgc/internal/logging.h",
"include/cppgc/internal/member-storage.h",
"include/cppgc/internal/name-trait.h", "include/cppgc/internal/name-trait.h",
"include/cppgc/internal/persistent-node.h", "include/cppgc/internal/persistent-node.h",
"include/cppgc/internal/pointer-policies.h", "include/cppgc/internal/pointer-policies.h",
...@@ -3056,8 +3057,8 @@ filegroup( ...@@ -3056,8 +3057,8 @@ filegroup(
"src/heap/cppgc/marking-visitor.h", "src/heap/cppgc/marking-visitor.h",
"src/heap/cppgc/marking-worklists.cc", "src/heap/cppgc/marking-worklists.cc",
"src/heap/cppgc/marking-worklists.h", "src/heap/cppgc/marking-worklists.h",
"src/heap/cppgc/member.cc", "src/heap/cppgc/member-storage.cc",
"src/heap/cppgc/member.h", "src/heap/cppgc/member-storage.h",
"src/heap/cppgc/memory.cc", "src/heap/cppgc/memory.cc",
"src/heap/cppgc/memory.h", "src/heap/cppgc/memory.h",
"src/heap/cppgc/metric-recorder.h", "src/heap/cppgc/metric-recorder.h",
......
...@@ -5737,6 +5737,7 @@ v8_header_set("cppgc_headers") { ...@@ -5737,6 +5737,7 @@ v8_header_set("cppgc_headers") {
"include/cppgc/internal/compiler-specific.h", "include/cppgc/internal/compiler-specific.h",
"include/cppgc/internal/finalizer-trait.h", "include/cppgc/internal/finalizer-trait.h",
"include/cppgc/internal/gc-info.h", "include/cppgc/internal/gc-info.h",
"include/cppgc/internal/member-storage.h",
"include/cppgc/internal/name-trait.h", "include/cppgc/internal/name-trait.h",
"include/cppgc/internal/persistent-node.h", "include/cppgc/internal/persistent-node.h",
"include/cppgc/internal/pointer-policies.h", "include/cppgc/internal/pointer-policies.h",
...@@ -5827,8 +5828,8 @@ v8_source_set("cppgc_base") { ...@@ -5827,8 +5828,8 @@ v8_source_set("cppgc_base") {
"src/heap/cppgc/marking-visitor.h", "src/heap/cppgc/marking-visitor.h",
"src/heap/cppgc/marking-worklists.cc", "src/heap/cppgc/marking-worklists.cc",
"src/heap/cppgc/marking-worklists.h", "src/heap/cppgc/marking-worklists.h",
"src/heap/cppgc/member.cc", "src/heap/cppgc/member-storage.cc",
"src/heap/cppgc/member.h", "src/heap/cppgc/member-storage.h",
"src/heap/cppgc/memory.cc", "src/heap/cppgc/memory.cc",
"src/heap/cppgc/memory.h", "src/heap/cppgc/memory.h",
"src/heap/cppgc/metric-recorder.h", "src/heap/cppgc/metric-recorder.h",
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "cppgc/internal/write-barrier.h" #include "cppgc/internal/write-barrier.h"
#include "cppgc/macros.h" #include "cppgc/macros.h"
#include "cppgc/member.h"
#include "cppgc/trace-trait.h" #include "cppgc/trace-trait.h"
#include "v8config.h" // NOLINT(build/include_directory) #include "v8config.h" // NOLINT(build/include_directory)
...@@ -47,6 +48,29 @@ class HeapConsistency final { ...@@ -47,6 +48,29 @@ class HeapConsistency final {
return internal::WriteBarrier::GetWriteBarrierType(slot, value, params); return internal::WriteBarrier::GetWriteBarrierType(slot, value, params);
} }
/**
* Gets the required write barrier type for a specific write. This override is
* only used for all the BasicMember types.
*
* \param slot Slot containing the pointer to the object. The slot itself
* must reside in an object that has been allocated using
* `MakeGarbageCollected()`.
* \param value The pointer to the object held via `BasicMember`.
* \param params Parameters that may be used for actual write barrier calls.
* Only filled if return value indicates that a write barrier is needed. The
* contents of the `params` are an implementation detail.
* \returns whether a write barrier is needed and which barrier to invoke.
*/
template <typename T, typename WeaknessTag, typename WriteBarrierPolicy,
typename CheckingPolicy>
static V8_INLINE WriteBarrierType GetWriteBarrierType(
const internal::BasicMember<T, WeaknessTag, WriteBarrierPolicy,
CheckingPolicy>& value,
WriteBarrierParams& params) {
return internal::WriteBarrier::GetWriteBarrierType(
value.GetRawSlot(), value.GetRawStorage(), params);
}
/** /**
* Gets the required write barrier type for a specific write. * Gets the required write barrier type for a specific write.
* *
......
// 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 INCLUDE_CPPGC_INTERNAL_MEMBER_STORAGE_H_
#define INCLUDE_CPPGC_INTERNAL_MEMBER_STORAGE_H_
#include <atomic>
#include <cstddef>
#include <type_traits>
#include "cppgc/internal/api-constants.h"
#include "cppgc/internal/logging.h"
#include "cppgc/sentinel-pointer.h"
#include "v8config.h" // NOLINT(build/include_directory)
namespace cppgc {
namespace internal {
#if defined(CPPGC_POINTER_COMPRESSION)
#if defined(__clang__)
// Attribute const allows the compiler to assume that CageBaseGlobal::g_base_
// doesn't change (e.g. across calls) and thereby avoid redundant loads.
#define CPPGC_CONST __attribute__((const))
#define CPPGC_REQUIRE_CONSTANT_INIT \
__attribute__((require_constant_initialization))
#else // defined(__clang__)
#define CPPGC_CONST
#define CPPGC_REQUIRE_CONSTANT_INIT
#endif // defined(__clang__)
class CageBaseGlobal final {
public:
V8_INLINE CPPGC_CONST static uintptr_t Get() {
CPPGC_DCHECK(IsBaseConsistent());
return g_base_;
}
V8_INLINE CPPGC_CONST static bool IsSet() {
CPPGC_DCHECK(IsBaseConsistent());
return (g_base_ & ~kLowerHalfWordMask) != 0;
}
private:
// We keep the lower halfword as ones to speed up decompression.
static constexpr uintptr_t kLowerHalfWordMask =
(api_constants::kCagedHeapReservationAlignment - 1);
static V8_EXPORT uintptr_t g_base_ CPPGC_REQUIRE_CONSTANT_INIT;
CageBaseGlobal() = delete;
V8_INLINE static bool IsBaseConsistent() {
return kLowerHalfWordMask == (g_base_ & kLowerHalfWordMask);
}
friend class CageBaseGlobalUpdater;
};
#undef CPPGC_REQUIRE_CONSTANT_INIT
#undef CPPGC_CONST
class CompressedPointer final {
public:
using IntegralType = uint32_t;
V8_INLINE CompressedPointer() : value_(0u) {}
V8_INLINE explicit CompressedPointer(const void* ptr)
: value_(Compress(ptr)) {}
V8_INLINE explicit CompressedPointer(std::nullptr_t) : value_(0u) {}
V8_INLINE explicit CompressedPointer(SentinelPointer)
: value_(kCompressedSentinel) {}
V8_INLINE const void* Load() const { return Decompress(value_); }
V8_INLINE const void* LoadAtomic() const {
return Decompress(
reinterpret_cast<const std::atomic<IntegralType>&>(value_).load(
std::memory_order_relaxed));
}
V8_INLINE void Store(const void* ptr) { value_ = Compress(ptr); }
V8_INLINE void StoreAtomic(const void* value) {
reinterpret_cast<std::atomic<IntegralType>&>(value_).store(
Compress(value), std::memory_order_relaxed);
}
V8_INLINE void Clear() { value_ = 0u; }
V8_INLINE bool IsCleared() const { return !value_; }
V8_INLINE bool IsSentinel() const { return value_ == kCompressedSentinel; }
V8_INLINE uint32_t GetAsInteger() const { return value_; }
V8_INLINE friend bool operator==(CompressedPointer a, CompressedPointer b) {
return a.value_ == b.value_;
}
V8_INLINE friend bool operator!=(CompressedPointer a, CompressedPointer b) {
return a.value_ != b.value_;
}
V8_INLINE friend bool operator<(CompressedPointer a, CompressedPointer b) {
return a.value_ < b.value_;
}
V8_INLINE friend bool operator<=(CompressedPointer a, CompressedPointer b) {
return a.value_ <= b.value_;
}
V8_INLINE friend bool operator>(CompressedPointer a, CompressedPointer b) {
return a.value_ > b.value_;
}
V8_INLINE friend bool operator>=(CompressedPointer a, CompressedPointer b) {
return a.value_ >= b.value_;
}
static V8_INLINE IntegralType Compress(const void* ptr) {
static_assert(
SentinelPointer::kSentinelValue == 0b10,
"The compression scheme relies on the sentinel encoded as 0b10");
static constexpr size_t kGigaCageMask =
~(api_constants::kCagedHeapReservationAlignment - 1);
CPPGC_DCHECK(CageBaseGlobal::IsSet());
const uintptr_t base = CageBaseGlobal::Get();
CPPGC_DCHECK(!ptr || ptr == kSentinelPointer ||
(base & kGigaCageMask) ==
(reinterpret_cast<uintptr_t>(ptr) & kGigaCageMask));
const auto uptr = reinterpret_cast<uintptr_t>(ptr);
// Shift the pointer by one and truncate.
auto compressed = static_cast<IntegralType>(uptr >> 1);
// Normal compressed pointers must have the MSB set.
CPPGC_DCHECK((!compressed || compressed == kCompressedSentinel) ||
(compressed & 0x80000000));
return compressed;
}
static V8_INLINE void* Decompress(IntegralType ptr) {
CPPGC_DCHECK(CageBaseGlobal::IsSet());
const uintptr_t base = CageBaseGlobal::Get();
// Treat compressed pointer as signed and cast it to uint64_t, which will
// sign-extend it. Then, shift the result by one. It's important to shift
// the unsigned value, as otherwise it would result in undefined behavior.
const uint64_t mask = static_cast<uint64_t>(static_cast<int32_t>(ptr)) << 1;
return reinterpret_cast<void*>(mask & base);
}
private:
static constexpr IntegralType kCompressedSentinel =
SentinelPointer::kSentinelValue >> 1;
// All constructors initialize `value_`. Do not add a default value here as it
// results in a non-atomic write on some builds, even when the atomic version
// of the constructor is used.
IntegralType value_;
};
#endif // defined(CPPGC_POINTER_COMPRESSION)
class RawPointer final {
public:
using IntegralType = uintptr_t;
V8_INLINE RawPointer() : ptr_(nullptr) {}
V8_INLINE explicit RawPointer(const void* ptr) : ptr_(ptr) {}
V8_INLINE const void* Load() const { return ptr_; }
V8_INLINE const void* LoadAtomic() const {
return reinterpret_cast<const std::atomic<const void*>&>(ptr_).load(
std::memory_order_relaxed);
}
V8_INLINE void Store(const void* ptr) { ptr_ = ptr; }
V8_INLINE void StoreAtomic(const void* ptr) {
reinterpret_cast<std::atomic<const void*>&>(ptr_).store(
ptr, std::memory_order_relaxed);
}
V8_INLINE void Clear() { ptr_ = nullptr; }
V8_INLINE bool IsCleared() const { return !ptr_; }
V8_INLINE bool IsSentinel() const { return ptr_ == kSentinelPointer; }
V8_INLINE uintptr_t GetAsInteger() const {
return reinterpret_cast<uintptr_t>(ptr_);
}
V8_INLINE friend bool operator==(RawPointer a, RawPointer b) {
return a.ptr_ == b.ptr_;
}
V8_INLINE friend bool operator!=(RawPointer a, RawPointer b) {
return a.ptr_ != b.ptr_;
}
V8_INLINE friend bool operator<(RawPointer a, RawPointer b) {
return a.ptr_ < b.ptr_;
}
V8_INLINE friend bool operator<=(RawPointer a, RawPointer b) {
return a.ptr_ <= b.ptr_;
}
V8_INLINE friend bool operator>(RawPointer a, RawPointer b) {
return a.ptr_ > b.ptr_;
}
V8_INLINE friend bool operator>=(RawPointer a, RawPointer b) {
return a.ptr_ >= b.ptr_;
}
private:
// All constructors initialize `ptr_`. Do not add a default value here as it
// results in a non-atomic write on some builds, even when the atomic version
// of the constructor is used.
const void* ptr_;
};
#if defined(CPPGC_POINTER_COMPRESSION)
using MemberStorage = CompressedPointer;
#else // !defined(CPPGC_POINTER_COMPRESSION)
using MemberStorage = RawPointer;
#endif // !defined(CPPGC_POINTER_COMPRESSION)
} // namespace internal
} // namespace cppgc
#endif // INCLUDE_CPPGC_INTERNAL_MEMBER_STORAGE_H_
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <cstdint> #include <cstdint>
#include <type_traits> #include <type_traits>
#include "cppgc/internal/member-storage.h"
#include "cppgc/internal/write-barrier.h" #include "cppgc/internal/write-barrier.h"
#include "cppgc/sentinel-pointer.h" #include "cppgc/sentinel-pointer.h"
#include "cppgc/source-location.h" #include "cppgc/source-location.h"
...@@ -31,9 +32,27 @@ struct DijkstraWriteBarrierPolicy { ...@@ -31,9 +32,27 @@ struct DijkstraWriteBarrierPolicy {
// Since in initializing writes the source object is always white, having no // Since in initializing writes the source object is always white, having no
// barrier doesn't break the tri-color invariant. // barrier doesn't break the tri-color invariant.
} }
V8_INLINE static void AssigningBarrier(const void* slot, const void* value) { V8_INLINE static void AssigningBarrier(const void* slot, const void* value) {
WriteBarrier::Params params; WriteBarrier::Params params;
switch (WriteBarrier::GetWriteBarrierType(slot, value, params)) { const WriteBarrier::Type type =
WriteBarrier::GetWriteBarrierType(slot, value, params);
WriteBarrier(type, params, slot, value);
}
V8_INLINE static void AssigningBarrier(const void* slot,
MemberStorage storage) {
WriteBarrier::Params params;
const WriteBarrier::Type type =
WriteBarrier::GetWriteBarrierType(slot, storage, params);
WriteBarrier(type, params, slot, storage.Load());
}
private:
V8_INLINE static void WriteBarrier(WriteBarrier::Type type,
const WriteBarrier::Params& params,
const void* slot, const void* value) {
switch (type) {
case WriteBarrier::Type::kGenerational: case WriteBarrier::Type::kGenerational:
WriteBarrier::GenerationalBarrier< WriteBarrier::GenerationalBarrier<
WriteBarrier::GenerationalBarrierType::kPreciseSlot>(params, slot); WriteBarrier::GenerationalBarrierType::kPreciseSlot>(params, slot);
...@@ -50,6 +69,7 @@ struct DijkstraWriteBarrierPolicy { ...@@ -50,6 +69,7 @@ struct DijkstraWriteBarrierPolicy {
struct NoWriteBarrierPolicy { struct NoWriteBarrierPolicy {
V8_INLINE static void InitializingBarrier(const void*, const void*) {} V8_INLINE static void InitializingBarrier(const void*, const void*) {}
V8_INLINE static void AssigningBarrier(const void*, const void*) {} V8_INLINE static void AssigningBarrier(const void*, const void*) {}
V8_INLINE static void AssigningBarrier(const void*, MemberStorage) {}
}; };
class V8_EXPORT SameThreadEnabledCheckingPolicyBase { class V8_EXPORT SameThreadEnabledCheckingPolicyBase {
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "cppgc/heap-state.h" #include "cppgc/heap-state.h"
#include "cppgc/internal/api-constants.h" #include "cppgc/internal/api-constants.h"
#include "cppgc/internal/atomic-entry-flag.h" #include "cppgc/internal/atomic-entry-flag.h"
#include "cppgc/internal/member-storage.h"
#include "cppgc/platform.h" #include "cppgc/platform.h"
#include "cppgc/sentinel-pointer.h" #include "cppgc/sentinel-pointer.h"
#include "cppgc/trace-trait.h" #include "cppgc/trace-trait.h"
...@@ -67,6 +68,9 @@ class V8_EXPORT WriteBarrier final { ...@@ -67,6 +68,9 @@ class V8_EXPORT WriteBarrier final {
// Returns the required write barrier for a given `slot` and `value`. // Returns the required write barrier for a given `slot` and `value`.
static V8_INLINE Type GetWriteBarrierType(const void* slot, const void* value, static V8_INLINE Type GetWriteBarrierType(const void* slot, const void* value,
Params& params); Params& params);
// Returns the required write barrier for a given `slot` and `value`.
static V8_INLINE Type GetWriteBarrierType(const void* slot, MemberStorage,
Params& params);
// Returns the required write barrier for a given `slot`. // Returns the required write barrier for a given `slot`.
template <typename HeapHandleCallback> template <typename HeapHandleCallback>
static V8_INLINE Type GetWriteBarrierType(const void* slot, Params& params, static V8_INLINE Type GetWriteBarrierType(const void* slot, Params& params,
...@@ -158,6 +162,13 @@ class V8_EXPORT WriteBarrierTypeForCagedHeapPolicy final { ...@@ -158,6 +162,13 @@ class V8_EXPORT WriteBarrierTypeForCagedHeapPolicy final {
return ValueModeDispatch<value_mode>::Get(slot, value, params, callback); return ValueModeDispatch<value_mode>::Get(slot, value, params, callback);
} }
template <WriteBarrier::ValueMode value_mode, typename HeapHandleCallback>
static V8_INLINE WriteBarrier::Type Get(const void* slot, MemberStorage value,
WriteBarrier::Params& params,
HeapHandleCallback callback) {
return ValueModeDispatch<value_mode>::Get(slot, value, params, callback);
}
template <WriteBarrier::ValueMode value_mode, typename HeapHandleCallback> template <WriteBarrier::ValueMode value_mode, typename HeapHandleCallback>
static V8_INLINE WriteBarrier::Type Get(const void* value, static V8_INLINE WriteBarrier::Type Get(const void* value,
WriteBarrier::Params& params, WriteBarrier::Params& params,
...@@ -195,6 +206,17 @@ class V8_EXPORT WriteBarrierTypeForCagedHeapPolicy final { ...@@ -195,6 +206,17 @@ class V8_EXPORT WriteBarrierTypeForCagedHeapPolicy final {
template <> template <>
struct WriteBarrierTypeForCagedHeapPolicy::ValueModeDispatch< struct WriteBarrierTypeForCagedHeapPolicy::ValueModeDispatch<
WriteBarrier::ValueMode::kValuePresent> { WriteBarrier::ValueMode::kValuePresent> {
template <typename HeapHandleCallback>
static V8_INLINE WriteBarrier::Type Get(const void* slot,
MemberStorage storage,
WriteBarrier::Params& params,
HeapHandleCallback) {
if (V8_LIKELY(!WriteBarrier::IsEnabled()))
return SetAndReturnType<WriteBarrier::Type::kNone>(params);
return BarrierEnabledGet(slot, storage.Load(), params);
}
template <typename HeapHandleCallback> template <typename HeapHandleCallback>
static V8_INLINE WriteBarrier::Type Get(const void* slot, const void* value, static V8_INLINE WriteBarrier::Type Get(const void* slot, const void* value,
WriteBarrier::Params& params, WriteBarrier::Params& params,
...@@ -202,6 +224,12 @@ struct WriteBarrierTypeForCagedHeapPolicy::ValueModeDispatch< ...@@ -202,6 +224,12 @@ struct WriteBarrierTypeForCagedHeapPolicy::ValueModeDispatch<
if (V8_LIKELY(!WriteBarrier::IsEnabled())) if (V8_LIKELY(!WriteBarrier::IsEnabled()))
return SetAndReturnType<WriteBarrier::Type::kNone>(params); return SetAndReturnType<WriteBarrier::Type::kNone>(params);
return BarrierEnabledGet(slot, value, params);
}
private:
static V8_INLINE WriteBarrier::Type BarrierEnabledGet(
const void* slot, const void* value, WriteBarrier::Params& params) {
const bool within_cage = CagedHeapBase::AreWithinCage(slot, value); const bool within_cage = CagedHeapBase::AreWithinCage(slot, value);
if (!within_cage) return WriteBarrier::Type::kNone; if (!within_cage) return WriteBarrier::Type::kNone;
...@@ -276,6 +304,16 @@ class V8_EXPORT WriteBarrierTypeForNonCagedHeapPolicy final { ...@@ -276,6 +304,16 @@ class V8_EXPORT WriteBarrierTypeForNonCagedHeapPolicy final {
return ValueModeDispatch<value_mode>::Get(slot, value, params, callback); return ValueModeDispatch<value_mode>::Get(slot, value, params, callback);
} }
template <WriteBarrier::ValueMode value_mode, typename HeapHandleCallback>
static V8_INLINE WriteBarrier::Type Get(const void* slot, MemberStorage value,
WriteBarrier::Params& params,
HeapHandleCallback callback) {
// `MemberStorage` will always be `RawPointer` for non-caged heap builds.
// Just convert to `void*` in this case.
return ValueModeDispatch<value_mode>::Get(slot, value.Load(), params,
callback);
}
template <WriteBarrier::ValueMode value_mode, typename HeapHandleCallback> template <WriteBarrier::ValueMode value_mode, typename HeapHandleCallback>
static V8_INLINE WriteBarrier::Type Get(const void* value, static V8_INLINE WriteBarrier::Type Get(const void* value,
WriteBarrier::Params& params, WriteBarrier::Params& params,
...@@ -343,6 +381,13 @@ WriteBarrier::Type WriteBarrier::GetWriteBarrierType( ...@@ -343,6 +381,13 @@ WriteBarrier::Type WriteBarrier::GetWriteBarrierType(
params, []() {}); params, []() {});
} }
// static
WriteBarrier::Type WriteBarrier::GetWriteBarrierType(
const void* slot, MemberStorage value, WriteBarrier::Params& params) {
return WriteBarrierTypePolicy::Get<ValueMode::kValuePresent>(slot, value,
params, []() {});
}
// static // static
template <typename HeapHandleCallback> template <typename HeapHandleCallback>
WriteBarrier::Type WriteBarrier::GetWriteBarrierType( WriteBarrier::Type WriteBarrier::GetWriteBarrierType(
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <type_traits> #include <type_traits>
#include "cppgc/internal/api-constants.h" #include "cppgc/internal/api-constants.h"
#include "cppgc/internal/member-storage.h"
#include "cppgc/internal/pointer-policies.h" #include "cppgc/internal/pointer-policies.h"
#include "cppgc/sentinel-pointer.h" #include "cppgc/sentinel-pointer.h"
#include "cppgc/type-traits.h" #include "cppgc/type-traits.h"
...@@ -17,201 +18,14 @@ ...@@ -17,201 +18,14 @@
namespace cppgc { namespace cppgc {
namespace subtle {
class HeapConsistency;
} // namespace subtle
class Visitor; class Visitor;
namespace internal { namespace internal {
#if defined(CPPGC_POINTER_COMPRESSION)
#if defined(__clang__)
// Attribute const allows the compiler to assume that CageBaseGlobal::g_base_
// doesn't change (e.g. across calls) and thereby avoid redundant loads.
#define CPPGC_CONST __attribute__((const))
#define CPPGC_REQUIRE_CONSTANT_INIT \
__attribute__((require_constant_initialization))
#else // defined(__clang__)
#define CPPGC_CONST
#define CPPGC_REQUIRE_CONSTANT_INIT
#endif // defined(__clang__)
class CageBaseGlobal final {
public:
V8_INLINE CPPGC_CONST static uintptr_t Get() {
CPPGC_DCHECK(IsBaseConsistent());
return g_base_;
}
V8_INLINE CPPGC_CONST static bool IsSet() {
CPPGC_DCHECK(IsBaseConsistent());
return (g_base_ & ~kLowerHalfWordMask) != 0;
}
private:
// We keep the lower halfword as ones to speed up decompression.
static constexpr uintptr_t kLowerHalfWordMask =
(api_constants::kCagedHeapReservationAlignment - 1);
static V8_EXPORT uintptr_t g_base_ CPPGC_REQUIRE_CONSTANT_INIT;
CageBaseGlobal() = delete;
V8_INLINE static bool IsBaseConsistent() {
return kLowerHalfWordMask == (g_base_ & kLowerHalfWordMask);
}
friend class CageBaseGlobalUpdater;
};
#undef CPPGC_REQUIRE_CONSTANT_INIT
#undef CPPGC_CONST
class CompressedPointer final {
public:
using IntegralType = uint32_t;
V8_INLINE CompressedPointer() : value_(0u) {}
V8_INLINE explicit CompressedPointer(const void* ptr)
: value_(Compress(ptr)) {}
V8_INLINE explicit CompressedPointer(std::nullptr_t) : value_(0u) {}
V8_INLINE explicit CompressedPointer(SentinelPointer)
: value_(kCompressedSentinel) {}
V8_INLINE const void* Load() const { return Decompress(value_); }
V8_INLINE const void* LoadAtomic() const {
return Decompress(
reinterpret_cast<const std::atomic<IntegralType>&>(value_).load(
std::memory_order_relaxed));
}
V8_INLINE void Store(const void* ptr) { value_ = Compress(ptr); }
V8_INLINE void StoreAtomic(const void* value) {
reinterpret_cast<std::atomic<IntegralType>&>(value_).store(
Compress(value), std::memory_order_relaxed);
}
V8_INLINE void Clear() { value_ = 0u; }
V8_INLINE bool IsCleared() const { return !value_; }
V8_INLINE bool IsSentinel() const { return value_ == kCompressedSentinel; }
V8_INLINE uint32_t GetAsInteger() const { return value_; }
V8_INLINE friend bool operator==(CompressedPointer a, CompressedPointer b) {
return a.value_ == b.value_;
}
V8_INLINE friend bool operator!=(CompressedPointer a, CompressedPointer b) {
return a.value_ != b.value_;
}
V8_INLINE friend bool operator<(CompressedPointer a, CompressedPointer b) {
return a.value_ < b.value_;
}
V8_INLINE friend bool operator<=(CompressedPointer a, CompressedPointer b) {
return a.value_ <= b.value_;
}
V8_INLINE friend bool operator>(CompressedPointer a, CompressedPointer b) {
return a.value_ > b.value_;
}
V8_INLINE friend bool operator>=(CompressedPointer a, CompressedPointer b) {
return a.value_ >= b.value_;
}
static V8_INLINE IntegralType Compress(const void* ptr) {
static_assert(
SentinelPointer::kSentinelValue == 0b10,
"The compression scheme relies on the sentinel encoded as 0b10");
static constexpr size_t kGigaCageMask =
~(api_constants::kCagedHeapReservationAlignment - 1);
CPPGC_DCHECK(CageBaseGlobal::IsSet());
const uintptr_t base = CageBaseGlobal::Get();
CPPGC_DCHECK(!ptr || ptr == kSentinelPointer ||
(base & kGigaCageMask) ==
(reinterpret_cast<uintptr_t>(ptr) & kGigaCageMask));
const auto uptr = reinterpret_cast<uintptr_t>(ptr);
// Shift the pointer by one and truncate.
auto compressed = static_cast<IntegralType>(uptr >> 1);
// Normal compressed pointers must have the MSB set.
CPPGC_DCHECK((!compressed || compressed == kCompressedSentinel) ||
(compressed & 0x80000000));
return compressed;
}
static V8_INLINE void* Decompress(IntegralType ptr) {
CPPGC_DCHECK(CageBaseGlobal::IsSet());
const uintptr_t base = CageBaseGlobal::Get();
// Treat compressed pointer as signed and cast it to uint64_t, which will
// sign-extend it. Then, shift the result by one. It's important to shift
// the unsigned value, as otherwise it would result in undefined behavior.
const uint64_t mask = static_cast<uint64_t>(static_cast<int32_t>(ptr)) << 1;
return reinterpret_cast<void*>(mask & base);
}
private:
static constexpr IntegralType kCompressedSentinel =
SentinelPointer::kSentinelValue >> 1;
// All constructors initialize `value_`. Do not add a default value here as it
// results in a non-atomic write on some builds, even when the atomic version
// of the constructor is used.
IntegralType value_;
};
#endif // defined(CPPGC_POINTER_COMPRESSION)
class RawPointer final {
public:
using IntegralType = uintptr_t;
V8_INLINE RawPointer() : ptr_(nullptr) {}
V8_INLINE explicit RawPointer(const void* ptr) : ptr_(ptr) {}
V8_INLINE const void* Load() const { return ptr_; }
V8_INLINE const void* LoadAtomic() const {
return reinterpret_cast<const std::atomic<const void*>&>(ptr_).load(
std::memory_order_relaxed);
}
V8_INLINE void Store(const void* ptr) { ptr_ = ptr; }
V8_INLINE void StoreAtomic(const void* ptr) {
reinterpret_cast<std::atomic<const void*>&>(ptr_).store(
ptr, std::memory_order_relaxed);
}
V8_INLINE void Clear() { ptr_ = nullptr; }
V8_INLINE bool IsCleared() const { return !ptr_; }
V8_INLINE bool IsSentinel() const { return ptr_ == kSentinelPointer; }
V8_INLINE uintptr_t GetAsInteger() const {
return reinterpret_cast<uintptr_t>(ptr_);
}
V8_INLINE friend bool operator==(RawPointer a, RawPointer b) {
return a.ptr_ == b.ptr_;
}
V8_INLINE friend bool operator!=(RawPointer a, RawPointer b) {
return a.ptr_ != b.ptr_;
}
V8_INLINE friend bool operator<(RawPointer a, RawPointer b) {
return a.ptr_ < b.ptr_;
}
V8_INLINE friend bool operator<=(RawPointer a, RawPointer b) {
return a.ptr_ <= b.ptr_;
}
V8_INLINE friend bool operator>(RawPointer a, RawPointer b) {
return a.ptr_ > b.ptr_;
}
V8_INLINE friend bool operator>=(RawPointer a, RawPointer b) {
return a.ptr_ >= b.ptr_;
}
private:
// All constructors initialize `ptr_`. Do not add a default value here as it
// results in a non-atomic write on some builds, even when the atomic version
// of the constructor is used.
const void* ptr_;
};
// MemberBase always refers to the object as const object and defers to // MemberBase always refers to the object as const object and defers to
// BasicMember on casting to the right type as needed. // BasicMember on casting to the right type as needed.
class MemberBase { class MemberBase {
...@@ -474,7 +288,7 @@ class BasicMember final : private MemberBase, private CheckingPolicy { ...@@ -474,7 +288,7 @@ class BasicMember final : private MemberBase, private CheckingPolicy {
V8_INLINE BasicMember& operator=(RawStorage other) { V8_INLINE BasicMember& operator=(RawStorage other) {
SetRawStorageAtomic(other); SetRawStorageAtomic(other);
AssigningWriteBarrier(Get()); AssigningWriteBarrier();
this->CheckPointer(Get()); this->CheckPointer(Get());
return *this; return *this;
} }
...@@ -489,11 +303,15 @@ class BasicMember final : private MemberBase, private CheckingPolicy { ...@@ -489,11 +303,15 @@ class BasicMember final : private MemberBase, private CheckingPolicy {
V8_INLINE void AssigningWriteBarrier(T* value) const { V8_INLINE void AssigningWriteBarrier(T* value) const {
WriteBarrierPolicy::AssigningBarrier(GetRawSlot(), value); WriteBarrierPolicy::AssigningBarrier(GetRawSlot(), value);
} }
V8_INLINE void AssigningWriteBarrier() const {
WriteBarrierPolicy::AssigningBarrier(GetRawSlot(), GetRawStorage());
}
V8_INLINE void ClearFromGC() const { MemberBase::ClearFromGC(); } V8_INLINE void ClearFromGC() const { MemberBase::ClearFromGC(); }
V8_INLINE T* GetFromGC() const { return Get(); } V8_INLINE T* GetFromGC() const { return Get(); }
friend class cppgc::subtle::HeapConsistency;
friend class cppgc::Visitor; friend class cppgc::Visitor;
template <typename U> template <typename U>
friend struct cppgc::TraceTrait; friend struct cppgc::TraceTrait;
......
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
#include "src/heap/cppgc/globals.h" #include "src/heap/cppgc/globals.h"
#include "src/heap/cppgc/heap-base.h" #include "src/heap/cppgc/heap-base.h"
#include "src/heap/cppgc/heap-page.h" #include "src/heap/cppgc/heap-page.h"
#include "src/heap/cppgc/member.h" #include "src/heap/cppgc/member-storage.h"
namespace cppgc { namespace cppgc {
namespace internal { namespace internal {
......
...@@ -10,7 +10,6 @@ ...@@ -10,7 +10,6 @@
#include "src/heap/cppgc/liveness-broker.h" #include "src/heap/cppgc/liveness-broker.h"
#include "src/heap/cppgc/marking-state.h" #include "src/heap/cppgc/marking-state.h"
#include "src/heap/cppgc/marking-visitor.h" #include "src/heap/cppgc/marking-visitor.h"
#include "src/heap/cppgc/member.h"
#include "src/heap/cppgc/stats-collector.h" #include "src/heap/cppgc/stats-collector.h"
namespace cppgc { namespace cppgc {
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "include/cppgc/member.h" #include "include/cppgc/internal/member-storage.h"
namespace cppgc { namespace cppgc {
namespace internal { namespace internal {
......
...@@ -2,10 +2,10 @@ ...@@ -2,10 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#ifndef V8_HEAP_CPPGC_MEMBER_H_ #ifndef V8_HEAP_CPPGC_MEMBER_STORAGE_H_
#define V8_HEAP_CPPGC_MEMBER_H_ #define V8_HEAP_CPPGC_MEMBER_STORAGE_H_
#include "include/cppgc/member.h" #include "include/cppgc/internal/member-storage.h"
namespace cppgc { namespace cppgc {
namespace internal { namespace internal {
...@@ -30,4 +30,4 @@ class CageBaseGlobalUpdater final { ...@@ -30,4 +30,4 @@ class CageBaseGlobalUpdater final {
} // namespace internal } // namespace internal
} // namespace cppgc } // namespace cppgc
#endif // V8_HEAP_CPPGC_MEMBER_H_ #endif // V8_HEAP_CPPGC_MEMBER_STORAGE_H_
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "include/cppgc/allocation.h" #include "include/cppgc/allocation.h"
#include "include/cppgc/garbage-collected.h" #include "include/cppgc/garbage-collected.h"
#include "include/cppgc/internal/member-storage.h"
#include "include/cppgc/persistent.h" #include "include/cppgc/persistent.h"
#include "include/cppgc/sentinel-pointer.h" #include "include/cppgc/sentinel-pointer.h"
#include "include/cppgc/type-traits.h" #include "include/cppgc/type-traits.h"
...@@ -67,6 +68,9 @@ struct CustomWriteBarrierPolicy { ...@@ -67,6 +68,9 @@ struct CustomWriteBarrierPolicy {
static void AssigningBarrier(const void* slot, const void* value) { static void AssigningBarrier(const void* slot, const void* value) {
++AssigningWriteBarriersTriggered; ++AssigningWriteBarriersTriggered;
} }
static void AssigningBarrier(const void* slot, MemberStorage) {
++AssigningWriteBarriersTriggered;
}
}; };
size_t CustomWriteBarrierPolicy::InitializingWriteBarriersTriggered = 0; size_t CustomWriteBarrierPolicy::InitializingWriteBarriersTriggered = 0;
size_t CustomWriteBarrierPolicy::AssigningWriteBarriersTriggered = 0; size_t CustomWriteBarrierPolicy::AssigningWriteBarriersTriggered = 0;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment