xref: /freebsd-src/contrib/llvm-project/llvm/lib/Support/SmallPtrSet.cpp (revision 04eeddc0aa8e0a417a16eaf9d7d095207f4a8623)
10b57cec5SDimitry Andric //===- llvm/ADT/SmallPtrSet.cpp - 'Normally small' pointer set ------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This file implements the SmallPtrSet class.  See SmallPtrSet.h for an
100b57cec5SDimitry Andric // overview of the algorithm.
110b57cec5SDimitry Andric //
120b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
130b57cec5SDimitry Andric 
140b57cec5SDimitry Andric #include "llvm/ADT/SmallPtrSet.h"
150b57cec5SDimitry Andric #include "llvm/ADT/DenseMapInfo.h"
16*fe6060f1SDimitry Andric #include "llvm/Support/MathExtras.h"
17*fe6060f1SDimitry Andric #include "llvm/Support/MemAlloc.h"
180b57cec5SDimitry Andric #include <algorithm>
190b57cec5SDimitry Andric #include <cassert>
200b57cec5SDimitry Andric #include <cstdlib>
210b57cec5SDimitry Andric 
220b57cec5SDimitry Andric using namespace llvm;
230b57cec5SDimitry Andric 
shrink_and_clear()240b57cec5SDimitry Andric void SmallPtrSetImplBase::shrink_and_clear() {
250b57cec5SDimitry Andric   assert(!isSmall() && "Can't shrink a small set!");
260b57cec5SDimitry Andric   free(CurArray);
270b57cec5SDimitry Andric 
280b57cec5SDimitry Andric   // Reduce the number of buckets.
290b57cec5SDimitry Andric   unsigned Size = size();
300b57cec5SDimitry Andric   CurArraySize = Size > 16 ? 1 << (Log2_32_Ceil(Size) + 1) : 32;
310b57cec5SDimitry Andric   NumNonEmpty = NumTombstones = 0;
320b57cec5SDimitry Andric 
330b57cec5SDimitry Andric   // Install the new array.  Clear all the buckets to empty.
340b57cec5SDimitry Andric   CurArray = (const void**)safe_malloc(sizeof(void*) * CurArraySize);
350b57cec5SDimitry Andric 
360b57cec5SDimitry Andric   memset(CurArray, -1, CurArraySize*sizeof(void*));
370b57cec5SDimitry Andric }
380b57cec5SDimitry Andric 
390b57cec5SDimitry Andric std::pair<const void *const *, bool>
insert_imp_big(const void * Ptr)400b57cec5SDimitry Andric SmallPtrSetImplBase::insert_imp_big(const void *Ptr) {
410b57cec5SDimitry Andric   if (LLVM_UNLIKELY(size() * 4 >= CurArraySize * 3)) {
420b57cec5SDimitry Andric     // If more than 3/4 of the array is full, grow.
430b57cec5SDimitry Andric     Grow(CurArraySize < 64 ? 128 : CurArraySize * 2);
440b57cec5SDimitry Andric   } else if (LLVM_UNLIKELY(CurArraySize - NumNonEmpty < CurArraySize / 8)) {
450b57cec5SDimitry Andric     // If fewer of 1/8 of the array is empty (meaning that many are filled with
460b57cec5SDimitry Andric     // tombstones), rehash.
470b57cec5SDimitry Andric     Grow(CurArraySize);
480b57cec5SDimitry Andric   }
490b57cec5SDimitry Andric 
500b57cec5SDimitry Andric   // Okay, we know we have space.  Find a hash bucket.
510b57cec5SDimitry Andric   const void **Bucket = const_cast<const void**>(FindBucketFor(Ptr));
520b57cec5SDimitry Andric   if (*Bucket == Ptr)
530b57cec5SDimitry Andric     return std::make_pair(Bucket, false); // Already inserted, good.
540b57cec5SDimitry Andric 
550b57cec5SDimitry Andric   // Otherwise, insert it!
560b57cec5SDimitry Andric   if (*Bucket == getTombstoneMarker())
570b57cec5SDimitry Andric     --NumTombstones;
580b57cec5SDimitry Andric   else
590b57cec5SDimitry Andric     ++NumNonEmpty; // Track density.
600b57cec5SDimitry Andric   *Bucket = Ptr;
610b57cec5SDimitry Andric   incrementEpoch();
620b57cec5SDimitry Andric   return std::make_pair(Bucket, true);
630b57cec5SDimitry Andric }
640b57cec5SDimitry Andric 
FindBucketFor(const void * Ptr) const650b57cec5SDimitry Andric const void * const *SmallPtrSetImplBase::FindBucketFor(const void *Ptr) const {
660b57cec5SDimitry Andric   unsigned Bucket = DenseMapInfo<void *>::getHashValue(Ptr) & (CurArraySize-1);
670b57cec5SDimitry Andric   unsigned ArraySize = CurArraySize;
680b57cec5SDimitry Andric   unsigned ProbeAmt = 1;
690b57cec5SDimitry Andric   const void *const *Array = CurArray;
700b57cec5SDimitry Andric   const void *const *Tombstone = nullptr;
710b57cec5SDimitry Andric   while (true) {
720b57cec5SDimitry Andric     // If we found an empty bucket, the pointer doesn't exist in the set.
730b57cec5SDimitry Andric     // Return a tombstone if we've seen one so far, or the empty bucket if
740b57cec5SDimitry Andric     // not.
750b57cec5SDimitry Andric     if (LLVM_LIKELY(Array[Bucket] == getEmptyMarker()))
760b57cec5SDimitry Andric       return Tombstone ? Tombstone : Array+Bucket;
770b57cec5SDimitry Andric 
780b57cec5SDimitry Andric     // Found Ptr's bucket?
790b57cec5SDimitry Andric     if (LLVM_LIKELY(Array[Bucket] == Ptr))
800b57cec5SDimitry Andric       return Array+Bucket;
810b57cec5SDimitry Andric 
820b57cec5SDimitry Andric     // If this is a tombstone, remember it.  If Ptr ends up not in the set, we
830b57cec5SDimitry Andric     // prefer to return it than something that would require more probing.
840b57cec5SDimitry Andric     if (Array[Bucket] == getTombstoneMarker() && !Tombstone)
850b57cec5SDimitry Andric       Tombstone = Array+Bucket;  // Remember the first tombstone found.
860b57cec5SDimitry Andric 
870b57cec5SDimitry Andric     // It's a hash collision or a tombstone. Reprobe.
880b57cec5SDimitry Andric     Bucket = (Bucket + ProbeAmt++) & (ArraySize-1);
890b57cec5SDimitry Andric   }
900b57cec5SDimitry Andric }
910b57cec5SDimitry Andric 
920b57cec5SDimitry Andric /// Grow - Allocate a larger backing store for the buckets and move it over.
930b57cec5SDimitry Andric ///
Grow(unsigned NewSize)940b57cec5SDimitry Andric void SmallPtrSetImplBase::Grow(unsigned NewSize) {
950b57cec5SDimitry Andric   const void **OldBuckets = CurArray;
960b57cec5SDimitry Andric   const void **OldEnd = EndPointer();
970b57cec5SDimitry Andric   bool WasSmall = isSmall();
980b57cec5SDimitry Andric 
990b57cec5SDimitry Andric   // Install the new array.  Clear all the buckets to empty.
1000b57cec5SDimitry Andric   const void **NewBuckets = (const void**) safe_malloc(sizeof(void*) * NewSize);
1010b57cec5SDimitry Andric 
1020b57cec5SDimitry Andric   // Reset member only if memory was allocated successfully
1030b57cec5SDimitry Andric   CurArray = NewBuckets;
1040b57cec5SDimitry Andric   CurArraySize = NewSize;
1050b57cec5SDimitry Andric   memset(CurArray, -1, NewSize*sizeof(void*));
1060b57cec5SDimitry Andric 
1070b57cec5SDimitry Andric   // Copy over all valid entries.
1080b57cec5SDimitry Andric   for (const void **BucketPtr = OldBuckets; BucketPtr != OldEnd; ++BucketPtr) {
1090b57cec5SDimitry Andric     // Copy over the element if it is valid.
1100b57cec5SDimitry Andric     const void *Elt = *BucketPtr;
1110b57cec5SDimitry Andric     if (Elt != getTombstoneMarker() && Elt != getEmptyMarker())
1120b57cec5SDimitry Andric       *const_cast<void**>(FindBucketFor(Elt)) = const_cast<void*>(Elt);
1130b57cec5SDimitry Andric   }
1140b57cec5SDimitry Andric 
1150b57cec5SDimitry Andric   if (!WasSmall)
1160b57cec5SDimitry Andric     free(OldBuckets);
1170b57cec5SDimitry Andric   NumNonEmpty -= NumTombstones;
1180b57cec5SDimitry Andric   NumTombstones = 0;
1190b57cec5SDimitry Andric }
1200b57cec5SDimitry Andric 
SmallPtrSetImplBase(const void ** SmallStorage,const SmallPtrSetImplBase & that)1210b57cec5SDimitry Andric SmallPtrSetImplBase::SmallPtrSetImplBase(const void **SmallStorage,
1220b57cec5SDimitry Andric                                          const SmallPtrSetImplBase &that) {
1230b57cec5SDimitry Andric   SmallArray = SmallStorage;
1240b57cec5SDimitry Andric 
1250b57cec5SDimitry Andric   // If we're becoming small, prepare to insert into our stack space
1260b57cec5SDimitry Andric   if (that.isSmall()) {
1270b57cec5SDimitry Andric     CurArray = SmallArray;
1280b57cec5SDimitry Andric   // Otherwise, allocate new heap space (unless we were the same size)
1290b57cec5SDimitry Andric   } else {
1300b57cec5SDimitry Andric     CurArray = (const void**)safe_malloc(sizeof(void*) * that.CurArraySize);
1310b57cec5SDimitry Andric   }
1320b57cec5SDimitry Andric 
1330b57cec5SDimitry Andric   // Copy over the that array.
1340b57cec5SDimitry Andric   CopyHelper(that);
1350b57cec5SDimitry Andric }
1360b57cec5SDimitry Andric 
SmallPtrSetImplBase(const void ** SmallStorage,unsigned SmallSize,SmallPtrSetImplBase && that)1370b57cec5SDimitry Andric SmallPtrSetImplBase::SmallPtrSetImplBase(const void **SmallStorage,
1380b57cec5SDimitry Andric                                          unsigned SmallSize,
1390b57cec5SDimitry Andric                                          SmallPtrSetImplBase &&that) {
1400b57cec5SDimitry Andric   SmallArray = SmallStorage;
1410b57cec5SDimitry Andric   MoveHelper(SmallSize, std::move(that));
1420b57cec5SDimitry Andric }
1430b57cec5SDimitry Andric 
CopyFrom(const SmallPtrSetImplBase & RHS)1440b57cec5SDimitry Andric void SmallPtrSetImplBase::CopyFrom(const SmallPtrSetImplBase &RHS) {
1450b57cec5SDimitry Andric   assert(&RHS != this && "Self-copy should be handled by the caller.");
1460b57cec5SDimitry Andric 
1470b57cec5SDimitry Andric   if (isSmall() && RHS.isSmall())
1480b57cec5SDimitry Andric     assert(CurArraySize == RHS.CurArraySize &&
1490b57cec5SDimitry Andric            "Cannot assign sets with different small sizes");
1500b57cec5SDimitry Andric 
1510b57cec5SDimitry Andric   // If we're becoming small, prepare to insert into our stack space
1520b57cec5SDimitry Andric   if (RHS.isSmall()) {
1530b57cec5SDimitry Andric     if (!isSmall())
1540b57cec5SDimitry Andric       free(CurArray);
1550b57cec5SDimitry Andric     CurArray = SmallArray;
1560b57cec5SDimitry Andric   // Otherwise, allocate new heap space (unless we were the same size)
1570b57cec5SDimitry Andric   } else if (CurArraySize != RHS.CurArraySize) {
1580b57cec5SDimitry Andric     if (isSmall())
1590b57cec5SDimitry Andric       CurArray = (const void**)safe_malloc(sizeof(void*) * RHS.CurArraySize);
1600b57cec5SDimitry Andric     else {
1610b57cec5SDimitry Andric       const void **T = (const void**)safe_realloc(CurArray,
1620b57cec5SDimitry Andric                                              sizeof(void*) * RHS.CurArraySize);
1630b57cec5SDimitry Andric       CurArray = T;
1640b57cec5SDimitry Andric     }
1650b57cec5SDimitry Andric   }
1660b57cec5SDimitry Andric 
1670b57cec5SDimitry Andric   CopyHelper(RHS);
1680b57cec5SDimitry Andric }
1690b57cec5SDimitry Andric 
CopyHelper(const SmallPtrSetImplBase & RHS)1700b57cec5SDimitry Andric void SmallPtrSetImplBase::CopyHelper(const SmallPtrSetImplBase &RHS) {
1710b57cec5SDimitry Andric   // Copy over the new array size
1720b57cec5SDimitry Andric   CurArraySize = RHS.CurArraySize;
1730b57cec5SDimitry Andric 
1740b57cec5SDimitry Andric   // Copy over the contents from the other set
1750b57cec5SDimitry Andric   std::copy(RHS.CurArray, RHS.EndPointer(), CurArray);
1760b57cec5SDimitry Andric 
1770b57cec5SDimitry Andric   NumNonEmpty = RHS.NumNonEmpty;
1780b57cec5SDimitry Andric   NumTombstones = RHS.NumTombstones;
1790b57cec5SDimitry Andric }
1800b57cec5SDimitry Andric 
MoveFrom(unsigned SmallSize,SmallPtrSetImplBase && RHS)1810b57cec5SDimitry Andric void SmallPtrSetImplBase::MoveFrom(unsigned SmallSize,
1820b57cec5SDimitry Andric                                    SmallPtrSetImplBase &&RHS) {
1830b57cec5SDimitry Andric   if (!isSmall())
1840b57cec5SDimitry Andric     free(CurArray);
1850b57cec5SDimitry Andric   MoveHelper(SmallSize, std::move(RHS));
1860b57cec5SDimitry Andric }
1870b57cec5SDimitry Andric 
MoveHelper(unsigned SmallSize,SmallPtrSetImplBase && RHS)1880b57cec5SDimitry Andric void SmallPtrSetImplBase::MoveHelper(unsigned SmallSize,
1890b57cec5SDimitry Andric                                      SmallPtrSetImplBase &&RHS) {
1900b57cec5SDimitry Andric   assert(&RHS != this && "Self-move should be handled by the caller.");
1910b57cec5SDimitry Andric 
1920b57cec5SDimitry Andric   if (RHS.isSmall()) {
1930b57cec5SDimitry Andric     // Copy a small RHS rather than moving.
1940b57cec5SDimitry Andric     CurArray = SmallArray;
1950b57cec5SDimitry Andric     std::copy(RHS.CurArray, RHS.CurArray + RHS.NumNonEmpty, CurArray);
1960b57cec5SDimitry Andric   } else {
1970b57cec5SDimitry Andric     CurArray = RHS.CurArray;
1980b57cec5SDimitry Andric     RHS.CurArray = RHS.SmallArray;
1990b57cec5SDimitry Andric   }
2000b57cec5SDimitry Andric 
2010b57cec5SDimitry Andric   // Copy the rest of the trivial members.
2020b57cec5SDimitry Andric   CurArraySize = RHS.CurArraySize;
2030b57cec5SDimitry Andric   NumNonEmpty = RHS.NumNonEmpty;
2040b57cec5SDimitry Andric   NumTombstones = RHS.NumTombstones;
2050b57cec5SDimitry Andric 
2060b57cec5SDimitry Andric   // Make the RHS small and empty.
2070b57cec5SDimitry Andric   RHS.CurArraySize = SmallSize;
2080b57cec5SDimitry Andric   assert(RHS.CurArray == RHS.SmallArray);
2090b57cec5SDimitry Andric   RHS.NumNonEmpty = 0;
2100b57cec5SDimitry Andric   RHS.NumTombstones = 0;
2110b57cec5SDimitry Andric }
2120b57cec5SDimitry Andric 
swap(SmallPtrSetImplBase & RHS)2130b57cec5SDimitry Andric void SmallPtrSetImplBase::swap(SmallPtrSetImplBase &RHS) {
2140b57cec5SDimitry Andric   if (this == &RHS) return;
2150b57cec5SDimitry Andric 
2160b57cec5SDimitry Andric   // We can only avoid copying elements if neither set is small.
2170b57cec5SDimitry Andric   if (!this->isSmall() && !RHS.isSmall()) {
2180b57cec5SDimitry Andric     std::swap(this->CurArray, RHS.CurArray);
2190b57cec5SDimitry Andric     std::swap(this->CurArraySize, RHS.CurArraySize);
2200b57cec5SDimitry Andric     std::swap(this->NumNonEmpty, RHS.NumNonEmpty);
2210b57cec5SDimitry Andric     std::swap(this->NumTombstones, RHS.NumTombstones);
2220b57cec5SDimitry Andric     return;
2230b57cec5SDimitry Andric   }
2240b57cec5SDimitry Andric 
2250b57cec5SDimitry Andric   // FIXME: From here on we assume that both sets have the same small size.
2260b57cec5SDimitry Andric 
2270b57cec5SDimitry Andric   // If only RHS is small, copy the small elements into LHS and move the pointer
2280b57cec5SDimitry Andric   // from LHS to RHS.
2290b57cec5SDimitry Andric   if (!this->isSmall() && RHS.isSmall()) {
2300b57cec5SDimitry Andric     assert(RHS.CurArray == RHS.SmallArray);
2310b57cec5SDimitry Andric     std::copy(RHS.CurArray, RHS.CurArray + RHS.NumNonEmpty, this->SmallArray);
2320b57cec5SDimitry Andric     std::swap(RHS.CurArraySize, this->CurArraySize);
2330b57cec5SDimitry Andric     std::swap(this->NumNonEmpty, RHS.NumNonEmpty);
2340b57cec5SDimitry Andric     std::swap(this->NumTombstones, RHS.NumTombstones);
2350b57cec5SDimitry Andric     RHS.CurArray = this->CurArray;
2360b57cec5SDimitry Andric     this->CurArray = this->SmallArray;
2370b57cec5SDimitry Andric     return;
2380b57cec5SDimitry Andric   }
2390b57cec5SDimitry Andric 
2400b57cec5SDimitry Andric   // If only LHS is small, copy the small elements into RHS and move the pointer
2410b57cec5SDimitry Andric   // from RHS to LHS.
2420b57cec5SDimitry Andric   if (this->isSmall() && !RHS.isSmall()) {
2430b57cec5SDimitry Andric     assert(this->CurArray == this->SmallArray);
2440b57cec5SDimitry Andric     std::copy(this->CurArray, this->CurArray + this->NumNonEmpty,
2450b57cec5SDimitry Andric               RHS.SmallArray);
2460b57cec5SDimitry Andric     std::swap(RHS.CurArraySize, this->CurArraySize);
2470b57cec5SDimitry Andric     std::swap(RHS.NumNonEmpty, this->NumNonEmpty);
2480b57cec5SDimitry Andric     std::swap(RHS.NumTombstones, this->NumTombstones);
2490b57cec5SDimitry Andric     this->CurArray = RHS.CurArray;
2500b57cec5SDimitry Andric     RHS.CurArray = RHS.SmallArray;
2510b57cec5SDimitry Andric     return;
2520b57cec5SDimitry Andric   }
2530b57cec5SDimitry Andric 
2540b57cec5SDimitry Andric   // Both a small, just swap the small elements.
2550b57cec5SDimitry Andric   assert(this->isSmall() && RHS.isSmall());
2560b57cec5SDimitry Andric   unsigned MinNonEmpty = std::min(this->NumNonEmpty, RHS.NumNonEmpty);
2570b57cec5SDimitry Andric   std::swap_ranges(this->SmallArray, this->SmallArray + MinNonEmpty,
2580b57cec5SDimitry Andric                    RHS.SmallArray);
2590b57cec5SDimitry Andric   if (this->NumNonEmpty > MinNonEmpty) {
2600b57cec5SDimitry Andric     std::copy(this->SmallArray + MinNonEmpty,
2610b57cec5SDimitry Andric               this->SmallArray + this->NumNonEmpty,
2620b57cec5SDimitry Andric               RHS.SmallArray + MinNonEmpty);
2630b57cec5SDimitry Andric   } else {
2640b57cec5SDimitry Andric     std::copy(RHS.SmallArray + MinNonEmpty, RHS.SmallArray + RHS.NumNonEmpty,
2650b57cec5SDimitry Andric               this->SmallArray + MinNonEmpty);
2660b57cec5SDimitry Andric   }
2670b57cec5SDimitry Andric   assert(this->CurArraySize == RHS.CurArraySize);
2680b57cec5SDimitry Andric   std::swap(this->NumNonEmpty, RHS.NumNonEmpty);
2690b57cec5SDimitry Andric   std::swap(this->NumTombstones, RHS.NumTombstones);
2700b57cec5SDimitry Andric }
271