v8/test/unittests/heap/object-start-bitmap-unittest.cc
Jake Hughes 5f6aa2e5bf [heap] Add object start bitmap for conservative stack scanning
With conservative stack scanning enabled, a snapshot of the call stack
upon entry to GC will be used to determine part of the root-set. When
the collector walks the stack, it looks at each value and determines
whether it could be a potential on-heap object pointer. However, unlike
with Handles, these on-stack pointers aren't guaranteed to point to the
start of the object: the compiler may decide hide these pointers, and
create interior pointers in C++ frames which the GC doesn't know about.

The solution to this is to include an object start bitmap in the header
of each page. Each bit in the bitmap represents a word in the page
payload which is set when an object is allocated. This means that when
the collector finds an arbitrary potential pointer into the page, it can
walk backwards through the bitmap until it finds the relevant object's
base pointer. To prevent the bitmap becoming stale after compaction, it
is rebuilt during object sweeping.

This is experimental, and currently only works with inline allocation
disabled, and single generational collection.

Bug: v8:10614
Change-Id: I28ebd9562f58f335f8b3c2d1189cdf39feaa1f52
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2375195
Commit-Queue: Anton Bikineev <bikineev@chromium.org>
Reviewed-by: Michael Achenbach <machenbach@chromium.org>
Reviewed-by: Dominik Inführ <dinfuehr@chromium.org>
Reviewed-by: Anton Bikineev <bikineev@chromium.org>
Cr-Commit-Position: refs/heads/master@{#69615}
2020-08-31 07:10:36 +00:00

175 lines
5.2 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/object-start-bitmap.h"
#include "src/base/macros.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace v8 {
namespace internal {
class ObjectStartBitmap;
namespace {
bool IsEmpty(const ObjectStartBitmap& bitmap) {
size_t count = 0;
bitmap.Iterate([&count](Address) { count++; });
return count == 0;
}
// Abstraction for objects that hides ObjectStartBitmap::kGranularity and
// the base address as getting either of it wrong will result in failed DCHECKs.
class TestObject {
public:
static Address kBaseOffset;
explicit TestObject(size_t number) : number_(number) {
const size_t max_entries = ObjectStartBitmap::MaxEntries();
EXPECT_GE(max_entries, number_);
}
Address base_ptr() const {
return kBaseOffset + ObjectStartBitmap::Granularity() * number_;
}
// Allow implicitly converting Object to Address.
operator Address() const { return base_ptr(); }
private:
const size_t number_;
};
Address TestObject::kBaseOffset = reinterpret_cast<Address>(0x4000ul);
} // namespace
TEST(V8ObjectStartBitmapTest, MoreThanZeroEntriesPossible) {
const size_t max_entries = ObjectStartBitmap::MaxEntries();
EXPECT_LT(0u, max_entries);
}
TEST(V8ObjectStartBitmapTest, InitialEmpty) {
ObjectStartBitmap bitmap(TestObject::kBaseOffset);
EXPECT_TRUE(IsEmpty(bitmap));
}
TEST(V8ObjectStartBitmapTest, SetBitImpliesNonEmpty) {
ObjectStartBitmap bitmap(TestObject::kBaseOffset);
bitmap.SetBit(TestObject(0));
EXPECT_FALSE(IsEmpty(bitmap));
}
TEST(V8ObjectStartBitmapTest, SetBitCheckBit) {
ObjectStartBitmap bitmap(TestObject::kBaseOffset);
TestObject object(7);
bitmap.SetBit(object);
EXPECT_TRUE(bitmap.CheckBit(object));
}
TEST(V8ObjectStartBitmapTest, SetBitClearbitCheckBit) {
ObjectStartBitmap bitmap(TestObject::kBaseOffset);
TestObject object(77);
bitmap.SetBit(object);
bitmap.ClearBit(object);
EXPECT_FALSE(bitmap.CheckBit(object));
}
TEST(V8ObjectStartBitmapTest, SetBitClearBitImpliesEmpty) {
ObjectStartBitmap bitmap(TestObject::kBaseOffset);
TestObject object(123);
bitmap.SetBit(object);
bitmap.ClearBit(object);
EXPECT_TRUE(IsEmpty(bitmap));
}
TEST(V8ObjectStartBitmapTest, AdjacentObjectsAtBegin) {
ObjectStartBitmap bitmap(TestObject::kBaseOffset);
TestObject object0(0);
TestObject object1(1);
bitmap.SetBit(object0);
bitmap.SetBit(object1);
EXPECT_FALSE(bitmap.CheckBit(TestObject(3)));
size_t count = 0;
bitmap.Iterate([&count, object0, object1](Address current) {
if (count == 0) {
EXPECT_EQ(object0.base_ptr(), current);
} else if (count == 1) {
EXPECT_EQ(object1.base_ptr(), current);
}
count++;
});
EXPECT_EQ(2u, count);
}
TEST(V8ObjectStartBitmapTest, AdjacentObjectsAtEnd) {
ObjectStartBitmap bitmap(TestObject::kBaseOffset);
const size_t last_entry_index = ObjectStartBitmap::MaxEntries() - 1;
TestObject object0(last_entry_index - 1);
TestObject object1(last_entry_index);
bitmap.SetBit(object0);
bitmap.SetBit(object1);
EXPECT_FALSE(bitmap.CheckBit(TestObject(last_entry_index - 2)));
size_t count = 0;
bitmap.Iterate([&count, object0, object1](Address current) {
if (count == 0) {
EXPECT_EQ(object0.base_ptr(), current);
} else if (count == 1) {
EXPECT_EQ(object1.base_ptr(), current);
}
count++;
});
EXPECT_EQ(2u, count);
}
TEST(V8ObjectStartBitmapTest, FindBasePtrExact) {
ObjectStartBitmap bitmap(TestObject::kBaseOffset);
TestObject object(654);
bitmap.SetBit(object);
EXPECT_EQ(object.base_ptr(), bitmap.FindBasePtr(object.base_ptr()));
}
TEST(V8ObjectStartBitmapTest, FindBasePtrApproximate) {
static const size_t kInternalDelta = 37;
ObjectStartBitmap bitmap(TestObject::kBaseOffset);
TestObject object(654);
bitmap.SetBit(object);
EXPECT_EQ(object.base_ptr(),
bitmap.FindBasePtr(object.base_ptr() + kInternalDelta));
}
TEST(V8ObjectStartBitmapTest, FindBasePtrIteratingWholeBitmap) {
ObjectStartBitmap bitmap(TestObject::kBaseOffset);
TestObject object_to_find(TestObject(0));
Address hint_index = TestObject(ObjectStartBitmap::MaxEntries() - 1);
bitmap.SetBit(object_to_find);
EXPECT_EQ(object_to_find.base_ptr(), bitmap.FindBasePtr(hint_index));
}
TEST(V8ObjectStartBitmapTest, FindBasePtrNextCell) {
// This white box test makes use of the fact that cells are of type uint32_t.
const size_t kCellSize = sizeof(uint32_t);
ObjectStartBitmap bitmap(TestObject::kBaseOffset);
TestObject object_to_find(TestObject(kCellSize - 1));
Address hint = TestObject(kCellSize);
bitmap.SetBit(TestObject(0));
bitmap.SetBit(object_to_find);
EXPECT_EQ(object_to_find.base_ptr(), bitmap.FindBasePtr(hint));
}
TEST(V8ObjectStartBitmapTest, FindBasePtrSameCell) {
// This white box test makes use of the fact that cells are of type uint32_t.
const size_t kCellSize = sizeof(uint32_t);
ObjectStartBitmap bitmap(TestObject::kBaseOffset);
TestObject object_to_find(TestObject(kCellSize - 1));
bitmap.SetBit(TestObject(0));
bitmap.SetBit(object_to_find);
EXPECT_EQ(object_to_find.base_ptr(),
bitmap.FindBasePtr(object_to_find.base_ptr()));
}
} // namespace internal
} // namespace v8