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:
Michael Lippautz 2020-04-06 15:29:41 +02:00 committed by Commit Bot
parent c4a2d8bb88
commit 6a429cf711
8 changed files with 141 additions and 55 deletions

View File

@ -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

View File

@ -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_;
};

View File

@ -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

View File

@ -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_; }

View File

@ -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",

View File

@ -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

View File

@ -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

View 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