diff --git a/llvm/include/llvm/ADT/ScopedHashTable.h b/llvm/include/llvm/ADT/ScopedHashTable.h --- a/llvm/include/llvm/ADT/ScopedHashTable.h +++ b/llvm/include/llvm/ADT/ScopedHashTable.h @@ -147,7 +147,9 @@ }; template -class ScopedHashTable { +class ScopedHashTable : detail::AllocatorHolder { + using AllocTy = detail::AllocatorHolder; + public: /// ScopeTy - This is a helpful typedef that allows clients to get easy access /// to the name of the scope for this hash table. @@ -162,11 +164,9 @@ DenseMap TopLevelMap; ScopeTy *CurScope = nullptr; - AllocatorTy Allocator; - public: ScopedHashTable() = default; - ScopedHashTable(AllocatorTy A) : Allocator(A) {} + ScopedHashTable(AllocatorTy A) : AllocTy(A) {} ScopedHashTable(const ScopedHashTable &) = delete; ScopedHashTable &operator=(const ScopedHashTable &) = delete; @@ -175,8 +175,7 @@ } /// Access to the allocator. - AllocatorTy &getAllocator() { return Allocator; } - const AllocatorTy &getAllocator() const { return Allocator; } + using AllocTy::getAllocator; /// Return 1 if the specified key is in the table, 0 otherwise. size_type count(const K &Key) const { @@ -217,7 +216,7 @@ assert(S && "No scope active!"); ScopedHashTableVal *&KeyEntry = TopLevelMap[Key]; KeyEntry = ValTy::Create(S->getLastValInScope(), KeyEntry, Key, Val, - Allocator); + getAllocator()); S->setLastValInScope(KeyEntry); } }; diff --git a/llvm/include/llvm/ADT/StringMap.h b/llvm/include/llvm/ADT/StringMap.h --- a/llvm/include/llvm/ADT/StringMap.h +++ b/llvm/include/llvm/ADT/StringMap.h @@ -107,8 +107,9 @@ /// funky memory allocation and hashing things to make it extremely efficient, /// storing the string data *after* the value in the map. template -class StringMap : public StringMapImpl { - AllocatorTy Allocator; +class StringMap : public StringMapImpl, + private detail::AllocatorHolder { + using AllocTy = detail::AllocatorHolder; public: using MapEntryTy = StringMapEntry; @@ -119,12 +120,11 @@ : StringMapImpl(InitialSize, static_cast(sizeof(MapEntryTy))) {} explicit StringMap(AllocatorTy A) - : StringMapImpl(static_cast(sizeof(MapEntryTy))), Allocator(A) { - } + : StringMapImpl(static_cast(sizeof(MapEntryTy))), AllocTy(A) {} StringMap(unsigned InitialSize, AllocatorTy A) : StringMapImpl(InitialSize, static_cast(sizeof(MapEntryTy))), - Allocator(A) {} + AllocTy(A) {} StringMap(std::initializer_list> List) : StringMapImpl(List.size(), static_cast(sizeof(MapEntryTy))) { @@ -132,11 +132,11 @@ } StringMap(StringMap &&RHS) - : StringMapImpl(std::move(RHS)), Allocator(std::move(RHS.Allocator)) {} + : StringMapImpl(std::move(RHS)), AllocTy(std::move(RHS.getAllocator())) {} StringMap(const StringMap &RHS) : StringMapImpl(static_cast(sizeof(MapEntryTy))), - Allocator(RHS.Allocator) { + AllocTy(RHS.getAllocator()) { if (RHS.empty()) return; @@ -156,7 +156,7 @@ } TheTable[I] = MapEntryTy::Create( - static_cast(Bucket)->getKey(), Allocator, + static_cast(Bucket)->getKey(), getAllocator(), static_cast(Bucket)->getValue()); HashTable[I] = RHSHashTable[I]; } @@ -171,7 +171,7 @@ StringMap &operator=(StringMap RHS) { StringMapImpl::swap(RHS); - std::swap(Allocator, RHS.Allocator); + std::swap(getAllocator(), RHS.getAllocator()); return *this; } @@ -183,15 +183,14 @@ for (unsigned I = 0, E = NumBuckets; I != E; ++I) { StringMapEntryBase *Bucket = TheTable[I]; if (Bucket && Bucket != getTombstoneVal()) { - static_cast(Bucket)->Destroy(Allocator); + static_cast(Bucket)->Destroy(getAllocator()); } } } free(TheTable); } - AllocatorTy &getAllocator() { return Allocator; } - const AllocatorTy &getAllocator() const { return Allocator; } + using AllocTy::getAllocator; using key_type = const char *; using mapped_type = ValueTy; @@ -336,7 +335,8 @@ if (Bucket == getTombstoneVal()) --NumTombstones; - Bucket = MapEntryTy::Create(Key, Allocator, std::forward(Args)...); + Bucket = + MapEntryTy::Create(Key, getAllocator(), std::forward(Args)...); ++NumItems; assert(NumItems + NumTombstones <= NumBuckets); @@ -354,7 +354,7 @@ for (unsigned I = 0, E = NumBuckets; I != E; ++I) { StringMapEntryBase *&Bucket = TheTable[I]; if (Bucket && Bucket != getTombstoneVal()) { - static_cast(Bucket)->Destroy(Allocator); + static_cast(Bucket)->Destroy(getAllocator()); } Bucket = nullptr; } @@ -370,7 +370,7 @@ void erase(iterator I) { MapEntryTy &V = *I; remove(&V); - V.Destroy(Allocator); + V.Destroy(getAllocator()); } bool erase(StringRef Key) { diff --git a/llvm/include/llvm/Support/Allocator.h b/llvm/include/llvm/Support/Allocator.h --- a/llvm/include/llvm/Support/Allocator.h +++ b/llvm/include/llvm/Support/Allocator.h @@ -63,7 +63,9 @@ class BumpPtrAllocatorImpl : public AllocatorBase>, - private AllocatorT { + private detail::AllocatorHolder { + using AllocTy = detail::AllocatorHolder; + public: static_assert(SizeThreshold <= SlabSize, "The SizeThreshold must be at most the SlabSize to ensure " @@ -77,12 +79,12 @@ template BumpPtrAllocatorImpl(T &&Allocator) - : AllocatorT(std::forward(Allocator)) {} + : AllocTy(std::forward(Allocator)) {} // Manually implement a move constructor as we must clear the old allocator's // slabs as a matter of correctness. BumpPtrAllocatorImpl(BumpPtrAllocatorImpl &&Old) - : AllocatorT(static_cast(Old)), CurPtr(Old.CurPtr), + : AllocTy(std::move(Old.getAllocator())), CurPtr(Old.CurPtr), End(Old.End), Slabs(std::move(Old.Slabs)), CustomSizedSlabs(std::move(Old.CustomSizedSlabs)), BytesAllocated(Old.BytesAllocated), RedZoneSize(Old.RedZoneSize) { @@ -107,7 +109,7 @@ RedZoneSize = RHS.RedZoneSize; Slabs = std::move(RHS.Slabs); CustomSizedSlabs = std::move(RHS.CustomSizedSlabs); - AllocatorT::operator=(static_cast(RHS)); + AllocTy::operator=(std::move(RHS.getAllocator())); RHS.CurPtr = RHS.End = nullptr; RHS.BytesAllocated = 0; @@ -175,7 +177,7 @@ size_t PaddedSize = SizeToAllocate + Alignment.value() - 1; if (PaddedSize > SizeThreshold) { void *NewSlab = - AllocatorT::Allocate(PaddedSize, alignof(std::max_align_t)); + this->getAllocator().Allocate(PaddedSize, alignof(std::max_align_t)); // We own the new slab and don't want anyone reading anyting other than // pieces returned from this method. So poison the whole slab. __asan_poison_memory_region(NewSlab, PaddedSize); @@ -334,8 +336,8 @@ void StartNewSlab() { size_t AllocatedSlabSize = computeSlabSize(Slabs.size()); - void *NewSlab = - AllocatorT::Allocate(AllocatedSlabSize, alignof(std::max_align_t)); + void *NewSlab = this->getAllocator().Allocate(AllocatedSlabSize, + alignof(std::max_align_t)); // We own the new slab and don't want anyone reading anything other than // pieces returned from this method. So poison the whole slab. __asan_poison_memory_region(NewSlab, AllocatedSlabSize); @@ -351,7 +353,8 @@ for (; I != E; ++I) { size_t AllocatedSlabSize = computeSlabSize(std::distance(Slabs.begin(), I)); - AllocatorT::Deallocate(*I, AllocatedSlabSize, alignof(std::max_align_t)); + this->getAllocator().Deallocate(*I, AllocatedSlabSize, + alignof(std::max_align_t)); } } @@ -360,7 +363,7 @@ for (auto &PtrAndSize : CustomSizedSlabs) { void *Ptr = PtrAndSize.first; size_t Size = PtrAndSize.second; - AllocatorT::Deallocate(Ptr, Size, alignof(std::max_align_t)); + this->getAllocator().Deallocate(Ptr, Size, alignof(std::max_align_t)); } } diff --git a/llvm/include/llvm/Support/AllocatorBase.h b/llvm/include/llvm/Support/AllocatorBase.h --- a/llvm/include/llvm/Support/AllocatorBase.h +++ b/llvm/include/llvm/Support/AllocatorBase.h @@ -99,6 +99,28 @@ void PrintStats() const {} }; +namespace detail { + +template class AllocatorHolder : Alloc { +public: + AllocatorHolder() = default; + AllocatorHolder(const Alloc &A) : Alloc(A) {} + AllocatorHolder(Alloc &&A) : Alloc(static_cast(A)) {} + Alloc &getAllocator() { return *this; } + const Alloc &getAllocator() const { return *this; } +}; + +template class AllocatorHolder { + Alloc &A; + +public: + AllocatorHolder(Alloc &A) : A(A) {} + Alloc &getAllocator() { return A; } + const Alloc &getAllocator() const { return A; } +}; + +} // namespace detail + } // namespace llvm #endif // LLVM_SUPPORT_ALLOCATORBASE_H diff --git a/llvm/unittests/ADT/StringMapTest.cpp b/llvm/unittests/ADT/StringMapTest.cpp --- a/llvm/unittests/ADT/StringMapTest.cpp +++ b/llvm/unittests/ADT/StringMapTest.cpp @@ -17,6 +17,10 @@ namespace { +static_assert(sizeof(StringMap) < + sizeof(StringMap), + "Ensure empty base optimization happens with default allocator"); + // Test fixture class StringMapTest : public testing::Test { protected: