[llvm] 4143eee - [Hashing] Replace hash_integer_value with splitmix64 finalizer (#199471)

via llvm-commits llvm-commits at lists.llvm.org
Tue Jun 9 20:59:16 PDT 2026


Author: Fangrui Song
Date: 2026-06-10T03:59:11Z
New Revision: 4143eee75d13ce3a10fbf81b56220e1e13b915f1

URL: https://github.com/llvm/llvm-project/commit/4143eee75d13ce3a10fbf81b56220e1e13b915f1
DIFF: https://github.com/llvm/llvm-project/commit/4143eee75d13ce3a10fbf81b56220e1e13b915f1.diff

LOG: [Hashing] Replace hash_integer_value with splitmix64 finalizer (#199471)

hash_integer_value used a CityHash-era construction: read the uint64_t
value as two endian-normalized 4-byte halves via fetch32, then combine
through hash_16_bytes. Treat the integer as a number instead.

A direct hash_16_bytes(value, seed) has the right shape but inherits a
defect from its Murmur three-multiply chain: the top input bit maps
deterministically to the top output bit. Use the splitmix64 finalizer
(xmxmx) instead -- the same shape xxh3 uses for its 4-8 byte path.

bit-independence bias on eight input classes (sequential/step-aligned
ints, stack-, malloc-, code-segment-shaped pointers, random uint64):

```
// Read https://jonkagstrom.com/bit-mixer-construction/ for columns
                      bic_mean   bic_max
  fetch32 + 16_bytes: 0.04-0.14% 7-13%
  hash_16_bytes:      0.18%      42%
  splitmix64 xmxmx:   0.04%      0.17%
```

This also drops fetch32 and hash_16_bytes from the header; the three
byte-stable out-of-header callers were already moved to file-local
copies.

Depends on #196854
Aided by Claude Opus 4.7

Added: 
    

Modified: 
    llvm/include/llvm/ADT/Hashing.h

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/ADT/Hashing.h b/llvm/include/llvm/ADT/Hashing.h
index 99310a35f80c5..bb965bea86d0c 100644
--- a/llvm/include/llvm/ADT/Hashing.h
+++ b/llvm/include/llvm/ADT/Hashing.h
@@ -47,7 +47,6 @@
 #include "llvm/Config/abi-breaking.h"
 #include "llvm/Support/DataTypes.h"
 #include "llvm/Support/ErrorHandling.h"
-#include "llvm/Support/SwapByteOrder.h"
 #include "llvm/Support/type_traits.h"
 #include "llvm/Support/xxhash.h"
 #include <algorithm>
@@ -138,25 +137,6 @@ template <typename T> hash_code hash_value(const std::optional<T> &arg);
 namespace hashing {
 namespace detail {
 
-inline uint32_t fetch32(const char *p) {
-  uint32_t result;
-  std::memcpy(&result, p, sizeof(result));
-  if (sys::IsBigEndianHost)
-    sys::swapByteOrder(result);
-  return result;
-}
-
-constexpr uint64_t hash_16_bytes(uint64_t low, uint64_t high) {
-  // Murmur-inspired hashing.
-  const uint64_t kMul = 0x9ddfea08eb382d69ULL;
-  uint64_t a = (low ^ high) * kMul;
-  a ^= (a >> 47);
-  uint64_t b = (high ^ a) * kMul;
-  b ^= (b >> 47);
-  b *= kMul;
-  return b;
-}
-
 /// In LLVM_ENABLE_ABI_BREAKING_CHECKS builds, the seed is non-deterministic
 /// per process (address of a function in LLVMSupport) to prevent having users
 /// depend on the particular hash values. On platforms without ASLR, this is
@@ -343,11 +323,11 @@ namespace detail {
 /// behavior in the presence of integral promotions. Essentially,
 /// "hash_value('4')" and "hash_value('0' + 4)" should be the same.
 inline hash_code hash_integer_value(uint64_t value) {
-  // Similar to hash_4to8_bytes but using a seed instead of length.
-  const uint64_t seed = get_execution_seed();
-  const char *s = reinterpret_cast<const char *>(&value);
-  const uint64_t a = fetch32(s);
-  return hash_16_bytes(seed + (a << 3), fetch32(s + 4));
+  // splitmix64 finalizer (xmxmx).
+  uint64_t x = value ^ get_execution_seed();
+  x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9ULL;
+  x = (x ^ (x >> 27)) * 0x94d049bb133111ebULL;
+  return x ^ (x >> 31);
 }
 
 } // namespace detail


        


More information about the llvm-commits mailing list