From 71a5c22c2b635ea903c3ae7d2695ca649b38726f Mon Sep 17 00:00:00 2001 From: Jeffrey Yasskin Date: Thu, 22 Oct 2009 22:11:22 +0000 Subject: [PATCH] Try r84890 again (adding ValueMap<>), now that I've tested the compile on gcc-4.4. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@84902 91177308-0d34-0410-b5e6-96231b3b80d8 --- docs/ProgrammersManual.html | 18 ++ include/llvm/ADT/ValueMap.h | 365 +++++++++++++++++++++++++++++++++++++ include/llvm/Support/type_traits.h | 9 + unittests/ADT/ValueMapTest.cpp | 291 +++++++++++++++++++++++++++++ 4 files changed, 683 insertions(+) create mode 100644 include/llvm/ADT/ValueMap.h create mode 100644 unittests/ADT/ValueMapTest.cpp diff --git a/docs/ProgrammersManual.html b/docs/ProgrammersManual.html index 68367de0bff..e4e3dc29c30 100644 --- a/docs/ProgrammersManual.html +++ b/docs/ProgrammersManual.html @@ -83,6 +83,7 @@ option
  • "llvm/ADT/StringMap.h"
  • "llvm/ADT/IndexedMap.h"
  • "llvm/ADT/DenseMap.h"
  • +
  • "llvm/ADT/ValueMap.h"
  • <map>
  • Other Map-Like Container Options
  • @@ -1492,6 +1493,23 @@ inserted into the map) that it needs internally.

    + "llvm/ADT/ValueMap.h" +
    + +
    + +

    +ValueMap is a wrapper around a DenseMap mapping +Value*s (or subclasses) to another type. When a Value is deleted or RAUW'ed, +ValueMap will update itself so the new version of the key is mapped to the same +value, just as if the key were a WeakVH. You can configure exactly how this +happens, and what else happens on these two events, by passing +a Config parameter to the ValueMap template.

    + +
    + + +
    <map>
    diff --git a/include/llvm/ADT/ValueMap.h b/include/llvm/ADT/ValueMap.h new file mode 100644 index 00000000000..14f21001df7 --- /dev/null +++ b/include/llvm/ADT/ValueMap.h @@ -0,0 +1,365 @@ +//===- llvm/ADT/ValueMap.h - Safe map from Values to data -------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the ValueMap class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_ADT_VALUEMAP_H +#define LLVM_ADT_VALUEMAP_H + +#include "llvm/ADT/DenseMap.h" +#include "llvm/Support/ValueHandle.h" +#include "llvm/Support/type_traits.h" +#include "llvm/System/Mutex.h" + +#include + +namespace llvm { + +template +class ValueMapCallbackVH; + +template +class ValueMapIterator; +template +class ValueMapConstIterator; + +template +struct ValueMapConfig { + /// If FollowRAUW is true, the ValueMap will update mappings on RAUW. If it's + /// false, the ValueMap will leave the original mapping in place. + enum { FollowRAUW = true }; + + // All methods will be called with a first argument of type ExtraData. The + // default implementations in this class take a templated first argument so + // that users' subclasses can use any type they want without having to + // override all the defaults. + struct ExtraData {}; + + template + static void onRAUW(const ExtraDataT &Data, KeyT Old, KeyT New) {} + template + static void onDeleted(const ExtraDataT &Data, KeyT Old) {} + + /// Returns a mutex that should be acquired around any changes to the map. + /// This is only acquired from the CallbackVH (and held around calls to onRAUW + /// and onDeleted) and not inside other ValueMap methods. NULL means that no + /// mutex is necessary. + template + static sys::Mutex *getMutex(const ExtraDataT &Data) { return NULL; } +}; + +/// ValueMap maps Value* or any subclass to an arbitrary other +/// type. It provides the DenseMap interface. When the key values are +/// deleted or RAUWed, ValueMap relies on the Config to decide what to +/// do. Config parameters should inherit from ValueMapConfig to +/// get default implementations of all the methods ValueMap uses. +/// +/// By default, when a key is RAUWed from V1 to V2, the old mapping +/// V1->target is removed, and a new mapping V2->target is added. If +/// V2 already existed, its old target is overwritten. When a key is +/// deleted, its mapping is removed. You can override Config to get +/// called back on each event. +template, + typename ValueInfoT = DenseMapInfo > +class ValueMap { + friend class ValueMapCallbackVH; + typedef ValueMapCallbackVH ValueMapCVH; + typedef DenseMap, + ValueInfoT> MapT; + typedef typename Config::ExtraData ExtraData; + MapT Map; + ExtraData Data; +public: + typedef KeyT key_type; + typedef ValueT mapped_type; + typedef std::pair value_type; + + ValueMap(const ValueMap& Other) : Map(Other.Map), Data(Other.Data) {} + + explicit ValueMap(unsigned NumInitBuckets = 64) + : Map(NumInitBuckets), Data() {} + explicit ValueMap(const ExtraData &Data, unsigned NumInitBuckets = 64) + : Map(NumInitBuckets), Data(Data) {} + + ~ValueMap() {} + + typedef ValueMapIterator iterator; + typedef ValueMapConstIterator const_iterator; + inline iterator begin() { return iterator(Map.begin()); } + inline iterator end() { return iterator(Map.end()); } + inline const_iterator begin() const { return const_iterator(Map.begin()); } + inline const_iterator end() const { return const_iterator(Map.end()); } + + bool empty() const { return Map.empty(); } + unsigned size() const { return Map.size(); } + + /// Grow the map so that it has at least Size buckets. Does not shrink + void resize(size_t Size) { Map.resize(Size); } + + void clear() { Map.clear(); } + + /// count - Return true if the specified key is in the map. + bool count(const KeyT &Val) const { + return Map.count(Wrap(Val)); + } + + iterator find(const KeyT &Val) { + return iterator(Map.find(Wrap(Val))); + } + const_iterator find(const KeyT &Val) const { + return const_iterator(Map.find(Wrap(Val))); + } + + /// lookup - Return the entry for the specified key, or a default + /// constructed value if no such entry exists. + ValueT lookup(const KeyT &Val) const { + return Map.lookup(Wrap(Val)); + } + + // Inserts key,value pair into the map if the key isn't already in the map. + // If the key is already in the map, it returns false and doesn't update the + // value. + std::pair insert(const std::pair &KV) { + std::pair map_result= + Map.insert(std::make_pair(Wrap(KV.first), KV.second)); + return std::make_pair(iterator(map_result.first), map_result.second); + } + + /// insert - Range insertion of pairs. + template + void insert(InputIt I, InputIt E) { + for (; I != E; ++I) + insert(*I); + } + + + bool erase(const KeyT &Val) { + return Map.erase(Wrap(Val)); + } + bool erase(iterator I) { + return Map.erase(I.base()); + } + + value_type& FindAndConstruct(const KeyT &Key) { + return Map.FindAndConstruct(Wrap(Key)); + } + + ValueT &operator[](const KeyT &Key) { + return Map[Wrap(Key)]; + } + + ValueMap& operator=(const ValueMap& Other) { + Map = Other.Map; + Data = Other.Data; + return *this; + } + + /// isPointerIntoBucketsArray - Return true if the specified pointer points + /// somewhere into the ValueMap's array of buckets (i.e. either to a key or + /// value in the ValueMap). + bool isPointerIntoBucketsArray(const void *Ptr) const { + return Map.isPointerIntoBucketsArray(Ptr); + } + + /// getPointerIntoBucketsArray() - Return an opaque pointer into the buckets + /// array. In conjunction with the previous method, this can be used to + /// determine whether an insertion caused the ValueMap to reallocate. + const void *getPointerIntoBucketsArray() const { + return Map.getPointerIntoBucketsArray(); + } + +private: + ValueMapCVH Wrap(KeyT key) const { + // The only way the resulting CallbackVH could try to modify *this (making + // the const_cast incorrect) is if it gets inserted into the map. But then + // this function must have been called from a non-const method, making the + // const_cast ok. + return ValueMapCVH(key, const_cast(this)); + } +}; + +template +class ValueMapCallbackVH : public CallbackVH { + friend class ValueMap; + friend class DenseMapInfo; + typedef ValueMap ValueMapT; + typedef typename llvm::remove_pointer::type KeySansPointerT; + + ValueMapT *Map; + + ValueMapCallbackVH(KeyT Key, ValueMapT *Map) + : CallbackVH(const_cast(static_cast(Key))), + Map(Map) {} + +public: + KeyT Unwrap() const { return cast_or_null(getValPtr()); } + + virtual void deleted() { + // Make a copy that won't get changed even when *this is destroyed. + ValueMapCallbackVH Copy(*this); + sys::Mutex *M = Config::getMutex(Copy.Map->Data); + if (M) + M->acquire(); + Config::onDeleted(Copy.Map->Data, Copy.Unwrap()); // May destroy *this. + Copy.Map->Map.erase(Copy); // Definitely destroys *this. + if (M) + M->release(); + } + virtual void allUsesReplacedWith(Value *new_key) { + assert(isa(new_key) && + "Invalid RAUW on key of ValueMap<>"); + // Make a copy that won't get changed even when *this is destroyed. + ValueMapCallbackVH Copy(*this); + sys::Mutex *M = Config::getMutex(Copy.Map->Data); + if (M) + M->acquire(); + + KeyT typed_new_key = cast(new_key); + // Can destroy *this: + Config::onRAUW(Copy.Map->Data, Copy.Unwrap(), typed_new_key); + if (Config::FollowRAUW) { + typename ValueMapT::MapT::iterator I = Copy.Map->Map.find(Copy); + // I could == Copy.Map->Map.end() if the onRAUW callback already + // removed the old mapping. + if (I != Copy.Map->Map.end()) { + ValueT Target(I->second); + Copy.Map->Map.erase(I); // Definitely destroys *this. + Copy.Map->insert(std::make_pair(typed_new_key, Target)); + } + } + if (M) + M->release(); + } +}; + +template +struct DenseMapInfo > { + typedef ValueMapCallbackVH VH; + typedef DenseMapInfo PointerInfo; + + static inline VH getEmptyKey() { + return VH(PointerInfo::getEmptyKey(), NULL); + } + static inline VH getTombstoneKey() { + return VH(PointerInfo::getTombstoneKey(), NULL); + } + static unsigned getHashValue(const VH &Val) { + return PointerInfo::getHashValue(Val.Unwrap()); + } + static bool isEqual(const VH &LHS, const VH &RHS) { + return LHS == RHS; + } + static bool isPod() { return false; } +}; + + +template +class ValueMapIterator : + public std::iterator, + ptrdiff_t> { + typedef typename DenseMapT::iterator BaseT; + typedef typename DenseMapT::mapped_type ValueT; + BaseT I; +public: + ValueMapIterator() : I() {} + + ValueMapIterator(BaseT I) : I(I) {} + + BaseT base() const { return I; } + + struct ValueTypeProxy { + const KeyT first; + ValueT& second; + ValueTypeProxy *operator->() { return this; } + operator std::pair() const { + return std::make_pair(first, second); + } + }; + + ValueTypeProxy operator*() const { + ValueTypeProxy Result = {I->first.Unwrap(), I->second}; + return Result; + } + + ValueTypeProxy operator->() const { + return operator*(); + } + + bool operator==(const ValueMapIterator &RHS) const { + return I == RHS.I; + } + bool operator!=(const ValueMapIterator &RHS) const { + return I != RHS.I; + } + + inline ValueMapIterator& operator++() { // Preincrement + ++I; + return *this; + } + ValueMapIterator operator++(int) { // Postincrement + ValueMapIterator tmp = *this; ++*this; return tmp; + } +}; + +template +class ValueMapConstIterator : + public std::iterator, + ptrdiff_t> { + typedef typename DenseMapT::const_iterator BaseT; + typedef typename DenseMapT::mapped_type ValueT; + BaseT I; +public: + ValueMapConstIterator() : I() {} + ValueMapConstIterator(BaseT I) : I(I) {} + ValueMapConstIterator(ValueMapIterator Other) + : I(Other.base()) {} + + BaseT base() const { return I; } + + struct ValueTypeProxy { + const KeyT first; + const ValueT& second; + ValueTypeProxy *operator->() { return this; } + operator std::pair() const { + return std::make_pair(first, second); + } + }; + + ValueTypeProxy operator*() const { + ValueTypeProxy Result = {I->first.Unwrap(), I->second}; + return Result; + } + + ValueTypeProxy operator->() const { + return operator*(); + } + + bool operator==(const ValueMapConstIterator &RHS) const { + return I == RHS.I; + } + bool operator!=(const ValueMapConstIterator &RHS) const { + return I != RHS.I; + } + + inline ValueMapConstIterator& operator++() { // Preincrement + ++I; + return *this; + } + ValueMapConstIterator operator++(int) { // Postincrement + ValueMapConstIterator tmp = *this; ++*this; return tmp; + } +}; + +} // end namespace llvm + +#endif diff --git a/include/llvm/Support/type_traits.h b/include/llvm/Support/type_traits.h index 5f799b850de..cfaae4b8419 100644 --- a/include/llvm/Support/type_traits.h +++ b/include/llvm/Support/type_traits.h @@ -87,6 +87,15 @@ struct is_base_of { sizeof(char) == sizeof(dont_use::base_of_helper((Derived*)0)); }; +// remove_pointer - Metafunction to turn Foo* into Foo. Defined in +// C++0x [meta.trans.ptr]. +template struct remove_pointer { typedef T type; }; +template struct remove_pointer { typedef T type; }; +template struct remove_pointer { typedef T type; }; +template struct remove_pointer { typedef T type; }; +template struct remove_pointer { + typedef T type; }; + } #endif diff --git a/unittests/ADT/ValueMapTest.cpp b/unittests/ADT/ValueMapTest.cpp new file mode 100644 index 00000000000..9de340c3f35 --- /dev/null +++ b/unittests/ADT/ValueMapTest.cpp @@ -0,0 +1,291 @@ +//===- llvm/unittest/ADT/ValueMapTest.cpp - ValueMap unit tests -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/ValueMap.h" + +#include "llvm/Instructions.h" +#include "llvm/ADT/OwningPtr.h" + +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { + +// Test fixture +template +class ValueMapTest : public testing::Test { +protected: + Constant *ConstantV; + OwningPtr BitcastV; + OwningPtr AddV; + + ValueMapTest() : + ConstantV(ConstantInt::get(Type::getInt32Ty(getGlobalContext()), 0)), + BitcastV(new BitCastInst(ConstantV, Type::getInt32Ty(getGlobalContext()))), + 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 KeyTypes; +TYPED_TEST_CASE(ValueMapTest, KeyTypes); + +TYPED_TEST(ValueMapTest, Null) { + ValueMap VM1; + VM1[NULL] = 7; + EXPECT_EQ(7, VM1.lookup(NULL)); +} + +TYPED_TEST(ValueMapTest, FollowsValue) { + ValueMap VM; + VM[this->BitcastV.get()] = 7; + EXPECT_EQ(7, VM.lookup(this->BitcastV.get())); + EXPECT_EQ(0, VM.count(this->AddV.get())); + this->BitcastV->replaceAllUsesWith(this->AddV.get()); + EXPECT_EQ(7, VM.lookup(this->AddV.get())); + EXPECT_EQ(0, VM.count(this->BitcastV.get())); + this->AddV.reset(); + EXPECT_EQ(0, VM.count(this->AddV.get())); + EXPECT_EQ(0, VM.count(this->BitcastV.get())); + EXPECT_EQ(0U, VM.size()); +} + +TYPED_TEST(ValueMapTest, OperationsWork) { + ValueMap VM; + ValueMap VM2(16); + typename ValueMapConfig::ExtraData Data; + ValueMap VM3(Data, 16); + EXPECT_TRUE(VM.empty()); + + VM[this->BitcastV.get()] = 7; + + // Find: + typename ValueMap::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 &CVM = VM; + typename ValueMap::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::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(true, VM.count(this->AddV.get())); + std::pair::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(false, VM.count(this->AddV.get())); + EXPECT_EQ(true, VM.count(this->BitcastV.get())); + VM.erase(this->BitcastV.get()); + EXPECT_EQ(false, VM.count(this->BitcastV.get())); + EXPECT_EQ(0U, VM.size()); + + // Range insert: + SmallVector, 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 +void CompileAssertHasType(VarType) { + typedef char assert[is_same::value ? 1 : -1]; +} + +TYPED_TEST(ValueMapTest, Iteration) { + ValueMap VM; + VM[this->BitcastV.get()] = 2; + VM[this->AddV.get()] = 3; + size_t size = 0; + for (typename ValueMap::iterator I = VM.begin(), E = VM.end(); + I != E; ++I) { + ++size; + std::pair value = *I; + CompileAssertHasType(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& CVM = VM; + for (typename ValueMap::const_iterator I = CVM.begin(), + E = CVM.end(); I != E; ++I) { + ++size; + std::pair value = *I; + CompileAssertHasType(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 VM; + VM[this->BitcastV.get()] = 7; + VM[this->AddV.get()] = 9; + this->BitcastV->replaceAllUsesWith(this->AddV.get()); + EXPECT_EQ(0, 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 +struct LockMutex : ValueMapConfig { + struct ExtraData { + sys::Mutex *M; + bool *CalledRAUW; + bool *CalledDeleted; + }; + static void onRAUW(const ExtraData &Data, KeyT Old, KeyT New) { + *Data.CalledRAUW = true; + EXPECT_FALSE(Data.M->tryacquire()) << "Mutex should already be locked."; + } + static void onDeleted(const ExtraData &Data, KeyT Old) { + *Data.CalledDeleted = true; + EXPECT_FALSE(Data.M->tryacquire()) << "Mutex should already be locked."; + } + static sys::Mutex *getMutex(const ExtraData &Data) { return Data.M; } +}; +TYPED_TEST(ValueMapTest, LocksMutex) { + sys::Mutex M(false); // Not recursive. + bool CalledRAUW = false, CalledDeleted = false; + typename LockMutex::ExtraData Data = + {&M, &CalledRAUW, &CalledDeleted}; + ValueMap > VM(Data); + VM[this->BitcastV.get()] = 7; + this->BitcastV->replaceAllUsesWith(this->AddV.get()); + this->AddV.reset(); + EXPECT_TRUE(CalledRAUW); + EXPECT_TRUE(CalledDeleted); +} + +template +struct NoFollow : ValueMapConfig { + enum { FollowRAUW = false }; +}; + +TYPED_TEST(ValueMapTest, NoFollowRAUW) { + ValueMap > VM; + VM[this->BitcastV.get()] = 7; + EXPECT_EQ(7, VM.lookup(this->BitcastV.get())); + EXPECT_EQ(0, 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 +struct CountOps : ValueMapConfig { + struct ExtraData { + int *Deletions; + int *RAUWs; + }; + + static void onRAUW(const ExtraData &Data, KeyT Old, KeyT New) { + ++*Data.RAUWs; + } + static void onDeleted(const ExtraData &Data, KeyT Old) { + ++*Data.Deletions; + } +}; + +TYPED_TEST(ValueMapTest, CallsConfig) { + int Deletions = 0, RAUWs = 0; + typename CountOps::ExtraData Data = {&Deletions, &RAUWs}; + ValueMap > 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 +struct ModifyingConfig : ValueMapConfig { + // 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 > **ExtraData; + + static void onRAUW(ExtraData Map, KeyT Old, KeyT New) { + (*Map)->erase(Old); + } + static void onDeleted(ExtraData Map, KeyT Old) { + (*Map)->erase(Old); + } +}; +TYPED_TEST(ValueMapTest, SurvivesModificationByConfig) { + ValueMap > *MapAddress; + ValueMap > 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_FALSE(VM.count(this->BitcastV.get())); + EXPECT_FALSE(VM.count(this->AddV.get())); + VM[this->AddV.get()] = 7; + this->AddV.reset(); + EXPECT_FALSE(VM.count(this->AddV.get())); +} + +} -- 2.11.0