[llvm] [FoldingSet] Switch to linear probing and Algorithm R deletion (PR #218190)
Fangrui Song via llvm-commits
llvm-commits at lists.llvm.org
Wed Aug 26 10:55:45 PDT 2026
================
@@ -207,145 +175,110 @@ FoldingSetBase::~FoldingSetBase() { free(Buckets); }
void FoldingSetBase::clear() {
incrementEpoch();
- // Set all but the last bucket to null pointers.
- memset(Buckets, 0, NumBuckets * sizeof(void *));
-
- // Set the very last bucket to be a non-null "pointer".
- Buckets[NumBuckets] = reinterpret_cast<void *>(-1);
-
- // Reset the node count to zero.
+ // Stale hashes are unreachable, so only the occupancy needs resetting.
+ if (NumBuckets)
+ memset(Buckets, 0, NumBuckets * sizeof(void *));
NumNodes = 0;
}
-void FoldingSetBase::GrowBucketCount(unsigned NewBucketCount,
- const FoldingSetInfo &Info) {
- assert((NewBucketCount > NumBuckets) &&
- "Can't shrink a folding set with GrowBucketCount");
- assert(isPowerOf2_32(NewBucketCount) && "Bad bucket count!");
+void FoldingSetBase::placeNode(Node *N, uint32_t Hash) {
+ unsigned Mask = NumBuckets - 1;
+ unsigned I = Hash & Mask;
+ while (Buckets[I]) {
+ // A second copy of N hashes here too, so the probe always passes it.
+ assert(Buckets[I] != N && "Node already in the folding set");
+ I = (I + 1) & Mask;
+ }
+ Buckets[I] = N;
+ ++NumNodes;
+}
+
+void FoldingSetBase::grow(unsigned MinNumBuckets) {
+ // The floor is the smallest size the constructor accepts.
+ unsigned NewBucketCount = std::max(64u, llvm::bit_ceil(MinNumBuckets));
+ assert(NewBucketCount > NumBuckets && "Can't shrink a folding set");
FoldingSetBase Tmp(llvm::Log2_32(NewBucketCount));
- FoldingSetNodeID TempID;
- for (unsigned i = 0; i != NumBuckets; ++i) {
- void *Probe = Buckets[i];
- if (!Probe)
- continue;
- while (Node *NodeInBucket = GetNextPtr(Probe)) {
- // Figure out the next link, remove NodeInBucket from the old link.
- Probe = NodeInBucket->getNextInBucket();
- NodeInBucket->SetNextInBucket(nullptr);
-
- // Insert the node into the new bucket, after recomputing the hash.
- Tmp.InsertNode(
- NodeInBucket,
- GetBucketFor(Info.ComputeNodeHash(this, NodeInBucket, TempID),
- Tmp.Buckets, Tmp.NumBuckets),
- Info);
- TempID.clear();
- }
- }
+ for (unsigned I = 0; I != NumBuckets; ++I)
+ if (void *N = Buckets[I])
+ Tmp.placeNode(static_cast<Node *>(N),
+ static_cast<Node *>(N)->getFoldingSetHash());
*this = std::move(Tmp);
}
-void FoldingSetBase::reserve(unsigned EltCount, const FoldingSetInfo &Info) {
- // This will give us somewhere between EltCount / 2 and
- // EltCount buckets. This puts us in the load factor
- // range of 1.0 - 2.0.
- if (EltCount <= capacity())
+void FoldingSetBase::reserve(unsigned N) {
+ if (N * 4 <= NumBuckets * 3)
return;
- GrowBucketCount(llvm::bit_floor(EltCount), Info);
+ // N + (N + 2) / 3 is ceil(4N/3).
+ grow(N + (N + 2) / 3);
+}
+
+LLVM_ATTRIBUTE_NOINLINE bool
+FoldingSetBase::nodeEquals(const FoldingSetInfo &Info,
+ const FoldingSetBase *Self, Node *N,
+ const FoldingSetNodeID &ID, unsigned IDHash) {
+ FoldingSetNodeID TempID;
+ return Info.NodeEquals(Self, N, ID, IDHash, TempID);
}
FoldingSetBase::Node *FoldingSetBase::FindNodeOrInsertPos(
const FoldingSetNodeID &ID, void *&InsertPos, const FoldingSetInfo &Info) {
unsigned IDHash = ID.ComputeHash();
- void **Bucket = GetBucketFor(IDHash, Buckets, NumBuckets);
- void *Probe = *Bucket;
-
- InsertPos = nullptr;
-
- FoldingSetNodeID TempID;
- while (Node *NodeInBucket = GetNextPtr(Probe)) {
- if (Info.NodeEquals(this, NodeInBucket, ID, IDHash, TempID))
- return NodeInBucket;
- TempID.clear();
-
- Probe = NodeInBucket->getNextInBucket();
+ unsigned Mask = NumBuckets - 1;
+ for (unsigned I = IDHash & Mask; Buckets[I]; I = (I + 1) & Mask) {
+ // Reject on the hash first, so a probe step touches no node.
+ Node *N = static_cast<Node *>(Buckets[I]);
+ if (N->getFoldingSetHash() != IDHash)
+ continue;
+ if (nodeEquals(Info, this, N, ID, IDHash)) {
+ InsertPos = nullptr;
+ return N;
+ }
}
- // Didn't find the node, return null with the bucket as the InsertPos.
- InsertPos = Bucket;
+ InsertPos = encodeHash(IDHash);
return nullptr;
}
-void FoldingSetBase::InsertNode(Node *N, void *InsertPos,
- const FoldingSetInfo &Info) {
- assert(!N->getNextInBucket());
+void FoldingSetBase::InsertNode(Node *N, void *InsertPos) {
+ assert(InsertPos && "Invalid InsertPos!");
incrementEpoch();
- // Do we need to grow the hashtable?
- if (NumNodes + 1 > capacity()) {
- GrowBucketCount(NumBuckets * 2, Info);
- FoldingSetNodeID TempID;
- InsertPos = GetBucketFor(Info.ComputeNodeHash(this, N, TempID), Buckets,
- NumBuckets);
- }
-
- ++NumNodes;
-
- /// The insert position is actually a bucket pointer.
- void **Bucket = static_cast<void **>(InsertPos);
-
- void *Next = *Bucket;
-
- // If this is the first insertion into this bucket, its next pointer will be
- // null. Pretend as if it pointed to itself, setting the low bit to indicate
- // that it is a pointer to the bucket.
- if (!Next)
- Next = reinterpret_cast<void *>(reinterpret_cast<intptr_t>(Bucket) | 1);
-
- // Set the node's next pointer, and make the bucket point to the node.
- N->SetNextInBucket(Next);
- *Bucket = N;
+ if (LLVM_UNLIKELY((NumNodes + 1) * 4 > NumBuckets * 3))
+ grow(NumBuckets * 2);
+ uint32_t Hash = decodeHash(InsertPos);
+ placeNode(N, Hash);
+ N->setFoldingSetHash(Hash);
}
bool FoldingSetBase::RemoveNode(Node *N) {
- // Because each bucket is a circular list, we don't need to compute N's hash
- // to remove it.
- void *Ptr = N->getNextInBucket();
- if (!Ptr)
- return false; // Not in folding set.
+ uint32_t Hash = N->getFoldingSetHash();
+ if (Hash == FoldingSetNodeIDRef::NotAHash)
+ return false; // Never inserted.
+
+ unsigned Mask = NumBuckets - 1;
+ unsigned I = Hash & Mask;
+ while (Buckets[I] != N) {
+ if (!Buckets[I])
----------------
MaskRay wrote:
Done
https://github.com/llvm/llvm-project/pull/218190
More information about the llvm-commits
mailing list