LLVM 24.0.0git
FoldingSet.cpp
Go to the documentation of this file.
1//===-- Support/FoldingSet.cpp - 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// This file implements a hash set that can be used to remove duplication of
10// nodes in a graph.
11//
12//===----------------------------------------------------------------------===//
13
14#include "llvm/ADT/FoldingSet.h"
15#include "llvm/ADT/STLExtras.h"
16#include "llvm/ADT/StringRef.h"
21#include <cassert>
22#include <cstring>
23using namespace llvm;
24
25//===----------------------------------------------------------------------===//
26// FoldingSetNodeIDRef Implementation
27
29 if (Size != RHS.Size)
30 return false;
31 return memcmp(Data, RHS.Data, Size * sizeof(*Data)) == 0;
32}
33
34/// Used to compare the "ordering" of two nodes as defined by the
35/// profiled bits and their ordering defined by memcmp().
37 if (Size != RHS.Size)
38 return Size < RHS.Size;
39 return memcmp(Data, RHS.Data, Size * sizeof(*Data)) < 0;
40}
41
42//===----------------------------------------------------------------------===//
43// FoldingSetNodeID Implementation
44
45/// Add* - Add various data types to Bit data.
46///
48 unsigned Size = String.size();
49
50 unsigned NumInserts = 1 + divideCeil(Size, 4);
51 Bits.reserve(Bits.size() + NumInserts);
52
53 Bits.push_back(Size);
54 if (!Size)
55 return;
56
57 unsigned Units = Size / 4;
58 unsigned Pos = 0;
59 const unsigned *Base = (const unsigned *)String.data();
60
61 // If the string is aligned do a bulk transfer.
62 if (!((intptr_t)Base & 3)) {
63 Bits.append(Base, Base + Units);
64 Pos = (Units + 1) * 4;
65 } else {
66 // Otherwise do it the hard way.
67 // To be compatible with above bulk transfer, we need to take endianness
68 // into account.
70 "Unexpected host endianness");
72 for (Pos += 4; Pos <= Size; Pos += 4) {
73 unsigned V = ((unsigned char)String[Pos - 4] << 24) |
74 ((unsigned char)String[Pos - 3] << 16) |
75 ((unsigned char)String[Pos - 2] << 8) |
76 (unsigned char)String[Pos - 1];
77 Bits.push_back(V);
78 }
79 } else { // Little-endian host
80 for (Pos += 4; Pos <= Size; Pos += 4) {
81 unsigned V = ((unsigned char)String[Pos - 1] << 24) |
82 ((unsigned char)String[Pos - 2] << 16) |
83 ((unsigned char)String[Pos - 3] << 8) |
84 (unsigned char)String[Pos - 4];
85 Bits.push_back(V);
86 }
87 }
88 }
89
90 // With the leftover bits.
91 unsigned V = 0;
92 // Pos will have overshot size by 4 - #bytes left over.
93 // No need to take endianness into account here - this is always executed.
94 switch (Pos - Size) {
95 case 1:
96 V = (V << 8) | (unsigned char)String[Size - 3];
97 [[fallthrough]];
98 case 2:
99 V = (V << 8) | (unsigned char)String[Size - 2];
100 [[fallthrough]];
101 case 3:
102 V = (V << 8) | (unsigned char)String[Size - 1];
103 break;
104 default:
105 return; // Nothing left.
106 }
107
108 Bits.push_back(V);
109}
110
111// AddNodeID - Adds the Bit data of another ID to *this.
113 Bits.append(ID.Bits.begin(), ID.Bits.end());
114}
115
116/// operator== - Used to compare two nodes to each other.
117///
119 return *this == FoldingSetNodeIDRef(RHS.Bits.data(), RHS.Bits.size());
120}
121
122/// operator== - Used to compare two nodes to each other.
123///
125 return FoldingSetNodeIDRef(Bits.data(), Bits.size()) == RHS;
126}
127
128/// Used to compare the "ordering" of two nodes as defined by the
129/// profiled bits and their ordering defined by memcmp().
131 return *this < FoldingSetNodeIDRef(RHS.Bits.data(), RHS.Bits.size());
132}
133
135 return FoldingSetNodeIDRef(Bits.data(), Bits.size()) < RHS;
136}
137
138/// Intern - Copy this node's data to a memory region allocated from the
139/// given allocator and return a FoldingSetNodeIDRef describing the
140/// interned data.
143 unsigned *New = Allocator.Allocate<unsigned>(Bits.size());
144 llvm::uninitialized_copy(Bits, New);
145 return FoldingSetNodeIDRef(New, Bits.size());
146}
147
148//===----------------------------------------------------------------------===//
149/// Helper functions for FoldingSetBase.
150
151/// GetNextPtr - In order to save space, each bucket is a
152/// singly-linked-list. In order to make deletion more efficient, we make
153/// the list circular, so we can delete a node without computing its hash.
154/// The problem with this is that the start of the hash buckets are not
155/// Nodes. If NextInBucketPtr is a bucket pointer, this method returns null:
156/// use GetBucketPtr when this happens.
157static FoldingSetBase::Node *GetNextPtr(void *NextInBucketPtr) {
158 // The low bit is set if this is the pointer back to the bucket.
159 if (reinterpret_cast<intptr_t>(NextInBucketPtr) & 1)
160 return nullptr;
161
162 return static_cast<FoldingSetBase::Node *>(NextInBucketPtr);
163}
164
165/// GetBucketPtr - Provides a casting of a bucket pointer for isNode
166/// testing.
167static void **GetBucketPtr(void *NextInBucketPtr) {
168 intptr_t Ptr = reinterpret_cast<intptr_t>(NextInBucketPtr);
169 assert((Ptr & 1) && "Not a bucket pointer");
170 return reinterpret_cast<void **>(Ptr & ~intptr_t(1));
171}
172
173/// GetBucketFor - Hash the specified node ID and return the hash bucket for
174/// the specified ID.
175static void **GetBucketFor(unsigned Hash, void **Buckets, unsigned NumBuckets) {
176 // NumBuckets is always a power of 2.
177 unsigned BucketNum = Hash & (NumBuckets - 1);
178 return Buckets + BucketNum;
179}
180
181/// AllocateBuckets - Allocated initialized bucket memory.
182static void **AllocateBuckets(unsigned NumBuckets) {
183 void **Buckets =
184 static_cast<void **>(safe_calloc(NumBuckets + 1, sizeof(void *)));
185 // Set the very last bucket to be a non-null "pointer".
186 Buckets[NumBuckets] = reinterpret_cast<void *>(-1);
187 return Buckets;
188}
189
190//===----------------------------------------------------------------------===//
191// FoldingSetBase Implementation
192
193FoldingSetBase::FoldingSetBase(unsigned Log2InitSize) {
194 assert(5 < Log2InitSize && Log2InitSize < 32 &&
195 "Initial hash table size out of range");
196 NumBuckets = 1 << Log2InitSize;
198 NumNodes = 0;
199}
200
203 Arg.Buckets = nullptr;
204 Arg.NumBuckets = 0;
205 Arg.NumNodes = 0;
206}
207
209 free(Buckets); // This may be null if the set is in a moved-from state.
210 Buckets = RHS.Buckets;
211 NumBuckets = RHS.NumBuckets;
212 NumNodes = RHS.NumNodes;
213 RHS.Buckets = nullptr;
214 RHS.NumBuckets = 0;
215 RHS.NumNodes = 0;
216 return *this;
217}
218
220
222 // Set all but the last bucket to null pointers.
223 memset(Buckets, 0, NumBuckets * sizeof(void *));
224
225 // Set the very last bucket to be a non-null "pointer".
226 Buckets[NumBuckets] = reinterpret_cast<void *>(-1);
227
228 // Reset the node count to zero.
229 NumNodes = 0;
230}
231
232void FoldingSetBase::GrowBucketCount(unsigned NewBucketCount,
233 const FoldingSetInfo &Info) {
234 assert((NewBucketCount > NumBuckets) &&
235 "Can't shrink a folding set with GrowBucketCount");
236 assert(isPowerOf2_32(NewBucketCount) && "Bad bucket count!");
237 void **OldBuckets = Buckets;
238 unsigned OldNumBuckets = NumBuckets;
239
240 // Clear out new buckets.
241 Buckets = AllocateBuckets(NewBucketCount);
242 // Set NumBuckets only if allocation of new buckets was successful.
243 NumBuckets = NewBucketCount;
244 NumNodes = 0;
245
246 // Walk the old buckets, rehashing nodes into their new place.
247 FoldingSetNodeID TempID;
248 for (unsigned i = 0; i != OldNumBuckets; ++i) {
249 void *Probe = OldBuckets[i];
250 if (!Probe)
251 continue;
252 while (Node *NodeInBucket = GetNextPtr(Probe)) {
253 // Figure out the next link, remove NodeInBucket from the old link.
254 Probe = NodeInBucket->getNextInBucket();
255 NodeInBucket->SetNextInBucket(nullptr);
256
257 // Insert the node into the new bucket, after recomputing the hash.
258 InsertNode(NodeInBucket,
259 GetBucketFor(Info.ComputeNodeHash(this, NodeInBucket, TempID),
261 Info);
262 TempID.clear();
263 }
264 }
265
266 free(OldBuckets);
267}
268
269/// GrowHashTable - Double the size of the hash table and rehash everything.
270///
271void FoldingSetBase::GrowHashTable(const FoldingSetInfo &Info) {
272 GrowBucketCount(NumBuckets * 2, Info);
273}
274
275void FoldingSetBase::reserve(unsigned EltCount, const FoldingSetInfo &Info) {
276 // This will give us somewhere between EltCount / 2 and
277 // EltCount buckets. This puts us in the load factor
278 // range of 1.0 - 2.0.
279 if (EltCount < capacity())
280 return;
281 GrowBucketCount(llvm::bit_floor(EltCount), Info);
282}
283
284/// FindNodeOrInsertPos - Look up the node specified by ID. If it exists,
285/// return it. If not, return the insertion token that will make insertion
286/// faster.
288 const FoldingSetNodeID &ID, void *&InsertPos, const FoldingSetInfo &Info) {
289 unsigned IDHash = ID.ComputeHash();
290 void **Bucket = GetBucketFor(IDHash, Buckets, NumBuckets);
291 void *Probe = *Bucket;
292
293 InsertPos = nullptr;
294
295 FoldingSetNodeID TempID;
296 while (Node *NodeInBucket = GetNextPtr(Probe)) {
297 if (Info.NodeEquals(this, NodeInBucket, ID, IDHash, TempID))
298 return NodeInBucket;
299 TempID.clear();
300
301 Probe = NodeInBucket->getNextInBucket();
302 }
303
304 // Didn't find the node, return null with the bucket as the InsertPos.
305 InsertPos = Bucket;
306 return nullptr;
307}
308
309/// InsertNode - Insert the specified node into the folding set, knowing that it
310/// is not already in the map. InsertPos must be obtained from
311/// FindNodeOrInsertPos.
312void FoldingSetBase::InsertNode(Node *N, void *InsertPos,
313 const FoldingSetInfo &Info) {
314 assert(!N->getNextInBucket());
315 // Do we need to grow the hashtable?
316 if (NumNodes + 1 > capacity()) {
317 GrowHashTable(Info);
318 FoldingSetNodeID TempID;
319 InsertPos = GetBucketFor(Info.ComputeNodeHash(this, N, TempID), Buckets,
320 NumBuckets);
321 }
322
323 ++NumNodes;
324
325 /// The insert position is actually a bucket pointer.
326 void **Bucket = static_cast<void **>(InsertPos);
327
328 void *Next = *Bucket;
329
330 // If this is the first insertion into this bucket, its next pointer will be
331 // null. Pretend as if it pointed to itself, setting the low bit to indicate
332 // that it is a pointer to the bucket.
333 if (!Next)
334 Next = reinterpret_cast<void *>(reinterpret_cast<intptr_t>(Bucket) | 1);
335
336 // Set the node's next pointer, and make the bucket point to the node.
337 N->SetNextInBucket(Next);
338 *Bucket = N;
339}
340
341/// RemoveNode - Remove a node from the folding set, returning true if one was
342/// removed or false if the node was not in the folding set.
344 // Because each bucket is a circular list, we don't need to compute N's hash
345 // to remove it.
346 void *Ptr = N->getNextInBucket();
347 if (!Ptr)
348 return false; // Not in folding set.
349
350 --NumNodes;
351 N->SetNextInBucket(nullptr);
352
353 // Remember what N originally pointed to, either a bucket or another node.
354 void *NodeNextPtr = Ptr;
355
356 // Chase around the list until we find the node (or bucket) which points to N.
357 while (true) {
358 if (Node *NodeInBucket = GetNextPtr(Ptr)) {
359 // Advance pointer.
360 Ptr = NodeInBucket->getNextInBucket();
361
362 // We found a node that points to N, change it to point to N's next node,
363 // removing N from the list.
364 if (Ptr == N) {
365 NodeInBucket->SetNextInBucket(NodeNextPtr);
366 return true;
367 }
368 } else {
369 void **Bucket = GetBucketPtr(Ptr);
370 Ptr = *Bucket;
371
372 // If we found that the bucket points to N, update the bucket to point to
373 // whatever is next.
374 if (Ptr == N) {
375 *Bucket = NodeNextPtr;
376 return true;
377 }
378 }
379 }
380}
381
382/// GetOrInsertNode - If there is an existing simple Node exactly
383/// equal to the specified node, return it. Otherwise, insert 'N' and it
384/// instead.
387 const FoldingSetInfo &Info) {
389 Info.GetNodeProfile(this, N, ID);
390 void *IP;
391 if (Node *E = FindNodeOrInsertPos(ID, IP, Info))
392 return E;
393 InsertNode(N, IP, Info);
394 return N;
395}
396
397//===----------------------------------------------------------------------===//
398// FoldingSetIteratorImpl Implementation
399
401 // Skip to the first non-null non-self-cycle bucket.
402 while (*Bucket != reinterpret_cast<void *>(-1) &&
403 (!*Bucket || !GetNextPtr(*Bucket)))
404 ++Bucket;
405
406 NodePtr = static_cast<FoldingSetNode *>(*Bucket);
407}
408
410 // If there is another link within this bucket, go to it.
411 void *Probe = NodePtr->getNextInBucket();
412
413 if (FoldingSetNode *NextNodeInBucket = GetNextPtr(Probe))
414 NodePtr = NextNodeInBucket;
415 else {
416 // Otherwise, this is the last link in this bucket.
417 void **Bucket = GetBucketPtr(Probe);
418
419 // Skip to the next non-null non-self-cycle bucket.
420 do {
421 ++Bucket;
422 } while (*Bucket != reinterpret_cast<void *>(-1) &&
423 (!*Bucket || !GetNextPtr(*Bucket)));
424
425 NodePtr = static_cast<FoldingSetNode *>(*Bucket);
426 }
427}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
This file defines the BumpPtrAllocator interface.
static void ** GetBucketPtr(void *NextInBucketPtr)
GetBucketPtr - Provides a casting of a bucket pointer for isNode testing.
static void ** GetBucketFor(unsigned Hash, void **Buckets, unsigned NumBuckets)
GetBucketFor - Hash the specified node ID and return the hash bucket for the specified ID.
static void ** AllocateBuckets(unsigned NumBuckets)
AllocateBuckets - Allocated initialized bucket memory.
static FoldingSetBase::Node * GetNextPtr(void *NextInBucketPtr)
Helper functions for FoldingSetBase.
This file defines a hash set that can be used to remove duplication of nodes in a graph.
This file contains some templates that are useful if you are working with the STL at all.
This class is used to maintain the singly linked bucket list in a folding set.
Definition FoldingSet.h:310
LLVM_ABI FoldingSetBase(unsigned Log2InitSize=6)
void ** Buckets
Array of bucket chains.
Definition FoldingSet.h:292
LLVM_ABI void reserve(unsigned EltCount, const FoldingSetInfo &Info)
Increase the number of buckets such that adding the EltCount th node won't cause a rebucket operation...
unsigned capacity() const
Returns the number of nodes permitted in the folding set before a rebucket operation is performed.
Definition FoldingSet.h:334
LLVM_ABI bool RemoveNode(Node *N)
Remove a node from the folding set, returning true if one was removed or false if the node was not in...
LLVM_ABI FoldingSetBase & operator=(FoldingSetBase &&RHS)
LLVM_ABI ~FoldingSetBase()
unsigned NumBuckets
Length of the Buckets array. Always a power of 2.
Definition FoldingSet.h:295
unsigned NumNodes
Number of nodes in the folding set.
Definition FoldingSet.h:299
LLVM_ABI Node * GetOrInsertNode(Node *N, const FoldingSetInfo &Info)
If there is an existing simple Node exactly equal to the node N, return it.
LLVM_ABI void InsertNode(Node *N, void *InsertPos, const FoldingSetInfo &Info)
Insert the specified node into the folding set, knowing that it is not already in the folding set.
LLVM_ABI void clear()
Remove all nodes from the folding set.
LLVM_ABI Node * FindNodeOrInsertPos(const FoldingSetNodeID &ID, void *&InsertPos, const FoldingSetInfo &Info)
Look up the node specified by ID.
LLVM_ABI FoldingSetIteratorImpl(void **Bucket)
This class describes a reference to an interned FoldingSetNodeID, which can be a useful to store node...
Definition FoldingSet.h:165
LLVM_ABI bool operator==(FoldingSetNodeIDRef) const
LLVM_ABI bool operator<(FoldingSetNodeIDRef) const
Used to compare the "ordering" of two nodes as defined by the profiled bits and their ordering define...
This class is used to gather all the unique data bits of a node.
Definition FoldingSet.h:202
LLVM_ABI FoldingSetNodeIDRef Intern(BumpPtrAllocator &Allocator) const
Copy this node's data to a memory region allocated from the given allocator and return a FoldingSetNo...
void clear()
Clear the accumulated profile, allowing this FoldingSetNodeID object to be used to compute a new prof...
Definition FoldingSet.h:247
LLVM_ABI bool operator==(const FoldingSetNodeID &RHS) const
operator== - Used to compare two nodes to each other.
LLVM_ABI bool operator<(const FoldingSetNodeID &RHS) const
Used to compare the "ordering" of two nodes as defined by the profiled bits and their ordering define...
LLVM_ABI void AddNodeID(const FoldingSetNodeID &ID)
LLVM_ABI void AddString(StringRef String)
Add* - Add various data types to Bit data.
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
constexpr bool IsLittleEndianHost
constexpr bool IsBigEndianHost
This is an optimization pass for GlobalISel generic memory operations.
FoldingSetBase::Node FoldingSetNode
Definition FoldingSet.h:401
auto uninitialized_copy(R &&Src, IterTy Dst)
Definition STLExtras.h:2111
LLVM_ATTRIBUTE_RETURNS_NONNULL void * safe_calloc(size_t Count, size_t Sz)
Definition MemAlloc.h:38
constexpr bool isPowerOf2_32(uint32_t Value)
Return true if the argument is a power of two > 0.
Definition MathExtras.h:280
constexpr T divideCeil(U Numerator, V Denominator)
Returns the integer ceil(Numerator / Denominator).
Definition MathExtras.h:395
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Next
Definition InstrProf.h:147
BumpPtrAllocatorImpl<> BumpPtrAllocator
The standard BumpPtrAllocator which just uses the default template parameters.
Definition Allocator.h:390
T bit_floor(T Value)
Returns the largest integral power of two no greater than Value if Value is nonzero.
Definition bit.h:347
#define N
Functions provided by the derived class to compute folding properties.
Definition FoldingSet.h:344