1f22ef01cSRoman Divacky //===- llvm/ADT/SmallPtrSet.cpp - 'Normally small' pointer set ------------===//
2f22ef01cSRoman Divacky //
3f22ef01cSRoman Divacky // The LLVM Compiler Infrastructure
4f22ef01cSRoman Divacky //
5f22ef01cSRoman Divacky // This file is distributed under the University of Illinois Open Source
6f22ef01cSRoman Divacky // License. See LICENSE.TXT for details.
7f22ef01cSRoman Divacky //
8f22ef01cSRoman Divacky //===----------------------------------------------------------------------===//
9f22ef01cSRoman Divacky //
10f22ef01cSRoman Divacky // This file implements the SmallPtrSet class. See SmallPtrSet.h for an
11f22ef01cSRoman Divacky // overview of the algorithm.
12f22ef01cSRoman Divacky //
13f22ef01cSRoman Divacky //===----------------------------------------------------------------------===//
14f22ef01cSRoman Divacky
15f22ef01cSRoman Divacky #include "llvm/ADT/SmallPtrSet.h"
16cb4dff85SDimitry Andric #include "llvm/ADT/DenseMapInfo.h"
17f22ef01cSRoman Divacky #include "llvm/Support/MathExtras.h"
182cab237bSDimitry Andric #include "llvm/Support/ErrorHandling.h"
19dff0c46cSDimitry Andric #include <algorithm>
20d88c1a5aSDimitry Andric #include <cassert>
21f22ef01cSRoman Divacky #include <cstdlib>
22f22ef01cSRoman Divacky
23f22ef01cSRoman Divacky using namespace llvm;
24f22ef01cSRoman Divacky
shrink_and_clear()2591bc56edSDimitry Andric void SmallPtrSetImplBase::shrink_and_clear() {
26f22ef01cSRoman Divacky assert(!isSmall() && "Can't shrink a small set!");
27f22ef01cSRoman Divacky free(CurArray);
28f22ef01cSRoman Divacky
29f22ef01cSRoman Divacky // Reduce the number of buckets.
303ca95b02SDimitry Andric unsigned Size = size();
313ca95b02SDimitry Andric CurArraySize = Size > 16 ? 1 << (Log2_32_Ceil(Size) + 1) : 32;
323ca95b02SDimitry Andric NumNonEmpty = NumTombstones = 0;
33f22ef01cSRoman Divacky
34f22ef01cSRoman Divacky // Install the new array. Clear all the buckets to empty.
35*4ba319b5SDimitry Andric CurArray = (const void**)safe_malloc(sizeof(void*) * CurArraySize);
362cab237bSDimitry Andric
37f22ef01cSRoman Divacky memset(CurArray, -1, CurArraySize*sizeof(void*));
38f22ef01cSRoman Divacky }
39f22ef01cSRoman Divacky
4039d628a0SDimitry Andric std::pair<const void *const *, bool>
insert_imp_big(const void * Ptr)413ca95b02SDimitry Andric SmallPtrSetImplBase::insert_imp_big(const void *Ptr) {
423ca95b02SDimitry Andric if (LLVM_UNLIKELY(size() * 4 >= CurArraySize * 3)) {
43f22ef01cSRoman Divacky // If more than 3/4 of the array is full, grow.
443b0f4066SDimitry Andric Grow(CurArraySize < 64 ? 128 : CurArraySize * 2);
453ca95b02SDimitry Andric } else if (LLVM_UNLIKELY(CurArraySize - NumNonEmpty < CurArraySize / 8)) {
463b0f4066SDimitry Andric // If fewer of 1/8 of the array is empty (meaning that many are filled with
473b0f4066SDimitry Andric // tombstones), rehash.
483b0f4066SDimitry Andric Grow(CurArraySize);
493b0f4066SDimitry Andric }
50f22ef01cSRoman Divacky
51f22ef01cSRoman Divacky // Okay, we know we have space. Find a hash bucket.
52f22ef01cSRoman Divacky const void **Bucket = const_cast<const void**>(FindBucketFor(Ptr));
5339d628a0SDimitry Andric if (*Bucket == Ptr)
5439d628a0SDimitry Andric return std::make_pair(Bucket, false); // Already inserted, good.
55f22ef01cSRoman Divacky
56f22ef01cSRoman Divacky // Otherwise, insert it!
57f22ef01cSRoman Divacky if (*Bucket == getTombstoneMarker())
58f22ef01cSRoman Divacky --NumTombstones;
593ca95b02SDimitry Andric else
603ca95b02SDimitry Andric ++NumNonEmpty; // Track density.
61f22ef01cSRoman Divacky *Bucket = Ptr;
622cab237bSDimitry Andric incrementEpoch();
6339d628a0SDimitry Andric return std::make_pair(Bucket, true);
64f22ef01cSRoman Divacky }
65f22ef01cSRoman Divacky
FindBucketFor(const void * Ptr) const6691bc56edSDimitry Andric const void * const *SmallPtrSetImplBase::FindBucketFor(const void *Ptr) const {
67cb4dff85SDimitry Andric unsigned Bucket = DenseMapInfo<void *>::getHashValue(Ptr) & (CurArraySize-1);
68f22ef01cSRoman Divacky unsigned ArraySize = CurArraySize;
69f22ef01cSRoman Divacky unsigned ProbeAmt = 1;
70f22ef01cSRoman Divacky const void *const *Array = CurArray;
7191bc56edSDimitry Andric const void *const *Tombstone = nullptr;
72d88c1a5aSDimitry Andric while (true) {
73f22ef01cSRoman Divacky // If we found an empty bucket, the pointer doesn't exist in the set.
74f22ef01cSRoman Divacky // Return a tombstone if we've seen one so far, or the empty bucket if
75f22ef01cSRoman Divacky // not.
76ff0cc061SDimitry Andric if (LLVM_LIKELY(Array[Bucket] == getEmptyMarker()))
77f22ef01cSRoman Divacky return Tombstone ? Tombstone : Array+Bucket;
78f22ef01cSRoman Divacky
79ff0cc061SDimitry Andric // Found Ptr's bucket?
80ff0cc061SDimitry Andric if (LLVM_LIKELY(Array[Bucket] == Ptr))
81ff0cc061SDimitry Andric return Array+Bucket;
82ff0cc061SDimitry Andric
83f22ef01cSRoman Divacky // If this is a tombstone, remember it. If Ptr ends up not in the set, we
84f22ef01cSRoman Divacky // prefer to return it than something that would require more probing.
85f22ef01cSRoman Divacky if (Array[Bucket] == getTombstoneMarker() && !Tombstone)
86f22ef01cSRoman Divacky Tombstone = Array+Bucket; // Remember the first tombstone found.
87f22ef01cSRoman Divacky
88f22ef01cSRoman Divacky // It's a hash collision or a tombstone. Reprobe.
89f22ef01cSRoman Divacky Bucket = (Bucket + ProbeAmt++) & (ArraySize-1);
90f22ef01cSRoman Divacky }
91f22ef01cSRoman Divacky }
92f22ef01cSRoman Divacky
93f22ef01cSRoman Divacky /// Grow - Allocate a larger backing store for the buckets and move it over.
94f22ef01cSRoman Divacky ///
Grow(unsigned NewSize)9591bc56edSDimitry Andric void SmallPtrSetImplBase::Grow(unsigned NewSize) {
96f22ef01cSRoman Divacky const void **OldBuckets = CurArray;
973ca95b02SDimitry Andric const void **OldEnd = EndPointer();
98f22ef01cSRoman Divacky bool WasSmall = isSmall();
99f22ef01cSRoman Divacky
100f22ef01cSRoman Divacky // Install the new array. Clear all the buckets to empty.
101*4ba319b5SDimitry Andric const void **NewBuckets = (const void**) safe_malloc(sizeof(void*) * NewSize);
1022cab237bSDimitry Andric
1032cab237bSDimitry Andric // Reset member only if memory was allocated successfully
1042cab237bSDimitry Andric CurArray = NewBuckets;
105f22ef01cSRoman Divacky CurArraySize = NewSize;
106f22ef01cSRoman Divacky memset(CurArray, -1, NewSize*sizeof(void*));
107f22ef01cSRoman Divacky
108f22ef01cSRoman Divacky // Copy over all valid entries.
1093ca95b02SDimitry Andric for (const void **BucketPtr = OldBuckets; BucketPtr != OldEnd; ++BucketPtr) {
110f22ef01cSRoman Divacky // Copy over the element if it is valid.
111f22ef01cSRoman Divacky const void *Elt = *BucketPtr;
112f22ef01cSRoman Divacky if (Elt != getTombstoneMarker() && Elt != getEmptyMarker())
113f22ef01cSRoman Divacky *const_cast<void**>(FindBucketFor(Elt)) = const_cast<void*>(Elt);
114f22ef01cSRoman Divacky }
115f22ef01cSRoman Divacky
1163ca95b02SDimitry Andric if (!WasSmall)
117f22ef01cSRoman Divacky free(OldBuckets);
1183ca95b02SDimitry Andric NumNonEmpty -= NumTombstones;
119f22ef01cSRoman Divacky NumTombstones = 0;
120f22ef01cSRoman Divacky }
121f22ef01cSRoman Divacky
SmallPtrSetImplBase(const void ** SmallStorage,const SmallPtrSetImplBase & that)12291bc56edSDimitry Andric SmallPtrSetImplBase::SmallPtrSetImplBase(const void **SmallStorage,
12391bc56edSDimitry Andric const SmallPtrSetImplBase &that) {
124ffd1746dSEd Schouten SmallArray = SmallStorage;
125ffd1746dSEd Schouten
126f22ef01cSRoman Divacky // If we're becoming small, prepare to insert into our stack space
127f22ef01cSRoman Divacky if (that.isSmall()) {
128ffd1746dSEd Schouten CurArray = SmallArray;
129f22ef01cSRoman Divacky // Otherwise, allocate new heap space (unless we were the same size)
130f22ef01cSRoman Divacky } else {
131*4ba319b5SDimitry Andric CurArray = (const void**)safe_malloc(sizeof(void*) * that.CurArraySize);
132f22ef01cSRoman Divacky }
133f22ef01cSRoman Divacky
1343ca95b02SDimitry Andric // Copy over the that array.
1353ca95b02SDimitry Andric CopyHelper(that);
136f22ef01cSRoman Divacky }
137f22ef01cSRoman Divacky
SmallPtrSetImplBase(const void ** SmallStorage,unsigned SmallSize,SmallPtrSetImplBase && that)13891bc56edSDimitry Andric SmallPtrSetImplBase::SmallPtrSetImplBase(const void **SmallStorage,
13991bc56edSDimitry Andric unsigned SmallSize,
14091bc56edSDimitry Andric SmallPtrSetImplBase &&that) {
14191bc56edSDimitry Andric SmallArray = SmallStorage;
1423ca95b02SDimitry Andric MoveHelper(SmallSize, std::move(that));
14339d628a0SDimitry Andric }
14491bc56edSDimitry Andric
CopyFrom(const SmallPtrSetImplBase & RHS)14591bc56edSDimitry Andric void SmallPtrSetImplBase::CopyFrom(const SmallPtrSetImplBase &RHS) {
14691bc56edSDimitry Andric assert(&RHS != this && "Self-copy should be handled by the caller.");
14791bc56edSDimitry Andric
148f22ef01cSRoman Divacky if (isSmall() && RHS.isSmall())
149f22ef01cSRoman Divacky assert(CurArraySize == RHS.CurArraySize &&
150f22ef01cSRoman Divacky "Cannot assign sets with different small sizes");
151f22ef01cSRoman Divacky
152f22ef01cSRoman Divacky // If we're becoming small, prepare to insert into our stack space
153f22ef01cSRoman Divacky if (RHS.isSmall()) {
154f22ef01cSRoman Divacky if (!isSmall())
155f22ef01cSRoman Divacky free(CurArray);
156ffd1746dSEd Schouten CurArray = SmallArray;
157f22ef01cSRoman Divacky // Otherwise, allocate new heap space (unless we were the same size)
158f22ef01cSRoman Divacky } else if (CurArraySize != RHS.CurArraySize) {
159f22ef01cSRoman Divacky if (isSmall())
160*4ba319b5SDimitry Andric CurArray = (const void**)safe_malloc(sizeof(void*) * RHS.CurArraySize);
161f785676fSDimitry Andric else {
162*4ba319b5SDimitry Andric const void **T = (const void**)safe_realloc(CurArray,
163f785676fSDimitry Andric sizeof(void*) * RHS.CurArraySize);
164f785676fSDimitry Andric CurArray = T;
165f785676fSDimitry Andric }
166f22ef01cSRoman Divacky }
167f22ef01cSRoman Divacky
1683ca95b02SDimitry Andric CopyHelper(RHS);
1693ca95b02SDimitry Andric }
1703ca95b02SDimitry Andric
CopyHelper(const SmallPtrSetImplBase & RHS)1713ca95b02SDimitry Andric void SmallPtrSetImplBase::CopyHelper(const SmallPtrSetImplBase &RHS) {
172f22ef01cSRoman Divacky // Copy over the new array size
173f22ef01cSRoman Divacky CurArraySize = RHS.CurArraySize;
174f22ef01cSRoman Divacky
175f22ef01cSRoman Divacky // Copy over the contents from the other set
1763ca95b02SDimitry Andric std::copy(RHS.CurArray, RHS.EndPointer(), CurArray);
177f22ef01cSRoman Divacky
1783ca95b02SDimitry Andric NumNonEmpty = RHS.NumNonEmpty;
179f22ef01cSRoman Divacky NumTombstones = RHS.NumTombstones;
180f22ef01cSRoman Divacky }
181f22ef01cSRoman Divacky
MoveFrom(unsigned SmallSize,SmallPtrSetImplBase && RHS)18291bc56edSDimitry Andric void SmallPtrSetImplBase::MoveFrom(unsigned SmallSize,
18391bc56edSDimitry Andric SmallPtrSetImplBase &&RHS) {
18491bc56edSDimitry Andric if (!isSmall())
18591bc56edSDimitry Andric free(CurArray);
1863ca95b02SDimitry Andric MoveHelper(SmallSize, std::move(RHS));
1873ca95b02SDimitry Andric }
1883ca95b02SDimitry Andric
MoveHelper(unsigned SmallSize,SmallPtrSetImplBase && RHS)1893ca95b02SDimitry Andric void SmallPtrSetImplBase::MoveHelper(unsigned SmallSize,
1903ca95b02SDimitry Andric SmallPtrSetImplBase &&RHS) {
1913ca95b02SDimitry Andric assert(&RHS != this && "Self-move should be handled by the caller.");
19291bc56edSDimitry Andric
19391bc56edSDimitry Andric if (RHS.isSmall()) {
19491bc56edSDimitry Andric // Copy a small RHS rather than moving.
19591bc56edSDimitry Andric CurArray = SmallArray;
1963ca95b02SDimitry Andric std::copy(RHS.CurArray, RHS.CurArray + RHS.NumNonEmpty, CurArray);
19791bc56edSDimitry Andric } else {
19891bc56edSDimitry Andric CurArray = RHS.CurArray;
19991bc56edSDimitry Andric RHS.CurArray = RHS.SmallArray;
20091bc56edSDimitry Andric }
20191bc56edSDimitry Andric
20291bc56edSDimitry Andric // Copy the rest of the trivial members.
20391bc56edSDimitry Andric CurArraySize = RHS.CurArraySize;
2043ca95b02SDimitry Andric NumNonEmpty = RHS.NumNonEmpty;
20591bc56edSDimitry Andric NumTombstones = RHS.NumTombstones;
20691bc56edSDimitry Andric
20791bc56edSDimitry Andric // Make the RHS small and empty.
20891bc56edSDimitry Andric RHS.CurArraySize = SmallSize;
20991bc56edSDimitry Andric assert(RHS.CurArray == RHS.SmallArray);
2103ca95b02SDimitry Andric RHS.NumNonEmpty = 0;
21191bc56edSDimitry Andric RHS.NumTombstones = 0;
21291bc56edSDimitry Andric }
21391bc56edSDimitry Andric
swap(SmallPtrSetImplBase & RHS)21491bc56edSDimitry Andric void SmallPtrSetImplBase::swap(SmallPtrSetImplBase &RHS) {
215dff0c46cSDimitry Andric if (this == &RHS) return;
216dff0c46cSDimitry Andric
217dff0c46cSDimitry Andric // We can only avoid copying elements if neither set is small.
218dff0c46cSDimitry Andric if (!this->isSmall() && !RHS.isSmall()) {
219dff0c46cSDimitry Andric std::swap(this->CurArray, RHS.CurArray);
220dff0c46cSDimitry Andric std::swap(this->CurArraySize, RHS.CurArraySize);
2213ca95b02SDimitry Andric std::swap(this->NumNonEmpty, RHS.NumNonEmpty);
222dff0c46cSDimitry Andric std::swap(this->NumTombstones, RHS.NumTombstones);
223dff0c46cSDimitry Andric return;
224dff0c46cSDimitry Andric }
225dff0c46cSDimitry Andric
226dff0c46cSDimitry Andric // FIXME: From here on we assume that both sets have the same small size.
227dff0c46cSDimitry Andric
228dff0c46cSDimitry Andric // If only RHS is small, copy the small elements into LHS and move the pointer
229dff0c46cSDimitry Andric // from LHS to RHS.
230dff0c46cSDimitry Andric if (!this->isSmall() && RHS.isSmall()) {
2313ca95b02SDimitry Andric assert(RHS.CurArray == RHS.SmallArray);
2323ca95b02SDimitry Andric std::copy(RHS.CurArray, RHS.CurArray + RHS.NumNonEmpty, this->SmallArray);
2333ca95b02SDimitry Andric std::swap(RHS.CurArraySize, this->CurArraySize);
2343ca95b02SDimitry Andric std::swap(this->NumNonEmpty, RHS.NumNonEmpty);
2353ca95b02SDimitry Andric std::swap(this->NumTombstones, RHS.NumTombstones);
236dff0c46cSDimitry Andric RHS.CurArray = this->CurArray;
237dff0c46cSDimitry Andric this->CurArray = this->SmallArray;
238dff0c46cSDimitry Andric return;
239dff0c46cSDimitry Andric }
240dff0c46cSDimitry Andric
241dff0c46cSDimitry Andric // If only LHS is small, copy the small elements into RHS and move the pointer
242dff0c46cSDimitry Andric // from RHS to LHS.
243dff0c46cSDimitry Andric if (this->isSmall() && !RHS.isSmall()) {
2443ca95b02SDimitry Andric assert(this->CurArray == this->SmallArray);
2453ca95b02SDimitry Andric std::copy(this->CurArray, this->CurArray + this->NumNonEmpty,
246dff0c46cSDimitry Andric RHS.SmallArray);
247dff0c46cSDimitry Andric std::swap(RHS.CurArraySize, this->CurArraySize);
2483ca95b02SDimitry Andric std::swap(RHS.NumNonEmpty, this->NumNonEmpty);
2493ca95b02SDimitry Andric std::swap(RHS.NumTombstones, this->NumTombstones);
250dff0c46cSDimitry Andric this->CurArray = RHS.CurArray;
251dff0c46cSDimitry Andric RHS.CurArray = RHS.SmallArray;
252dff0c46cSDimitry Andric return;
253dff0c46cSDimitry Andric }
254dff0c46cSDimitry Andric
255dff0c46cSDimitry Andric // Both a small, just swap the small elements.
256dff0c46cSDimitry Andric assert(this->isSmall() && RHS.isSmall());
2573ca95b02SDimitry Andric unsigned MinNonEmpty = std::min(this->NumNonEmpty, RHS.NumNonEmpty);
2583ca95b02SDimitry Andric std::swap_ranges(this->SmallArray, this->SmallArray + MinNonEmpty,
259dff0c46cSDimitry Andric RHS.SmallArray);
2603ca95b02SDimitry Andric if (this->NumNonEmpty > MinNonEmpty) {
2613ca95b02SDimitry Andric std::copy(this->SmallArray + MinNonEmpty,
2623ca95b02SDimitry Andric this->SmallArray + this->NumNonEmpty,
2633ca95b02SDimitry Andric RHS.SmallArray + MinNonEmpty);
2643ca95b02SDimitry Andric } else {
2653ca95b02SDimitry Andric std::copy(RHS.SmallArray + MinNonEmpty, RHS.SmallArray + RHS.NumNonEmpty,
2663ca95b02SDimitry Andric this->SmallArray + MinNonEmpty);
267dff0c46cSDimitry Andric }
2683ca95b02SDimitry Andric assert(this->CurArraySize == RHS.CurArraySize);
2693ca95b02SDimitry Andric std::swap(this->NumNonEmpty, RHS.NumNonEmpty);
2703ca95b02SDimitry Andric std::swap(this->NumTombstones, RHS.NumTombstones);
271f22ef01cSRoman Divacky }
272