[llvm] [DenseMap] Share rehash and grow for relocatable bucket types. NFC (PR #225018)

Alexis Engelke via llvm-commits llvm-commits at lists.llvm.org
Mon Sep 21 00:17:52 PDT 2026


================
@@ -0,0 +1,101 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/Support/MemAlloc.h"
+#include <cstring>
+
+using namespace llvm;
+using namespace llvm::densemap;
+using namespace llvm::densemap::detail;
+
+// A nonzero FixedSize turns the bucket copy into a couple of stores.
+template <size_t FixedSize, bool InlinePtrHash>
+static void rehashLoop(void *DstBuckets, UsedT *DstUsed, unsigned Mask,
+                       const void *SrcBuckets, const UsedT *SrcUsed,
+                       unsigned SrcNumBuckets, size_t RuntimeSize,
+                       BucketHasher Hasher) {
+  const size_t BucketSize = FixedSize ? FixedSize : RuntimeSize;
+  char *Dst = static_cast<char *>(DstBuckets);
+  const char *Src = static_cast<const char *>(SrcBuckets);
+  forEachUsed(SrcUsed, SrcNumBuckets, [&](unsigned I) {
+    const char *SrcBucket = Src + static_cast<size_t>(I) * BucketSize;
+    unsigned Hash;
+    if constexpr (InlinePtrHash) {
+      void *Key;
+      std::memcpy(&Key, SrcBucket, sizeof(Key));
+      Hash = DenseMapInfo<void *>::getHashValue(Key);
+    } else {
+      Hash = Hasher(SrcBucket);
+    }
+    unsigned BucketNo = Hash & Mask;
+    while (used(DstUsed, BucketNo))
+      BucketNo = (BucketNo + 1) & Mask;
+    std::memcpy(Dst + static_cast<size_t>(BucketNo) * BucketSize, SrcBucket,
+                BucketSize);
+    setUsed(DstUsed, BucketNo);
+  });
+}
+
+template <bool InlinePtrHash>
+static void rehashBySize(void *Dst, UsedT *DstUsed, unsigned Mask,
+                         const void *Src, const UsedT *SrcUsed,
+                         unsigned SrcNumBuckets, size_t BucketSize,
+                         BucketHasher Hasher) {
+  // The bucket sizes of 95% of the grow instantiations in an LLVM build.
+  switch (BucketSize) {
+#define REHASH_CASE(N)                                                         \
+  case N:                                                                      \
+    return rehashLoop<N, InlinePtrHash>(Dst, DstUsed, Mask, Src, SrcUsed,      \
+                                        SrcNumBuckets, BucketSize, Hasher);
+    REHASH_CASE(4)
+    REHASH_CASE(8)
+    REHASH_CASE(12)
+    REHASH_CASE(16)
+    REHASH_CASE(24)
+    REHASH_CASE(32)
+    REHASH_CASE(40)
+    REHASH_CASE(48)
+#undef REHASH_CASE
+  default:
+    return rehashLoop<0, InlinePtrHash>(Dst, DstUsed, Mask, Src, SrcUsed,
+                                        SrcNumBuckets, BucketSize, Hasher);
+  }
----------------
aengelke wrote:

In my experience (haven't checked this instance), LLVM is bad at lowering switches of calls (it generates a jump table to jumps). I had better experience with function pointer tables. Unless of course all these function calls get inlined? (What does the generated code look like here?)

https://github.com/llvm/llvm-project/pull/225018


More information about the llvm-commits mailing list