1009b1c42SEd Schouten //===- llvm/ADT/SmallPtrSet.cpp - 'Normally small' pointer set ------------===//
2009b1c42SEd Schouten //
3e6d15924SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e6d15924SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5e6d15924SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6009b1c42SEd Schouten //
7009b1c42SEd Schouten //===----------------------------------------------------------------------===//
8009b1c42SEd Schouten //
9009b1c42SEd Schouten // This file implements the SmallPtrSet class. See SmallPtrSet.h for an
10009b1c42SEd Schouten // overview of the algorithm.
11009b1c42SEd Schouten //
12009b1c42SEd Schouten //===----------------------------------------------------------------------===//
13009b1c42SEd Schouten
14009b1c42SEd Schouten #include "llvm/ADT/SmallPtrSet.h"
15b61ab53cSDimitry Andric #include "llvm/ADT/DenseMapInfo.h"
16344a3780SDimitry Andric #include "llvm/Support/MathExtras.h"
17344a3780SDimitry Andric #include "llvm/Support/MemAlloc.h"
1863faed5bSDimitry Andric #include <algorithm>
19b915e9e0SDimitry Andric #include <cassert>
20009b1c42SEd Schouten #include <cstdlib>
21009b1c42SEd Schouten
22009b1c42SEd Schouten using namespace llvm;
23009b1c42SEd Schouten
shrink_and_clear()245ca98fd9SDimitry Andric void SmallPtrSetImplBase::shrink_and_clear() {
25009b1c42SEd Schouten assert(!isSmall() && "Can't shrink a small set!");
26009b1c42SEd Schouten free(CurArray);
27009b1c42SEd Schouten
28009b1c42SEd Schouten // Reduce the number of buckets.
2901095a5dSDimitry Andric unsigned Size = size();
3001095a5dSDimitry Andric CurArraySize = Size > 16 ? 1 << (Log2_32_Ceil(Size) + 1) : 32;
3101095a5dSDimitry Andric NumNonEmpty = NumTombstones = 0;
32009b1c42SEd Schouten
33009b1c42SEd Schouten // Install the new array. Clear all the buckets to empty.
34eb11fae6SDimitry Andric CurArray = (const void**)safe_malloc(sizeof(void*) * CurArraySize);
35044eb2f6SDimitry Andric
36009b1c42SEd Schouten memset(CurArray, -1, CurArraySize*sizeof(void*));
37009b1c42SEd Schouten }
38009b1c42SEd Schouten
3967c32a98SDimitry Andric std::pair<const void *const *, bool>
insert_imp_big(const void * Ptr)4001095a5dSDimitry Andric SmallPtrSetImplBase::insert_imp_big(const void *Ptr) {
4101095a5dSDimitry Andric if (LLVM_UNLIKELY(size() * 4 >= CurArraySize * 3)) {
42009b1c42SEd Schouten // If more than 3/4 of the array is full, grow.
436b943ff3SDimitry Andric Grow(CurArraySize < 64 ? 128 : CurArraySize * 2);
4401095a5dSDimitry Andric } else if (LLVM_UNLIKELY(CurArraySize - NumNonEmpty < CurArraySize / 8)) {
456b943ff3SDimitry Andric // If fewer of 1/8 of the array is empty (meaning that many are filled with
466b943ff3SDimitry Andric // tombstones), rehash.
476b943ff3SDimitry Andric Grow(CurArraySize);
486b943ff3SDimitry Andric }
49009b1c42SEd Schouten
50009b1c42SEd Schouten // Okay, we know we have space. Find a hash bucket.
51009b1c42SEd Schouten const void **Bucket = const_cast<const void**>(FindBucketFor(Ptr));
5267c32a98SDimitry Andric if (*Bucket == Ptr)
5367c32a98SDimitry Andric return std::make_pair(Bucket, false); // Already inserted, good.
54009b1c42SEd Schouten
55009b1c42SEd Schouten // Otherwise, insert it!
56009b1c42SEd Schouten if (*Bucket == getTombstoneMarker())
57009b1c42SEd Schouten --NumTombstones;
5801095a5dSDimitry Andric else
5901095a5dSDimitry Andric ++NumNonEmpty; // Track density.
60009b1c42SEd Schouten *Bucket = Ptr;
61044eb2f6SDimitry Andric incrementEpoch();
6267c32a98SDimitry Andric return std::make_pair(Bucket, true);
63009b1c42SEd Schouten }
64009b1c42SEd Schouten
FindBucketFor(const void * Ptr) const655ca98fd9SDimitry Andric const void * const *SmallPtrSetImplBase::FindBucketFor(const void *Ptr) const {
66b61ab53cSDimitry Andric unsigned Bucket = DenseMapInfo<void *>::getHashValue(Ptr) & (CurArraySize-1);
67009b1c42SEd Schouten unsigned ArraySize = CurArraySize;
68009b1c42SEd Schouten unsigned ProbeAmt = 1;
69009b1c42SEd Schouten const void *const *Array = CurArray;
705ca98fd9SDimitry Andric const void *const *Tombstone = nullptr;
71b915e9e0SDimitry Andric while (true) {
72009b1c42SEd Schouten // If we found an empty bucket, the pointer doesn't exist in the set.
73009b1c42SEd Schouten // Return a tombstone if we've seen one so far, or the empty bucket if
74009b1c42SEd Schouten // not.
755a5ac124SDimitry Andric if (LLVM_LIKELY(Array[Bucket] == getEmptyMarker()))
76009b1c42SEd Schouten return Tombstone ? Tombstone : Array+Bucket;
77009b1c42SEd Schouten
785a5ac124SDimitry Andric // Found Ptr's bucket?
795a5ac124SDimitry Andric if (LLVM_LIKELY(Array[Bucket] == Ptr))
805a5ac124SDimitry Andric return Array+Bucket;
815a5ac124SDimitry Andric
82009b1c42SEd Schouten // If this is a tombstone, remember it. If Ptr ends up not in the set, we
83009b1c42SEd Schouten // prefer to return it than something that would require more probing.
84009b1c42SEd Schouten if (Array[Bucket] == getTombstoneMarker() && !Tombstone)
85009b1c42SEd Schouten Tombstone = Array+Bucket; // Remember the first tombstone found.
86009b1c42SEd Schouten
87009b1c42SEd Schouten // It's a hash collision or a tombstone. Reprobe.
88009b1c42SEd Schouten Bucket = (Bucket + ProbeAmt++) & (ArraySize-1);
89009b1c42SEd Schouten }
90009b1c42SEd Schouten }
91009b1c42SEd Schouten
92009b1c42SEd Schouten /// Grow - Allocate a larger backing store for the buckets and move it over.
93009b1c42SEd Schouten ///
Grow(unsigned NewSize)945ca98fd9SDimitry Andric void SmallPtrSetImplBase::Grow(unsigned NewSize) {
95009b1c42SEd Schouten const void **OldBuckets = CurArray;
9601095a5dSDimitry Andric const void **OldEnd = EndPointer();
97009b1c42SEd Schouten bool WasSmall = isSmall();
98009b1c42SEd Schouten
99009b1c42SEd Schouten // Install the new array. Clear all the buckets to empty.
100eb11fae6SDimitry Andric const void **NewBuckets = (const void**) safe_malloc(sizeof(void*) * NewSize);
101044eb2f6SDimitry Andric
102044eb2f6SDimitry Andric // Reset member only if memory was allocated successfully
103044eb2f6SDimitry Andric CurArray = NewBuckets;
104009b1c42SEd Schouten CurArraySize = NewSize;
105009b1c42SEd Schouten memset(CurArray, -1, NewSize*sizeof(void*));
106009b1c42SEd Schouten
107009b1c42SEd Schouten // Copy over all valid entries.
10801095a5dSDimitry Andric for (const void **BucketPtr = OldBuckets; BucketPtr != OldEnd; ++BucketPtr) {
109009b1c42SEd Schouten // Copy over the element if it is valid.
110009b1c42SEd Schouten const void *Elt = *BucketPtr;
111009b1c42SEd Schouten if (Elt != getTombstoneMarker() && Elt != getEmptyMarker())
112009b1c42SEd Schouten *const_cast<void**>(FindBucketFor(Elt)) = const_cast<void*>(Elt);
113009b1c42SEd Schouten }
114009b1c42SEd Schouten
11501095a5dSDimitry Andric if (!WasSmall)
116009b1c42SEd Schouten free(OldBuckets);
11701095a5dSDimitry Andric NumNonEmpty -= NumTombstones;
118009b1c42SEd Schouten NumTombstones = 0;
119009b1c42SEd Schouten }
120009b1c42SEd Schouten
SmallPtrSetImplBase(const void ** SmallStorage,const SmallPtrSetImplBase & that)1215ca98fd9SDimitry Andric SmallPtrSetImplBase::SmallPtrSetImplBase(const void **SmallStorage,
1225ca98fd9SDimitry Andric const SmallPtrSetImplBase &that) {
12366e41e3cSRoman Divacky SmallArray = SmallStorage;
12466e41e3cSRoman Divacky
125009b1c42SEd Schouten // If we're becoming small, prepare to insert into our stack space
126009b1c42SEd Schouten if (that.isSmall()) {
12766e41e3cSRoman Divacky CurArray = SmallArray;
128009b1c42SEd Schouten // Otherwise, allocate new heap space (unless we were the same size)
129009b1c42SEd Schouten } else {
130eb11fae6SDimitry Andric CurArray = (const void**)safe_malloc(sizeof(void*) * that.CurArraySize);
131009b1c42SEd Schouten }
132009b1c42SEd Schouten
13301095a5dSDimitry Andric // Copy over the that array.
13401095a5dSDimitry Andric CopyHelper(that);
135009b1c42SEd Schouten }
136009b1c42SEd Schouten
SmallPtrSetImplBase(const void ** SmallStorage,unsigned SmallSize,SmallPtrSetImplBase && that)1375ca98fd9SDimitry Andric SmallPtrSetImplBase::SmallPtrSetImplBase(const void **SmallStorage,
1385ca98fd9SDimitry Andric unsigned SmallSize,
1395ca98fd9SDimitry Andric SmallPtrSetImplBase &&that) {
1405ca98fd9SDimitry Andric SmallArray = SmallStorage;
14101095a5dSDimitry Andric MoveHelper(SmallSize, std::move(that));
14267c32a98SDimitry Andric }
1435ca98fd9SDimitry Andric
CopyFrom(const SmallPtrSetImplBase & RHS)1445ca98fd9SDimitry Andric void SmallPtrSetImplBase::CopyFrom(const SmallPtrSetImplBase &RHS) {
1455ca98fd9SDimitry Andric assert(&RHS != this && "Self-copy should be handled by the caller.");
1465ca98fd9SDimitry Andric
147009b1c42SEd Schouten if (isSmall() && RHS.isSmall())
148009b1c42SEd Schouten assert(CurArraySize == RHS.CurArraySize &&
149009b1c42SEd Schouten "Cannot assign sets with different small sizes");
150009b1c42SEd Schouten
151009b1c42SEd Schouten // If we're becoming small, prepare to insert into our stack space
152009b1c42SEd Schouten if (RHS.isSmall()) {
153009b1c42SEd Schouten if (!isSmall())
154009b1c42SEd Schouten free(CurArray);
15566e41e3cSRoman Divacky CurArray = SmallArray;
156009b1c42SEd Schouten // Otherwise, allocate new heap space (unless we were the same size)
157009b1c42SEd Schouten } else if (CurArraySize != RHS.CurArraySize) {
158009b1c42SEd Schouten if (isSmall())
159eb11fae6SDimitry Andric CurArray = (const void**)safe_malloc(sizeof(void*) * RHS.CurArraySize);
160f8af5cf6SDimitry Andric else {
161eb11fae6SDimitry Andric const void **T = (const void**)safe_realloc(CurArray,
162f8af5cf6SDimitry Andric sizeof(void*) * RHS.CurArraySize);
163f8af5cf6SDimitry Andric CurArray = T;
164f8af5cf6SDimitry Andric }
165009b1c42SEd Schouten }
166009b1c42SEd Schouten
16701095a5dSDimitry Andric CopyHelper(RHS);
16801095a5dSDimitry Andric }
16901095a5dSDimitry Andric
CopyHelper(const SmallPtrSetImplBase & RHS)17001095a5dSDimitry Andric void SmallPtrSetImplBase::CopyHelper(const SmallPtrSetImplBase &RHS) {
171009b1c42SEd Schouten // Copy over the new array size
172009b1c42SEd Schouten CurArraySize = RHS.CurArraySize;
173009b1c42SEd Schouten
174009b1c42SEd Schouten // Copy over the contents from the other set
17501095a5dSDimitry Andric std::copy(RHS.CurArray, RHS.EndPointer(), CurArray);
176009b1c42SEd Schouten
17701095a5dSDimitry Andric NumNonEmpty = RHS.NumNonEmpty;
178009b1c42SEd Schouten NumTombstones = RHS.NumTombstones;
179009b1c42SEd Schouten }
180009b1c42SEd Schouten
MoveFrom(unsigned SmallSize,SmallPtrSetImplBase && RHS)1815ca98fd9SDimitry Andric void SmallPtrSetImplBase::MoveFrom(unsigned SmallSize,
1825ca98fd9SDimitry Andric SmallPtrSetImplBase &&RHS) {
1835ca98fd9SDimitry Andric if (!isSmall())
1845ca98fd9SDimitry Andric free(CurArray);
18501095a5dSDimitry Andric MoveHelper(SmallSize, std::move(RHS));
18601095a5dSDimitry Andric }
18701095a5dSDimitry Andric
MoveHelper(unsigned SmallSize,SmallPtrSetImplBase && RHS)18801095a5dSDimitry Andric void SmallPtrSetImplBase::MoveHelper(unsigned SmallSize,
18901095a5dSDimitry Andric SmallPtrSetImplBase &&RHS) {
19001095a5dSDimitry Andric assert(&RHS != this && "Self-move should be handled by the caller.");
1915ca98fd9SDimitry Andric
1925ca98fd9SDimitry Andric if (RHS.isSmall()) {
1935ca98fd9SDimitry Andric // Copy a small RHS rather than moving.
1945ca98fd9SDimitry Andric CurArray = SmallArray;
19501095a5dSDimitry Andric std::copy(RHS.CurArray, RHS.CurArray + RHS.NumNonEmpty, CurArray);
1965ca98fd9SDimitry Andric } else {
1975ca98fd9SDimitry Andric CurArray = RHS.CurArray;
1985ca98fd9SDimitry Andric RHS.CurArray = RHS.SmallArray;
1995ca98fd9SDimitry Andric }
2005ca98fd9SDimitry Andric
2015ca98fd9SDimitry Andric // Copy the rest of the trivial members.
2025ca98fd9SDimitry Andric CurArraySize = RHS.CurArraySize;
20301095a5dSDimitry Andric NumNonEmpty = RHS.NumNonEmpty;
2045ca98fd9SDimitry Andric NumTombstones = RHS.NumTombstones;
2055ca98fd9SDimitry Andric
2065ca98fd9SDimitry Andric // Make the RHS small and empty.
2075ca98fd9SDimitry Andric RHS.CurArraySize = SmallSize;
2085ca98fd9SDimitry Andric assert(RHS.CurArray == RHS.SmallArray);
20901095a5dSDimitry Andric RHS.NumNonEmpty = 0;
2105ca98fd9SDimitry Andric RHS.NumTombstones = 0;
2115ca98fd9SDimitry Andric }
2125ca98fd9SDimitry Andric
swap(SmallPtrSetImplBase & RHS)2135ca98fd9SDimitry Andric void SmallPtrSetImplBase::swap(SmallPtrSetImplBase &RHS) {
21463faed5bSDimitry Andric if (this == &RHS) return;
21563faed5bSDimitry Andric
21663faed5bSDimitry Andric // We can only avoid copying elements if neither set is small.
21763faed5bSDimitry Andric if (!this->isSmall() && !RHS.isSmall()) {
21863faed5bSDimitry Andric std::swap(this->CurArray, RHS.CurArray);
21963faed5bSDimitry Andric std::swap(this->CurArraySize, RHS.CurArraySize);
22001095a5dSDimitry Andric std::swap(this->NumNonEmpty, RHS.NumNonEmpty);
22163faed5bSDimitry Andric std::swap(this->NumTombstones, RHS.NumTombstones);
22263faed5bSDimitry Andric return;
22363faed5bSDimitry Andric }
22463faed5bSDimitry Andric
22563faed5bSDimitry Andric // FIXME: From here on we assume that both sets have the same small size.
22663faed5bSDimitry Andric
22763faed5bSDimitry Andric // If only RHS is small, copy the small elements into LHS and move the pointer
22863faed5bSDimitry Andric // from LHS to RHS.
22963faed5bSDimitry Andric if (!this->isSmall() && RHS.isSmall()) {
23001095a5dSDimitry Andric assert(RHS.CurArray == RHS.SmallArray);
23101095a5dSDimitry Andric std::copy(RHS.CurArray, RHS.CurArray + RHS.NumNonEmpty, this->SmallArray);
23201095a5dSDimitry Andric std::swap(RHS.CurArraySize, this->CurArraySize);
23301095a5dSDimitry Andric std::swap(this->NumNonEmpty, RHS.NumNonEmpty);
23401095a5dSDimitry Andric std::swap(this->NumTombstones, RHS.NumTombstones);
23563faed5bSDimitry Andric RHS.CurArray = this->CurArray;
23663faed5bSDimitry Andric this->CurArray = this->SmallArray;
23763faed5bSDimitry Andric return;
23863faed5bSDimitry Andric }
23963faed5bSDimitry Andric
24063faed5bSDimitry Andric // If only LHS is small, copy the small elements into RHS and move the pointer
24163faed5bSDimitry Andric // from RHS to LHS.
24263faed5bSDimitry Andric if (this->isSmall() && !RHS.isSmall()) {
24301095a5dSDimitry Andric assert(this->CurArray == this->SmallArray);
24401095a5dSDimitry Andric std::copy(this->CurArray, this->CurArray + this->NumNonEmpty,
24563faed5bSDimitry Andric RHS.SmallArray);
24663faed5bSDimitry Andric std::swap(RHS.CurArraySize, this->CurArraySize);
24701095a5dSDimitry Andric std::swap(RHS.NumNonEmpty, this->NumNonEmpty);
24801095a5dSDimitry Andric std::swap(RHS.NumTombstones, this->NumTombstones);
24963faed5bSDimitry Andric this->CurArray = RHS.CurArray;
25063faed5bSDimitry Andric RHS.CurArray = RHS.SmallArray;
25163faed5bSDimitry Andric return;
25263faed5bSDimitry Andric }
25363faed5bSDimitry Andric
25463faed5bSDimitry Andric // Both a small, just swap the small elements.
25563faed5bSDimitry Andric assert(this->isSmall() && RHS.isSmall());
25601095a5dSDimitry Andric unsigned MinNonEmpty = std::min(this->NumNonEmpty, RHS.NumNonEmpty);
25701095a5dSDimitry Andric std::swap_ranges(this->SmallArray, this->SmallArray + MinNonEmpty,
25863faed5bSDimitry Andric RHS.SmallArray);
25901095a5dSDimitry Andric if (this->NumNonEmpty > MinNonEmpty) {
26001095a5dSDimitry Andric std::copy(this->SmallArray + MinNonEmpty,
26101095a5dSDimitry Andric this->SmallArray + this->NumNonEmpty,
26201095a5dSDimitry Andric RHS.SmallArray + MinNonEmpty);
26301095a5dSDimitry Andric } else {
26401095a5dSDimitry Andric std::copy(RHS.SmallArray + MinNonEmpty, RHS.SmallArray + RHS.NumNonEmpty,
26501095a5dSDimitry Andric this->SmallArray + MinNonEmpty);
26663faed5bSDimitry Andric }
26701095a5dSDimitry Andric assert(this->CurArraySize == RHS.CurArraySize);
26801095a5dSDimitry Andric std::swap(this->NumNonEmpty, RHS.NumNonEmpty);
26901095a5dSDimitry Andric std::swap(this->NumTombstones, RHS.NumTombstones);
270009b1c42SEd Schouten }
271