Commit 0d57754c authored by Anton Bikineev's avatar Anton Bikineev Committed by V8 LUCI CQ

cppgc: young-gen: Pack AgeTable knowing allocation granularity

This reduces card granularity from 4096 to 512 bytes with the goal to
improve write barrier filtering.

Bug: chromium:1029379
Change-Id: I22e2a9c61ef4c36c3db65404370213d0a8048e08
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3582393Reviewed-by: 's avatarMichael Lippautz <mlippautz@chromium.org>
Commit-Queue: Anton Bikineev <bikineev@chromium.org>
Cr-Commit-Position: refs/heads/main@{#79960}
parent 682ba0ef
...@@ -44,6 +44,9 @@ static constexpr size_t kDefaultAlignment = sizeof(void*); ...@@ -44,6 +44,9 @@ static constexpr size_t kDefaultAlignment = sizeof(void*);
// Maximum support alignment for a type as in `alignof(T)`. // Maximum support alignment for a type as in `alignof(T)`.
static constexpr size_t kMaxSupportedAlignment = 2 * kDefaultAlignment; static constexpr size_t kMaxSupportedAlignment = 2 * kDefaultAlignment;
// Granularity of heap allocations.
constexpr size_t kAllocationGranularity = sizeof(void*);
} // namespace api_constants } // namespace api_constants
} // namespace internal } // namespace internal
......
...@@ -6,6 +6,8 @@ ...@@ -6,6 +6,8 @@
#define INCLUDE_CPPGC_INTERNAL_CAGED_HEAP_LOCAL_DATA_H_ #define INCLUDE_CPPGC_INTERNAL_CAGED_HEAP_LOCAL_DATA_H_
#include <array> #include <array>
#include <cstddef>
#include <cstdint>
#include "cppgc/internal/api-constants.h" #include "cppgc/internal/api-constants.h"
#include "cppgc/internal/logging.h" #include "cppgc/internal/logging.h"
...@@ -19,32 +21,41 @@ class HeapBase; ...@@ -19,32 +21,41 @@ class HeapBase;
#if defined(CPPGC_YOUNG_GENERATION) #if defined(CPPGC_YOUNG_GENERATION)
// AgeTable contains entries that correspond to 4KB memory regions. Each entry // AgeTable is the bytemap needed for the fast generation check in the write
// can be in one of three states: kOld, kYoung or kUnknown. // barrier. AgeTable contains entries that correspond to 512 bytes memory
// regions (cards). Each entry in the table represents generation of the objects
// that reside on the corresponding card (young, old or mixed).
class AgeTable final { class AgeTable final {
static constexpr size_t kGranularityBits = 12; // 4KiB per byte. static constexpr size_t kRequiredSize = 1 * api_constants::kMB;
static constexpr size_t kAllocationGranularity =
api_constants::kAllocationGranularity;
public: public:
enum class Age : uint8_t { kOld, kYoung, kUnknown }; enum class Age : uint8_t { kOld, kYoung, kMixed };
static constexpr size_t kEntrySizeInBytes = 1 << kGranularityBits; static constexpr size_t kCardSizeInBytes =
(api_constants::kCagedHeapReservationSize / kAllocationGranularity) /
kRequiredSize;
Age& operator[](uintptr_t offset) { return table_[entry(offset)]; } void SetAge(uintptr_t cage_offset, Age age) {
Age operator[](uintptr_t offset) const { return table_[entry(offset)]; } table_[card(cage_offset)] = age;
}
V8_INLINE Age GetAge(uintptr_t cage_offset) const {
return table_[card(cage_offset)];
}
void Reset(PageAllocator* allocator); void Reset(PageAllocator* allocator);
private: private:
static constexpr size_t kAgeTableSize = V8_INLINE size_t card(uintptr_t offset) const {
api_constants::kCagedHeapReservationSize >> kGranularityBits; constexpr size_t kGranularityBits =
__builtin_ctz(static_cast<uint32_t>(kCardSizeInBytes));
size_t entry(uintptr_t offset) const {
const size_t entry = offset >> kGranularityBits; const size_t entry = offset >> kGranularityBits;
CPPGC_DCHECK(table_.size() > entry); CPPGC_DCHECK(table_.size() > entry);
return entry; return entry;
} }
std::array<Age, kAgeTableSize> table_; std::array<Age, kRequiredSize> table_;
}; };
static_assert(sizeof(AgeTable) == 1 * api_constants::kMB, static_assert(sizeof(AgeTable) == 1 * api_constants::kMB,
......
...@@ -405,7 +405,8 @@ void WriteBarrier::GenerationalBarrier(const Params& params, const void* slot) { ...@@ -405,7 +405,8 @@ void WriteBarrier::GenerationalBarrier(const Params& params, const void* slot) {
const AgeTable& age_table = local_data.age_table; const AgeTable& age_table = local_data.age_table;
// Bail out if the slot is in young generation. // Bail out if the slot is in young generation.
if (V8_LIKELY(age_table[params.slot_offset] == AgeTable::Age::kYoung)) return; if (V8_LIKELY(age_table.GetAge(params.slot_offset) == AgeTable::Age::kYoung))
return;
GenerationalBarrierSlow(local_data, age_table, slot, params.value_offset); GenerationalBarrierSlow(local_data, age_table, slot, params.value_offset);
} }
...@@ -420,7 +421,8 @@ void WriteBarrier::GenerationalBarrierForSourceObject( ...@@ -420,7 +421,8 @@ void WriteBarrier::GenerationalBarrierForSourceObject(
// Assume that if the first element is in young generation, the whole range is // Assume that if the first element is in young generation, the whole range is
// in young generation. // in young generation.
if (V8_LIKELY(age_table[params.slot_offset] == AgeTable::Age::kYoung)) return; if (V8_LIKELY(age_table.GetAge(params.slot_offset) == AgeTable::Age::kYoung))
return;
GenerationalBarrierForSourceObjectSlow(local_data, inner_pointer); GenerationalBarrierForSourceObjectSlow(local_data, inner_pointer);
} }
......
...@@ -106,14 +106,12 @@ bool MarkingVerifierBase::VisitHeapObjectHeader(HeapObjectHeader& header) { ...@@ -106,14 +106,12 @@ bool MarkingVerifierBase::VisitHeapObjectHeader(HeapObjectHeader& header) {
#if defined(CPPGC_YOUNG_GENERATION) #if defined(CPPGC_YOUNG_GENERATION)
if (collection_type_ == Heap::Config::CollectionType::kMinor) { if (collection_type_ == Heap::Config::CollectionType::kMinor) {
const auto age = heap_.caged_heap() const auto age = heap_.caged_heap().local_data().age_table.GetAge(
.local_data() heap_.caged_heap().OffsetFromAddress(header.ObjectStart()));
.age_table[heap_.caged_heap().OffsetFromAddress(
header.ObjectStart())];
if (age == AgeTable::Age::kOld) { if (age == AgeTable::Age::kOld) {
// Do not verify old objects. // Do not verify old objects.
return true; return true;
} else if (age == AgeTable::Age::kUnknown) { } else if (age == AgeTable::Age::kMixed) {
// If the age is not known, the marked bytes may not be exact as possibly // If the age is not known, the marked bytes may not be exact as possibly
// old objects are verified as well. // old objects are verified as well.
verifier_found_marked_bytes_are_exact_ = false; verifier_found_marked_bytes_are_exact_ = false;
......
...@@ -30,7 +30,7 @@ void MarkRangeAsYoung(BasePage* page, Address begin, Address end) { ...@@ -30,7 +30,7 @@ void MarkRangeAsYoung(BasePage* page, Address begin, Address end) {
#if defined(CPPGC_YOUNG_GENERATION) #if defined(CPPGC_YOUNG_GENERATION)
DCHECK_LT(begin, end); DCHECK_LT(begin, end);
static constexpr auto kEntrySize = AgeTable::kEntrySizeInBytes; static constexpr auto kEntrySize = AgeTable::kCardSizeInBytes;
const uintptr_t offset_begin = CagedHeap::OffsetFromAddress(begin); const uintptr_t offset_begin = CagedHeap::OffsetFromAddress(begin);
const uintptr_t offset_end = CagedHeap::OffsetFromAddress(end); const uintptr_t offset_end = CagedHeap::OffsetFromAddress(end);
...@@ -44,16 +44,16 @@ void MarkRangeAsYoung(BasePage* page, Address begin, Address end) { ...@@ -44,16 +44,16 @@ void MarkRangeAsYoung(BasePage* page, Address begin, Address end) {
auto& age_table = page->heap().caged_heap().local_data().age_table; auto& age_table = page->heap().caged_heap().local_data().age_table;
for (auto offset = young_offset_begin; offset < young_offset_end; for (auto offset = young_offset_begin; offset < young_offset_end;
offset += AgeTable::kEntrySizeInBytes) { offset += AgeTable::kCardSizeInBytes) {
age_table[offset] = AgeTable::Age::kYoung; age_table.SetAge(offset, AgeTable::Age::kYoung);
} }
// Set to kUnknown the first and the last regions of the newly allocated // Set to kUnknown the first and the last regions of the newly allocated
// linear buffer. // linear buffer.
if (begin != page->PayloadStart() && !IsAligned(offset_begin, kEntrySize)) if (begin != page->PayloadStart() && !IsAligned(offset_begin, kEntrySize))
age_table[offset_begin] = AgeTable::Age::kUnknown; age_table.SetAge(offset_begin, AgeTable::Age::kMixed);
if (end != page->PayloadEnd() && !IsAligned(offset_end, kEntrySize)) if (end != page->PayloadEnd() && !IsAligned(offset_end, kEntrySize))
age_table[offset_end] = AgeTable::Age::kUnknown; age_table.SetAge(offset_end, AgeTable::Age::kMixed);
#endif #endif
} }
......
...@@ -146,6 +146,8 @@ void* ObjectAllocator::AllocateObjectOnSpace(NormalPageSpace& space, ...@@ -146,6 +146,8 @@ void* ObjectAllocator::AllocateObjectOnSpace(NormalPageSpace& space,
STATIC_ASSERT(2 * kAllocationGranularity == STATIC_ASSERT(2 * kAllocationGranularity ==
api_constants::kMaxSupportedAlignment); api_constants::kMaxSupportedAlignment);
STATIC_ASSERT(kAllocationGranularity == sizeof(HeapObjectHeader)); STATIC_ASSERT(kAllocationGranularity == sizeof(HeapObjectHeader));
STATIC_ASSERT(kAllocationGranularity ==
api_constants::kAllocationGranularity);
DCHECK_EQ(2 * sizeof(HeapObjectHeader), static_cast<size_t>(alignment)); DCHECK_EQ(2 * sizeof(HeapObjectHeader), static_cast<size_t>(alignment));
constexpr size_t kAlignment = 2 * kAllocationGranularity; constexpr size_t kAlignment = 2 * kAllocationGranularity;
constexpr size_t kAlignmentMask = kAlignment - 1; constexpr size_t kAlignmentMask = kAlignment - 1;
......
...@@ -135,7 +135,7 @@ void WriteBarrier::GenerationalBarrierSlow(const CagedHeapLocalData& local_data, ...@@ -135,7 +135,7 @@ void WriteBarrier::GenerationalBarrierSlow(const CagedHeapLocalData& local_data,
// results in applying the generational barrier. // results in applying the generational barrier.
if (local_data.heap_base.in_atomic_pause()) return; if (local_data.heap_base.in_atomic_pause()) return;
if (value_offset > 0 && age_table[value_offset] == AgeTable::Age::kOld) if (value_offset > 0 && age_table.GetAge(value_offset) == AgeTable::Age::kOld)
return; return;
// Record slot. // Record slot.
......
...@@ -262,8 +262,9 @@ void InterGenerationalPointerTest(MinorGCTest* test, cppgc::Heap* heap) { ...@@ -262,8 +262,9 @@ void InterGenerationalPointerTest(MinorGCTest* test, cppgc::Heap* heap) {
const uintptr_t offset = const uintptr_t offset =
internal_heap->caged_heap().OffsetFromAddress(young); internal_heap->caged_heap().OffsetFromAddress(young);
// Age may be young or unknown. // Age may be young or unknown.
EXPECT_NE(AgeTable::Age::kOld, EXPECT_NE(
Heap::From(heap)->caged_heap().local_data().age_table[offset]); AgeTable::Age::kOld,
Heap::From(heap)->caged_heap().local_data().age_table.GetAge(offset));
} }
} }
......
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