Reland "cppgc: Integrate conservative stack scan into GC"
With this change we support allocation of objects and keeping them
alive via conservative stack scan.
This reverts commit 2b047a58f8
.
Change-Id: Iac1913e7ef0556c28399509a160777a89e60150c
Bug: chromium:1056170
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2137402
Auto-Submit: Michael Lippautz <mlippautz@chromium.org>
Reviewed-by: Anton Bikineev <bikineev@chromium.org>
Reviewed-by: Omer Katz <omerkatz@chromium.org>
Commit-Queue: Michael Lippautz <mlippautz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#67016}
This commit is contained in:
parent
c4a2d8bb88
commit
6a429cf711
@ -6,7 +6,9 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "src/heap/cppgc/heap-object-header.h"
|
||||
#include "src/base/platform/platform.h"
|
||||
#include "src/heap/cppgc/heap-object-header-inl.h"
|
||||
#include "src/heap/cppgc/stack.h"
|
||||
|
||||
namespace cppgc {
|
||||
|
||||
@ -16,12 +18,54 @@ std::unique_ptr<Heap> Heap::Create() {
|
||||
|
||||
namespace internal {
|
||||
|
||||
void Heap::CollectGarbage() {
|
||||
for (HeapObjectHeader* header : objects_) {
|
||||
header->Finalize();
|
||||
free(header);
|
||||
namespace {
|
||||
|
||||
constexpr bool NeedsConservativeStackScan(Heap::GCConfig config) {
|
||||
return config.stack_state == Heap::GCConfig::StackState::kNonEmpty;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// TODO(chromium:1056170): Replace with fast stack scanning once
|
||||
// object are allocated actual arenas/spaces.
|
||||
class StackMarker final : public StackVisitor {
|
||||
public:
|
||||
explicit StackMarker(const std::vector<HeapObjectHeader*>& objects)
|
||||
: objects_(objects) {}
|
||||
|
||||
void VisitPointer(const void* address) final {
|
||||
for (auto* header : objects_) {
|
||||
if (address >= header->Payload() &&
|
||||
address < (header + header->GetSize())) {
|
||||
header->TryMarkAtomic();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const std::vector<HeapObjectHeader*>& objects_;
|
||||
};
|
||||
|
||||
Heap::Heap()
|
||||
: stack_(std::make_unique<Stack>(v8::base::Stack::GetStackStart())) {}
|
||||
|
||||
void Heap::CollectGarbage(GCConfig config) {
|
||||
current_config_ = config;
|
||||
// TODO(chromium:1056170): Replace with proper mark-sweep algorithm.
|
||||
// "Marking".
|
||||
if (NeedsConservativeStackScan(current_config_)) {
|
||||
StackMarker marker(objects_);
|
||||
stack_->IteratePointers(&marker);
|
||||
}
|
||||
// "Sweeping and finalization".
|
||||
for (HeapObjectHeader* header : objects_) {
|
||||
if (header->IsMarked()) {
|
||||
header->Unmark();
|
||||
} else {
|
||||
header->Finalize();
|
||||
free(header);
|
||||
}
|
||||
}
|
||||
objects_.clear();
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
@ -5,6 +5,7 @@
|
||||
#ifndef V8_HEAP_CPPGC_HEAP_H_
|
||||
#define V8_HEAP_CPPGC_HEAP_H_
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "include/cppgc/gc-info.h"
|
||||
@ -14,18 +15,34 @@
|
||||
namespace cppgc {
|
||||
namespace internal {
|
||||
|
||||
class Stack;
|
||||
|
||||
class V8_EXPORT_PRIVATE Heap final : public cppgc::Heap {
|
||||
public:
|
||||
struct GCConfig {
|
||||
enum class StackState : uint8_t {
|
||||
kEmpty,
|
||||
kNonEmpty,
|
||||
};
|
||||
|
||||
static GCConfig Default() { return {StackState::kNonEmpty}; }
|
||||
|
||||
StackState stack_state = StackState::kNonEmpty;
|
||||
};
|
||||
|
||||
static Heap* From(cppgc::Heap* heap) { return static_cast<Heap*>(heap); }
|
||||
|
||||
Heap() = default;
|
||||
Heap();
|
||||
~Heap() final = default;
|
||||
|
||||
inline void* Allocate(size_t size, GCInfoIndex index);
|
||||
|
||||
void CollectGarbage();
|
||||
void CollectGarbage(GCConfig config = GCConfig::Default());
|
||||
|
||||
private:
|
||||
GCConfig current_config_;
|
||||
|
||||
std::unique_ptr<Stack> stack_;
|
||||
std::vector<HeapObjectHeader*> objects_;
|
||||
};
|
||||
|
||||
|
@ -122,14 +122,16 @@ void IteratePointersImpl(const Stack* stack, StackVisitor* visitor,
|
||||
|
||||
} // namespace
|
||||
|
||||
#ifdef CPPGC_SUPPORTS_CONSERVATIVE_STACK_SCAN
|
||||
void Stack::IteratePointers(StackVisitor* visitor) const {
|
||||
#ifdef CPPGC_SUPPORTS_CONSERVATIVE_STACK_SCAN
|
||||
PushAllRegistersAndIterateStack(this, visitor, &IteratePointersImpl);
|
||||
// No need to deal with callee-saved registers as they will be kept alive by
|
||||
// the regular conservative stack iteration.
|
||||
IterateSafeStackIfNecessary(visitor);
|
||||
#else // !CPPGC_SUPPORTS_CONSERVATIVE_STACK_SCAN
|
||||
FATAL("Conservative stack scan not supported on current platform.");
|
||||
#endif // !CPPGC_SUPPORTS_CONSERVATIVE_STACK_SCAN
|
||||
}
|
||||
#endif // CPPGC_SUPPORTS_CONSERVATIVE_STACK_SCAN
|
||||
|
||||
} // namespace internal
|
||||
} // namespace cppgc
|
||||
|
@ -28,9 +28,7 @@ class V8_EXPORT_PRIVATE Stack final {
|
||||
|
||||
// Word-aligned iteration of the stack. Slot values are passed on to
|
||||
// |visitor|.
|
||||
#ifdef CPPGC_SUPPORTS_CONSERVATIVE_STACK_SCAN
|
||||
void IteratePointers(StackVisitor* visitor) const;
|
||||
#endif // CPPGC_SUPPORTS_CONSERVATIVE_STACK_SCAN
|
||||
|
||||
// Returns the start of the stack.
|
||||
const void* stack_start() const { return stack_start_; }
|
||||
|
@ -44,11 +44,11 @@ v8_source_set("cppgc_unittests_sources") {
|
||||
testonly = true
|
||||
|
||||
sources = [
|
||||
"heap/cppgc/allocation_unittest.cc",
|
||||
"heap/cppgc/finalizer-trait_unittest.cc",
|
||||
"heap/cppgc/garbage-collected_unittest.cc",
|
||||
"heap/cppgc/gc-info_unittest.cc",
|
||||
"heap/cppgc/heap-object-header_unittest.cc",
|
||||
"heap/cppgc/heap_unittest.cc",
|
||||
"heap/cppgc/member_unittests.cc",
|
||||
"heap/cppgc/source-location_unittest.cc",
|
||||
"heap/cppgc/stack_unittest.cc",
|
||||
|
@ -1,42 +0,0 @@
|
||||
// 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 "include/cppgc/allocation.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "src/heap/cppgc/heap.h"
|
||||
#include "test/unittests/heap/cppgc/tests.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
namespace cppgc {
|
||||
|
||||
TEST(GCBasicHeapTest, CreateAndDestroyHeap) {
|
||||
std::unique_ptr<Heap> heap{Heap::Create()};
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class Foo : public GarbageCollected<Foo> {
|
||||
public:
|
||||
static size_t destructor_callcount;
|
||||
|
||||
Foo() { destructor_callcount = 0; }
|
||||
~Foo() { destructor_callcount++; }
|
||||
};
|
||||
|
||||
size_t Foo::destructor_callcount;
|
||||
|
||||
class GCAllocationTest : public testing::TestWithHeap {};
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_F(GCAllocationTest, MakeGarbageCollectedAndReclaim) {
|
||||
MakeGarbageCollected<Foo>(GetHeap());
|
||||
EXPECT_EQ(0u, Foo::destructor_callcount);
|
||||
internal::Heap::From(GetHeap())->CollectGarbage();
|
||||
EXPECT_EQ(1u, Foo::destructor_callcount);
|
||||
}
|
||||
|
||||
} // namespace cppgc
|
@ -60,6 +60,8 @@ TEST(GarbageCollectedTest, GarbageCollectedMixinTrait) {
|
||||
STATIC_ASSERT(IsGarbageCollectedMixinType<GCWithMergedMixins>::value);
|
||||
}
|
||||
|
||||
#ifdef CPPGC_SUPPORTS_CONSERVATIVE_STACK_SCAN
|
||||
|
||||
TEST_F(GarbageCollectedTestWithHeap, GetObjectStartReturnsCorrentAddress) {
|
||||
GCed* gced = MakeGarbageCollected<GCed>(GetHeap());
|
||||
GCedWithMixin* gced_with_mixin =
|
||||
@ -68,5 +70,8 @@ TEST_F(GarbageCollectedTestWithHeap, GetObjectStartReturnsCorrentAddress) {
|
||||
static_cast<Mixin*>(gced_with_mixin)->GetObjectStart());
|
||||
EXPECT_NE(gced, static_cast<Mixin*>(gced_with_mixin)->GetObjectStart());
|
||||
}
|
||||
|
||||
#endif // CPPGC_SUPPORTS_CONSERVATIVE_STACK_SCAN
|
||||
|
||||
} // namespace internal
|
||||
} // namespace cppgc
|
||||
|
62
test/unittests/heap/cppgc/heap_unittest.cc
Normal file
62
test/unittests/heap/cppgc/heap_unittest.cc
Normal file
@ -0,0 +1,62 @@
|
||||
// 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.
|
||||
|
||||
#ifdef CPPGC_SUPPORTS_CONSERVATIVE_STACK_SCAN
|
||||
|
||||
#include "src/heap/cppgc/heap.h"
|
||||
#include "include/cppgc/allocation.h"
|
||||
#include "test/unittests/heap/cppgc/tests.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
namespace cppgc {
|
||||
namespace internal {
|
||||
|
||||
namespace {
|
||||
|
||||
class GCHeapTest : public testing::TestWithHeap {
|
||||
public:
|
||||
void ConservativeGC() {
|
||||
internal::Heap::From(GetHeap())->CollectGarbage(
|
||||
{Heap::GCConfig::StackState::kNonEmpty});
|
||||
}
|
||||
void PreciseGC() {
|
||||
internal::Heap::From(GetHeap())->CollectGarbage(
|
||||
{Heap::GCConfig::StackState::kEmpty});
|
||||
}
|
||||
};
|
||||
|
||||
class Foo : public GarbageCollected<Foo> {
|
||||
public:
|
||||
static size_t destructor_callcount;
|
||||
|
||||
Foo() { destructor_callcount = 0; }
|
||||
~Foo() { destructor_callcount++; }
|
||||
};
|
||||
|
||||
size_t Foo::destructor_callcount;
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_F(GCHeapTest, PreciseGCReclaimsObjectOnStack) {
|
||||
Foo* volatile do_not_acces = MakeGarbageCollected<Foo>(GetHeap());
|
||||
USE(do_not_acces);
|
||||
EXPECT_EQ(0u, Foo::destructor_callcount);
|
||||
PreciseGC();
|
||||
EXPECT_EQ(1u, Foo::destructor_callcount);
|
||||
}
|
||||
|
||||
TEST_F(GCHeapTest, ConservaitveGCRetainsObjectOnStack) {
|
||||
Foo* volatile do_not_acces = MakeGarbageCollected<Foo>(GetHeap());
|
||||
USE(do_not_acces);
|
||||
EXPECT_EQ(0u, Foo::destructor_callcount);
|
||||
ConservativeGC();
|
||||
EXPECT_EQ(0u, Foo::destructor_callcount);
|
||||
PreciseGC();
|
||||
EXPECT_EQ(1u, Foo::destructor_callcount);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace cppgc
|
||||
|
||||
#endif // CPPGC_SUPPORTS_CONSERVATIVE_STACK_SCAN
|
Loading…
Reference in New Issue
Block a user