|  | //===- llvm/unittest/ADT/ValueMapTest.cpp - ValueMap unit tests -*- C++ -*-===// | 
|  | // | 
|  | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
|  | // See https://llvm.org/LICENSE.txt for license information. | 
|  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "llvm/IR/ValueMap.h" | 
|  | #include "llvm/Config/llvm-config.h" | 
|  | #include "llvm/IR/Constants.h" | 
|  | #include "llvm/IR/Instructions.h" | 
|  | #include "llvm/IR/LLVMContext.h" | 
|  | #include "gtest/gtest.h" | 
|  |  | 
|  | using namespace llvm; | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // Test fixture | 
|  | template<typename T> | 
|  | class ValueMapTest : public testing::Test { | 
|  | protected: | 
|  | LLVMContext Context; | 
|  | Constant *ConstantV; | 
|  | std::unique_ptr<BitCastInst> BitcastV; | 
|  | std::unique_ptr<BinaryOperator> AddV; | 
|  |  | 
|  | ValueMapTest() | 
|  | : ConstantV(ConstantInt::get(Type::getInt32Ty(Context), 0)), | 
|  | BitcastV(new BitCastInst(ConstantV, Type::getInt32Ty(Context))), | 
|  | AddV(BinaryOperator::CreateAdd(ConstantV, ConstantV)) {} | 
|  | }; | 
|  |  | 
|  | // Run everything on Value*, a subtype to make sure that casting works as | 
|  | // expected, and a const subtype to make sure we cast const correctly. | 
|  | typedef ::testing::Types<Value, Instruction, const Instruction> KeyTypes; | 
|  | TYPED_TEST_SUITE(ValueMapTest, KeyTypes, ); | 
|  |  | 
|  | TYPED_TEST(ValueMapTest, Null) { | 
|  | ValueMap<TypeParam*, int> VM1; | 
|  | VM1[nullptr] = 7; | 
|  | EXPECT_EQ(7, VM1.lookup(nullptr)); | 
|  | } | 
|  |  | 
|  | TYPED_TEST(ValueMapTest, FollowsValue) { | 
|  | ValueMap<TypeParam*, int> VM; | 
|  | VM[this->BitcastV.get()] = 7; | 
|  | EXPECT_EQ(7, VM.lookup(this->BitcastV.get())); | 
|  | EXPECT_EQ(0u, VM.count(this->AddV.get())); | 
|  | this->BitcastV->replaceAllUsesWith(this->AddV.get()); | 
|  | EXPECT_EQ(7, VM.lookup(this->AddV.get())); | 
|  | EXPECT_EQ(0u, VM.count(this->BitcastV.get())); | 
|  | this->AddV.reset(); | 
|  | EXPECT_EQ(0u, VM.count(this->AddV.get())); | 
|  | EXPECT_EQ(0u, VM.count(this->BitcastV.get())); | 
|  | EXPECT_EQ(0U, VM.size()); | 
|  | } | 
|  |  | 
|  | TYPED_TEST(ValueMapTest, OperationsWork) { | 
|  | ValueMap<TypeParam*, int> VM; | 
|  | ValueMap<TypeParam*, int> VM2(16);  (void)VM2; | 
|  | typename ValueMapConfig<TypeParam*>::ExtraData Data; | 
|  | ValueMap<TypeParam*, int> VM3(Data, 16);  (void)VM3; | 
|  | EXPECT_TRUE(VM.empty()); | 
|  |  | 
|  | VM[this->BitcastV.get()] = 7; | 
|  |  | 
|  | // Find: | 
|  | typename ValueMap<TypeParam*, int>::iterator I = | 
|  | VM.find(this->BitcastV.get()); | 
|  | ASSERT_TRUE(I != VM.end()); | 
|  | EXPECT_EQ(this->BitcastV.get(), I->first); | 
|  | EXPECT_EQ(7, I->second); | 
|  | EXPECT_TRUE(VM.find(this->AddV.get()) == VM.end()); | 
|  |  | 
|  | // Const find: | 
|  | const ValueMap<TypeParam*, int> &CVM = VM; | 
|  | typename ValueMap<TypeParam*, int>::const_iterator CI = | 
|  | CVM.find(this->BitcastV.get()); | 
|  | ASSERT_TRUE(CI != CVM.end()); | 
|  | EXPECT_EQ(this->BitcastV.get(), CI->first); | 
|  | EXPECT_EQ(7, CI->second); | 
|  | EXPECT_TRUE(CVM.find(this->AddV.get()) == CVM.end()); | 
|  |  | 
|  | // Insert: | 
|  | std::pair<typename ValueMap<TypeParam*, int>::iterator, bool> InsertResult1 = | 
|  | VM.insert(std::make_pair(this->AddV.get(), 3)); | 
|  | EXPECT_EQ(this->AddV.get(), InsertResult1.first->first); | 
|  | EXPECT_EQ(3, InsertResult1.first->second); | 
|  | EXPECT_TRUE(InsertResult1.second); | 
|  | EXPECT_EQ(1u, VM.count(this->AddV.get())); | 
|  | std::pair<typename ValueMap<TypeParam*, int>::iterator, bool> InsertResult2 = | 
|  | VM.insert(std::make_pair(this->AddV.get(), 5)); | 
|  | EXPECT_EQ(this->AddV.get(), InsertResult2.first->first); | 
|  | EXPECT_EQ(3, InsertResult2.first->second); | 
|  | EXPECT_FALSE(InsertResult2.second); | 
|  |  | 
|  | // Erase: | 
|  | VM.erase(InsertResult2.first); | 
|  | EXPECT_EQ(0U, VM.count(this->AddV.get())); | 
|  | EXPECT_EQ(1U, VM.count(this->BitcastV.get())); | 
|  | VM.erase(this->BitcastV.get()); | 
|  | EXPECT_EQ(0U, VM.count(this->BitcastV.get())); | 
|  | EXPECT_EQ(0U, VM.size()); | 
|  |  | 
|  | // Range insert: | 
|  | SmallVector<std::pair<Instruction*, int>, 2> Elems; | 
|  | Elems.push_back(std::make_pair(this->AddV.get(), 1)); | 
|  | Elems.push_back(std::make_pair(this->BitcastV.get(), 2)); | 
|  | VM.insert(Elems.begin(), Elems.end()); | 
|  | EXPECT_EQ(1, VM.lookup(this->AddV.get())); | 
|  | EXPECT_EQ(2, VM.lookup(this->BitcastV.get())); | 
|  | } | 
|  |  | 
|  | template<typename ExpectedType, typename VarType> | 
|  | void CompileAssertHasType(VarType) { | 
|  | static_assert(std::is_same_v<ExpectedType, VarType>, "Not the same type"); | 
|  | } | 
|  |  | 
|  | TYPED_TEST(ValueMapTest, Iteration) { | 
|  | ValueMap<TypeParam*, int> VM; | 
|  | VM[this->BitcastV.get()] = 2; | 
|  | VM[this->AddV.get()] = 3; | 
|  | size_t size = 0; | 
|  | for (typename ValueMap<TypeParam*, int>::iterator I = VM.begin(), E = VM.end(); | 
|  | I != E; ++I) { | 
|  | ++size; | 
|  | std::pair<TypeParam*, int> value = *I; (void)value; | 
|  | CompileAssertHasType<TypeParam*>(I->first); | 
|  | if (I->second == 2) { | 
|  | EXPECT_EQ(this->BitcastV.get(), I->first); | 
|  | I->second = 5; | 
|  | } else if (I->second == 3) { | 
|  | EXPECT_EQ(this->AddV.get(), I->first); | 
|  | I->second = 6; | 
|  | } else { | 
|  | ADD_FAILURE() << "Iterated through an extra value."; | 
|  | } | 
|  | } | 
|  | EXPECT_EQ(2U, size); | 
|  | EXPECT_EQ(5, VM[this->BitcastV.get()]); | 
|  | EXPECT_EQ(6, VM[this->AddV.get()]); | 
|  |  | 
|  | size = 0; | 
|  | // Cast to const ValueMap to avoid a bug in DenseMap's iterators. | 
|  | const ValueMap<TypeParam*, int>& CVM = VM; | 
|  | for (typename ValueMap<TypeParam*, int>::const_iterator I = CVM.begin(), | 
|  | E = CVM.end(); I != E; ++I) { | 
|  | ++size; | 
|  | std::pair<TypeParam*, int> value = *I;  (void)value; | 
|  | CompileAssertHasType<TypeParam*>(I->first); | 
|  | if (I->second == 5) { | 
|  | EXPECT_EQ(this->BitcastV.get(), I->first); | 
|  | } else if (I->second == 6) { | 
|  | EXPECT_EQ(this->AddV.get(), I->first); | 
|  | } else { | 
|  | ADD_FAILURE() << "Iterated through an extra value."; | 
|  | } | 
|  | } | 
|  | EXPECT_EQ(2U, size); | 
|  | } | 
|  |  | 
|  | TYPED_TEST(ValueMapTest, DefaultCollisionBehavior) { | 
|  | // By default, we overwrite the old value with the replaced value. | 
|  | ValueMap<TypeParam*, int> VM; | 
|  | VM[this->BitcastV.get()] = 7; | 
|  | VM[this->AddV.get()] = 9; | 
|  | this->BitcastV->replaceAllUsesWith(this->AddV.get()); | 
|  | EXPECT_EQ(0u, VM.count(this->BitcastV.get())); | 
|  | EXPECT_EQ(9, VM.lookup(this->AddV.get())); | 
|  | } | 
|  |  | 
|  | TYPED_TEST(ValueMapTest, ConfiguredCollisionBehavior) { | 
|  | // TODO: Implement this when someone needs it. | 
|  | } | 
|  |  | 
|  | template<typename KeyT, typename MutexT> | 
|  | struct LockMutex : ValueMapConfig<KeyT, MutexT> { | 
|  | struct ExtraData { | 
|  | MutexT *M; | 
|  | bool *CalledRAUW; | 
|  | bool *CalledDeleted; | 
|  | }; | 
|  | static void onRAUW(const ExtraData &Data, KeyT Old, KeyT New) { | 
|  | *Data.CalledRAUW = true; | 
|  | EXPECT_FALSE(Data.M->try_lock()) << "Mutex should already be locked."; | 
|  | } | 
|  | static void onDelete(const ExtraData &Data, KeyT Old) { | 
|  | *Data.CalledDeleted = true; | 
|  | EXPECT_FALSE(Data.M->try_lock()) << "Mutex should already be locked."; | 
|  | } | 
|  | static MutexT *getMutex(const ExtraData &Data) { return Data.M; } | 
|  | }; | 
|  | // FIXME: These tests started failing on Windows. | 
|  | #if LLVM_ENABLE_THREADS && !defined(_WIN32) | 
|  | TYPED_TEST(ValueMapTest, LocksMutex) { | 
|  | std::mutex M; | 
|  | bool CalledRAUW = false, CalledDeleted = false; | 
|  | typedef LockMutex<TypeParam*, std::mutex> ConfigType; | 
|  | typename ConfigType::ExtraData Data = {&M, &CalledRAUW, &CalledDeleted}; | 
|  | ValueMap<TypeParam*, int, ConfigType> VM(Data); | 
|  | VM[this->BitcastV.get()] = 7; | 
|  | this->BitcastV->replaceAllUsesWith(this->AddV.get()); | 
|  | this->AddV.reset(); | 
|  | EXPECT_TRUE(CalledRAUW); | 
|  | EXPECT_TRUE(CalledDeleted); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | template<typename KeyT> | 
|  | struct NoFollow : ValueMapConfig<KeyT> { | 
|  | enum { FollowRAUW = false }; | 
|  | }; | 
|  |  | 
|  | TYPED_TEST(ValueMapTest, NoFollowRAUW) { | 
|  | ValueMap<TypeParam*, int, NoFollow<TypeParam*> > VM; | 
|  | VM[this->BitcastV.get()] = 7; | 
|  | EXPECT_EQ(7, VM.lookup(this->BitcastV.get())); | 
|  | EXPECT_EQ(0u, VM.count(this->AddV.get())); | 
|  | this->BitcastV->replaceAllUsesWith(this->AddV.get()); | 
|  | EXPECT_EQ(7, VM.lookup(this->BitcastV.get())); | 
|  | EXPECT_EQ(0, VM.lookup(this->AddV.get())); | 
|  | this->AddV.reset(); | 
|  | EXPECT_EQ(7, VM.lookup(this->BitcastV.get())); | 
|  | EXPECT_EQ(0, VM.lookup(this->AddV.get())); | 
|  | this->BitcastV.reset(); | 
|  | EXPECT_EQ(0, VM.lookup(this->BitcastV.get())); | 
|  | EXPECT_EQ(0, VM.lookup(this->AddV.get())); | 
|  | EXPECT_EQ(0U, VM.size()); | 
|  | } | 
|  |  | 
|  | template<typename KeyT> | 
|  | struct CountOps : ValueMapConfig<KeyT> { | 
|  | struct ExtraData { | 
|  | int *Deletions; | 
|  | int *RAUWs; | 
|  | }; | 
|  |  | 
|  | static void onRAUW(const ExtraData &Data, KeyT Old, KeyT New) { | 
|  | ++*Data.RAUWs; | 
|  | } | 
|  | static void onDelete(const ExtraData &Data, KeyT Old) { | 
|  | ++*Data.Deletions; | 
|  | } | 
|  | }; | 
|  |  | 
|  | TYPED_TEST(ValueMapTest, CallsConfig) { | 
|  | int Deletions = 0, RAUWs = 0; | 
|  | typename CountOps<TypeParam*>::ExtraData Data = {&Deletions, &RAUWs}; | 
|  | ValueMap<TypeParam*, int, CountOps<TypeParam*> > VM(Data); | 
|  | VM[this->BitcastV.get()] = 7; | 
|  | this->BitcastV->replaceAllUsesWith(this->AddV.get()); | 
|  | EXPECT_EQ(0, Deletions); | 
|  | EXPECT_EQ(1, RAUWs); | 
|  | this->AddV.reset(); | 
|  | EXPECT_EQ(1, Deletions); | 
|  | EXPECT_EQ(1, RAUWs); | 
|  | this->BitcastV.reset(); | 
|  | EXPECT_EQ(1, Deletions); | 
|  | EXPECT_EQ(1, RAUWs); | 
|  | } | 
|  |  | 
|  | template<typename KeyT> | 
|  | struct ModifyingConfig : ValueMapConfig<KeyT> { | 
|  | // We'll put a pointer here back to the ValueMap this key is in, so | 
|  | // that we can modify it (and clobber *this) before the ValueMap | 
|  | // tries to do the same modification.  In previous versions of | 
|  | // ValueMap, that exploded. | 
|  | typedef ValueMap<KeyT, int, ModifyingConfig<KeyT> > **ExtraData; | 
|  |  | 
|  | static void onRAUW(ExtraData Map, KeyT Old, KeyT New) { | 
|  | (*Map)->erase(Old); | 
|  | } | 
|  | static void onDelete(ExtraData Map, KeyT Old) { | 
|  | (*Map)->erase(Old); | 
|  | } | 
|  | }; | 
|  | TYPED_TEST(ValueMapTest, SurvivesModificationByConfig) { | 
|  | ValueMap<TypeParam*, int, ModifyingConfig<TypeParam*> > *MapAddress; | 
|  | ValueMap<TypeParam*, int, ModifyingConfig<TypeParam*> > VM(&MapAddress); | 
|  | MapAddress = &VM; | 
|  | // Now the ModifyingConfig can modify the Map inside a callback. | 
|  | VM[this->BitcastV.get()] = 7; | 
|  | this->BitcastV->replaceAllUsesWith(this->AddV.get()); | 
|  | EXPECT_EQ(0u, VM.count(this->BitcastV.get())); | 
|  | EXPECT_EQ(0u, VM.count(this->AddV.get())); | 
|  | VM[this->AddV.get()] = 7; | 
|  | this->AddV.reset(); | 
|  | EXPECT_EQ(0u, VM.count(this->AddV.get())); | 
|  | } | 
|  |  | 
|  | } // end namespace |