summaryrefslogtreecommitdiffstats
path: root/gnu/llvm/unittests/ADT/DenseMapTest.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gnu/llvm/unittests/ADT/DenseMapTest.cpp')
-rw-r--r--gnu/llvm/unittests/ADT/DenseMapTest.cpp181
1 files changed, 181 insertions, 0 deletions
diff --git a/gnu/llvm/unittests/ADT/DenseMapTest.cpp b/gnu/llvm/unittests/ADT/DenseMapTest.cpp
index f3dcf95e92f..db00f8cf8e5 100644
--- a/gnu/llvm/unittests/ADT/DenseMapTest.cpp
+++ b/gnu/llvm/unittests/ADT/DenseMapTest.cpp
@@ -339,6 +339,138 @@ TYPED_TEST(DenseMapTest, ConstIteratorTest) {
EXPECT_TRUE(cit == cit2);
}
+namespace {
+// Simple class that counts how many moves and copy happens when growing a map
+struct CountCopyAndMove {
+ static int Move;
+ static int Copy;
+ CountCopyAndMove() {}
+
+ CountCopyAndMove(const CountCopyAndMove &) { Copy++; }
+ CountCopyAndMove &operator=(const CountCopyAndMove &) {
+ Copy++;
+ return *this;
+ }
+ CountCopyAndMove(CountCopyAndMove &&) { Move++; }
+ CountCopyAndMove &operator=(const CountCopyAndMove &&) {
+ Move++;
+ return *this;
+ }
+};
+int CountCopyAndMove::Copy = 0;
+int CountCopyAndMove::Move = 0;
+
+} // anonymous namespace
+
+// Test for the default minimum size of a DenseMap
+TEST(DenseMapCustomTest, DefaultMinReservedSizeTest) {
+ // IF THIS VALUE CHANGE, please update InitialSizeTest, InitFromIterator, and
+ // ReserveTest as well!
+ const int ExpectedInitialBucketCount = 64;
+ // Formula from DenseMap::getMinBucketToReserveForEntries()
+ const int ExpectedMaxInitialEntries = ExpectedInitialBucketCount * 3 / 4 - 1;
+
+ DenseMap<int, CountCopyAndMove> Map;
+ // Will allocate 64 buckets
+ Map.reserve(1);
+ unsigned MemorySize = Map.getMemorySize();
+ CountCopyAndMove::Copy = 0;
+ CountCopyAndMove::Move = 0;
+ for (int i = 0; i < ExpectedMaxInitialEntries; ++i)
+ Map.insert(std::pair<int, CountCopyAndMove>(std::piecewise_construct,
+ std::forward_as_tuple(i),
+ std::forward_as_tuple()));
+ // Check that we didn't grow
+ EXPECT_EQ(MemorySize, Map.getMemorySize());
+ // Check that move was called the expected number of times
+ EXPECT_EQ(ExpectedMaxInitialEntries, CountCopyAndMove::Move);
+ // Check that no copy occured
+ EXPECT_EQ(0, CountCopyAndMove::Copy);
+
+ // Adding one extra element should grow the map
+ Map.insert(std::pair<int, CountCopyAndMove>(
+ std::piecewise_construct,
+ std::forward_as_tuple(ExpectedMaxInitialEntries),
+ std::forward_as_tuple()));
+ // Check that we grew
+ EXPECT_NE(MemorySize, Map.getMemorySize());
+ // Check that move was called the expected number of times
+ // This relies on move-construction elision, and cannot be reliably tested.
+ // EXPECT_EQ(ExpectedMaxInitialEntries + 2, CountCopyAndMove::Move);
+ // Check that no copy occured
+ EXPECT_EQ(0, CountCopyAndMove::Copy);
+}
+
+// Make sure creating the map with an initial size of N actually gives us enough
+// buckets to insert N items without increasing allocation size.
+TEST(DenseMapCustomTest, InitialSizeTest) {
+ // Test a few different sizes, 48 is *not* a random choice: we need a value
+ // that is 2/3 of a power of two to stress the grow() condition, and the power
+ // of two has to be at least 64 because of minimum size allocation in the
+ // DenseMap (see DefaultMinReservedSizeTest). 66 is a value just above the
+ // 64 default init.
+ for (auto Size : {1, 2, 48, 66}) {
+ DenseMap<int, CountCopyAndMove> Map(Size);
+ unsigned MemorySize = Map.getMemorySize();
+ CountCopyAndMove::Copy = 0;
+ CountCopyAndMove::Move = 0;
+ for (int i = 0; i < Size; ++i)
+ Map.insert(std::pair<int, CountCopyAndMove>(std::piecewise_construct,
+ std::forward_as_tuple(i),
+ std::forward_as_tuple()));
+ // Check that we didn't grow
+ EXPECT_EQ(MemorySize, Map.getMemorySize());
+ // Check that move was called the expected number of times
+ EXPECT_EQ(Size, CountCopyAndMove::Move);
+ // Check that no copy occured
+ EXPECT_EQ(0, CountCopyAndMove::Copy);
+ }
+}
+
+// Make sure creating the map with a iterator range does not trigger grow()
+TEST(DenseMapCustomTest, InitFromIterator) {
+ std::vector<std::pair<int, CountCopyAndMove>> Values;
+ // The size is a random value greater than 64 (hardcoded DenseMap min init)
+ const int Count = 65;
+ for (int i = 0; i < Count; i++)
+ Values.emplace_back(i, CountCopyAndMove());
+
+ CountCopyAndMove::Move = 0;
+ CountCopyAndMove::Copy = 0;
+ DenseMap<int, CountCopyAndMove> Map(Values.begin(), Values.end());
+ // Check that no move occured
+ EXPECT_EQ(0, CountCopyAndMove::Move);
+ // Check that copy was called the expected number of times
+ EXPECT_EQ(Count, CountCopyAndMove::Copy);
+}
+
+// Make sure reserve actually gives us enough buckets to insert N items
+// without increasing allocation size.
+TEST(DenseMapCustomTest, ReserveTest) {
+ // Test a few different size, 48 is *not* a random choice: we need a value
+ // that is 2/3 of a power of two to stress the grow() condition, and the power
+ // of two has to be at least 64 because of minimum size allocation in the
+ // DenseMap (see DefaultMinReservedSizeTest). 66 is a value just above the
+ // 64 default init.
+ for (auto Size : {1, 2, 48, 66}) {
+ DenseMap<int, CountCopyAndMove> Map;
+ Map.reserve(Size);
+ unsigned MemorySize = Map.getMemorySize();
+ CountCopyAndMove::Copy = 0;
+ CountCopyAndMove::Move = 0;
+ for (int i = 0; i < Size; ++i)
+ Map.insert(std::pair<int, CountCopyAndMove>(std::piecewise_construct,
+ std::forward_as_tuple(i),
+ std::forward_as_tuple()));
+ // Check that we didn't grow
+ EXPECT_EQ(MemorySize, Map.getMemorySize());
+ // Check that move was called the expected number of times
+ EXPECT_EQ(Size, CountCopyAndMove::Move);
+ // Check that no copy occured
+ EXPECT_EQ(0, CountCopyAndMove::Copy);
+ }
+}
+
// Make sure DenseMap works with StringRef keys.
TEST(DenseMapCustomTest, StringRefTest) {
DenseMap<StringRef, int> M;
@@ -364,6 +496,55 @@ TEST(DenseMapCustomTest, StringRefTest) {
EXPECT_EQ(42, M.lookup(StringRef("a", 0)));
}
+struct CachedHashTest {
+ unsigned Val;
+ unsigned *Counter = nullptr;
+ CachedHashTest(unsigned Val) : Val(Val) {}
+ CachedHashTest(unsigned Val, unsigned *Counter)
+ : Val(Val), Counter(Counter) {}
+};
+}
+namespace llvm {
+template <> struct DenseMapInfo<CachedHashTest> {
+ static CachedHashTest getEmptyKey() { return ~0; }
+ static CachedHashTest getTombstoneKey() { return ~0U - 1; }
+ static unsigned getHashValue(const CachedHashTest &X) {
+ ++*X.Counter;
+ return X.Val;
+ }
+ static bool isEqual(const CachedHashTest &LHS, const CachedHashTest &RHS) {
+ return LHS.Val == RHS.Val;
+ }
+};
+}
+namespace {
+
+TEST(DenseMapCustomTest, CachedHashTest) {
+ unsigned Counter = 0;
+ CachedHashTest Val(0, &Counter);
+ DenseMap<CachedHashTest, int> Map;
+
+ Map[Val] = 0;
+ ASSERT_EQ(1u, Counter);
+
+ Map.reserve(64);
+ ASSERT_EQ(2u, Counter);
+}
+
+// Like above, but now cache the hash.
+TEST(DenseMapCustomTest, CachedHashTest2) {
+ unsigned Counter = 0;
+ CachedHashTest Val(0, &Counter);
+ typedef CachedHash<CachedHashTest> Cached;
+ DenseMap<Cached, int> Map;
+
+ Map[Val] = 0;
+ ASSERT_EQ(1u, Counter);
+
+ Map.reserve(64);
+ ASSERT_EQ(1u, Counter);
+}
+
// Key traits that allows lookup with either an unsigned or char* key;
// In the latter case, "a" == 0, "b" == 1 and so on.
struct TestDenseMapInfo {