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