1 //===- llvm/ADT/FoldingSet.h - Uniquing Hash Set ----------------*- 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 /// \file
10 /// This file defines a hash set that can be used to remove duplication of nodes
11 /// in a graph. This code was originally created by Chris Lattner for use with
12 /// SelectionDAGCSEMap, but was isolated to provide use across the llvm code
13 /// set.
14 //===----------------------------------------------------------------------===//
15
16 #ifndef LLVM_ADT_FOLDINGSET_H
17 #define LLVM_ADT_FOLDINGSET_H
18
19 #include "llvm/ADT/Hashing.h"
20 #include "llvm/ADT/STLForwardCompat.h"
21 #include "llvm/ADT/SmallVector.h"
22 #include "llvm/ADT/iterator.h"
23 #include "llvm/Support/Allocator.h"
24 #include "llvm/Support/Compiler.h"
25 #include "llvm/Support/xxhash.h"
26 #include <cassert>
27 #include <cstddef>
28 #include <cstdint>
29 #include <type_traits>
30 #include <utility>
31
32 namespace llvm {
33
34 /// This folding set used for two purposes:
35 /// 1. Given information about a node we want to create, look up the unique
36 /// instance of the node in the set. If the node already exists, return
37 /// it, otherwise return the bucket it should be inserted into.
38 /// 2. Given a node that has already been created, remove it from the set.
39 ///
40 /// This class is implemented as a single-link chained hash table, where the
41 /// "buckets" are actually the nodes themselves (the next pointer is in the
42 /// node). The last node points back to the bucket to simplify node removal.
43 ///
44 /// Any node that is to be included in the folding set must be a subclass of
45 /// FoldingSetNode. The node class must also define a Profile method used to
46 /// establish the unique bits of data for the node. The Profile method is
47 /// passed a FoldingSetNodeID object which is used to gather the bits. Just
48 /// call one of the Add* functions defined in the FoldingSetBase::NodeID class.
49 /// NOTE: That the folding set does not own the nodes and it is the
50 /// responsibility of the user to dispose of the nodes.
51 ///
52 /// Eg.
53 /// class MyNode : public FoldingSetNode {
54 /// private:
55 /// std::string Name;
56 /// unsigned Value;
57 /// public:
58 /// MyNode(const char *N, unsigned V) : Name(N), Value(V) {}
59 /// ...
60 /// void Profile(FoldingSetNodeID &ID) const {
61 /// ID.AddString(Name);
62 /// ID.AddInteger(Value);
63 /// }
64 /// ...
65 /// };
66 ///
67 /// To define the folding set itself use the FoldingSet template;
68 ///
69 /// Eg.
70 /// FoldingSet<MyNode> MyFoldingSet;
71 ///
72 /// Four public methods are available to manipulate the folding set;
73 ///
74 /// 1) If you have an existing node that you want add to the set but unsure
75 /// that the node might already exist then call;
76 ///
77 /// MyNode *M = MyFoldingSet.GetOrInsertNode(N);
78 ///
79 /// If The result is equal to the input then the node has been inserted.
80 /// Otherwise, the result is the node existing in the folding set, and the
81 /// input can be discarded (use the result instead.)
82 ///
83 /// 2) If you are ready to construct a node but want to check if it already
84 /// exists, then call FindNodeOrInsertPos with a FoldingSetNodeID of the bits to
85 /// check;
86 ///
87 /// FoldingSetNodeID ID;
88 /// ID.AddString(Name);
89 /// ID.AddInteger(Value);
90 /// void *InsertPoint;
91 ///
92 /// MyNode *M = MyFoldingSet.FindNodeOrInsertPos(ID, InsertPoint);
93 ///
94 /// If found then M will be non-NULL, else InsertPoint will point to where it
95 /// should be inserted using InsertNode.
96 ///
97 /// 3) If you get a NULL result from FindNodeOrInsertPos then you can insert a
98 /// new node with InsertNode;
99 ///
100 /// MyFoldingSet.InsertNode(M, InsertPoint);
101 ///
102 /// 4) Finally, if you want to remove a node from the folding set call;
103 ///
104 /// bool WasRemoved = MyFoldingSet.RemoveNode(M);
105 ///
106 /// The result indicates whether the node existed in the folding set.
107
108 class FoldingSetNodeID;
109 class StringRef;
110
111 //===----------------------------------------------------------------------===//
112 /// FoldingSetBase - Implements the folding set functionality. The main
113 /// structure is an array of buckets. Each bucket is indexed by the hash of
114 /// the nodes it contains. The bucket itself points to the nodes contained
115 /// in the bucket via a singly linked list. The last node in the list points
116 /// back to the bucket to facilitate node removal.
117 ///
118 class FoldingSetBase {
119 protected:
120 /// Buckets - Array of bucket chains.
121 void **Buckets;
122
123 /// NumBuckets - Length of the Buckets array. Always a power of 2.
124 unsigned NumBuckets;
125
126 /// NumNodes - Number of nodes in the folding set. Growth occurs when NumNodes
127 /// is greater than twice the number of buckets.
128 unsigned NumNodes;
129
130 LLVM_ABI explicit FoldingSetBase(unsigned Log2InitSize = 6);
131 LLVM_ABI FoldingSetBase(FoldingSetBase &&Arg);
132 LLVM_ABI FoldingSetBase &operator=(FoldingSetBase &&RHS);
133 LLVM_ABI ~FoldingSetBase();
134
135 public:
136 //===--------------------------------------------------------------------===//
137 /// Node - This class is used to maintain the singly linked bucket list in
138 /// a folding set.
139 class Node {
140 private:
141 // NextInFoldingSetBucket - next link in the bucket list.
142 void *NextInFoldingSetBucket = nullptr;
143
144 public:
145 Node() = default;
146
147 // Accessors
getNextInBucket()148 void *getNextInBucket() const { return NextInFoldingSetBucket; }
SetNextInBucket(void * N)149 void SetNextInBucket(void *N) { NextInFoldingSetBucket = N; }
150 };
151
152 /// clear - Remove all nodes from the folding set.
153 LLVM_ABI void clear();
154
155 /// size - Returns the number of nodes in the folding set.
size()156 unsigned size() const { return NumNodes; }
157
158 /// empty - Returns true if there are no nodes in the folding set.
empty()159 bool empty() const { return NumNodes == 0; }
160
161 /// capacity - Returns the number of nodes permitted in the folding set
162 /// before a rebucket operation is performed.
capacity()163 unsigned capacity() {
164 // We allow a load factor of up to 2.0,
165 // so that means our capacity is NumBuckets * 2
166 return NumBuckets * 2;
167 }
168
169 protected:
170 /// Functions provided by the derived class to compute folding properties.
171 /// This is effectively a vtable for FoldingSetBase, except that we don't
172 /// actually store a pointer to it in the object.
173 struct FoldingSetInfo {
174 /// GetNodeProfile - Instantiations of the FoldingSet template implement
175 /// this function to gather data bits for the given node.
176 void (*GetNodeProfile)(const FoldingSetBase *Self, Node *N,
177 FoldingSetNodeID &ID);
178
179 /// NodeEquals - Instantiations of the FoldingSet template implement
180 /// this function to compare the given node with the given ID.
181 bool (*NodeEquals)(const FoldingSetBase *Self, Node *N,
182 const FoldingSetNodeID &ID, unsigned IDHash,
183 FoldingSetNodeID &TempID);
184
185 /// ComputeNodeHash - Instantiations of the FoldingSet template implement
186 /// this function to compute a hash value for the given node.
187 unsigned (*ComputeNodeHash)(const FoldingSetBase *Self, Node *N,
188 FoldingSetNodeID &TempID);
189 };
190
191 private:
192 /// GrowHashTable - Double the size of the hash table and rehash everything.
193 void GrowHashTable(const FoldingSetInfo &Info);
194
195 /// GrowBucketCount - resize the hash table and rehash everything.
196 /// NewBucketCount must be a power of two, and must be greater than the old
197 /// bucket count.
198 void GrowBucketCount(unsigned NewBucketCount, const FoldingSetInfo &Info);
199
200 protected:
201 // The below methods are protected to encourage subclasses to provide a more
202 // type-safe API.
203
204 /// reserve - Increase the number of buckets such that adding the
205 /// EltCount-th node won't cause a rebucket operation. reserve is permitted
206 /// to allocate more space than requested by EltCount.
207 LLVM_ABI void reserve(unsigned EltCount, const FoldingSetInfo &Info);
208
209 /// RemoveNode - Remove a node from the folding set, returning true if one
210 /// was removed or false if the node was not in the folding set.
211 LLVM_ABI bool RemoveNode(Node *N);
212
213 /// GetOrInsertNode - If there is an existing simple Node exactly
214 /// equal to the specified node, return it. Otherwise, insert 'N' and return
215 /// it instead.
216 LLVM_ABI Node *GetOrInsertNode(Node *N, const FoldingSetInfo &Info);
217
218 /// FindNodeOrInsertPos - Look up the node specified by ID. If it exists,
219 /// return it. If not, return the insertion token that will make insertion
220 /// faster.
221 LLVM_ABI Node *FindNodeOrInsertPos(const FoldingSetNodeID &ID,
222 void *&InsertPos,
223 const FoldingSetInfo &Info);
224
225 /// InsertNode - Insert the specified node into the folding set, knowing that
226 /// it is not already in the folding set. InsertPos must be obtained from
227 /// FindNodeOrInsertPos.
228 LLVM_ABI void InsertNode(Node *N, void *InsertPos,
229 const FoldingSetInfo &Info);
230 };
231
232 //===----------------------------------------------------------------------===//
233
234 /// DefaultFoldingSetTrait - This class provides default implementations
235 /// for FoldingSetTrait implementations.
236 template<typename T> struct DefaultFoldingSetTrait {
ProfileDefaultFoldingSetTrait237 static void Profile(const T &X, FoldingSetNodeID &ID) {
238 X.Profile(ID);
239 }
ProfileDefaultFoldingSetTrait240 static void Profile(T &X, FoldingSetNodeID &ID) {
241 X.Profile(ID);
242 }
243
244 // Equals - Test if the profile for X would match ID, using TempID
245 // to compute a temporary ID if necessary. The default implementation
246 // just calls Profile and does a regular comparison. Implementations
247 // can override this to provide more efficient implementations.
248 static inline bool Equals(T &X, const FoldingSetNodeID &ID, unsigned IDHash,
249 FoldingSetNodeID &TempID);
250
251 // ComputeHash - Compute a hash value for X, using TempID to
252 // compute a temporary ID if necessary. The default implementation
253 // just calls Profile and does a regular hash computation.
254 // Implementations can override this to provide more efficient
255 // implementations.
256 static inline unsigned ComputeHash(T &X, FoldingSetNodeID &TempID);
257 };
258
259 /// FoldingSetTrait - This trait class is used to define behavior of how
260 /// to "profile" (in the FoldingSet parlance) an object of a given type.
261 /// The default behavior is to invoke a 'Profile' method on an object, but
262 /// through template specialization the behavior can be tailored for specific
263 /// types. Combined with the FoldingSetNodeWrapper class, one can add objects
264 /// to FoldingSets that were not originally designed to have that behavior.
265 template <typename T, typename Enable = void>
266 struct FoldingSetTrait : public DefaultFoldingSetTrait<T> {};
267
268 /// DefaultContextualFoldingSetTrait - Like DefaultFoldingSetTrait, but
269 /// for ContextualFoldingSets.
270 template<typename T, typename Ctx>
271 struct DefaultContextualFoldingSetTrait {
ProfileDefaultContextualFoldingSetTrait272 static void Profile(T &X, FoldingSetNodeID &ID, Ctx Context) {
273 X.Profile(ID, Context);
274 }
275
276 static inline bool Equals(T &X, const FoldingSetNodeID &ID, unsigned IDHash,
277 FoldingSetNodeID &TempID, Ctx Context);
278 static inline unsigned ComputeHash(T &X, FoldingSetNodeID &TempID,
279 Ctx Context);
280 };
281
282 /// ContextualFoldingSetTrait - Like FoldingSetTrait, but for
283 /// ContextualFoldingSets.
284 template<typename T, typename Ctx> struct ContextualFoldingSetTrait
285 : public DefaultContextualFoldingSetTrait<T, Ctx> {};
286
287 //===--------------------------------------------------------------------===//
288 /// FoldingSetNodeIDRef - This class describes a reference to an interned
289 /// FoldingSetNodeID, which can be a useful to store node id data rather
290 /// than using plain FoldingSetNodeIDs, since the 32-element SmallVector
291 /// is often much larger than necessary, and the possibility of heap
292 /// allocation means it requires a non-trivial destructor call.
293 class FoldingSetNodeIDRef {
294 const unsigned *Data = nullptr;
295 size_t Size = 0;
296
297 public:
298 FoldingSetNodeIDRef() = default;
FoldingSetNodeIDRef(const unsigned * D,size_t S)299 FoldingSetNodeIDRef(const unsigned *D, size_t S) : Data(D), Size(S) {}
300
301 // Compute a strong hash value used to lookup the node in the FoldingSetBase.
302 // The hash value is not guaranteed to be deterministic across processes.
ComputeHash()303 unsigned ComputeHash() const {
304 return static_cast<unsigned>(hash_combine_range(Data, Data + Size));
305 }
306
307 // Compute a deterministic hash value across processes that is suitable for
308 // on-disk serialization.
computeStableHash()309 unsigned computeStableHash() const {
310 return static_cast<unsigned>(xxh3_64bits(ArrayRef(
311 reinterpret_cast<const uint8_t *>(Data), sizeof(unsigned) * Size)));
312 }
313
314 LLVM_ABI bool operator==(FoldingSetNodeIDRef) const;
315
316 bool operator!=(FoldingSetNodeIDRef RHS) const { return !(*this == RHS); }
317
318 /// Used to compare the "ordering" of two nodes as defined by the
319 /// profiled bits and their ordering defined by memcmp().
320 LLVM_ABI bool operator<(FoldingSetNodeIDRef) const;
321
getData()322 const unsigned *getData() const { return Data; }
getSize()323 size_t getSize() const { return Size; }
324 };
325
326 //===--------------------------------------------------------------------===//
327 /// FoldingSetNodeID - This class is used to gather all the unique data bits of
328 /// a node. When all the bits are gathered this class is used to produce a
329 /// hash value for the node.
330 class FoldingSetNodeID {
331 /// Bits - Vector of all the data bits that make the node unique.
332 /// Use a SmallVector to avoid a heap allocation in the common case.
333 SmallVector<unsigned, 32> Bits;
334
335 public:
336 FoldingSetNodeID() = default;
337
FoldingSetNodeID(FoldingSetNodeIDRef Ref)338 FoldingSetNodeID(FoldingSetNodeIDRef Ref)
339 : Bits(Ref.getData(), Ref.getData() + Ref.getSize()) {}
340
341 /// Add* - Add various data types to Bit data.
AddPointer(const void * Ptr)342 void AddPointer(const void *Ptr) {
343 // Note: this adds pointers to the hash using sizes and endianness that
344 // depend on the host. It doesn't matter, however, because hashing on
345 // pointer values is inherently unstable. Nothing should depend on the
346 // ordering of nodes in the folding set.
347 static_assert(sizeof(uintptr_t) <= sizeof(unsigned long long),
348 "unexpected pointer size");
349 AddInteger(reinterpret_cast<uintptr_t>(Ptr));
350 }
AddInteger(signed I)351 void AddInteger(signed I) { Bits.push_back(I); }
AddInteger(unsigned I)352 void AddInteger(unsigned I) { Bits.push_back(I); }
AddInteger(long I)353 void AddInteger(long I) { AddInteger((unsigned long)I); }
AddInteger(unsigned long I)354 void AddInteger(unsigned long I) {
355 if (sizeof(long) == sizeof(int))
356 AddInteger(unsigned(I));
357 else if (sizeof(long) == sizeof(long long)) {
358 AddInteger((unsigned long long)I);
359 } else {
360 llvm_unreachable("unexpected sizeof(long)");
361 }
362 }
AddInteger(long long I)363 void AddInteger(long long I) { AddInteger((unsigned long long)I); }
AddInteger(unsigned long long I)364 void AddInteger(unsigned long long I) {
365 AddInteger(unsigned(I));
366 AddInteger(unsigned(I >> 32));
367 }
368
AddBoolean(bool B)369 void AddBoolean(bool B) { AddInteger(B ? 1U : 0U); }
370 LLVM_ABI void AddString(StringRef String);
371 LLVM_ABI void AddNodeID(const FoldingSetNodeID &ID);
372
373 template <typename T>
Add(const T & x)374 inline void Add(const T &x) { FoldingSetTrait<T>::Profile(x, *this); }
375
376 /// clear - Clear the accumulated profile, allowing this FoldingSetNodeID
377 /// object to be used to compute a new profile.
clear()378 inline void clear() { Bits.clear(); }
379
380 // Compute a strong hash value for this FoldingSetNodeID, used to lookup the
381 // node in the FoldingSetBase. The hash value is not guaranteed to be
382 // deterministic across processes.
ComputeHash()383 unsigned ComputeHash() const {
384 return FoldingSetNodeIDRef(Bits.data(), Bits.size()).ComputeHash();
385 }
386
387 // Compute a deterministic hash value across processes that is suitable for
388 // on-disk serialization.
computeStableHash()389 unsigned computeStableHash() const {
390 return FoldingSetNodeIDRef(Bits.data(), Bits.size()).computeStableHash();
391 }
392
393 /// operator== - Used to compare two nodes to each other.
394 LLVM_ABI bool operator==(const FoldingSetNodeID &RHS) const;
395 LLVM_ABI bool operator==(const FoldingSetNodeIDRef RHS) const;
396
397 bool operator!=(const FoldingSetNodeID &RHS) const { return !(*this == RHS); }
398 bool operator!=(const FoldingSetNodeIDRef RHS) const { return !(*this ==RHS);}
399
400 /// Used to compare the "ordering" of two nodes as defined by the
401 /// profiled bits and their ordering defined by memcmp().
402 LLVM_ABI bool operator<(const FoldingSetNodeID &RHS) const;
403 LLVM_ABI bool operator<(const FoldingSetNodeIDRef RHS) const;
404
405 /// Intern - Copy this node's data to a memory region allocated from the
406 /// given allocator and return a FoldingSetNodeIDRef describing the
407 /// interned data.
408 LLVM_ABI FoldingSetNodeIDRef Intern(BumpPtrAllocator &Allocator) const;
409 };
410
411 // Convenience type to hide the implementation of the folding set.
412 using FoldingSetNode = FoldingSetBase::Node;
413 template<class T> class FoldingSetIterator;
414 template<class T> class FoldingSetBucketIterator;
415
416 // Definitions of FoldingSetTrait and ContextualFoldingSetTrait functions, which
417 // require the definition of FoldingSetNodeID.
418 template<typename T>
419 inline bool
Equals(T & X,const FoldingSetNodeID & ID,unsigned,FoldingSetNodeID & TempID)420 DefaultFoldingSetTrait<T>::Equals(T &X, const FoldingSetNodeID &ID,
421 unsigned /*IDHash*/,
422 FoldingSetNodeID &TempID) {
423 FoldingSetTrait<T>::Profile(X, TempID);
424 return TempID == ID;
425 }
426 template<typename T>
427 inline unsigned
ComputeHash(T & X,FoldingSetNodeID & TempID)428 DefaultFoldingSetTrait<T>::ComputeHash(T &X, FoldingSetNodeID &TempID) {
429 FoldingSetTrait<T>::Profile(X, TempID);
430 return TempID.ComputeHash();
431 }
432 template<typename T, typename Ctx>
433 inline bool
Equals(T & X,const FoldingSetNodeID & ID,unsigned,FoldingSetNodeID & TempID,Ctx Context)434 DefaultContextualFoldingSetTrait<T, Ctx>::Equals(T &X,
435 const FoldingSetNodeID &ID,
436 unsigned /*IDHash*/,
437 FoldingSetNodeID &TempID,
438 Ctx Context) {
439 ContextualFoldingSetTrait<T, Ctx>::Profile(X, TempID, Context);
440 return TempID == ID;
441 }
442 template<typename T, typename Ctx>
443 inline unsigned
ComputeHash(T & X,FoldingSetNodeID & TempID,Ctx Context)444 DefaultContextualFoldingSetTrait<T, Ctx>::ComputeHash(T &X,
445 FoldingSetNodeID &TempID,
446 Ctx Context) {
447 ContextualFoldingSetTrait<T, Ctx>::Profile(X, TempID, Context);
448 return TempID.ComputeHash();
449 }
450
451 //===----------------------------------------------------------------------===//
452 /// FoldingSetImpl - An implementation detail that lets us share code between
453 /// FoldingSet and ContextualFoldingSet.
454 template <class Derived, class T> class FoldingSetImpl : public FoldingSetBase {
455 protected:
FoldingSetImpl(unsigned Log2InitSize)456 explicit FoldingSetImpl(unsigned Log2InitSize)
457 : FoldingSetBase(Log2InitSize) {}
458
459 FoldingSetImpl(FoldingSetImpl &&Arg) = default;
460 FoldingSetImpl &operator=(FoldingSetImpl &&RHS) = default;
461 ~FoldingSetImpl() = default;
462
463 public:
464 using iterator = FoldingSetIterator<T>;
465
begin()466 iterator begin() { return iterator(Buckets); }
end()467 iterator end() { return iterator(Buckets+NumBuckets); }
468
469 using const_iterator = FoldingSetIterator<const T>;
470
begin()471 const_iterator begin() const { return const_iterator(Buckets); }
end()472 const_iterator end() const { return const_iterator(Buckets+NumBuckets); }
473
474 using bucket_iterator = FoldingSetBucketIterator<T>;
475
bucket_begin(unsigned hash)476 bucket_iterator bucket_begin(unsigned hash) {
477 return bucket_iterator(Buckets + (hash & (NumBuckets-1)));
478 }
479
bucket_end(unsigned hash)480 bucket_iterator bucket_end(unsigned hash) {
481 return bucket_iterator(Buckets + (hash & (NumBuckets-1)), true);
482 }
483
484 /// reserve - Increase the number of buckets such that adding the
485 /// EltCount-th node won't cause a rebucket operation. reserve is permitted
486 /// to allocate more space than requested by EltCount.
reserve(unsigned EltCount)487 void reserve(unsigned EltCount) {
488 return FoldingSetBase::reserve(EltCount, Derived::getFoldingSetInfo());
489 }
490
491 /// RemoveNode - Remove a node from the folding set, returning true if one
492 /// was removed or false if the node was not in the folding set.
RemoveNode(T * N)493 bool RemoveNode(T *N) {
494 return FoldingSetBase::RemoveNode(N);
495 }
496
497 /// GetOrInsertNode - If there is an existing simple Node exactly
498 /// equal to the specified node, return it. Otherwise, insert 'N' and
499 /// return it instead.
GetOrInsertNode(T * N)500 T *GetOrInsertNode(T *N) {
501 return static_cast<T *>(
502 FoldingSetBase::GetOrInsertNode(N, Derived::getFoldingSetInfo()));
503 }
504
505 /// FindNodeOrInsertPos - Look up the node specified by ID. If it exists,
506 /// return it. If not, return the insertion token that will make insertion
507 /// faster.
FindNodeOrInsertPos(const FoldingSetNodeID & ID,void * & InsertPos)508 T *FindNodeOrInsertPos(const FoldingSetNodeID &ID, void *&InsertPos) {
509 return static_cast<T *>(FoldingSetBase::FindNodeOrInsertPos(
510 ID, InsertPos, Derived::getFoldingSetInfo()));
511 }
512
513 /// InsertNode - Insert the specified node into the folding set, knowing that
514 /// it is not already in the folding set. InsertPos must be obtained from
515 /// FindNodeOrInsertPos.
InsertNode(T * N,void * InsertPos)516 void InsertNode(T *N, void *InsertPos) {
517 FoldingSetBase::InsertNode(N, InsertPos, Derived::getFoldingSetInfo());
518 }
519
520 /// InsertNode - Insert the specified node into the folding set, knowing that
521 /// it is not already in the folding set.
InsertNode(T * N)522 void InsertNode(T *N) {
523 T *Inserted = GetOrInsertNode(N);
524 (void)Inserted;
525 assert(Inserted == N && "Node already inserted!");
526 }
527 };
528
529 //===----------------------------------------------------------------------===//
530 /// FoldingSet - This template class is used to instantiate a specialized
531 /// implementation of the folding set to the node class T. T must be a
532 /// subclass of FoldingSetNode and implement a Profile function.
533 ///
534 /// Note that this set type is movable and move-assignable. However, its
535 /// moved-from state is not a valid state for anything other than
536 /// move-assigning and destroying. This is primarily to enable movable APIs
537 /// that incorporate these objects.
538 template <class T>
539 class FoldingSet : public FoldingSetImpl<FoldingSet<T>, T> {
540 using Super = FoldingSetImpl<FoldingSet, T>;
541 using Node = typename Super::Node;
542
543 /// GetNodeProfile - Each instantiation of the FoldingSet needs to provide a
544 /// way to convert nodes into a unique specifier.
GetNodeProfile(const FoldingSetBase *,Node * N,FoldingSetNodeID & ID)545 static void GetNodeProfile(const FoldingSetBase *, Node *N,
546 FoldingSetNodeID &ID) {
547 T *TN = static_cast<T *>(N);
548 FoldingSetTrait<T>::Profile(*TN, ID);
549 }
550
551 /// NodeEquals - Instantiations may optionally provide a way to compare a
552 /// node with a specified ID.
NodeEquals(const FoldingSetBase *,Node * N,const FoldingSetNodeID & ID,unsigned IDHash,FoldingSetNodeID & TempID)553 static bool NodeEquals(const FoldingSetBase *, Node *N,
554 const FoldingSetNodeID &ID, unsigned IDHash,
555 FoldingSetNodeID &TempID) {
556 T *TN = static_cast<T *>(N);
557 return FoldingSetTrait<T>::Equals(*TN, ID, IDHash, TempID);
558 }
559
560 /// ComputeNodeHash - Instantiations may optionally provide a way to compute a
561 /// hash value directly from a node.
ComputeNodeHash(const FoldingSetBase *,Node * N,FoldingSetNodeID & TempID)562 static unsigned ComputeNodeHash(const FoldingSetBase *, Node *N,
563 FoldingSetNodeID &TempID) {
564 T *TN = static_cast<T *>(N);
565 return FoldingSetTrait<T>::ComputeHash(*TN, TempID);
566 }
567
getFoldingSetInfo()568 static const FoldingSetBase::FoldingSetInfo &getFoldingSetInfo() {
569 static constexpr FoldingSetBase::FoldingSetInfo Info = {
570 GetNodeProfile, NodeEquals, ComputeNodeHash};
571 return Info;
572 }
573 friend Super;
574
575 public:
Super(Log2InitSize)576 explicit FoldingSet(unsigned Log2InitSize = 6) : Super(Log2InitSize) {}
577 FoldingSet(FoldingSet &&Arg) = default;
578 FoldingSet &operator=(FoldingSet &&RHS) = default;
579 };
580
581 //===----------------------------------------------------------------------===//
582 /// ContextualFoldingSet - This template class is a further refinement
583 /// of FoldingSet which provides a context argument when calling
584 /// Profile on its nodes. Currently, that argument is fixed at
585 /// initialization time.
586 ///
587 /// T must be a subclass of FoldingSetNode and implement a Profile
588 /// function with signature
589 /// void Profile(FoldingSetNodeID &, Ctx);
590 template <class T, class Ctx>
591 class ContextualFoldingSet
592 : public FoldingSetImpl<ContextualFoldingSet<T, Ctx>, T> {
593 // Unfortunately, this can't derive from FoldingSet<T> because the
594 // construction of the vtable for FoldingSet<T> requires
595 // FoldingSet<T>::GetNodeProfile to be instantiated, which in turn
596 // requires a single-argument T::Profile().
597
598 using Super = FoldingSetImpl<ContextualFoldingSet, T>;
599 using Node = typename Super::Node;
600
601 Ctx Context;
602
getContext(const FoldingSetBase * Base)603 static const Ctx &getContext(const FoldingSetBase *Base) {
604 return static_cast<const ContextualFoldingSet*>(Base)->Context;
605 }
606
607 /// GetNodeProfile - Each instantiatation of the FoldingSet needs to provide a
608 /// way to convert nodes into a unique specifier.
GetNodeProfile(const FoldingSetBase * Base,Node * N,FoldingSetNodeID & ID)609 static void GetNodeProfile(const FoldingSetBase *Base, Node *N,
610 FoldingSetNodeID &ID) {
611 T *TN = static_cast<T *>(N);
612 ContextualFoldingSetTrait<T, Ctx>::Profile(*TN, ID, getContext(Base));
613 }
614
NodeEquals(const FoldingSetBase * Base,Node * N,const FoldingSetNodeID & ID,unsigned IDHash,FoldingSetNodeID & TempID)615 static bool NodeEquals(const FoldingSetBase *Base, Node *N,
616 const FoldingSetNodeID &ID, unsigned IDHash,
617 FoldingSetNodeID &TempID) {
618 T *TN = static_cast<T *>(N);
619 return ContextualFoldingSetTrait<T, Ctx>::Equals(*TN, ID, IDHash, TempID,
620 getContext(Base));
621 }
622
ComputeNodeHash(const FoldingSetBase * Base,Node * N,FoldingSetNodeID & TempID)623 static unsigned ComputeNodeHash(const FoldingSetBase *Base, Node *N,
624 FoldingSetNodeID &TempID) {
625 T *TN = static_cast<T *>(N);
626 return ContextualFoldingSetTrait<T, Ctx>::ComputeHash(*TN, TempID,
627 getContext(Base));
628 }
629
getFoldingSetInfo()630 static const FoldingSetBase::FoldingSetInfo &getFoldingSetInfo() {
631 static constexpr FoldingSetBase::FoldingSetInfo Info = {
632 GetNodeProfile, NodeEquals, ComputeNodeHash};
633 return Info;
634 }
635 friend Super;
636
637 public:
638 explicit ContextualFoldingSet(Ctx Context, unsigned Log2InitSize = 6)
Super(Log2InitSize)639 : Super(Log2InitSize), Context(Context) {}
640
getContext()641 Ctx getContext() const { return Context; }
642 };
643
644 //===----------------------------------------------------------------------===//
645 /// FoldingSetVector - This template class combines a FoldingSet and a vector
646 /// to provide the interface of FoldingSet but with deterministic iteration
647 /// order based on the insertion order. T must be a subclass of FoldingSetNode
648 /// and implement a Profile function.
649 template <class T, class VectorT = SmallVector<T*, 8>>
650 class FoldingSetVector {
651 FoldingSet<T> Set;
652 VectorT Vector;
653
654 public:
Set(Log2InitSize)655 explicit FoldingSetVector(unsigned Log2InitSize = 6) : Set(Log2InitSize) {}
656
657 using iterator = pointee_iterator<typename VectorT::iterator>;
658
begin()659 iterator begin() { return Vector.begin(); }
end()660 iterator end() { return Vector.end(); }
661
662 using const_iterator = pointee_iterator<typename VectorT::const_iterator>;
663
begin()664 const_iterator begin() const { return Vector.begin(); }
end()665 const_iterator end() const { return Vector.end(); }
666
667 /// clear - Remove all nodes from the folding set.
clear()668 void clear() { Set.clear(); Vector.clear(); }
669
670 /// FindNodeOrInsertPos - Look up the node specified by ID. If it exists,
671 /// return it. If not, return the insertion token that will make insertion
672 /// faster.
FindNodeOrInsertPos(const FoldingSetNodeID & ID,void * & InsertPos)673 T *FindNodeOrInsertPos(const FoldingSetNodeID &ID, void *&InsertPos) {
674 return Set.FindNodeOrInsertPos(ID, InsertPos);
675 }
676
677 /// GetOrInsertNode - If there is an existing simple Node exactly
678 /// equal to the specified node, return it. Otherwise, insert 'N' and
679 /// return it instead.
GetOrInsertNode(T * N)680 T *GetOrInsertNode(T *N) {
681 T *Result = Set.GetOrInsertNode(N);
682 if (Result == N) Vector.push_back(N);
683 return Result;
684 }
685
686 /// InsertNode - Insert the specified node into the folding set, knowing that
687 /// it is not already in the folding set. InsertPos must be obtained from
688 /// FindNodeOrInsertPos.
InsertNode(T * N,void * InsertPos)689 void InsertNode(T *N, void *InsertPos) {
690 Set.InsertNode(N, InsertPos);
691 Vector.push_back(N);
692 }
693
694 /// InsertNode - Insert the specified node into the folding set, knowing that
695 /// it is not already in the folding set.
InsertNode(T * N)696 void InsertNode(T *N) {
697 Set.InsertNode(N);
698 Vector.push_back(N);
699 }
700
701 /// size - Returns the number of nodes in the folding set.
size()702 unsigned size() const { return Set.size(); }
703
704 /// empty - Returns true if there are no nodes in the folding set.
empty()705 bool empty() const { return Set.empty(); }
706 };
707
708 //===----------------------------------------------------------------------===//
709 /// FoldingSetIteratorImpl - This is the common iterator support shared by all
710 /// folding sets, which knows how to walk the folding set hash table.
711 class FoldingSetIteratorImpl {
712 protected:
713 FoldingSetNode *NodePtr;
714
715 LLVM_ABI FoldingSetIteratorImpl(void **Bucket);
716
717 LLVM_ABI void advance();
718
719 public:
720 bool operator==(const FoldingSetIteratorImpl &RHS) const {
721 return NodePtr == RHS.NodePtr;
722 }
723 bool operator!=(const FoldingSetIteratorImpl &RHS) const {
724 return NodePtr != RHS.NodePtr;
725 }
726 };
727
728 template <class T> class FoldingSetIterator : public FoldingSetIteratorImpl {
729 public:
FoldingSetIterator(void ** Bucket)730 explicit FoldingSetIterator(void **Bucket) : FoldingSetIteratorImpl(Bucket) {}
731
732 T &operator*() const {
733 return *static_cast<T*>(NodePtr);
734 }
735
736 T *operator->() const {
737 return static_cast<T*>(NodePtr);
738 }
739
740 inline FoldingSetIterator &operator++() { // Preincrement
741 advance();
742 return *this;
743 }
744 FoldingSetIterator operator++(int) { // Postincrement
745 FoldingSetIterator tmp = *this; ++*this; return tmp;
746 }
747 };
748
749 //===----------------------------------------------------------------------===//
750 /// FoldingSetBucketIteratorImpl - This is the common bucket iterator support
751 /// shared by all folding sets, which knows how to walk a particular bucket
752 /// of a folding set hash table.
753 class FoldingSetBucketIteratorImpl {
754 protected:
755 void *Ptr;
756
757 LLVM_ABI explicit FoldingSetBucketIteratorImpl(void **Bucket);
758
FoldingSetBucketIteratorImpl(void ** Bucket,bool)759 FoldingSetBucketIteratorImpl(void **Bucket, bool) : Ptr(Bucket) {}
760
advance()761 void advance() {
762 void *Probe = static_cast<FoldingSetNode*>(Ptr)->getNextInBucket();
763 uintptr_t x = reinterpret_cast<uintptr_t>(Probe) & ~0x1;
764 Ptr = reinterpret_cast<void*>(x);
765 }
766
767 public:
768 bool operator==(const FoldingSetBucketIteratorImpl &RHS) const {
769 return Ptr == RHS.Ptr;
770 }
771 bool operator!=(const FoldingSetBucketIteratorImpl &RHS) const {
772 return Ptr != RHS.Ptr;
773 }
774 };
775
776 template <class T>
777 class FoldingSetBucketIterator : public FoldingSetBucketIteratorImpl {
778 public:
FoldingSetBucketIterator(void ** Bucket)779 explicit FoldingSetBucketIterator(void **Bucket) :
780 FoldingSetBucketIteratorImpl(Bucket) {}
781
FoldingSetBucketIterator(void ** Bucket,bool)782 FoldingSetBucketIterator(void **Bucket, bool) :
783 FoldingSetBucketIteratorImpl(Bucket, true) {}
784
785 T &operator*() const { return *static_cast<T*>(Ptr); }
786 T *operator->() const { return static_cast<T*>(Ptr); }
787
788 inline FoldingSetBucketIterator &operator++() { // Preincrement
789 advance();
790 return *this;
791 }
792 FoldingSetBucketIterator operator++(int) { // Postincrement
793 FoldingSetBucketIterator tmp = *this; ++*this; return tmp;
794 }
795 };
796
797 //===----------------------------------------------------------------------===//
798 /// FoldingSetNodeWrapper - This template class is used to "wrap" arbitrary
799 /// types in an enclosing object so that they can be inserted into FoldingSets.
800 template <typename T>
801 class FoldingSetNodeWrapper : public FoldingSetNode {
802 T data;
803
804 public:
805 template <typename... Ts>
FoldingSetNodeWrapper(Ts &&...Args)806 explicit FoldingSetNodeWrapper(Ts &&... Args)
807 : data(std::forward<Ts>(Args)...) {}
808
Profile(FoldingSetNodeID & ID)809 void Profile(FoldingSetNodeID &ID) { FoldingSetTrait<T>::Profile(data, ID); }
810
getValue()811 T &getValue() { return data; }
getValue()812 const T &getValue() const { return data; }
813
814 operator T&() { return data; }
815 operator const T&() const { return data; }
816 };
817
818 //===----------------------------------------------------------------------===//
819 /// FastFoldingSetNode - This is a subclass of FoldingSetNode which stores
820 /// a FoldingSetNodeID value rather than requiring the node to recompute it
821 /// each time it is needed. This trades space for speed (which can be
822 /// significant if the ID is long), and it also permits nodes to drop
823 /// information that would otherwise only be required for recomputing an ID.
824 class FastFoldingSetNode : public FoldingSetNode {
825 FoldingSetNodeID FastID;
826
827 protected:
FastFoldingSetNode(const FoldingSetNodeID & ID)828 explicit FastFoldingSetNode(const FoldingSetNodeID &ID) : FastID(ID) {}
829
830 public:
Profile(FoldingSetNodeID & ID)831 void Profile(FoldingSetNodeID &ID) const { ID.AddNodeID(FastID); }
832 };
833
834 //===----------------------------------------------------------------------===//
835 // Partial specializations of FoldingSetTrait.
836
837 template<typename T> struct FoldingSetTrait<T*> {
838 static inline void Profile(T *X, FoldingSetNodeID &ID) {
839 ID.AddPointer(X);
840 }
841 };
842 template <typename T1, typename T2>
843 struct FoldingSetTrait<std::pair<T1, T2>> {
844 static inline void Profile(const std::pair<T1, T2> &P,
845 FoldingSetNodeID &ID) {
846 ID.Add(P.first);
847 ID.Add(P.second);
848 }
849 };
850
851 template <typename T>
852 struct FoldingSetTrait<T, std::enable_if_t<std::is_enum<T>::value>> {
853 static void Profile(const T &X, FoldingSetNodeID &ID) {
854 ID.AddInteger(llvm::to_underlying(X));
855 }
856 };
857
858 } // end namespace llvm
859
860 #endif // LLVM_ADT_FOLDINGSET_H
861