[llvm] [ADT][TableGen] Add UniquingSet, a FoldingSet with typed keys (PR #219630)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Aug 28 22:27:55 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-tablegen
Author: Fangrui Song (MaskRay)
<details>
<summary>Changes</summary>
FoldingSet serializes a key into a FoldingSetNodeID to look a node up and
rebuilds the stored node's profile to compare against it. Where a key can be
read out of a node, neither is necessary.
UniquingSet reuses FoldingSetBase's storage, growth, removal and
insert-position protocol and replaces only the key: the node's `getKey()`
supplies it, the key type's `operator==` compares it, and DenseMapInfo
hashes it inline. An Info parameter overrides the key type or its hash.
The hash cached on each node keeps growth and erasure from calling
`getKey()`, which a DenseSet cannot avoid.
find hands back a typed FoldingSetInsertPos rather than FoldingSet's
void *; FoldingSet's own API is unchanged.
Prefer `UniquingSet` where a key can be read out of a node in O(1) and
the lookup key is built beside `getKey()`; keep FoldingSet for keys that
are wide, polymorphic or assembled at many call sites, where one Profile
helper keeps both sides consistent. insert asserts that a node hashes as
its lookup did.
TableGen's list and unary/binary/ternary operator Init pools are the first user:
each Profile and its ProfileXxxInit helper become one getKey.
Instructions retired by llvm-tblgen:
```
gen-dag-isel asm-matcher subtarget register-info
X86 -1.01% -2.81% -1.01% -3.92%
AArch64 -0.85% -2.90% -0.26% -3.90%
RISCV -1.30% -6.96% -3.29% -7.86%
AMDGPU -1.65% -1.40% -3.66% -1.87%
```
A FoldingSetTrait::Equals that matches the serialized ID against the
node's fields, keeping FoldingSet, captures 31% of this in every cell;
the rest is serializing and hashing the key.
LLM-aided
---
Patch is 23.12 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/219630.diff
5 Files Affected:
- (modified) llvm/docs/ProgrammersManual.md (+32)
- (modified) llvm/include/llvm/ADT/FoldingSet.h (+108)
- (modified) llvm/include/llvm/TableGen/Record.h (+19-8)
- (modified) llvm/lib/TableGen/Record.cpp (+16-81)
- (modified) llvm/unittests/ADT/FoldingSet.cpp (+174)
``````````diff
diff --git a/llvm/docs/ProgrammersManual.md b/llvm/docs/ProgrammersManual.md
index e4c07a389891a..818a70f3048e7 100644
--- a/llvm/docs/ProgrammersManual.md
+++ b/llvm/docs/ProgrammersManual.md
@@ -2083,6 +2083,38 @@ stable: inserting or removing elements does not invalidate any pointers to other
elements. The iterators, however, are invalidated whenever an insertion or
erasure occurs, as in `DenseMap` and `StringMap`.
+See {ref}`UniquingSet <dss_uniquingset>` for a variant keyed on a typed key.
+
+(dss_uniquingset)=
+
+#### UniquingSet (llvm/ADT/FoldingSet.h)
+
+`UniquingSet` is a {ref}`FoldingSet <dss_FoldingSet>` whose nodes are compared
+against a typed key instead of a serialized `FoldingSetNodeID`. Each node
+supplies its key through `getKey()`; a lookup builds the same key from what it
+already holds and hashes it inline with `DenseMapInfo`, and `find` returns the
+matching node or an insertion token for `insert`. Growth and removal use the
+hash cached in each node and never call `getKey`. An `Info` template argument
+can override the key type or the hash.
+
+```cpp
+std::tuple<unsigned, const Value *, const Value *> FooNode::getKey() const {
+ return {Opcode, LHS, RHS};
+}
+
+UniquingSet<FooNode> Pool;
+FoldingSetInsertPos IP;
+if (FooNode *N = Pool.find({Opcode, LHS, RHS}, IP))
+ return N;
+Pool.insert(new (Allocator) FooNode(Opcode, LHS, RHS), IP);
+```
+
+Prefer `UniquingSet` when a key can be read out of a node in O(1) and the lookup
+key is built next to `getKey`. Keep `FoldingSet` for keys that are wide,
+polymorphic, or assembled at many call sites: one `Profile` helper then keeps
+both sides consistent, whereas `getKey` and a lookup site can silently disagree.
+`insert` asserts that a node hashes as its lookup did.
+
(dss_set)=
#### `<set>`
diff --git a/llvm/include/llvm/ADT/FoldingSet.h b/llvm/include/llvm/ADT/FoldingSet.h
index c984c00b86fd1..42cdc3098e716 100644
--- a/llvm/include/llvm/ADT/FoldingSet.h
+++ b/llvm/include/llvm/ADT/FoldingSet.h
@@ -16,6 +16,7 @@
#ifndef LLVM_ADT_FOLDINGSET_H
#define LLVM_ADT_FOLDINGSET_H
+#include "llvm/ADT/DenseMapInfo.h"
#include "llvm/ADT/EpochTracker.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/STLForwardCompat.h"
@@ -159,6 +160,19 @@ template <typename T, typename Ctx> struct DefaultContextualFoldingSetTrait {
template <typename T, typename Ctx>
struct ContextualFoldingSetTrait : DefaultContextualFoldingSetTrait<T, Ctx> {};
+class FoldingSetInsertPos {
+ uint64_t Value = 0;
+
+ explicit FoldingSetInsertPos(uint32_t Hash) : Value(uint64_t(Hash) + 1) {}
+ uint32_t hash() const { return uint32_t(Value - 1); }
+
+ template <typename, typename> friend class UniquingSet;
+
+public:
+ FoldingSetInsertPos() = default;
+ explicit operator bool() const { return Value != 0; }
+};
+
//===--------------------------------------------------------------------===//
/// This class describes a reference to an interned FoldingSetNodeID, which can
/// be a useful to store node id data rather than using plain FoldingSetNodeIDs,
@@ -389,6 +403,11 @@ class FoldingSetBase : public DebugEpochBase {
/// it is not already in the folding set. InsertPos must be obtained from
/// FindNodeOrInsertPos for an ID that \p N profiles identically to.
LLVM_ABI void InsertNode(Node *N, void *InsertPos);
+
+ /// Encode \p Hash, which must not be NotAHash, as the token InsertNode takes.
+ static void *encodeInsertPos(uint32_t Hash) {
+ return reinterpret_cast<void *>(static_cast<uintptr_t>(Hash));
+ }
};
// Convenience type to hide the implementation of the folding set.
@@ -663,6 +682,95 @@ template <typename T> class FoldingSetNodeWrapper : public FoldingSetNode {
operator const T &() const { return data; }
};
+//===----------------------------------------------------------------------===//
+/// The default UniquingSet Info: \p T supplies its own key.
+template <typename T> struct UniquingSetInfo {
+ using KeyTy = remove_cvref_t<decltype(std::declval<const T &>().getKey())>;
+ static KeyTy getKey(const T &N) { return N.getKey(); }
+ static unsigned getHashValue(const KeyTy &Key) {
+ return DenseMapInfo<KeyTy>::getHashValue(Key);
+ }
+};
+
+/// A uniquing set that compares nodes against a typed key rather than a
+/// serialized FoldingSetNodeID.
+///
+/// \p T must derive from FoldingSetNode and provide a getKey() whose result is
+/// comparable with == and for which DenseMapInfo<KeyTy>::getHashValue exists.
+/// \p Info overrides that:
+///
+/// \code
+/// using KeyTy = ...;
+/// static KeyTy getKey(const T &N);
+/// static unsigned getHashValue(const KeyTy &K);
+/// \endcode
+///
+/// Derive \p Info from UniquingSetInfo<T> to override only the hash. The
+/// default Info needs \p T complete wherever UniquingSet<T> is instantiated;
+/// FoldingSet does not. A key may alias storage owned by the node; it is only
+/// used within a single find().
+///
+/// Prefer FoldingSet when a key cannot be read cheaply out of a node: a
+/// FoldingSetNodeID cannot disagree with itself, whereas getKey and the code
+/// that builds a key to look up must be kept in step by hand.
+template <typename T, typename Info = UniquingSetInfo<T>>
+class UniquingSet : public FoldingSetBase {
+public:
+ using KeyTy = typename Info::KeyTy;
+
+ explicit UniquingSet(unsigned Log2InitSize = 6)
+ : FoldingSetBase(Log2InitSize) {}
+
+ using iterator = FoldingSetIterator<T>;
+ iterator begin() { return iterator(Buckets, Buckets + NumBuckets, this); }
+ iterator end() {
+ return iterator(Buckets + NumBuckets, Buckets + NumBuckets, this);
+ }
+
+ using const_iterator = FoldingSetIterator<const T>;
+ const_iterator begin() const {
+ return const_iterator(Buckets, Buckets + NumBuckets, this);
+ }
+ const_iterator end() const {
+ return const_iterator(Buckets + NumBuckets, Buckets + NumBuckets, this);
+ }
+
+ /// Look up \p Key. On a hit \p InsertPos is cleared; on a miss it receives a
+ /// token for insert().
+ T *find(const KeyTy &Key, FoldingSetInsertPos &InsertPos) {
+ uint32_t Hash = hashKey(Key);
+ unsigned Mask = NumBuckets - 1;
+ for (unsigned I = Hash & Mask; Buckets[I]; I = (I + 1) & Mask) {
+ T *N = static_cast<T *>(static_cast<Node *>(Buckets[I]));
+ if (N->getFoldingSetHash() == Hash && Key == Info::getKey(*N)) {
+ InsertPos = {};
+ return N;
+ }
+ }
+ InsertPos = FoldingSetInsertPos(Hash);
+ return nullptr;
+ }
+
+ /// Insert \p N, which must key identically to the lookup that produced
+ /// \p InsertPos.
+ void insert(T *N, FoldingSetInsertPos InsertPos) {
+ assert(InsertPos && "Invalid InsertPos!");
+ assert(hashKey(Info::getKey(*N)) == InsertPos.hash() &&
+ "N does not key as the lookup that produced InsertPos did");
+ FoldingSetBase::InsertNode(N, encodeInsertPos(InsertPos.hash()));
+ }
+
+ /// Remove \p N, returning whether it was present.
+ bool erase(T *N) { return FoldingSetBase::RemoveNode(N); }
+
+private:
+ // Never NotAHash, for the reason FoldingSetNodeIDRef::ComputeHash gives.
+ static uint32_t hashKey(const KeyTy &Key) {
+ uint32_t Hash = Info::getHashValue(Key);
+ return Hash == FoldingSetNodeIDRef::NotAHash ? 1 : Hash;
+ }
+};
+
//===----------------------------------------------------------------------===//
/// This is a subclass of FoldingSetNode which stores a FoldingSetNodeID value
/// rather than requiring the node to recompute it each time it is needed. This
diff --git a/llvm/include/llvm/TableGen/Record.h b/llvm/include/llvm/TableGen/Record.h
index 80e3b17364e37..ca9c813b8a0ca 100644
--- a/llvm/include/llvm/TableGen/Record.h
+++ b/llvm/include/llvm/TableGen/Record.h
@@ -36,6 +36,7 @@
#include <memory>
#include <optional>
#include <string>
+#include <tuple>
#include <utility>
#include <variant>
#include <vector>
@@ -770,8 +771,6 @@ class ListInit final : public TypedInit,
}
static const ListInit *get(ArrayRef<const Init *> Range, const RecTy *EltTy);
- void Profile(FoldingSetNodeID &ID) const;
-
ArrayRef<const Init *> getElements() const {
return ArrayRef(getTrailingObjects(), NumElements);
}
@@ -781,6 +780,10 @@ class ListInit final : public TypedInit,
const Init *getElement(unsigned Idx) const { return getElements()[Idx]; }
+ std::pair<ArrayRef<const Init *>, const RecTy *> getKey() const {
+ return {getElements(), getElementType()};
+ }
+
const RecTy *getElementType() const {
return cast<ListRecTy>(getType())->getElementType();
}
@@ -867,11 +870,13 @@ class UnOpInit final : public OpInit, public FoldingSetNode {
static const UnOpInit *get(UnaryOp opc, const Init *lhs, const RecTy *Type);
- void Profile(FoldingSetNodeID &ID) const;
-
UnaryOp getOpcode() const { return (UnaryOp)Opc; }
const Init *getOperand() const { return LHS; }
+ std::tuple<UnaryOp, const Init *, const RecTy *> getKey() const {
+ return {getOpcode(), LHS, getType()};
+ }
+
// Fold - If possible, fold this to a simpler init. Return this if not
// possible to fold.
const Init *Fold(const Record *CurRec, bool IsFinal = false) const;
@@ -936,12 +941,15 @@ class BinOpInit final : public OpInit, public FoldingSetNode {
static const Init *getStrConcat(const Init *lhs, const Init *rhs);
static const Init *getListConcat(const TypedInit *lhs, const Init *rhs);
- void Profile(FoldingSetNodeID &ID) const;
-
BinaryOp getOpcode() const { return (BinaryOp)Opc; }
const Init *getLHS() const { return LHS; }
const Init *getRHS() const { return RHS; }
+ std::tuple<BinaryOp, const Init *, const Init *, const RecTy *>
+ getKey() const {
+ return {getOpcode(), LHS, RHS, getType()};
+ }
+
std::optional<bool> CompareInit(unsigned Opc, const Init *LHS,
const Init *RHS) const;
@@ -989,13 +997,16 @@ class TernOpInit final : public OpInit, public FoldingSetNode {
static const TernOpInit *get(TernaryOp opc, const Init *lhs, const Init *mhs,
const Init *rhs, const RecTy *Type);
- void Profile(FoldingSetNodeID &ID) const;
-
TernaryOp getOpcode() const { return (TernaryOp)Opc; }
const Init *getLHS() const { return LHS; }
const Init *getMHS() const { return MHS; }
const Init *getRHS() const { return RHS; }
+ std::tuple<TernaryOp, const Init *, const Init *, const Init *, const RecTy *>
+ getKey() const {
+ return {getOpcode(), LHS, MHS, RHS, getType()};
+ }
+
// Fold - If possible, fold this to a simpler init. Return this if not
// possible to fold.
const Init *Fold(const Record *CurRec) const;
diff --git a/llvm/lib/TableGen/Record.cpp b/llvm/lib/TableGen/Record.cpp
index ac62e2f5785ae..bf9824a3c5428 100644
--- a/llvm/lib/TableGen/Record.cpp
+++ b/llvm/lib/TableGen/Record.cpp
@@ -75,10 +75,10 @@ struct detail::RecordKeeperImpl {
std::map<int64_t, IntInit *> TheIntInitPool;
StringMap<const StringInit *, BumpPtrAllocator &> StringInitStringPool;
StringMap<const StringInit *, BumpPtrAllocator &> StringInitCodePool;
- FoldingSet<ListInit> TheListInitPool;
- FoldingSet<UnOpInit> TheUnOpInitPool;
- FoldingSet<BinOpInit> TheBinOpInitPool;
- FoldingSet<TernOpInit> TheTernOpInitPool;
+ UniquingSet<ListInit> TheListInitPool;
+ UniquingSet<UnOpInit> TheUnOpInitPool;
+ UniquingSet<BinOpInit> TheBinOpInitPool;
+ UniquingSet<TernOpInit> TheTernOpInitPool;
FoldingSet<FoldOpInit> TheFoldOpInitPool;
FoldingSet<IsAOpInit> TheIsAOpInitPool;
FoldingSet<ExistsOpInit> TheExistsOpInitPool;
@@ -694,16 +694,6 @@ const Init *StringInit::convertInitializerTo(const RecTy *Ty) const {
return nullptr;
}
-static void ProfileListInit(FoldingSetNodeID &ID,
- ArrayRef<const Init *> Elements,
- const RecTy *EltTy) {
- ID.AddInteger(Elements.size());
- ID.AddPointer(EltTy);
-
- for (const Init *E : Elements)
- ID.AddPointer(E);
-}
-
ListInit::ListInit(ArrayRef<const Init *> Elements, const RecTy *EltTy)
: TypedInit(IK_ListInit, ListRecTy::get(EltTy)),
NumElements(Elements.size()) {
@@ -712,12 +702,9 @@ ListInit::ListInit(ArrayRef<const Init *> Elements, const RecTy *EltTy)
const ListInit *ListInit::get(ArrayRef<const Init *> Elements,
const RecTy *EltTy) {
- FoldingSetNodeID ID;
- ProfileListInit(ID, Elements, EltTy);
-
detail::RecordKeeperImpl &RK = EltTy->getRecordKeeper().getImpl();
- void *IP = nullptr;
- if (const ListInit *I = RK.TheListInitPool.FindNodeOrInsertPos(ID, IP))
+ FoldingSetInsertPos IP;
+ if (const ListInit *I = RK.TheListInitPool.find({Elements, EltTy}, IP))
return I;
assert(Elements.empty() || !isa<TypedInit>(Elements[0]) ||
@@ -726,15 +713,10 @@ const ListInit *ListInit::get(ArrayRef<const Init *> Elements,
void *Mem = RK.Allocator.Allocate(
totalSizeToAlloc<const Init *>(Elements.size()), alignof(ListInit));
ListInit *I = new (Mem) ListInit(Elements, EltTy);
- RK.TheListInitPool.InsertNode(I, IP);
+ RK.TheListInitPool.insert(I, IP);
return I;
}
-void ListInit::Profile(FoldingSetNodeID &ID) const {
- const RecTy *EltTy = cast<ListRecTy>(getType())->getElementType();
- ProfileListInit(ID, getElements(), EltTy);
-}
-
const Init *ListInit::convertInitializerTo(const RecTy *Ty) const {
if (getType() == Ty)
return this;
@@ -814,31 +796,17 @@ const Init *OpInit::getBit(unsigned Bit) const {
return VarBitInit::get(this, Bit);
}
-static void ProfileUnOpInit(FoldingSetNodeID &ID, unsigned Opcode,
- const Init *Op, const RecTy *Type) {
- ID.AddInteger(Opcode);
- ID.AddPointer(Op);
- ID.AddPointer(Type);
-}
-
const UnOpInit *UnOpInit::get(UnaryOp Opc, const Init *LHS, const RecTy *Type) {
- FoldingSetNodeID ID;
- ProfileUnOpInit(ID, Opc, LHS, Type);
-
detail::RecordKeeperImpl &RK = Type->getRecordKeeper().getImpl();
- void *IP = nullptr;
- if (const UnOpInit *I = RK.TheUnOpInitPool.FindNodeOrInsertPos(ID, IP))
+ FoldingSetInsertPos IP;
+ if (const UnOpInit *I = RK.TheUnOpInitPool.find({Opc, LHS, Type}, IP))
return I;
UnOpInit *I = new (RK.Allocator) UnOpInit(Opc, LHS, Type);
- RK.TheUnOpInitPool.InsertNode(I, IP);
+ RK.TheUnOpInitPool.insert(I, IP);
return I;
}
-void UnOpInit::Profile(FoldingSetNodeID &ID) const {
- ProfileUnOpInit(ID, getOpcode(), getOperand(), getType());
-}
-
const Init *UnOpInit::Fold(const Record *CurRec, bool IsFinal) const {
RecordKeeper &RK = getRecordKeeper();
switch (getOpcode()) {
@@ -1083,34 +1051,18 @@ std::string UnOpInit::getAsString() const {
return Result + "(" + LHS->getAsString() + ")";
}
-static void ProfileBinOpInit(FoldingSetNodeID &ID, unsigned Opcode,
- const Init *LHS, const Init *RHS,
- const RecTy *Type) {
- ID.AddInteger(Opcode);
- ID.AddPointer(LHS);
- ID.AddPointer(RHS);
- ID.AddPointer(Type);
-}
-
const BinOpInit *BinOpInit::get(BinaryOp Opc, const Init *LHS, const Init *RHS,
const RecTy *Type) {
- FoldingSetNodeID ID;
- ProfileBinOpInit(ID, Opc, LHS, RHS, Type);
-
detail::RecordKeeperImpl &RK = LHS->getRecordKeeper().getImpl();
- void *IP = nullptr;
- if (const BinOpInit *I = RK.TheBinOpInitPool.FindNodeOrInsertPos(ID, IP))
+ FoldingSetInsertPos IP;
+ if (const BinOpInit *I = RK.TheBinOpInitPool.find({Opc, LHS, RHS, Type}, IP))
return I;
BinOpInit *I = new (RK.Allocator) BinOpInit(Opc, LHS, RHS, Type);
- RK.TheBinOpInitPool.InsertNode(I, IP);
+ RK.TheBinOpInitPool.insert(I, IP);
return I;
}
-void BinOpInit::Profile(FoldingSetNodeID &ID) const {
- ProfileBinOpInit(ID, getOpcode(), getLHS(), getRHS(), getType());
-}
-
static const StringInit *ConcatStringInits(const StringInit *I0,
const StringInit *I1) {
SmallString<80> Concat(I0->getValue());
@@ -1676,36 +1628,19 @@ std::string BinOpInit::getAsString() const {
return Result + "(" + LHS->getAsString() + ", " + RHS->getAsString() + ")";
}
-static void ProfileTernOpInit(FoldingSetNodeID &ID, unsigned Opcode,
- const Init *LHS, const Init *MHS, const Init *RHS,
- const RecTy *Type) {
- ID.AddInteger(Opcode);
- ID.AddPointer(LHS);
- ID.AddPointer(MHS);
- ID.AddPointer(RHS);
- ID.AddPointer(Type);
-}
-
const TernOpInit *TernOpInit::get(TernaryOp Opc, const Init *LHS,
const Init *MHS, const Init *RHS,
const RecTy *Type) {
- FoldingSetNodeID ID;
- ProfileTernOpInit(ID, Opc, LHS, MHS, RHS, Type);
-
detail::RecordKeeperImpl &RK = LHS->getRecordKeeper().getImpl();
- void *IP = nullptr;
- if (TernOpInit *I = RK.TheTernOpInitPool.FindNodeOrInsertPos(ID, IP))
+ FoldingSetInsertPos IP;
+ if (TernOpInit *I = RK.TheTernOpInitPool.find({Opc, LHS, MHS, RHS, Type}, IP))
return I;
TernOpInit *I = new (RK.Allocator) TernOpInit(Opc, LHS, MHS, RHS, Type);
- RK.TheTernOpInitPool.InsertNode(I, IP);
+ RK.TheTernOpInitPool.insert(I, IP);
return I;
}
-void TernOpInit::Profile(FoldingSetNodeID &ID) const {
- ProfileTernOpInit(ID, getOpcode(), getLHS(), getMHS(), getRHS(), getType());
-}
-
static const Init *ItemApply(const Init *LHS, const Init *MHSe, const Init *RHS,
const Record *CurRec) {
MapResolver R(CurRec);
diff --git a/llvm/unittests/ADT/FoldingSet.cpp b/llvm/unittests/ADT/FoldingSet.cpp
index 2556d64bae7a3..534b532c01419 100644
--- a/llvm/unittests/ADT/FoldingSet.cpp
+++ b/llvm/unittests/ADT/FoldingSet.cpp
@@ -11,6 +11,7 @@
//===----------------------------------------------------------------------===//
#include "llvm/ADT/FoldingSet.h"
+#include "llvm/ADT/ArrayRef.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <map>
@@ -515,4 +516,177 @@ TEST(FoldingSetTest, InsertPosSurvivesGrowth) {
EXPECT_EQ(201u, Set.size());
}
+// FoldingSetNode is a non-first base, so find()'s two-step cast must adjust.
+struct KeyedPair : NonEmptyBase, FoldingSetNode {
+ unsigned A, B;
+ KeyedPair(unsigned A, unsigned B) : A(A), B(B) {}
+ std::pair<unsigned, unsigned> getKey() const { return {A, B}; }
+};
+
+TEST(UniquingSetTest, Basic) {
+ UniquingSet<KeyedPair> Set;
+ FoldingSetInsertPos InsertPos;
+ EXPECT_EQ(nullptr, Set.find({1, 2}, InsertPos));
+ EXPECT_TRUE(bool(InsertPos));
+
+ KeyedPair A(1, 2);
+ Set.insert(&A, InsertPos);
+ EXPECT_EQ(1u, Set.size());
+
+ // A hit clears the token; seed it with the one the miss produced.
+ FoldingSetInsertPos Token = InsertPos;
+ EXPECT_EQ(&A, Set.find({1, 2}, Token));
+ EXPECT_FALSE(bool(Token));
+ EXPECT_EQ(nullptr, Set.find({2, 1}, Token));
+ EXPECT_TRUE(bool(Token));
+
+ KeyedPair B(2, 1);
+ Set.insert(&B, Token);
+
+ std::vector<KeyedPair *> Visited;
+ for (KeyedPair &N : Set)
+ Visited.push_back(&N);
+ EXPECT_THAT(Visited, UnorderedElementsAre(&A, &B));
+
+ std::vector<const KeyedPair *> ConstVisited;
+ for (const KeyedPair &N : std::as_const(Set))
+ ConstVisited.push_back(&N);
+ EXPECT_THAT(ConstVisited, UnorderedElementsAre(&A, &B));
+
+ EXPECT_TRUE(Set.erase(&A));
+ EXPECT_FALSE(Set.erase(&A));
+ KeyedPair NeverInserted(3, 4);
+ EXPECT_FALSE(Set.erase(&NeverInserted));
+ EXPECT_EQ(1u, Set.size());
+ EXPECT_EQ(nullptr, Set.find({1, 2}, Token));
+}
+
+// Every key hashes to NotAHash, which must be remapped so that erase() does not
+// read a live node as never-inserted.
+struct ZeroHashNode : FoldingSetNode {
+ unsigned Key;
+ explicit ZeroHashNode(unsigned Key) : Key(Key) {}
+ unsigned getKey() const { return Key; }
+};
+
+struct ZeroHashInfo : UniquingSetInfo<ZeroHashNode> {
+ static unsigned getHashValue(const KeyTy &) {
+ return FoldingSetNodeIDRef::NotAHash;
+ }
+};
+
+TEST(UniquingSetTest, KeyHashingToNotAHash) {
+ UniquingSet<ZeroHashNode, ZeroHashInfo> Set;
+ ZeroHashNode A(1), B(2);
+
+ FoldingSetInsertPos P;
+ ASSERT_EQ(nullptr, Set.find(1, P));
+ ASSERT_TRUE(bool(P));
+ Set.insert(&A, P);
+ // Same bucket, different key: the probe must walk past A.
+ ASSERT_EQ(nullptr, Set.find(2, P));
+ ASSERT_TRUE(bool(P));
+ Set.insert(&B, P);
+
+ FoldingSetInsertPos Unused;
+ EXPECT_EQ(&A, Set.find(1, Unused));
+ EXPECT_EQ(&B, Set.find(2, Unused));
+ EXPECT_EQ(2u, Set.size());
+
+ EXPECT_TRUE(Set.erase(&A));
+ EXPECT_FALSE(Set.erase(&A));
+ EXPE...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/219630
More information about the llvm-commits
mailing list