[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