[llvm] [ADT] Extract DenseMapStorage from DenseMap (NFC) (PR #226664)
Kazu Hirata via llvm-commits
llvm-commits at lists.llvm.org
Sun Sep 27 17:42:01 PDT 2026
https://github.com/kazutakahirata updated https://github.com/llvm/llvm-project/pull/226664
>From 09a81de18b5449046d30bbfa2d17884bf4b51877 Mon Sep 17 00:00:00 2001
From: Kazu Hirata <kazu at google.com>
Date: Sun, 27 Sep 2026 17:18:59 -0700
Subject: [PATCH] Rebase.
---
llvm/include/llvm/ADT/DenseMap.h | 185 +++++++++++++++++++------------
1 file changed, 112 insertions(+), 73 deletions(-)
diff --git a/llvm/include/llvm/ADT/DenseMap.h b/llvm/include/llvm/ADT/DenseMap.h
index f1763dcfed62a..a690fa7561df8 100644
--- a/llvm/include/llvm/ADT/DenseMap.h
+++ b/llvm/include/llvm/ADT/DenseMap.h
@@ -192,6 +192,101 @@ LLVM_ABI void *growRelocatable(void *OldBuckets, const UsedT *OldUsed,
size_t BucketSize, size_t Align,
BucketHasher Hasher, bool FreeOld);
+// A snapshot of the three fields the hot lookup paths need. Fetching them
+// together lets SmallDenseMap test its Small discriminator once rather than
+// once per accessor; for plain DenseMap it is three member loads either way.
+template <typename BucketT> struct StorageRep {
+ const BucketT *Buckets;
+ const UsedT *Used;
+ unsigned NumBuckets;
+};
+
+template <typename BucketT> class DenseMapStorage {
+ BucketT *Buckets = nullptr;
+ UsedT *Used = nullptr;
+ unsigned NumEntries = 0;
+ unsigned NumBuckets = 0;
+
+public:
+ DenseMapStorage() = default;
+
+ unsigned getNumEntries() const { return NumEntries; }
+ void setNumEntries(unsigned Num) { NumEntries = Num; }
+
+ BucketT *getBuckets() const { return Buckets; }
+ UsedT *getUsed() const { return Used; }
+ unsigned getNumBuckets() const { return NumBuckets; }
+ StorageRep<BucketT> getRep() const { return {Buckets, Used, NumBuckets}; }
+
+ void swap(DenseMapStorage &RHS) {
+ std::swap(Buckets, RHS.Buckets);
+ std::swap(Used, RHS.Used);
+ std::swap(NumEntries, RHS.NumEntries);
+ std::swap(NumBuckets, RHS.NumBuckets);
+ }
+
+ void setStorage(void *Storage, unsigned Num) {
+ Buckets = static_cast<BucketT *>(Storage);
+ Used = usedFor(Storage, sizeof(BucketT), Num);
+ NumBuckets = Num;
+ }
+
+ void growShared(unsigned MinNumBuckets, BucketHasher Hasher) {
+ unsigned NewNumBuckets = roundUpNumBuckets(MinNumBuckets);
+ setStorage(growRelocatable(Buckets, Used, NumBuckets, NewNumBuckets,
+ sizeof(BucketT), allocAlign<BucketT>(), Hasher,
+ /*FreeOld=*/true),
+ NewNumBuckets);
+ }
+
+ void deallocateBuckets() {
+ if (NumBuckets == 0)
+ return;
+ deallocate_buffer(Buckets, allocBytes<BucketT>(NumBuckets),
+ allocAlign<BucketT>());
+ Buckets = nullptr;
+ Used = nullptr;
+ NumBuckets = 0;
+ }
+
+ bool allocateBuckets(unsigned Num) {
+ if (Num == 0) {
+ Buckets = nullptr;
+ Used = nullptr;
+ NumBuckets = 0;
+ return false;
+ }
+ setStorage(allocate_buffer(allocBytes<BucketT>(Num), allocAlign<BucketT>()),
+ Num);
+ return true;
+ }
+
+ // Put the zombie instance in a known good state after a move.
+ // deallocateBuckets() already resets to the empty state.
+ void kill() { deallocateBuckets(); }
+
+ static unsigned roundUpNumBuckets(unsigned MinNumBuckets) {
+ return std::max(64u, MinNumBuckets);
+ }
+
+ // Plan how to shrink the bucket table. Return:
+ // - {false, 0} to reuse the existing bucket table
+ // - {true, N} to reallocate a bucket table with N entries
+ std::pair<bool, unsigned> planShrinkAndClear() const {
+ unsigned NewNumBuckets = 0;
+ if (NumEntries)
+ NewNumBuckets = std::max(64u, 1u << (Log2_32_Ceil(NumEntries) + 1));
+ if (NewNumBuckets == NumBuckets)
+ return {false, 0}; // Reuse.
+ return {true, NewNumBuckets}; // Reallocate.
+ }
+
+ bool maybeMoveFast(DenseMapStorage &&Other) {
+ swap(Other);
+ return true;
+ }
+};
+
} // namespace densemap::detail
// Befriended below so DenseMapBase can expose its bucket-relocation callback
@@ -545,14 +640,7 @@ class DenseMapBase : public DebugEpochBase {
struct ExactBucketCount {};
- // A snapshot of the three fields the hot lookup paths need. Fetching them
- // together lets SmallDenseMap test its Small discriminator once rather than
- // once per accessor; for plain DenseMap it is three member loads either way.
- struct Rep {
- const BucketT *Buckets;
- const UsedT *Used;
- unsigned NumBuckets;
- };
+ using Rep = llvm::densemap::detail::StorageRep<BucketT>;
void initWithExactBucketCount(unsigned NewNumBuckets) {
if (derived().allocateBuckets(NewNumBuckets))
@@ -942,10 +1030,7 @@ class DenseMap : public DenseMapBase<DenseMap<KeyT, ValueT, KeyInfoT, BucketT>,
using BaseT = DenseMapBase<DenseMap, KeyT, ValueT, KeyInfoT, BucketT>;
using UsedT = llvm::densemap::detail::UsedT;
- BucketT *Buckets = nullptr;
- UsedT *Used = nullptr;
- unsigned NumEntries = 0;
- unsigned NumBuckets = 0;
+ densemap::detail::DenseMapStorage<BucketT> Storage;
explicit DenseMap(unsigned NumBuckets, typename BaseT::ExactBucketCount) {
this->initWithExactBucketCount(NumBuckets);
@@ -994,87 +1079,41 @@ class DenseMap : public DenseMapBase<DenseMap<KeyT, ValueT, KeyInfoT, BucketT>,
}
private:
- void swapImpl(DenseMap &RHS) {
- std::swap(Buckets, RHS.Buckets);
- std::swap(Used, RHS.Used);
- std::swap(NumEntries, RHS.NumEntries);
- std::swap(NumBuckets, RHS.NumBuckets);
- }
+ void swapImpl(DenseMap &RHS) { Storage.swap(RHS.Storage); }
- unsigned getNumEntries() const { return NumEntries; }
-
- void setNumEntries(unsigned Num) { NumEntries = Num; }
+ unsigned getNumEntries() const { return Storage.getNumEntries(); }
- BucketT *getBuckets() const { return Buckets; }
+ void setNumEntries(unsigned Num) { Storage.setNumEntries(Num); }
- typename BaseT::Rep getRep() const { return {Buckets, Used, NumBuckets}; }
+ BucketT *getBuckets() const { return Storage.getBuckets(); }
- void setStorage(void *Storage, unsigned Num) {
- Buckets = static_cast<BucketT *>(Storage);
- Used = llvm::densemap::detail::usedFor(Storage, sizeof(BucketT), Num);
- NumBuckets = Num;
- }
+ typename BaseT::Rep getRep() const { return Storage.getRep(); }
void growShared(unsigned MinNumBuckets) {
- unsigned NewNumBuckets = roundUpNumBuckets(MinNumBuckets);
- setStorage(llvm::densemap::detail::growRelocatable(
- Buckets, Used, NumBuckets, NewNumBuckets, sizeof(BucketT),
- llvm::densemap::detail::allocAlign<BucketT>(),
- BaseT::hasher(), /*FreeOld=*/true),
- NewNumBuckets);
+ Storage.growShared(MinNumBuckets, BaseT::hasher());
}
- UsedT *getUsed() const { return Used; }
+ UsedT *getUsed() const { return Storage.getUsed(); }
- unsigned getNumBuckets() const { return NumBuckets; }
+ unsigned getNumBuckets() const { return Storage.getNumBuckets(); }
- void deallocateBuckets() {
- if (NumBuckets == 0)
- return;
- deallocate_buffer(Buckets,
- llvm::densemap::detail::allocBytes<BucketT>(NumBuckets),
- llvm::densemap::detail::allocAlign<BucketT>());
- Buckets = nullptr;
- Used = nullptr;
- NumBuckets = 0;
- }
+ void deallocateBuckets() { Storage.deallocateBuckets(); }
- bool allocateBuckets(unsigned Num) {
- if (Num == 0) {
- Buckets = nullptr;
- Used = nullptr;
- NumBuckets = 0;
- return false;
- }
- setStorage(allocate_buffer(llvm::densemap::detail::allocBytes<BucketT>(Num),
- llvm::densemap::detail::allocAlign<BucketT>()),
- Num);
- return true;
- }
+ bool allocateBuckets(unsigned Num) { return Storage.allocateBuckets(Num); }
- // Put the zombie instance in a known good state after a move.
- // deallocateBuckets() already resets to the empty state.
- void kill() { deallocateBuckets(); }
+ void kill() { Storage.kill(); }
static unsigned roundUpNumBuckets(unsigned MinNumBuckets) {
- return std::max(64u, MinNumBuckets);
+ return densemap::detail::DenseMapStorage<BucketT>::roundUpNumBuckets(
+ MinNumBuckets);
}
bool maybeMoveFast(DenseMap &&Other) {
- swapImpl(Other);
- return true;
+ return Storage.maybeMoveFast(std::move(Other.Storage));
}
- // Plan how to shrink the bucket table. Return:
- // - {false, 0} to reuse the existing bucket table
- // - {true, N} to reallocate a bucket table with N entries
std::pair<bool, unsigned> planShrinkAndClear() const {
- unsigned NewNumBuckets = 0;
- if (NumEntries)
- NewNumBuckets = std::max(64u, 1u << (Log2_32_Ceil(NumEntries) + 1));
- if (NewNumBuckets == NumBuckets)
- return {false, 0}; // Reuse.
- return {true, NewNumBuckets}; // Reallocate.
+ return Storage.planShrinkAndClear();
}
};
More information about the llvm-commits
mailing list