|
8 | 8 | // |
9 | 9 | // This file provides types and functions for computing and combining stable |
10 | 10 | // hashes. Stable hashes can be useful for hashing across different modules, |
11 | | -// processes, or compiler runs. |
| 11 | +// processes, machines, or compiler runs for a specific compiler version. It |
| 12 | +// currently employs the xxh3_64bits hashing algorithm. Be aware that this |
| 13 | +// implementation may be adjusted or updated as improvements to the compiler are |
| 14 | +// made. |
12 | 15 | // |
13 | 16 | //===----------------------------------------------------------------------===// |
14 | 17 |
|
15 | 18 | #ifndef LLVM_ADT_STABLEHASHING_H |
16 | 19 | #define LLVM_ADT_STABLEHASHING_H |
17 | 20 |
|
18 | 21 | #include "llvm/ADT/StringRef.h" |
| 22 | +#include "llvm/Support/xxhash.h" |
19 | 23 |
|
20 | 24 | namespace llvm { |
21 | 25 |
|
22 | 26 | /// An opaque object representing a stable hash code. It can be serialized, |
23 | 27 | /// deserialized, and is stable across processes and executions. |
24 | 28 | using stable_hash = uint64_t; |
25 | 29 |
|
26 | | -// Implementation details |
27 | | -namespace hashing { |
28 | | -namespace detail { |
29 | | - |
30 | | -// Stable hashes are based on the 64-bit FNV-1 hash: |
31 | | -// https://en.wikipedia.org/wiki/Fowler-Noll-Vo_hash_function |
32 | | - |
33 | | -const uint64_t FNV_PRIME_64 = 1099511628211u; |
34 | | -const uint64_t FNV_OFFSET_64 = 14695981039346656037u; |
35 | | - |
36 | | -inline void stable_hash_append(stable_hash &Hash, const char Value) { |
37 | | - Hash = Hash ^ (Value & 0xFF); |
38 | | - Hash = Hash * FNV_PRIME_64; |
39 | | -} |
40 | | - |
41 | | -inline void stable_hash_append(stable_hash &Hash, stable_hash Value) { |
42 | | - for (unsigned I = 0; I < 8; ++I) { |
43 | | - stable_hash_append(Hash, static_cast<char>(Value)); |
44 | | - Value >>= 8; |
45 | | - } |
| 30 | +inline stable_hash stable_hash_combine(ArrayRef<stable_hash> Buffer) { |
| 31 | + const uint8_t *Ptr = reinterpret_cast<const uint8_t *>(Buffer.data()); |
| 32 | + size_t Size = Buffer.size() * sizeof(stable_hash); |
| 33 | + return xxh3_64bits(ArrayRef<uint8_t>(Ptr, Size)); |
46 | 34 | } |
47 | 35 |
|
48 | | -} // namespace detail |
49 | | -} // namespace hashing |
50 | | - |
51 | 36 | inline stable_hash stable_hash_combine(stable_hash A, stable_hash B) { |
52 | | - stable_hash Hash = hashing::detail::FNV_OFFSET_64; |
53 | | - hashing::detail::stable_hash_append(Hash, A); |
54 | | - hashing::detail::stable_hash_append(Hash, B); |
55 | | - return Hash; |
| 37 | + stable_hash Hashes[2] = {A, B}; |
| 38 | + return stable_hash_combine(Hashes); |
56 | 39 | } |
57 | 40 |
|
58 | 41 | inline stable_hash stable_hash_combine(stable_hash A, stable_hash B, |
59 | 42 | stable_hash C) { |
60 | | - stable_hash Hash = hashing::detail::FNV_OFFSET_64; |
61 | | - hashing::detail::stable_hash_append(Hash, A); |
62 | | - hashing::detail::stable_hash_append(Hash, B); |
63 | | - hashing::detail::stable_hash_append(Hash, C); |
64 | | - return Hash; |
| 43 | + stable_hash Hashes[3] = {A, B, C}; |
| 44 | + return stable_hash_combine(Hashes); |
65 | 45 | } |
66 | 46 |
|
67 | 47 | inline stable_hash stable_hash_combine(stable_hash A, stable_hash B, |
68 | 48 | stable_hash C, stable_hash D) { |
69 | | - stable_hash Hash = hashing::detail::FNV_OFFSET_64; |
70 | | - hashing::detail::stable_hash_append(Hash, A); |
71 | | - hashing::detail::stable_hash_append(Hash, B); |
72 | | - hashing::detail::stable_hash_append(Hash, C); |
73 | | - hashing::detail::stable_hash_append(Hash, D); |
74 | | - return Hash; |
75 | | -} |
76 | | - |
77 | | -/// Compute a stable_hash for a sequence of values. |
78 | | -/// |
79 | | -/// This hashes a sequence of values. It produces the same stable_hash as |
80 | | -/// 'stable_hash_combine(a, b, c, ...)', but can run over arbitrary sized |
81 | | -/// sequences and is significantly faster given pointers and types which |
82 | | -/// can be hashed as a sequence of bytes. |
83 | | -template <typename InputIteratorT> |
84 | | -stable_hash stable_hash_combine_range(InputIteratorT First, |
85 | | - InputIteratorT Last) { |
86 | | - stable_hash Hash = hashing::detail::FNV_OFFSET_64; |
87 | | - for (auto I = First; I != Last; ++I) |
88 | | - hashing::detail::stable_hash_append(Hash, *I); |
89 | | - return Hash; |
90 | | -} |
91 | | - |
92 | | -inline stable_hash stable_hash_combine_array(const stable_hash *P, size_t C) { |
93 | | - stable_hash Hash = hashing::detail::FNV_OFFSET_64; |
94 | | - for (size_t I = 0; I < C; ++I) |
95 | | - hashing::detail::stable_hash_append(Hash, P[I]); |
96 | | - return Hash; |
| 49 | + stable_hash Hashes[4] = {A, B, C, D}; |
| 50 | + return stable_hash_combine(Hashes); |
97 | 51 | } |
98 | 52 |
|
99 | | -inline stable_hash stable_hash_combine_string(const StringRef &S) { |
100 | | - return stable_hash_combine_range(S.begin(), S.end()); |
| 53 | +// Removes suffixes introduced by LLVM from the name to enhance stability and |
| 54 | +// maintain closeness to the original name across different builds. |
| 55 | +inline StringRef get_stable_name(StringRef Name) { |
| 56 | + auto [P1, S1] = Name.rsplit(".llvm."); |
| 57 | + auto [P2, S2] = P1.rsplit(".__uniq."); |
| 58 | + return P2; |
101 | 59 | } |
102 | 60 |
|
103 | | -inline stable_hash stable_hash_combine_string(const char *C) { |
104 | | - stable_hash Hash = hashing::detail::FNV_OFFSET_64; |
105 | | - while (*C) |
106 | | - hashing::detail::stable_hash_append(Hash, *(C++)); |
107 | | - return Hash; |
| 61 | +// Generates a consistent hash value for a given input name across different |
| 62 | +// program executions and environments. This function first converts the input |
| 63 | +// name into a stable form using the `get_stable_name` function, and then |
| 64 | +// computes a hash of this stable name. For instance, `foo.llvm.1234` would have |
| 65 | +// the same hash as `foo.llvm.5678. |
| 66 | +inline stable_hash stable_hash_name(StringRef Name) { |
| 67 | + return xxh3_64bits(get_stable_name(Name)); |
108 | 68 | } |
109 | 69 |
|
110 | 70 | } // namespace llvm |
|
0 commit comments