36610bbdd7
This CL adds to the existing experimental implementation of the object start bitmap, that is evaluated as a mechanism for resolving inner pointers (behind the flag v8_enable_conservative_stack_scanning). It fixes method ObjectStartBitmap::FindBasePtr to ensure that the correct base pointer is returned, even if the bitmap is not fully populated (e.g., with object evacuation or inline object allocation). This method now recalculates the part of the bitmap that is required for returning the correct result, by iterating through objects of the page. A special constructor has been introduced to the PagedSpaceObjectIterator for this purpose. It also moves the existing inline methods of ObjectStartBitmap to a new -inl.h header file, to avoid circular dependencies. Bug: v8:12851 Change-Id: Iabd0df020bee3bb63ef9d4888591b25d24d79dd9 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3641179 Reviewed-by: Michael Lippautz <mlippautz@chromium.org> Commit-Queue: Nikolaos Papaspyrou <nikolaos@chromium.org> Reviewed-by: Omer Katz <omerkatz@chromium.org> Cr-Commit-Position: refs/heads/main@{#80538}
178 lines
5.4 KiB
C++
178 lines
5.4 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 "src/heap/object-start-bitmap-inl.h"
|
|
#include "testing/gtest/include/gtest/gtest.h"
|
|
|
|
namespace v8 {
|
|
namespace internal {
|
|
|
|
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.FindBasePtrImpl(object.base_ptr()));
|
|
}
|
|
|
|
TEST(V8ObjectStartBitmapTest, FindBasePtrApproximate) {
|
|
const size_t kInternalDelta = 37;
|
|
ObjectStartBitmap bitmap(TestObject::kBaseOffset);
|
|
TestObject object(654);
|
|
bitmap.SetBit(object);
|
|
EXPECT_EQ(object.base_ptr(),
|
|
bitmap.FindBasePtrImpl(object.base_ptr() + kInternalDelta));
|
|
}
|
|
|
|
TEST(V8ObjectStartBitmapTest, FindBasePtrIteratingWholeBitmap) {
|
|
const size_t kLastWordDelta = ObjectStartBitmap::MaxEntries() - 1;
|
|
ObjectStartBitmap bitmap(TestObject::kBaseOffset);
|
|
TestObject object_to_find(0);
|
|
bitmap.SetBit(object_to_find);
|
|
Address hint_index = TestObject(kLastWordDelta);
|
|
EXPECT_EQ(object_to_find.base_ptr(), bitmap.FindBasePtrImpl(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(kCellSize - 1);
|
|
Address hint = TestObject(kCellSize);
|
|
bitmap.SetBit(TestObject(0));
|
|
bitmap.SetBit(object_to_find);
|
|
EXPECT_EQ(object_to_find.base_ptr(), bitmap.FindBasePtrImpl(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(kCellSize - 1);
|
|
Address hint = object_to_find;
|
|
bitmap.SetBit(TestObject(0));
|
|
bitmap.SetBit(object_to_find);
|
|
EXPECT_EQ(object_to_find.base_ptr(), bitmap.FindBasePtrImpl(hint));
|
|
}
|
|
|
|
// TODO(v8:12851): If the ObjectStartBitmap implementation stays, unit tests
|
|
// should be added to test the functionality of method FindBasePtr.
|
|
|
|
} // namespace internal
|
|
} // namespace v8
|