[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