Commit cfd97ee1 authored by Anton Bikineev's avatar Anton Bikineev Committed by Commit Bot

heap: Generate more GCInfo entries to avoid empty finalizers

Before this patch, in order to support finalization of classes in a
hierarchy, the base class must have had a destructor declared as virtual.
This hurt sweeping time as the sweeper had to execute finalizers for
classes that didn't really have any finalization logic. The patch adds
support for non-virtual destructors in Oilpan that provides the basis
for reduction of finalizers in the followup CLs.

In the backend the logic remained the same for classes that have either
 - virtual Base::~Base or
 - trivial Base::~Base and trivial Derived::~Derived or
 - Base::FinalizeGarbageCollectedObject.
The new logic is:
 - if all of the conditions above don't satisfy, separate GCInfo entries
   will be created for Derived classes.

Bug: 1015427

Change-Id: I99481f0508d99668558283417a15cdf0bfe1ae6f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1866712
Commit-Queue: Anton Bikineev <bikineev@chromium.org>
Reviewed-by: default avatarKentaro Hara <haraken@chromium.org>
Reviewed-by: default avatarMichael Lippautz <mlippautz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#707365}
parent 664fe23c
...@@ -33,13 +33,14 @@ class Length; ...@@ -33,13 +33,14 @@ class Length;
class CORE_EXPORT CSSValue : public GarbageCollected<CSSValue> { class CORE_EXPORT CSSValue : public GarbageCollected<CSSValue> {
public: public:
template <typename T>
static void* AllocateObject(size_t size) { static void* AllocateObject(size_t size) {
ThreadState* state = ThreadState* state =
ThreadStateFor<ThreadingTrait<CSSValue>::kAffinity>::GetState(); ThreadStateFor<ThreadingTrait<CSSValue>::kAffinity>::GetState();
const char* type_name = "blink::CSSValue"; const char* type_name = "blink::CSSValue";
return state->Heap().AllocateOnArenaIndex( return state->Heap().AllocateOnArenaIndex(
state, size, BlinkGC::kCSSValueArenaIndex, state, size, BlinkGC::kCSSValueArenaIndex,
GCInfoTrait<CSSValue>::Index(), type_name); GCInfoTrait<GCInfoFoldedType<CSSValue>>::Index(), type_name);
} }
// TODO(sashab): Remove this method and move logic to the caller. // TODO(sashab): Remove this method and move logic to the caller.
......
...@@ -163,15 +163,14 @@ class CORE_EXPORT Node : public EventTarget { ...@@ -163,15 +163,14 @@ class CORE_EXPORT Node : public EventTarget {
kDocumentPositionImplementationSpecific = 0x20, kDocumentPositionImplementationSpecific = 0x20,
}; };
// Override operator new to allocate Node subtype objects onto template <typename T>
// a dedicated heap.
static void* AllocateObject(size_t size) { static void* AllocateObject(size_t size) {
ThreadState* state = ThreadState* state =
ThreadStateFor<ThreadingTrait<Node>::kAffinity>::GetState(); ThreadStateFor<ThreadingTrait<Node>::kAffinity>::GetState();
const char* type_name = "blink::Node"; const char* type_name = "blink::Node";
return state->Heap().AllocateOnArenaIndex( return state->Heap().AllocateOnArenaIndex(
state, size, BlinkGC::kNodeArenaIndex, state, size, BlinkGC::kNodeArenaIndex,
GCInfoTrait<EventTarget>::Index(), type_name); GCInfoTrait<GCInfoFoldedType<T>>::Index(), type_name);
} }
static void DumpStatistics(); static void DumpStatistics();
......
...@@ -38,18 +38,27 @@ template <typename T> ...@@ -38,18 +38,27 @@ template <typename T>
struct FinalizerTraitImpl<T, true> { struct FinalizerTraitImpl<T, true> {
private: private:
STATIC_ONLY(FinalizerTraitImpl); STATIC_ONLY(FinalizerTraitImpl);
struct CustomDispatch { struct Custom {
static void Call(void* obj) { static void Call(void* obj) {
static_cast<T*>(obj)->FinalizeGarbageCollectedObject(); static_cast<T*>(obj)->FinalizeGarbageCollectedObject();
} }
}; };
struct DestructorDispatch { struct Destructor {
static void Call(void* obj) { static_cast<T*>(obj)->~T(); } static void Call(void* obj) {
// The garbage collector differs from regular C++ here as it remembers whether
// an object's base class has a virtual destructor. In case there is no virtual
// destructor present, the object is always finalized through its leaf type. In
// other words: there is no finalization through a base pointer.
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdelete-non-abstract-non-virtual-dtor"
static_cast<T*>(obj)->~T();
#pragma GCC diagnostic pop
}
}; };
using FinalizeImpl = using FinalizeImpl =
std::conditional_t<HasFinalizeGarbageCollectedObject<T>::value, std::conditional_t<HasFinalizeGarbageCollectedObject<T>::value,
CustomDispatch, Custom,
DestructorDispatch>; Destructor>;
public: public:
static void Finalize(void* obj) { static void Finalize(void* obj) {
......
...@@ -93,11 +93,9 @@ class PLATFORM_EXPORT GCInfoTable { ...@@ -93,11 +93,9 @@ class PLATFORM_EXPORT GCInfoTable {
Mutex table_mutex_; Mutex table_mutex_;
}; };
// GCInfoAtBaseType should be used when returning a unique 14 bit integer
// for a given gcInfo.
template <typename T> template <typename T>
struct GCInfoAtBaseType { struct GCInfoTrait {
STATIC_ONLY(GCInfoAtBaseType); STATIC_ONLY(GCInfoTrait);
static uint32_t Index() { static uint32_t Index() {
static_assert(sizeof(T), "T must be fully defined"); static_assert(sizeof(T), "T must be fully defined");
static const GCInfo kGcInfo = { static const GCInfo kGcInfo = {
...@@ -117,31 +115,6 @@ struct GCInfoAtBaseType { ...@@ -117,31 +115,6 @@ struct GCInfoAtBaseType {
} }
}; };
template <typename T,
bool = WTF::IsSubclassOfTemplate<typename std::remove_const<T>::type,
GarbageCollected>::value>
struct GetGarbageCollectedType;
template <typename T>
struct GetGarbageCollectedType<T, true> {
STATIC_ONLY(GetGarbageCollectedType);
using type = typename T::GarbageCollectedType;
};
template <typename T>
struct GetGarbageCollectedType<T, false> {
STATIC_ONLY(GetGarbageCollectedType);
using type = T;
};
template <typename T>
struct GCInfoTrait {
STATIC_ONLY(GCInfoTrait);
static uint32_t Index() {
return GCInfoAtBaseType<typename GetGarbageCollectedType<T>::type>::Index();
}
};
template <typename U> template <typename U>
class GCInfoTrait<const U> : public GCInfoTrait<U> {}; class GCInfoTrait<const U> : public GCInfoTrait<U> {};
......
...@@ -518,22 +518,48 @@ class GarbageCollected { ...@@ -518,22 +518,48 @@ class GarbageCollected {
#endif #endif
public: public:
using GarbageCollectedType = T; using ParentMostGarbageCollectedType = T;
void* operator new(size_t size) = delete; // Must use MakeGarbageCollected. void* operator new(size_t size) = delete; // Must use MakeGarbageCollected.
template <typename Derived>
static void* AllocateObject(size_t size) { static void* AllocateObject(size_t size) {
if (IsGarbageCollectedMixin<T>::value) { if (IsGarbageCollectedMixin<T>::value) {
// Ban large mixin so we can use PageFromObject() on them. // Ban large mixin so we can use PageFromObject() on them.
CHECK_GE(kLargeObjectSizeThreshold, size) CHECK_GE(kLargeObjectSizeThreshold, size)
<< "GarbageCollectedMixin may not be a large object"; << "GarbageCollectedMixin may not be a large object";
} }
return ThreadHeap::Allocate<T>(size); return ThreadHeap::Allocate<GCInfoFoldedType<Derived>>(size);
} }
void operator delete(void* p) { NOTREACHED(); } void operator delete(void* p) { NOTREACHED(); }
protected: protected:
// This trait in theory can be moved to gc_info.h, but that would cause
// significant memory bloat caused by huge number of ThreadHeap::Allocate<>
// instantiations, which linker is not able to fold.
template <typename Derived>
class GCInfoFolded {
static constexpr bool is_virtual_destructor_at_base =
std::has_virtual_destructor<ParentMostGarbageCollectedType>::value;
static constexpr bool both_trivially_destructible =
std::is_trivially_destructible<ParentMostGarbageCollectedType>::value &&
std::is_trivially_destructible<Derived>::value;
static constexpr bool has_custom_dispatch_at_base =
internal::HasFinalizeGarbageCollectedObject<
ParentMostGarbageCollectedType>::value;
public:
using Type = std::conditional_t<is_virtual_destructor_at_base ||
both_trivially_destructible ||
has_custom_dispatch_at_base,
ParentMostGarbageCollectedType,
Derived>;
};
template <typename Derived>
using GCInfoFoldedType = typename GCInfoFolded<Derived>::Type;
GarbageCollected() = default; GarbageCollected() = default;
DISALLOW_COPY_AND_ASSIGN(GarbageCollected); DISALLOW_COPY_AND_ASSIGN(GarbageCollected);
...@@ -552,7 +578,7 @@ T* MakeGarbageCollected(Args&&... args) { ...@@ -552,7 +578,7 @@ T* MakeGarbageCollected(Args&&... args) {
internal::HasFinalizeGarbageCollectedObject<T>::value, internal::HasFinalizeGarbageCollectedObject<T>::value,
"Finalized GarbageCollected class should either have a virtual " "Finalized GarbageCollected class should either have a virtual "
"destructor or be marked as final."); "destructor or be marked as final.");
void* memory = T::AllocateObject(sizeof(T)); void* memory = T::template AllocateObject<T>(sizeof(T));
HeapObjectHeader* header = HeapObjectHeader::FromPayload(memory); HeapObjectHeader* header = HeapObjectHeader::FromPayload(memory);
// Placement new as regular operator new() is deleted. // Placement new as regular operator new() is deleted.
T* object = ::new (memory) T(std::forward<Args>(args)...); T* object = ::new (memory) T(std::forward<Args>(args)...);
...@@ -579,7 +605,8 @@ T* MakeGarbageCollected(AdditionalBytes additional_bytes, Args&&... args) { ...@@ -579,7 +605,8 @@ T* MakeGarbageCollected(AdditionalBytes additional_bytes, Args&&... args) {
internal::HasFinalizeGarbageCollectedObject<T>::value, internal::HasFinalizeGarbageCollectedObject<T>::value,
"Finalized GarbageCollected class should either have a virtual " "Finalized GarbageCollected class should either have a virtual "
"destructor or be marked as final."); "destructor or be marked as final.");
void* memory = T::AllocateObject(sizeof(T) + additional_bytes.value); void* memory =
T::template AllocateObject<T>(sizeof(T) + additional_bytes.value);
HeapObjectHeader* header = HeapObjectHeader::FromPayload(memory); HeapObjectHeader* header = HeapObjectHeader::FromPayload(memory);
// Placement new as regular operator new() is deleted. // Placement new as regular operator new() is deleted.
T* object = ::new (memory) T(std::forward<Args>(args)...); T* object = ::new (memory) T(std::forward<Args>(args)...);
......
...@@ -510,6 +510,7 @@ class HeapHashMap : public HashMap<KeyArg, ...@@ -510,6 +510,7 @@ class HeapHashMap : public HashMap<KeyArg,
} }
public: public:
template <typename>
static void* AllocateObject(size_t size) { static void* AllocateObject(size_t size) {
return ThreadHeap::Allocate< return ThreadHeap::Allocate<
HeapHashMap<KeyArg, MappedArg, HashArg, KeyTraitsArg, MappedTraitsArg>>( HeapHashMap<KeyArg, MappedArg, HashArg, KeyTraitsArg, MappedTraitsArg>>(
...@@ -539,6 +540,7 @@ class HeapHashSet ...@@ -539,6 +540,7 @@ class HeapHashSet
} }
public: public:
template <typename>
static void* AllocateObject(size_t size) { static void* AllocateObject(size_t size) {
return ThreadHeap::Allocate<HeapHashSet<ValueArg, HashArg, TraitsArg>>( return ThreadHeap::Allocate<HeapHashSet<ValueArg, HashArg, TraitsArg>>(
size); size);
...@@ -568,6 +570,7 @@ class HeapLinkedHashSet ...@@ -568,6 +570,7 @@ class HeapLinkedHashSet
} }
public: public:
template <typename>
static void* AllocateObject(size_t size) { static void* AllocateObject(size_t size) {
return ThreadHeap::Allocate< return ThreadHeap::Allocate<
HeapLinkedHashSet<ValueArg, HashArg, TraitsArg>>(size); HeapLinkedHashSet<ValueArg, HashArg, TraitsArg>>(size);
...@@ -601,6 +604,7 @@ class HeapListHashSet ...@@ -601,6 +604,7 @@ class HeapListHashSet
} }
public: public:
template <typename>
static void* AllocateObject(size_t size) { static void* AllocateObject(size_t size) {
return ThreadHeap::Allocate< return ThreadHeap::Allocate<
HeapListHashSet<ValueArg, inlineCapacity, HashArg>>(size); HeapListHashSet<ValueArg, inlineCapacity, HashArg>>(size);
...@@ -629,6 +633,7 @@ class HeapHashCountedSet ...@@ -629,6 +633,7 @@ class HeapHashCountedSet
} }
public: public:
template <typename>
static void* AllocateObject(size_t size) { static void* AllocateObject(size_t size) {
return ThreadHeap::Allocate< return ThreadHeap::Allocate<
HeapHashCountedSet<Value, HashFunctions, Traits>>(size); HeapHashCountedSet<Value, HashFunctions, Traits>>(size);
...@@ -655,6 +660,7 @@ class HeapVector : public Vector<T, inlineCapacity, HeapAllocator> { ...@@ -655,6 +660,7 @@ class HeapVector : public Vector<T, inlineCapacity, HeapAllocator> {
} }
public: public:
template <typename>
static void* AllocateObject(size_t size) { static void* AllocateObject(size_t size) {
// On-heap HeapVectors generally should not have inline capacity, but it is // On-heap HeapVectors generally should not have inline capacity, but it is
// hard to avoid when using a type alias. Hence we only disallow the // hard to avoid when using a type alias. Hence we only disallow the
...@@ -706,6 +712,7 @@ class HeapDeque : public Deque<T, inlineCapacity, HeapAllocator> { ...@@ -706,6 +712,7 @@ class HeapDeque : public Deque<T, inlineCapacity, HeapAllocator> {
} }
public: public:
template <typename>
static void* AllocateObject(size_t size) { static void* AllocateObject(size_t size) {
// On-heap HeapDeques generally should not have inline capacity, but it is // On-heap HeapDeques generally should not have inline capacity, but it is
// hard to avoid when using a type alias. Hence we only disallow the // hard to avoid when using a type alias. Hence we only disallow the
......
...@@ -6100,4 +6100,24 @@ TEST_F(HeapTest, AccessDeletedBackingStore) { ...@@ -6100,4 +6100,24 @@ TEST_F(HeapTest, AccessDeletedBackingStore) {
} }
} }
class GCBase : public GarbageCollected<GCBase> {
public:
virtual void Trace(Visitor*) {}
};
class GCDerived final : public GCBase {
public:
static int destructor_called;
void Trace(Visitor*) override {}
~GCDerived() { ++destructor_called; }
};
int GCDerived::destructor_called = 0;
TEST_F(HeapTest, CallMostDerivedFinalizer) {
MakeGarbageCollected<GCDerived>();
PreciselyCollectGarbage();
EXPECT_EQ(1, GCDerived::destructor_called);
}
} // namespace blink } // namespace blink
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment