LLVM 22.0.0git
HashBuilder.h
Go to the documentation of this file.
1//===- llvm/Support/HashBuilder.h - Convenient hashing interface-*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file implements an interface allowing to conveniently build hashes of
10// various data types, without relying on the underlying hasher type to know
11// about hashed data types.
12//
13//===----------------------------------------------------------------------===//
14
15#ifndef LLVM_SUPPORT_HASHBUILDER_H
16#define LLVM_SUPPORT_HASHBUILDER_H
17
18#include "llvm/ADT/ArrayRef.h"
19#include "llvm/ADT/Hashing.h"
20#include "llvm/ADT/STLExtras.h"
21#include "llvm/ADT/StringRef.h"
22#include "llvm/Support/Endian.h"
24
25#include <iterator>
26#include <optional>
27#include <utility>
28
29namespace llvm {
30
32/// Trait to indicate whether a type's bits can be hashed directly (after
33/// endianness correction).
34template <typename U> struct IsHashableData : is_integral_or_enum<U> {};
35
36} // namespace hashbuilder_detail
37
38/// Declares the hasher member, and functions forwarding directly to the hasher.
39template <typename HasherT> class HashBuilderBase {
40public:
41 template <typename HasherT_ = HasherT>
42 using HashResultTy = decltype(std::declval<HasherT_ &>().final());
43
44 HasherT &getHasher() { return Hasher; }
45
46 /// Forward to `HasherT::update(ArrayRef<uint8_t>)`.
47 ///
48 /// This may not take the size of `Data` into account.
49 /// Users of this function should pay attention to respect endianness
50 /// contraints.
51 void update(ArrayRef<uint8_t> Data) { this->getHasher().update(Data); }
52
53 /// Forward to `HasherT::update(ArrayRef<uint8_t>)`.
54 ///
55 /// This may not take the size of `Data` into account.
56 /// Users of this function should pay attention to respect endianness
57 /// contraints.
59 update(
60 ArrayRef(reinterpret_cast<const uint8_t *>(Data.data()), Data.size()));
61 }
62
63 /// Forward to `HasherT::final()` if available.
64 template <typename HasherT_ = HasherT> HashResultTy<HasherT_> final() {
65 return this->getHasher().final();
66 }
67
68 /// Forward to `HasherT::result()` if available.
69 template <typename HasherT_ = HasherT> HashResultTy<HasherT_> result() {
70 return this->getHasher().result();
71 }
72
73protected:
74 explicit HashBuilderBase(HasherT &Hasher) : Hasher(Hasher) {}
75
76 template <typename... ArgTypes>
77 explicit HashBuilderBase(ArgTypes &&...Args)
78 : OptionalHasher(std::in_place, std::forward<ArgTypes>(Args)...),
79 Hasher(*OptionalHasher) {}
80
81private:
82 std::optional<HasherT> OptionalHasher;
83 HasherT &Hasher;
84};
85
86/// Interface to help hash various types through a hasher type.
87///
88/// Via provided specializations of `add`, `addRange`, and `addRangeElements`
89/// functions, various types (e.g. `ArrayRef`, `StringRef`, etc.) can be hashed
90/// without requiring any knowledge of hashed types from the hasher type.
91///
92/// The only method expected from the templated hasher type `HasherT` is:
93/// * void update(ArrayRef<uint8_t> Data)
94///
95/// Additionally, the following methods will be forwarded to the hasher type:
96/// * decltype(std::declval<HasherT &>().final()) final()
97/// * decltype(std::declval<HasherT &>().result()) result()
98///
99/// From a user point of view, the interface provides the following:
100/// * `template<typename T> add(const T &Value)`
101/// The `add` function implements hashing of various types.
102/// * `template <typename ItT> void addRange(ItT First, ItT Last)`
103/// The `addRange` function is designed to aid hashing a range of values.
104/// It explicitly adds the size of the range in the hash.
105/// * `template <typename ItT> void addRangeElements(ItT First, ItT Last)`
106/// The `addRangeElements` function is also designed to aid hashing a range of
107/// values. In contrast to `addRange`, it **ignores** the size of the range,
108/// behaving as if elements were added one at a time with `add`.
109///
110/// User-defined `struct` types can participate in this interface by providing
111/// an `addHash` templated function. See the associated template specialization
112/// for details.
113///
114/// This interface does not impose requirements on the hasher
115/// `update(ArrayRef<uint8_t> Data)` method. We want to avoid collisions for
116/// variable-size types; for example for
117/// ```
118/// builder.add({1});
119/// builder.add({2, 3});
120/// ```
121/// and
122/// ```
123/// builder.add({1, 2});
124/// builder.add({3});
125/// ```
126/// . Thus, specializations of `add` and `addHash` for variable-size types must
127/// not assume that the hasher type considers the size as part of the hash; they
128/// must explicitly add the size to the hash. See for example specializations
129/// for `ArrayRef` and `StringRef`.
130///
131/// Additionally, since types are eventually forwarded to the hasher's
132/// `void update(ArrayRef<uint8_t>)` method, endianness plays a role in the hash
133/// computation (for example when computing `add((int)123)`).
134/// Specifiying a non-`native` `Endianness` template parameter allows to compute
135/// stable hash across platforms with different endianness.
136template <typename HasherT, llvm::endianness Endianness>
137class HashBuilder : public HashBuilderBase<HasherT> {
138public:
139 explicit HashBuilder(HasherT &Hasher) : HashBuilderBase<HasherT>(Hasher) {}
140 template <typename... ArgTypes>
141 explicit HashBuilder(ArgTypes &&...Args)
142 : HashBuilderBase<HasherT>(Args...) {}
143
144 /// Implement hashing for hashable data types, e.g. integral or enum values.
145 template <typename T>
146 std::enable_if_t<hashbuilder_detail::IsHashableData<T>::value, HashBuilder &>
150
151 /// Support hashing `ArrayRef`.
152 ///
153 /// `Value.size()` is taken into account to ensure cases like
154 /// ```
155 /// builder.add({1});
156 /// builder.add({2, 3});
157 /// ```
158 /// and
159 /// ```
160 /// builder.add({1, 2});
161 /// builder.add({3});
162 /// ```
163 /// do not collide.
164 template <typename T> HashBuilder &add(ArrayRef<T> Value) {
165 // As of implementation time, simply calling `addRange(Value)` would also go
166 // through the `update` fast path. But that would rely on the implementation
167 // details of `ArrayRef::begin()` and `ArrayRef::end()`. Explicitly call
168 // `update` to guarantee the fast path.
169 add(Value.size());
171 Endianness == llvm::endianness::native) {
172 this->update(ArrayRef(reinterpret_cast<const uint8_t *>(Value.begin()),
173 Value.size() * sizeof(T)));
174 } else {
175 for (auto &V : Value)
176 add(V);
177 }
178 return *this;
179 }
180
181 /// Support hashing `StringRef`.
182 ///
183 /// `Value.size()` is taken into account to ensure cases like
184 /// ```
185 /// builder.add("a");
186 /// builder.add("bc");
187 /// ```
188 /// and
189 /// ```
190 /// builder.add("ab");
191 /// builder.add("c");
192 /// ```
193 /// do not collide.
195 // As of implementation time, simply calling `addRange(Value)` would also go
196 // through `update`. But that would rely on the implementation of
197 // `StringRef::begin()` and `StringRef::end()`. Explicitly call `update` to
198 // guarantee the fast path.
199 add(Value.size());
200 this->update(ArrayRef(reinterpret_cast<const uint8_t *>(Value.begin()),
201 Value.size()));
202 return *this;
203 }
204
205 template <typename T>
207 decltype(addHash(std::declval<HashBuilder &>(), std::declval<T &>()));
208 /// Implement hashing for user-defined `struct`s.
209 ///
210 /// Any user-define `struct` can participate in hashing via `HashBuilder` by
211 /// providing a `addHash` templated function.
212 ///
213 /// ```
214 /// template <typename HasherT, llvm::endianness Endianness>
215 /// void addHash(HashBuilder<HasherT, Endianness> &HBuilder,
216 /// const UserDefinedStruct &Value);
217 /// ```
218 ///
219 /// For example:
220 /// ```
221 /// struct SimpleStruct {
222 /// char c;
223 /// int i;
224 /// };
225 ///
226 /// template <typename HasherT, llvm::endianness Endianness>
227 /// void addHash(HashBuilder<HasherT, Endianness> &HBuilder,
228 /// const SimpleStruct &Value) {
229 /// HBuilder.add(Value.c);
230 /// HBuilder.add(Value.i);
231 /// }
232 /// ```
233 ///
234 /// To avoid endianness issues, specializations of `addHash` should
235 /// generally rely on exising `add`, `addRange`, and `addRangeElements`
236 /// functions. If directly using `update`, an implementation must correctly
237 /// handle endianness.
238 ///
239 /// ```
240 /// struct __attribute__ ((packed)) StructWithFastHash {
241 /// int I;
242 /// char C;
243 ///
244 /// // If possible, we want to hash both `I` and `C` in a single
245 /// // `update` call for performance concerns.
246 /// template <typename HasherT, llvm::endianness Endianness>
247 /// friend void addHash(HashBuilder<HasherT, Endianness> &HBuilder,
248 /// const StructWithFastHash &Value) {
249 /// if (Endianness == llvm::endianness::native) {
250 /// HBuilder.update(ArrayRef(
251 /// reinterpret_cast<const uint8_t *>(&Value), sizeof(Value)));
252 /// } else {
253 /// // Rely on existing `add` methods to handle endianness.
254 /// HBuilder.add(Value.I);
255 /// HBuilder.add(Value.C);
256 /// }
257 /// }
258 /// };
259 /// ```
260 ///
261 /// To avoid collisions, specialization of `addHash` for variable-size
262 /// types must take the size into account.
263 ///
264 /// For example:
265 /// ```
266 /// struct CustomContainer {
267 /// private:
268 /// size_t Size;
269 /// int Elements[100];
270 ///
271 /// public:
272 /// CustomContainer(size_t Size) : Size(Size) {
273 /// for (size_t I = 0; I != Size; ++I)
274 /// Elements[I] = I;
275 /// }
276 /// template <typename HasherT, llvm::endianness Endianness>
277 /// friend void addHash(HashBuilder<HasherT, Endianness> &HBuilder,
278 /// const CustomContainer &Value) {
279 /// if (Endianness == llvm::endianness::native) {
280 /// HBuilder.update(ArrayRef(
281 /// reinterpret_cast<const uint8_t *>(&Value.Size),
282 /// sizeof(Value.Size) + Value.Size * sizeof(Value.Elements[0])));
283 /// } else {
284 /// // `addRange` will take care of encoding the size.
285 /// HBuilder.addRange(&Value.Elements[0], &Value.Elements[0] +
286 /// Value.Size);
287 /// }
288 /// }
289 /// };
290 /// ```
291 template <typename T>
292 std::enable_if_t<is_detected<HasAddHashT, T>::value &&
294 HashBuilder &>
295 add(const T &Value) {
296 addHash(*this, Value);
297 return *this;
298 }
299
300 template <typename T1, typename T2>
301 HashBuilder &add(const std::pair<T1, T2> &Value) {
302 return add(Value.first, Value.second);
303 }
304
305 template <typename... Ts> HashBuilder &add(const std::tuple<Ts...> &Arg) {
306 std::apply([this](const auto &...Args) { this->add(Args...); }, Arg);
307 return *this;
308 }
309
310 /// A convenenience variadic helper.
311 /// It simply iterates over its arguments, in order.
312 /// ```
313 /// add(Arg1, Arg2);
314 /// ```
315 /// is equivalent to
316 /// ```
317 /// add(Arg1)
318 /// add(Arg2)
319 /// ```
320 template <typename... Ts>
321 std::enable_if_t<(sizeof...(Ts) > 1), HashBuilder &> add(const Ts &...Args) {
322 return (add(Args), ...);
323 }
324
325 template <typename ForwardIteratorT>
326 HashBuilder &addRange(ForwardIteratorT First, ForwardIteratorT Last) {
327 add(std::distance(First, Last));
328 return addRangeElements(First, Last);
329 }
330
331 template <typename RangeT> HashBuilder &addRange(const RangeT &Range) {
333 }
334
335 template <typename ForwardIteratorT>
336 HashBuilder &addRangeElements(ForwardIteratorT First, ForwardIteratorT Last) {
337 return addRangeElementsImpl(
338 First, Last,
339 typename std::iterator_traits<ForwardIteratorT>::iterator_category());
340 }
341
342 template <typename RangeT>
346
347 template <typename T>
349 std::declval<T &>(), llvm::endianness::little));
350 /// Adjust `Value` for the target endianness and add it to the hash.
351 template <typename T>
352 std::enable_if_t<is_detected<HasByteSwapT, T>::value, HashBuilder &>
354 T SwappedValue = support::endian::byte_swap(Value, Endianness);
355 this->update(ArrayRef(reinterpret_cast<const uint8_t *>(&SwappedValue),
356 sizeof(SwappedValue)));
357 return *this;
358 }
359
360private:
361 // FIXME: Once available, specialize this function for `contiguous_iterator`s,
362 // and use it for `ArrayRef` and `StringRef`.
363 template <typename ForwardIteratorT>
364 HashBuilder &addRangeElementsImpl(ForwardIteratorT First,
365 ForwardIteratorT Last,
366 std::forward_iterator_tag) {
367 using T = typename std::iterator_traits<ForwardIteratorT>::value_type;
368 if constexpr (std::is_pointer_v<ForwardIteratorT> &&
370 Endianness == llvm::endianness::native) {
371 this->update(ArrayRef(reinterpret_cast<const uint8_t *>(First),
372 (Last - First) * sizeof(T)));
373 } else {
374 for (auto It = First; It != Last; ++It)
375 add(*It);
376 }
377 return *this;
378 }
379};
380
381namespace hashbuilder_detail {
383public:
386 hash_code DataCode = hash_value(Data);
387 Code = hash_combine(Code, DataCode);
388 }
390};
391
394} // namespace hashbuilder_detail
395
396/// Provide a default implementation of `hash_value` when `addHash(const T &)`
397/// is supported.
398template <typename T>
399std::enable_if_t<
401 hash_code>
404 HBuilder.add(Value);
405 return HBuilder.getHasher().Code;
406}
407} // end namespace llvm
408
409#endif // LLVM_SUPPORT_HASHBUILDER_H
#define T
ConstantRange Range(APInt(BitWidth, Low), APInt(BitWidth, High))
This file contains some templates that are useful if you are working with the STL at all.
ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition ArrayRef.h:41
void update(StringRef Data)
Forward to HasherT::update(ArrayRef<uint8_t>).
Definition HashBuilder.h:58
HashResultTy< HasherT_ > result()
Forward to HasherT::result() if available.
Definition HashBuilder.h:69
HashBuilderBase(HasherT &Hasher)
Definition HashBuilder.h:74
HashBuilderBase(ArgTypes &&...Args)
Definition HashBuilder.h:77
decltype(std::declval< HasherT_ & >().final()) HashResultTy
Definition HashBuilder.h:42
void update(ArrayRef< uint8_t > Data)
Forward to HasherT::update(ArrayRef<uint8_t>).
Definition HashBuilder.h:51
HasherT & getHasher()
Definition HashBuilder.h:44
Interface to help hash various types through a hasher type.
std::enable_if_t< is_detected< HasAddHashT, T >::value &&!hashbuilder_detail::IsHashableData< T >::value, HashBuilder & > add(const T &Value)
Implement hashing for user-defined structs.
HashBuilder & add(StringRef Value)
Support hashing StringRef.
std::enable_if_t< is_detected< HasByteSwapT, T >::value, HashBuilder & > adjustForEndiannessAndAdd(const T &Value)
Adjust Value for the target endianness and add it to the hash.
HashBuilder(HasherT &Hasher)
std::enable_if_t<(sizeof...(Ts) > 1), HashBuilder & > add(const Ts &...Args)
A convenenience variadic helper.
HashBuilder & addRangeElements(const RangeT &Range)
HashBuilder & addRange(const RangeT &Range)
HashBuilder & addRangeElements(ForwardIteratorT First, ForwardIteratorT Last)
decltype(addHash(std::declval< HashBuilder & >(), std::declval< T & >())) HasAddHashT
HashBuilder & add(const std::tuple< Ts... > &Arg)
HashBuilder & add(ArrayRef< T > Value)
Support hashing ArrayRef.
std::enable_if_t< hashbuilder_detail::IsHashableData< T >::value, HashBuilder & > add(T Value)
Implement hashing for hashable data types, e.g. integral or enum values.
HashBuilder & addRange(ForwardIteratorT First, ForwardIteratorT Last)
HashBuilder & add(const std::pair< T1, T2 > &Value)
decltype(support::endian::byte_swap( std::declval< T & >(), llvm::endianness::little)) HasByteSwapT
HashBuilder(ArgTypes &&...Args)
StringRef - Represent a constant reference to a string, i.e.
Definition StringRef.h:55
LLVM Value Representation.
Definition Value.h:75
An opaque object representing a hash code.
Definition Hashing.h:76
void update(ArrayRef< uint8_t > Data)
Metafunction that determines whether the given type is either an integral type or an enumeration type...
Definition type_traits.h:29
HashBuilder< hashbuilder_detail::HashCodeHasher, llvm::endianness::native > HashCodeHashBuilder
value_type byte_swap(value_type value, endianness endian)
Definition Endian.h:44
This is an optimization pass for GlobalISel generic memory operations.
hash_code hash_value(const FixedPointSemantics &Val)
constexpr auto adl_begin(RangeT &&range) -> decltype(adl_detail::begin_impl(std::forward< RangeT >(range)))
Returns the begin iterator to range using std::begin and function found through Argument-Dependent Lo...
Definition ADL.h:78
constexpr auto adl_end(RangeT &&range) -> decltype(adl_detail::end_impl(std::forward< RangeT >(range)))
Returns the end iterator to range using std::end and functions found through Argument-Dependent Looku...
Definition ADL.h:86
@ First
Helpers to iterate all locations in the MemoryEffectsBase class.
Definition ModRef.h:71
FunctionAddr VTableAddr uintptr_t uintptr_t Data
Definition InstrProf.h:189
ArrayRef(const T &OneElt) -> ArrayRef< T >
typename detail::detector< void, Op, Args... >::value_t is_detected
Detects if a given trait holds for some set of arguments 'Args'.
hash_code hash_combine(const Ts &...args)
Combine values into a single hash_code.
Definition Hashing.h:592
Implement std::hash so that hash_code can be used in STL containers.
Definition BitVector.h:870
Trait to indicate whether a type's bits can be hashed directly (after endianness correction).
Definition HashBuilder.h:34