xref: /netbsd-src/external/apache2/llvm/dist/llvm/lib/Support/SmallPtrSet.cpp (revision 7330f729ccf0bd976a06f95fad452fe774fc7fd1)
1*7330f729Sjoerg //===- llvm/ADT/SmallPtrSet.cpp - 'Normally small' pointer set ------------===//
2*7330f729Sjoerg //
3*7330f729Sjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*7330f729Sjoerg // See https://llvm.org/LICENSE.txt for license information.
5*7330f729Sjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*7330f729Sjoerg //
7*7330f729Sjoerg //===----------------------------------------------------------------------===//
8*7330f729Sjoerg //
9*7330f729Sjoerg // This file implements the SmallPtrSet class.  See SmallPtrSet.h for an
10*7330f729Sjoerg // overview of the algorithm.
11*7330f729Sjoerg //
12*7330f729Sjoerg //===----------------------------------------------------------------------===//
13*7330f729Sjoerg 
14*7330f729Sjoerg #include "llvm/ADT/SmallPtrSet.h"
15*7330f729Sjoerg #include "llvm/ADT/DenseMapInfo.h"
16*7330f729Sjoerg #include "llvm/Support/MathExtras.h"
17*7330f729Sjoerg #include "llvm/Support/ErrorHandling.h"
18*7330f729Sjoerg #include <algorithm>
19*7330f729Sjoerg #include <cassert>
20*7330f729Sjoerg #include <cstdlib>
21*7330f729Sjoerg 
22*7330f729Sjoerg using namespace llvm;
23*7330f729Sjoerg 
shrink_and_clear()24*7330f729Sjoerg void SmallPtrSetImplBase::shrink_and_clear() {
25*7330f729Sjoerg   assert(!isSmall() && "Can't shrink a small set!");
26*7330f729Sjoerg   free(CurArray);
27*7330f729Sjoerg 
28*7330f729Sjoerg   // Reduce the number of buckets.
29*7330f729Sjoerg   unsigned Size = size();
30*7330f729Sjoerg   CurArraySize = Size > 16 ? 1 << (Log2_32_Ceil(Size) + 1) : 32;
31*7330f729Sjoerg   NumNonEmpty = NumTombstones = 0;
32*7330f729Sjoerg 
33*7330f729Sjoerg   // Install the new array.  Clear all the buckets to empty.
34*7330f729Sjoerg   CurArray = (const void**)safe_malloc(sizeof(void*) * CurArraySize);
35*7330f729Sjoerg 
36*7330f729Sjoerg   memset(CurArray, -1, CurArraySize*sizeof(void*));
37*7330f729Sjoerg }
38*7330f729Sjoerg 
39*7330f729Sjoerg std::pair<const void *const *, bool>
insert_imp_big(const void * Ptr)40*7330f729Sjoerg SmallPtrSetImplBase::insert_imp_big(const void *Ptr) {
41*7330f729Sjoerg   if (LLVM_UNLIKELY(size() * 4 >= CurArraySize * 3)) {
42*7330f729Sjoerg     // If more than 3/4 of the array is full, grow.
43*7330f729Sjoerg     Grow(CurArraySize < 64 ? 128 : CurArraySize * 2);
44*7330f729Sjoerg   } else if (LLVM_UNLIKELY(CurArraySize - NumNonEmpty < CurArraySize / 8)) {
45*7330f729Sjoerg     // If fewer of 1/8 of the array is empty (meaning that many are filled with
46*7330f729Sjoerg     // tombstones), rehash.
47*7330f729Sjoerg     Grow(CurArraySize);
48*7330f729Sjoerg   }
49*7330f729Sjoerg 
50*7330f729Sjoerg   // Okay, we know we have space.  Find a hash bucket.
51*7330f729Sjoerg   const void **Bucket = const_cast<const void**>(FindBucketFor(Ptr));
52*7330f729Sjoerg   if (*Bucket == Ptr)
53*7330f729Sjoerg     return std::make_pair(Bucket, false); // Already inserted, good.
54*7330f729Sjoerg 
55*7330f729Sjoerg   // Otherwise, insert it!
56*7330f729Sjoerg   if (*Bucket == getTombstoneMarker())
57*7330f729Sjoerg     --NumTombstones;
58*7330f729Sjoerg   else
59*7330f729Sjoerg     ++NumNonEmpty; // Track density.
60*7330f729Sjoerg   *Bucket = Ptr;
61*7330f729Sjoerg   incrementEpoch();
62*7330f729Sjoerg   return std::make_pair(Bucket, true);
63*7330f729Sjoerg }
64*7330f729Sjoerg 
FindBucketFor(const void * Ptr) const65*7330f729Sjoerg const void * const *SmallPtrSetImplBase::FindBucketFor(const void *Ptr) const {
66*7330f729Sjoerg   unsigned Bucket = DenseMapInfo<void *>::getHashValue(Ptr) & (CurArraySize-1);
67*7330f729Sjoerg   unsigned ArraySize = CurArraySize;
68*7330f729Sjoerg   unsigned ProbeAmt = 1;
69*7330f729Sjoerg   const void *const *Array = CurArray;
70*7330f729Sjoerg   const void *const *Tombstone = nullptr;
71*7330f729Sjoerg   while (true) {
72*7330f729Sjoerg     // If we found an empty bucket, the pointer doesn't exist in the set.
73*7330f729Sjoerg     // Return a tombstone if we've seen one so far, or the empty bucket if
74*7330f729Sjoerg     // not.
75*7330f729Sjoerg     if (LLVM_LIKELY(Array[Bucket] == getEmptyMarker()))
76*7330f729Sjoerg       return Tombstone ? Tombstone : Array+Bucket;
77*7330f729Sjoerg 
78*7330f729Sjoerg     // Found Ptr's bucket?
79*7330f729Sjoerg     if (LLVM_LIKELY(Array[Bucket] == Ptr))
80*7330f729Sjoerg       return Array+Bucket;
81*7330f729Sjoerg 
82*7330f729Sjoerg     // If this is a tombstone, remember it.  If Ptr ends up not in the set, we
83*7330f729Sjoerg     // prefer to return it than something that would require more probing.
84*7330f729Sjoerg     if (Array[Bucket] == getTombstoneMarker() && !Tombstone)
85*7330f729Sjoerg       Tombstone = Array+Bucket;  // Remember the first tombstone found.
86*7330f729Sjoerg 
87*7330f729Sjoerg     // It's a hash collision or a tombstone. Reprobe.
88*7330f729Sjoerg     Bucket = (Bucket + ProbeAmt++) & (ArraySize-1);
89*7330f729Sjoerg   }
90*7330f729Sjoerg }
91*7330f729Sjoerg 
92*7330f729Sjoerg /// Grow - Allocate a larger backing store for the buckets and move it over.
93*7330f729Sjoerg ///
Grow(unsigned NewSize)94*7330f729Sjoerg void SmallPtrSetImplBase::Grow(unsigned NewSize) {
95*7330f729Sjoerg   const void **OldBuckets = CurArray;
96*7330f729Sjoerg   const void **OldEnd = EndPointer();
97*7330f729Sjoerg   bool WasSmall = isSmall();
98*7330f729Sjoerg 
99*7330f729Sjoerg   // Install the new array.  Clear all the buckets to empty.
100*7330f729Sjoerg   const void **NewBuckets = (const void**) safe_malloc(sizeof(void*) * NewSize);
101*7330f729Sjoerg 
102*7330f729Sjoerg   // Reset member only if memory was allocated successfully
103*7330f729Sjoerg   CurArray = NewBuckets;
104*7330f729Sjoerg   CurArraySize = NewSize;
105*7330f729Sjoerg   memset(CurArray, -1, NewSize*sizeof(void*));
106*7330f729Sjoerg 
107*7330f729Sjoerg   // Copy over all valid entries.
108*7330f729Sjoerg   for (const void **BucketPtr = OldBuckets; BucketPtr != OldEnd; ++BucketPtr) {
109*7330f729Sjoerg     // Copy over the element if it is valid.
110*7330f729Sjoerg     const void *Elt = *BucketPtr;
111*7330f729Sjoerg     if (Elt != getTombstoneMarker() && Elt != getEmptyMarker())
112*7330f729Sjoerg       *const_cast<void**>(FindBucketFor(Elt)) = const_cast<void*>(Elt);
113*7330f729Sjoerg   }
114*7330f729Sjoerg 
115*7330f729Sjoerg   if (!WasSmall)
116*7330f729Sjoerg     free(OldBuckets);
117*7330f729Sjoerg   NumNonEmpty -= NumTombstones;
118*7330f729Sjoerg   NumTombstones = 0;
119*7330f729Sjoerg }
120*7330f729Sjoerg 
SmallPtrSetImplBase(const void ** SmallStorage,const SmallPtrSetImplBase & that)121*7330f729Sjoerg SmallPtrSetImplBase::SmallPtrSetImplBase(const void **SmallStorage,
122*7330f729Sjoerg                                          const SmallPtrSetImplBase &that) {
123*7330f729Sjoerg   SmallArray = SmallStorage;
124*7330f729Sjoerg 
125*7330f729Sjoerg   // If we're becoming small, prepare to insert into our stack space
126*7330f729Sjoerg   if (that.isSmall()) {
127*7330f729Sjoerg     CurArray = SmallArray;
128*7330f729Sjoerg   // Otherwise, allocate new heap space (unless we were the same size)
129*7330f729Sjoerg   } else {
130*7330f729Sjoerg     CurArray = (const void**)safe_malloc(sizeof(void*) * that.CurArraySize);
131*7330f729Sjoerg   }
132*7330f729Sjoerg 
133*7330f729Sjoerg   // Copy over the that array.
134*7330f729Sjoerg   CopyHelper(that);
135*7330f729Sjoerg }
136*7330f729Sjoerg 
SmallPtrSetImplBase(const void ** SmallStorage,unsigned SmallSize,SmallPtrSetImplBase && that)137*7330f729Sjoerg SmallPtrSetImplBase::SmallPtrSetImplBase(const void **SmallStorage,
138*7330f729Sjoerg                                          unsigned SmallSize,
139*7330f729Sjoerg                                          SmallPtrSetImplBase &&that) {
140*7330f729Sjoerg   SmallArray = SmallStorage;
141*7330f729Sjoerg   MoveHelper(SmallSize, std::move(that));
142*7330f729Sjoerg }
143*7330f729Sjoerg 
CopyFrom(const SmallPtrSetImplBase & RHS)144*7330f729Sjoerg void SmallPtrSetImplBase::CopyFrom(const SmallPtrSetImplBase &RHS) {
145*7330f729Sjoerg   assert(&RHS != this && "Self-copy should be handled by the caller.");
146*7330f729Sjoerg 
147*7330f729Sjoerg   if (isSmall() && RHS.isSmall())
148*7330f729Sjoerg     assert(CurArraySize == RHS.CurArraySize &&
149*7330f729Sjoerg            "Cannot assign sets with different small sizes");
150*7330f729Sjoerg 
151*7330f729Sjoerg   // If we're becoming small, prepare to insert into our stack space
152*7330f729Sjoerg   if (RHS.isSmall()) {
153*7330f729Sjoerg     if (!isSmall())
154*7330f729Sjoerg       free(CurArray);
155*7330f729Sjoerg     CurArray = SmallArray;
156*7330f729Sjoerg   // Otherwise, allocate new heap space (unless we were the same size)
157*7330f729Sjoerg   } else if (CurArraySize != RHS.CurArraySize) {
158*7330f729Sjoerg     if (isSmall())
159*7330f729Sjoerg       CurArray = (const void**)safe_malloc(sizeof(void*) * RHS.CurArraySize);
160*7330f729Sjoerg     else {
161*7330f729Sjoerg       const void **T = (const void**)safe_realloc(CurArray,
162*7330f729Sjoerg                                              sizeof(void*) * RHS.CurArraySize);
163*7330f729Sjoerg       CurArray = T;
164*7330f729Sjoerg     }
165*7330f729Sjoerg   }
166*7330f729Sjoerg 
167*7330f729Sjoerg   CopyHelper(RHS);
168*7330f729Sjoerg }
169*7330f729Sjoerg 
CopyHelper(const SmallPtrSetImplBase & RHS)170*7330f729Sjoerg void SmallPtrSetImplBase::CopyHelper(const SmallPtrSetImplBase &RHS) {
171*7330f729Sjoerg   // Copy over the new array size
172*7330f729Sjoerg   CurArraySize = RHS.CurArraySize;
173*7330f729Sjoerg 
174*7330f729Sjoerg   // Copy over the contents from the other set
175*7330f729Sjoerg   std::copy(RHS.CurArray, RHS.EndPointer(), CurArray);
176*7330f729Sjoerg 
177*7330f729Sjoerg   NumNonEmpty = RHS.NumNonEmpty;
178*7330f729Sjoerg   NumTombstones = RHS.NumTombstones;
179*7330f729Sjoerg }
180*7330f729Sjoerg 
MoveFrom(unsigned SmallSize,SmallPtrSetImplBase && RHS)181*7330f729Sjoerg void SmallPtrSetImplBase::MoveFrom(unsigned SmallSize,
182*7330f729Sjoerg                                    SmallPtrSetImplBase &&RHS) {
183*7330f729Sjoerg   if (!isSmall())
184*7330f729Sjoerg     free(CurArray);
185*7330f729Sjoerg   MoveHelper(SmallSize, std::move(RHS));
186*7330f729Sjoerg }
187*7330f729Sjoerg 
MoveHelper(unsigned SmallSize,SmallPtrSetImplBase && RHS)188*7330f729Sjoerg void SmallPtrSetImplBase::MoveHelper(unsigned SmallSize,
189*7330f729Sjoerg                                      SmallPtrSetImplBase &&RHS) {
190*7330f729Sjoerg   assert(&RHS != this && "Self-move should be handled by the caller.");
191*7330f729Sjoerg 
192*7330f729Sjoerg   if (RHS.isSmall()) {
193*7330f729Sjoerg     // Copy a small RHS rather than moving.
194*7330f729Sjoerg     CurArray = SmallArray;
195*7330f729Sjoerg     std::copy(RHS.CurArray, RHS.CurArray + RHS.NumNonEmpty, CurArray);
196*7330f729Sjoerg   } else {
197*7330f729Sjoerg     CurArray = RHS.CurArray;
198*7330f729Sjoerg     RHS.CurArray = RHS.SmallArray;
199*7330f729Sjoerg   }
200*7330f729Sjoerg 
201*7330f729Sjoerg   // Copy the rest of the trivial members.
202*7330f729Sjoerg   CurArraySize = RHS.CurArraySize;
203*7330f729Sjoerg   NumNonEmpty = RHS.NumNonEmpty;
204*7330f729Sjoerg   NumTombstones = RHS.NumTombstones;
205*7330f729Sjoerg 
206*7330f729Sjoerg   // Make the RHS small and empty.
207*7330f729Sjoerg   RHS.CurArraySize = SmallSize;
208*7330f729Sjoerg   assert(RHS.CurArray == RHS.SmallArray);
209*7330f729Sjoerg   RHS.NumNonEmpty = 0;
210*7330f729Sjoerg   RHS.NumTombstones = 0;
211*7330f729Sjoerg }
212*7330f729Sjoerg 
swap(SmallPtrSetImplBase & RHS)213*7330f729Sjoerg void SmallPtrSetImplBase::swap(SmallPtrSetImplBase &RHS) {
214*7330f729Sjoerg   if (this == &RHS) return;
215*7330f729Sjoerg 
216*7330f729Sjoerg   // We can only avoid copying elements if neither set is small.
217*7330f729Sjoerg   if (!this->isSmall() && !RHS.isSmall()) {
218*7330f729Sjoerg     std::swap(this->CurArray, RHS.CurArray);
219*7330f729Sjoerg     std::swap(this->CurArraySize, RHS.CurArraySize);
220*7330f729Sjoerg     std::swap(this->NumNonEmpty, RHS.NumNonEmpty);
221*7330f729Sjoerg     std::swap(this->NumTombstones, RHS.NumTombstones);
222*7330f729Sjoerg     return;
223*7330f729Sjoerg   }
224*7330f729Sjoerg 
225*7330f729Sjoerg   // FIXME: From here on we assume that both sets have the same small size.
226*7330f729Sjoerg 
227*7330f729Sjoerg   // If only RHS is small, copy the small elements into LHS and move the pointer
228*7330f729Sjoerg   // from LHS to RHS.
229*7330f729Sjoerg   if (!this->isSmall() && RHS.isSmall()) {
230*7330f729Sjoerg     assert(RHS.CurArray == RHS.SmallArray);
231*7330f729Sjoerg     std::copy(RHS.CurArray, RHS.CurArray + RHS.NumNonEmpty, this->SmallArray);
232*7330f729Sjoerg     std::swap(RHS.CurArraySize, this->CurArraySize);
233*7330f729Sjoerg     std::swap(this->NumNonEmpty, RHS.NumNonEmpty);
234*7330f729Sjoerg     std::swap(this->NumTombstones, RHS.NumTombstones);
235*7330f729Sjoerg     RHS.CurArray = this->CurArray;
236*7330f729Sjoerg     this->CurArray = this->SmallArray;
237*7330f729Sjoerg     return;
238*7330f729Sjoerg   }
239*7330f729Sjoerg 
240*7330f729Sjoerg   // If only LHS is small, copy the small elements into RHS and move the pointer
241*7330f729Sjoerg   // from RHS to LHS.
242*7330f729Sjoerg   if (this->isSmall() && !RHS.isSmall()) {
243*7330f729Sjoerg     assert(this->CurArray == this->SmallArray);
244*7330f729Sjoerg     std::copy(this->CurArray, this->CurArray + this->NumNonEmpty,
245*7330f729Sjoerg               RHS.SmallArray);
246*7330f729Sjoerg     std::swap(RHS.CurArraySize, this->CurArraySize);
247*7330f729Sjoerg     std::swap(RHS.NumNonEmpty, this->NumNonEmpty);
248*7330f729Sjoerg     std::swap(RHS.NumTombstones, this->NumTombstones);
249*7330f729Sjoerg     this->CurArray = RHS.CurArray;
250*7330f729Sjoerg     RHS.CurArray = RHS.SmallArray;
251*7330f729Sjoerg     return;
252*7330f729Sjoerg   }
253*7330f729Sjoerg 
254*7330f729Sjoerg   // Both a small, just swap the small elements.
255*7330f729Sjoerg   assert(this->isSmall() && RHS.isSmall());
256*7330f729Sjoerg   unsigned MinNonEmpty = std::min(this->NumNonEmpty, RHS.NumNonEmpty);
257*7330f729Sjoerg   std::swap_ranges(this->SmallArray, this->SmallArray + MinNonEmpty,
258*7330f729Sjoerg                    RHS.SmallArray);
259*7330f729Sjoerg   if (this->NumNonEmpty > MinNonEmpty) {
260*7330f729Sjoerg     std::copy(this->SmallArray + MinNonEmpty,
261*7330f729Sjoerg               this->SmallArray + this->NumNonEmpty,
262*7330f729Sjoerg               RHS.SmallArray + MinNonEmpty);
263*7330f729Sjoerg   } else {
264*7330f729Sjoerg     std::copy(RHS.SmallArray + MinNonEmpty, RHS.SmallArray + RHS.NumNonEmpty,
265*7330f729Sjoerg               this->SmallArray + MinNonEmpty);
266*7330f729Sjoerg   }
267*7330f729Sjoerg   assert(this->CurArraySize == RHS.CurArraySize);
268*7330f729Sjoerg   std::swap(this->NumNonEmpty, RHS.NumNonEmpty);
269*7330f729Sjoerg   std::swap(this->NumTombstones, RHS.NumTombstones);
270*7330f729Sjoerg }
271