Proteus
Programmable JIT compilation and optimization for C/C++ using LLVM
Loading...
Searching...
No Matches
Hashing.h
Go to the documentation of this file.
1#ifndef PROTEUS_HASHING_H
2#define PROTEUS_HASHING_H
3
9
10#include <llvm/ADT/ArrayRef.h>
11#include <llvm/ADT/DenseMap.h>
12#include <llvm/ADT/SmallVector.h>
13#if LLVM_VERSION_MAJOR >= 18
14#include <llvm/ADT/StableHashing.h>
15#else
16#include <llvm/CodeGen/StableHashing.h>
17#endif
18
19#include <algorithm>
20#include <string>
21#include <type_traits>
22
23namespace proteus {
24
25using namespace llvm;
26
27class HashT {
28private:
29 stable_hash Value;
30
31public:
32 inline HashT(const stable_hash HashValue) { Value = HashValue; }
33 inline HashT(const StringRef &S) { S.getAsInteger(0, Value); }
34 inline stable_hash getValue() const { return Value; }
35 inline std::string toString() const { return std::to_string(Value); }
36 // Returns a suffix for mangled JIT function names. CUDA uses "$" delimiters
37 // because "." generates invalid PTX. HIP/Host use "." for demangle-ability.
38 inline std::string toMangledSuffix() const {
39#if PROTEUS_ENABLE_CUDA
40 return "$jit$" + toString() + "$";
41#else
42 return ".jit." + toString();
43#endif
44 }
45 inline bool operator==(const HashT &Other) const {
46 return Value == Other.Value;
47 }
48
49 inline bool operator<(const HashT &Other) const {
50 return Value < Other.Value;
51 }
52};
53
54inline HashT hashValue(const HashT &H) { return H; }
55
56// Function that abstracts interface differences in stable hashing across LLVM.
57inline HashT hashValue(const StringRef &S) {
58#if LLVM_VERSION_MAJOR >= 20
59 ArrayRef<uint8_t> Bytes(reinterpret_cast<const uint8_t *>(S.data()),
60 S.size());
61 return xxh3_64bits(Bytes);
62#else
63 return stable_hash_combine_string(S);
64#endif
65}
66
67inline HashT hashValue(const std::string &S) { return hashValue(StringRef{S}); }
68
69template <typename T>
70inline std::enable_if_t<std::is_scalar<T>::value, HashT> hashValue(const T &V) {
71 return hashValue(StringRef{reinterpret_cast<const char *>(&V), sizeof(T)});
72}
73
74template <typename T>
76 if (RC.ArrInfo.NumElts <= 0)
77 reportFatalError("Invalid number of elements in array: " +
78 std::to_string(RC.ArrInfo.NumElts));
79
80 if (!RC.ArrInfo.Blob)
81 reportFatalError("Expected non-null Blob");
82
83 return hashValue(
84 StringRef{reinterpret_cast<const char *>(RC.ArrInfo.Blob.get()),
85 sizeof(T) * RC.ArrInfo.NumElts});
86}
87
89 if (RC.ObjInfo.Size <= 0)
90 reportFatalError("Invalid object size <= 0");
91
92 if (!RC.ObjInfo.Blob)
93 reportFatalError("Expected non-null Blob");
94
95 return hashValue(
96 StringRef{reinterpret_cast<const char *>(RC.ObjInfo.Blob.get()),
97 static_cast<size_t>(RC.ObjInfo.Size)});
98}
99
104 switch (RC.ArrInfo.EltType) {
106 return hashRuntimeConstantArray<bool>(RC);
108 return hashRuntimeConstantArray<int8_t>(RC);
110 return hashRuntimeConstantArray<int32_t>(RC);
112 return hashRuntimeConstantArray<int64_t>(RC);
114 return hashRuntimeConstantArray<float>(RC);
116 return hashRuntimeConstantArray<double>(RC);
117 default:
118 reportFatalError("Unsupported array element type: " +
120 }
121 } else if (RC.Type == RuntimeConstantType::OBJECT) {
122 return hashRuntimeConstantObject(RC);
123 } else if (isScalarRuntimeConstantType(RC.Type)) {
124 return hashValue(
125 StringRef{reinterpret_cast<const char *>(&RC.Value), sizeof(RC.Value)});
126 }
127
128 reportFatalError("Unsupported type " + toString(RC.Type));
129}
130
131inline HashT hashValue(const RuntimeConstant &RC) {
132 HashT H = hashValue(static_cast<int32_t>(RC.Type));
133 H = stable_hash_combine(H.getValue(), hashValue(RC.Pos).getValue());
134 H = stable_hash_combine(H.getValue(), hashValue(RC.Offset).getValue());
135
139 H = stable_hash_combine(H.getValue(),
141 H = stable_hash_combine(
142 H.getValue(),
143 hashValue(static_cast<int32_t>(RC.ArrInfo.EltType)).getValue());
144 H = stable_hash_combine(H.getValue(), hashArrayRefElement(RC).getValue());
145 return H;
146 }
147
149 H = stable_hash_combine(H.getValue(),
151 H = stable_hash_combine(
152 H.getValue(),
153 hashValue(static_cast<int32_t>(RC.ObjInfo.PassByValue)).getValue());
154 H = stable_hash_combine(H.getValue(), hashArrayRefElement(RC).getValue());
155 return H;
156 }
157
159 H = stable_hash_combine(H.getValue(), hashArrayRefElement(RC).getValue());
160 return H;
161 }
162
163 // For NONE/unsupported values, the type/pos/offset hash above is all we keep.
164 return H;
165}
166
167inline HashT hashValue(ArrayRef<RuntimeConstant> Arr) {
168 if (Arr.empty())
169 return 0;
170
171 HashT HashValue = hashArrayRefElement(Arr[0]);
172 for (int I = 1, E = Arr.size(); I < E; ++I)
173 HashValue = stable_hash_combine(HashValue.getValue(),
174 hashArrayRefElement(Arr[I]).getValue());
175
176 return HashValue;
177}
178
179template <typename T> inline HashT hashValue(ArrayRef<T> Arr) {
180 if (Arr.empty())
181 return 0;
182
183 HashT H = hashValue(static_cast<uint64_t>(Arr.size()));
184 for (const auto &Elt : Arr)
185 H = stable_hash_combine(H.getValue(), hashValue(Elt).getValue());
186
187 return H;
188}
189
190template <typename T, unsigned N>
191inline HashT hashValue(const SmallVector<T, N> &Vec) {
192 return hashValue(ArrayRef<T>(Vec));
193}
194
195template <typename Key, typename Value>
196inline std::enable_if_t<
197 std::is_integral<Key>::value || std::is_enum<Key>::value, HashT>
198hashValue(const DenseMap<Key, Value> &Map) {
199 if (Map.empty())
200 return 0;
201
202 SmallVector<Key, 32> Keys;
203 Keys.reserve(Map.size());
204 for (const auto &KV : Map)
205 Keys.push_back(KV.first);
206
207 std::sort(Keys.begin(), Keys.end());
208
209 HashT H = hashValue(static_cast<uint64_t>(Map.size()));
210 for (Key K : Keys) {
211 auto It = Map.find(K);
212 if (It == Map.end())
213 reportFatalError("Internal error: DenseMap key vanished during hashing");
214
215 HashT PairH = stable_hash_combine(hashValue(K).getValue(),
216 hashValue(It->second).getValue());
217 H = stable_hash_combine(H.getValue(), PairH.getValue());
218 }
219
220 return H;
221}
222
223template <typename Key, typename Value>
224inline HashT hashValue(DenseMap<Key, Value> &Map) {
225 return hashValue(static_cast<const DenseMap<Key, Value> &>(Map));
226}
227
229 return stable_hash_combine(A.getValue(), B.getValue());
230}
231
233 HashT H = hashValue(static_cast<int>(CGConfig.codeGenOption()));
234 H = hashCombine(H, hashValue(CGConfig.optLevel()));
235 H = hashCombine(H, hashValue(CGConfig.codeGenOptLevel()));
236 if (auto Pipeline = CGConfig.optPipeline())
237 H = hashCombine(H, hashValue(Pipeline.value()));
238 for (const auto &Plugin : getJITPassPluginConfigs()) {
239 H = hashCombine(H, hashValue(Plugin.Path));
240 H = hashCombine(H, hashValue(Plugin.Insertion.has_value()));
241 if (Plugin.Insertion) {
242 H = hashCombine(H, hashValue(Plugin.Insertion->Pipeline));
243 H = hashCombine(H,
244 hashValue(static_cast<int>(Plugin.Insertion->Position)));
245 }
246 H = hashCombine(H, hashValue(Plugin.Fingerprint));
247 }
248 return H;
249}
250
251inline HashT
253 HashT H = hashValue(CGConfig.specializeArgs());
254 H = hashCombine(H, hashValue(CGConfig.specializeDims()));
255 H = hashCombine(H, hashValue(CGConfig.specializeDimsRange()));
256 H = hashCombine(H, hashValue(CGConfig.specializeLaunchBounds()));
257 return H;
258}
259
260// The generic CodeGenerationConfig hash is codegen-only so frontend module
261// caches do not depend on runtime specialization policy. Runtime JIT cache keys
262// add hashRuntimeSpecializationConfig explicitly where those flags apply.
263inline HashT hashValue(const CodeGenerationConfig &CGConfig) {
264 return hashCodeGenConfig(CGConfig);
265}
266
267template <typename FirstT, typename... RestTs>
268inline HashT hash(FirstT &&First, RestTs &&...Rest) {
269 TIMESCOPE("proteus::hash");
270 HashT HashValue = hashValue(First);
271
272 ((HashValue = hashCombine(HashValue, hashValue(Rest))), ...);
273
274 return HashValue;
275}
276
277template <typename T> inline HashT hash(T &&Data) {
278 HashT HashValue = hashValue(Data);
279 return HashValue;
280}
281
282} // namespace proteus
283
284namespace std {
285template <> struct hash<proteus::HashT> {
286 std::size_t operator()(const proteus::HashT &Key) const {
287 return Key.getValue();
288 }
289};
290
291} // namespace std
292#endif
#define TIMESCOPE(...)
Definition TimeTracing.h:66
Definition Config.h:167
bool specializeArgs() const
Definition Config.h:289
bool specializeDimsRange() const
Definition Config.h:291
bool specializeLaunchBounds() const
Definition Config.h:292
std::optional< const std::string > optPipeline() const
Definition Config.h:295
bool specializeDims() const
Definition Config.h:290
char optLevel() const
Definition Config.h:293
CodegenOption codeGenOption() const
Definition Config.h:288
int codeGenOptLevel() const
Definition Config.h:294
Definition Hashing.h:27
std::string toString() const
Definition Hashing.h:35
bool operator==(const HashT &Other) const
Definition Hashing.h:45
stable_hash getValue() const
Definition Hashing.h:34
bool operator<(const HashT &Other) const
Definition Hashing.h:49
HashT(const stable_hash HashValue)
Definition Hashing.h:32
HashT(const StringRef &S)
Definition Hashing.h:33
std::string toMangledSuffix() const
Definition Hashing.h:38
Definition CompiledLibrary.h:8
Definition MemoryCache.h:27
HashT hashValue(const HashT &H)
Definition Hashing.h:54
std::vector< JITPassPluginConfig > getJITPassPluginConfigs()
Definition JITPassPluginRegistry.cpp:105
@ ARRAY
Definition CompilerInterfaceTypes.h:33
@ VECTOR
Definition CompilerInterfaceTypes.h:32
@ INT32
Definition CompilerInterfaceTypes.h:25
@ INT64
Definition CompilerInterfaceTypes.h:26
@ FLOAT
Definition CompilerInterfaceTypes.h:27
@ STATIC_ARRAY
Definition CompilerInterfaceTypes.h:31
@ INT8
Definition CompilerInterfaceTypes.h:24
@ BOOL
Definition CompilerInterfaceTypes.h:23
@ OBJECT
Definition CompilerInterfaceTypes.h:34
@ DOUBLE
Definition CompilerInterfaceTypes.h:28
HashT hash(FirstT &&First, RestTs &&...Rest)
Definition Hashing.h:268
T getValue(const RuntimeConstant &RC)
Definition RuntimeConstantTypeHelpers.h:95
void reportFatalError(const llvm::Twine &Reason, const char *FILE, unsigned Line)
Definition Error.cpp:14
HashT hashRuntimeSpecializationConfig(const CodeGenerationConfig &CGConfig)
Definition Hashing.h:252
HashT hashCodeGenConfig(const CodeGenerationConfig &CGConfig)
Definition Hashing.h:232
HashT hashArrayRefElement(const RuntimeConstant &RC)
Definition Hashing.h:100
HashT hashCombine(HashT A, HashT B)
Definition Hashing.h:228
std::string toString(CodegenOption Option)
Definition Config.h:31
HashT hashRuntimeConstantObject(const RuntimeConstant &RC)
Definition Hashing.h:88
bool isScalarRuntimeConstantType(RuntimeConstantType RCType)
Definition RuntimeConstantTypeHelpers.h:148
HashT hashRuntimeConstantArray(const RuntimeConstant &RC)
Definition Hashing.h:75
Definition Hashing.h:284
int32_t NumElts
Definition CompilerInterfaceTypes.h:42
RuntimeConstantType EltType
Definition CompilerInterfaceTypes.h:43
std::shared_ptr< unsigned char[]> Blob
Definition CompilerInterfaceTypes.h:44
std::shared_ptr< unsigned char[]> Blob
Definition CompilerInterfaceTypes.h:53
bool PassByValue
Definition CompilerInterfaceTypes.h:52
int32_t Size
Definition CompilerInterfaceTypes.h:51
Definition CompilerInterfaceTypes.h:72
ArrayInfo ArrInfo
Definition CompilerInterfaceTypes.h:78
RuntimeConstantValue Value
Definition CompilerInterfaceTypes.h:73
RuntimeConstantType Type
Definition CompilerInterfaceTypes.h:74
int32_t Offset
Definition CompilerInterfaceTypes.h:76
ObjectInfo ObjInfo
Definition CompilerInterfaceTypes.h:79
int32_t Pos
Definition CompilerInterfaceTypes.h:75
std::size_t operator()(const proteus::HashT &Key) const
Definition Hashing.h:286