62a0841b9a
Change-Id: I38cd955d3e41861d955c529ec56890b45effccf0 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3284897 Commit-Queue: Michael Lippautz <mlippautz@chromium.org> Reviewed-by: Omer Katz <omerkatz@chromium.org> Cr-Commit-Position: refs/heads/main@{#77948}
246 lines
8.0 KiB
C++
246 lines
8.0 KiB
C++
// Copyright 2020 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/compactor.h"
|
|
|
|
#include "include/cppgc/allocation.h"
|
|
#include "include/cppgc/custom-space.h"
|
|
#include "include/cppgc/persistent.h"
|
|
#include "src/heap/cppgc/garbage-collector.h"
|
|
#include "src/heap/cppgc/heap-object-header.h"
|
|
#include "src/heap/cppgc/heap-page.h"
|
|
#include "src/heap/cppgc/marker.h"
|
|
#include "src/heap/cppgc/stats-collector.h"
|
|
#include "test/unittests/heap/cppgc/tests.h"
|
|
#include "testing/gtest/include/gtest/gtest.h"
|
|
|
|
namespace cppgc {
|
|
|
|
class CompactableCustomSpace : public CustomSpace<CompactableCustomSpace> {
|
|
public:
|
|
static constexpr size_t kSpaceIndex = 0;
|
|
static constexpr bool kSupportsCompaction = true;
|
|
};
|
|
|
|
namespace internal {
|
|
|
|
namespace {
|
|
|
|
struct CompactableGCed : public GarbageCollected<CompactableGCed> {
|
|
public:
|
|
~CompactableGCed() { ++g_destructor_callcount; }
|
|
void Trace(Visitor* visitor) const {
|
|
visitor->Trace(other);
|
|
visitor->RegisterMovableReference(other.GetSlotForTesting());
|
|
}
|
|
static size_t g_destructor_callcount;
|
|
Member<CompactableGCed> other;
|
|
size_t id = 0;
|
|
};
|
|
// static
|
|
size_t CompactableGCed::g_destructor_callcount = 0;
|
|
|
|
template <int kNumObjects>
|
|
struct CompactableHolder
|
|
: public GarbageCollected<CompactableHolder<kNumObjects>> {
|
|
public:
|
|
explicit CompactableHolder(cppgc::AllocationHandle& allocation_handle) {
|
|
for (int i = 0; i < kNumObjects; ++i)
|
|
objects[i] = MakeGarbageCollected<CompactableGCed>(allocation_handle);
|
|
}
|
|
|
|
void Trace(Visitor* visitor) const {
|
|
for (int i = 0; i < kNumObjects; ++i) {
|
|
visitor->Trace(objects[i]);
|
|
visitor->RegisterMovableReference(objects[i].GetSlotForTesting());
|
|
}
|
|
}
|
|
Member<CompactableGCed> objects[kNumObjects];
|
|
};
|
|
|
|
class CompactorTest : public testing::TestWithPlatform {
|
|
public:
|
|
CompactorTest() {
|
|
Heap::HeapOptions options;
|
|
options.custom_spaces.emplace_back(
|
|
std::make_unique<CompactableCustomSpace>());
|
|
heap_ = Heap::Create(platform_, std::move(options));
|
|
}
|
|
|
|
void StartCompaction() {
|
|
compactor().EnableForNextGCForTesting();
|
|
compactor().InitializeIfShouldCompact(
|
|
GarbageCollector::Config::MarkingType::kIncremental,
|
|
GarbageCollector::Config::StackState::kNoHeapPointers);
|
|
EXPECT_TRUE(compactor().IsEnabledForTesting());
|
|
}
|
|
|
|
void FinishCompaction() { compactor().CompactSpacesIfEnabled(); }
|
|
|
|
void StartGC() {
|
|
CompactableGCed::g_destructor_callcount = 0u;
|
|
StartCompaction();
|
|
heap()->StartIncrementalGarbageCollection(
|
|
GarbageCollector::Config::PreciseIncrementalConfig());
|
|
}
|
|
|
|
void EndGC() {
|
|
heap()->marker()->FinishMarking(
|
|
GarbageCollector::Config::StackState::kNoHeapPointers);
|
|
heap()->GetMarkerRefForTesting().reset();
|
|
FinishCompaction();
|
|
// Sweeping also verifies the object start bitmap.
|
|
const Sweeper::SweepingConfig sweeping_config{
|
|
Sweeper::SweepingConfig::SweepingType::kAtomic,
|
|
Sweeper::SweepingConfig::CompactableSpaceHandling::kIgnore};
|
|
heap()->sweeper().Start(sweeping_config);
|
|
}
|
|
|
|
Heap* heap() { return Heap::From(heap_.get()); }
|
|
cppgc::AllocationHandle& GetAllocationHandle() {
|
|
return heap_->GetAllocationHandle();
|
|
}
|
|
Compactor& compactor() { return heap()->compactor(); }
|
|
|
|
private:
|
|
std::unique_ptr<cppgc::Heap> heap_;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
} // namespace internal
|
|
|
|
template <>
|
|
struct SpaceTrait<internal::CompactableGCed> {
|
|
using Space = CompactableCustomSpace;
|
|
};
|
|
|
|
namespace internal {
|
|
|
|
TEST_F(CompactorTest, NothingToCompact) {
|
|
StartCompaction();
|
|
heap()->stats_collector()->NotifyMarkingStarted(
|
|
GarbageCollector::Config::CollectionType::kMajor,
|
|
GarbageCollector::Config::IsForcedGC::kNotForced);
|
|
heap()->stats_collector()->NotifyMarkingCompleted(0);
|
|
FinishCompaction();
|
|
heap()->stats_collector()->NotifySweepingCompleted();
|
|
}
|
|
|
|
TEST_F(CompactorTest, NonEmptySpaceAllLive) {
|
|
static constexpr int kNumObjects = 10;
|
|
Persistent<CompactableHolder<kNumObjects>> holder =
|
|
MakeGarbageCollected<CompactableHolder<kNumObjects>>(
|
|
GetAllocationHandle(), GetAllocationHandle());
|
|
CompactableGCed* references[kNumObjects] = {nullptr};
|
|
for (int i = 0; i < kNumObjects; ++i) {
|
|
references[i] = holder->objects[i];
|
|
}
|
|
StartGC();
|
|
EndGC();
|
|
EXPECT_EQ(0u, CompactableGCed::g_destructor_callcount);
|
|
for (int i = 0; i < kNumObjects; ++i) {
|
|
EXPECT_EQ(holder->objects[i], references[i]);
|
|
}
|
|
}
|
|
|
|
TEST_F(CompactorTest, NonEmptySpaceAllDead) {
|
|
static constexpr int kNumObjects = 10;
|
|
Persistent<CompactableHolder<kNumObjects>> holder =
|
|
MakeGarbageCollected<CompactableHolder<kNumObjects>>(
|
|
GetAllocationHandle(), GetAllocationHandle());
|
|
CompactableGCed::g_destructor_callcount = 0u;
|
|
StartGC();
|
|
for (int i = 0; i < kNumObjects; ++i) {
|
|
holder->objects[i] = nullptr;
|
|
}
|
|
EndGC();
|
|
EXPECT_EQ(10u, CompactableGCed::g_destructor_callcount);
|
|
}
|
|
|
|
TEST_F(CompactorTest, NonEmptySpaceHalfLive) {
|
|
static constexpr int kNumObjects = 10;
|
|
Persistent<CompactableHolder<kNumObjects>> holder =
|
|
MakeGarbageCollected<CompactableHolder<kNumObjects>>(
|
|
GetAllocationHandle(), GetAllocationHandle());
|
|
CompactableGCed* references[kNumObjects] = {nullptr};
|
|
for (int i = 0; i < kNumObjects; ++i) {
|
|
references[i] = holder->objects[i];
|
|
}
|
|
StartGC();
|
|
for (int i = 0; i < kNumObjects; i += 2) {
|
|
holder->objects[i] = nullptr;
|
|
}
|
|
EndGC();
|
|
// Half of object were destroyed.
|
|
EXPECT_EQ(5u, CompactableGCed::g_destructor_callcount);
|
|
// Remaining objects are compacted.
|
|
for (int i = 1; i < kNumObjects; i += 2) {
|
|
EXPECT_EQ(holder->objects[i], references[i / 2]);
|
|
}
|
|
}
|
|
|
|
TEST_F(CompactorTest, CompactAcrossPages) {
|
|
Persistent<CompactableHolder<1>> holder =
|
|
MakeGarbageCollected<CompactableHolder<1>>(GetAllocationHandle(),
|
|
GetAllocationHandle());
|
|
CompactableGCed* reference = holder->objects[0];
|
|
static constexpr size_t kObjectsPerPage =
|
|
kPageSize / (sizeof(CompactableGCed) + sizeof(HeapObjectHeader));
|
|
for (size_t i = 0; i < kObjectsPerPage; ++i) {
|
|
holder->objects[0] =
|
|
MakeGarbageCollected<CompactableGCed>(GetAllocationHandle());
|
|
}
|
|
// Last allocated object should be on a new page.
|
|
EXPECT_NE(reference, holder->objects[0]);
|
|
EXPECT_NE(BasePage::FromInnerAddress(heap(), reference),
|
|
BasePage::FromInnerAddress(heap(), holder->objects[0].Get()));
|
|
StartGC();
|
|
EndGC();
|
|
// Half of object were destroyed.
|
|
EXPECT_EQ(kObjectsPerPage, CompactableGCed::g_destructor_callcount);
|
|
EXPECT_EQ(reference, holder->objects[0]);
|
|
}
|
|
|
|
TEST_F(CompactorTest, InteriorSlotToPreviousObject) {
|
|
static constexpr int kNumObjects = 3;
|
|
Persistent<CompactableHolder<kNumObjects>> holder =
|
|
MakeGarbageCollected<CompactableHolder<kNumObjects>>(
|
|
GetAllocationHandle(), GetAllocationHandle());
|
|
CompactableGCed* references[kNumObjects] = {nullptr};
|
|
for (int i = 0; i < kNumObjects; ++i) {
|
|
references[i] = holder->objects[i];
|
|
}
|
|
holder->objects[2]->other = holder->objects[1];
|
|
holder->objects[1] = nullptr;
|
|
holder->objects[0] = nullptr;
|
|
StartGC();
|
|
EndGC();
|
|
EXPECT_EQ(1u, CompactableGCed::g_destructor_callcount);
|
|
EXPECT_EQ(references[1], holder->objects[2]);
|
|
EXPECT_EQ(references[0], holder->objects[2]->other);
|
|
}
|
|
|
|
TEST_F(CompactorTest, InteriorSlotToNextObject) {
|
|
static constexpr int kNumObjects = 3;
|
|
Persistent<CompactableHolder<kNumObjects>> holder =
|
|
MakeGarbageCollected<CompactableHolder<kNumObjects>>(
|
|
GetAllocationHandle(), GetAllocationHandle());
|
|
CompactableGCed* references[kNumObjects] = {nullptr};
|
|
for (int i = 0; i < kNumObjects; ++i) {
|
|
references[i] = holder->objects[i];
|
|
}
|
|
holder->objects[1]->other = holder->objects[2];
|
|
holder->objects[2] = nullptr;
|
|
holder->objects[0] = nullptr;
|
|
StartGC();
|
|
EndGC();
|
|
EXPECT_EQ(1u, CompactableGCed::g_destructor_callcount);
|
|
EXPECT_EQ(references[0], holder->objects[1]);
|
|
EXPECT_EQ(references[1], holder->objects[1]->other);
|
|
}
|
|
|
|
} // namespace internal
|
|
} // namespace cppgc
|