diff options
author | Jeffrey Yasskin <jyasskin@google.com> | 2009-10-22 20:10:20 +0000 |
---|---|---|
committer | Jeffrey Yasskin <jyasskin@google.com> | 2009-10-22 20:10:20 +0000 |
commit | e0a234029b1b65f21169536f4c45e83743ed3cfa (patch) | |
tree | 47c339af1f2fbc95bfa69c64488190662f759a32 | |
parent | 9d89df169027035bf692b3d397bb93cff7bdc860 (diff) |
Add a ValueMap<ValueOrSubclass*, T> type. ValueMap<Value*, T> is safe to use
even when keys get RAUWed and deleted during its lifetime. By default the keys
act like WeakVHs, but users can pass a third template parameter to configure
how updates work and whether to do anything beyond updating the map on each
action.
It's also possible to automatically acquire a lock around ValueMap updates
triggered by RAUWs and deletes, to support the ExecutionEngine.
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@84890 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | docs/ProgrammersManual.html | 18 | ||||
-rw-r--r-- | include/llvm/ADT/ValueMap.h | 365 | ||||
-rw-r--r-- | include/llvm/Support/type_traits.h | 9 | ||||
-rw-r--r-- | unittests/ADT/ValueMapTest.cpp | 291 |
4 files changed, 683 insertions, 0 deletions
diff --git a/docs/ProgrammersManual.html b/docs/ProgrammersManual.html index 68367de0bf..e4e3dc29c3 100644 --- a/docs/ProgrammersManual.html +++ b/docs/ProgrammersManual.html @@ -83,6 +83,7 @@ option</a></li> <li><a href="#dss_stringmap">"llvm/ADT/StringMap.h"</a></li> <li><a href="#dss_indexedmap">"llvm/ADT/IndexedMap.h"</a></li> <li><a href="#dss_densemap">"llvm/ADT/DenseMap.h"</a></li> + <li><a href="#dss_valuemap">"llvm/ADT/ValueMap.h"</a></li> <li><a href="#dss_map"><map></a></li> <li><a href="#dss_othermap">Other Map-Like Container Options</a></li> </ul></li> @@ -1492,6 +1493,23 @@ inserted into the map) that it needs internally.</p> <!-- _______________________________________________________________________ --> <div class="doc_subsubsection"> + <a name="dss_valuemap">"llvm/ADT/ValueMap.h"</a> +</div> + +<div class="doc_text"> + +<p> +ValueMap is a wrapper around a <a href="#dss_densemap">DenseMap</a> 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 <code>Config</code> parameter to the ValueMap template.</p> + +</div> + +<!-- _______________________________________________________________________ --> +<div class="doc_subsubsection"> <a name="dss_map"><map></a> </div> diff --git a/include/llvm/ADT/ValueMap.h b/include/llvm/ADT/ValueMap.h new file mode 100644 index 0000000000..a63671b5e7 --- /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 <iterator> + +namespace llvm { + +template<typename KeyT, typename ValueT, typename Config, typename ValueInfoT> +class ValueMapCallbackVH; + +template<typename DenseMapT, typename KeyT> +class ValueMapIterator; +template<typename DenseMapT, typename KeyT> +class ValueMapConstIterator; + +template<typename KeyT> +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<typename ExtraDataT> + static void onRAUW(const ExtraDataT &Data, KeyT Old, KeyT New) {} + template<typename ExtraDataT> + 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<typename ExtraDataT> + 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<KeyT> 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 KeyT, typename ValueT, typename Config = ValueMapConfig<KeyT>, + typename ValueInfoT = DenseMapInfo<ValueT> > +class ValueMap { + friend class ValueMapCallbackVH<KeyT, ValueT, Config, ValueInfoT>; + typedef ValueMapCallbackVH<KeyT, ValueT, Config, ValueInfoT> ValueMapCVH; + typedef DenseMap<ValueMapCVH, ValueT, DenseMapInfo<ValueMapCVH>, + ValueInfoT> MapT; + typedef typename Config::ExtraData ExtraData; + MapT Map; + ExtraData Data; +public: + typedef KeyT key_type; + typedef ValueT mapped_type; + typedef std::pair<KeyT, ValueT> 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<MapT, KeyT> iterator; + typedef ValueMapConstIterator<MapT, KeyT> 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<iterator, bool> insert(const std::pair<KeyT, ValueT> &KV) { + std::pair<typename MapT::iterator, bool> 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<typename InputIt> + 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<ValueMap*>(this)); + } +}; + +template<typename KeyT, typename ValueT, typename Config, typename ValueInfoT> +class ValueMapCallbackVH : public CallbackVH { + friend class ValueMap<KeyT, ValueT, Config, ValueInfoT>; + friend class DenseMapInfo<ValueMapCallbackVH>; + typedef ValueMap<KeyT, ValueT, Config, ValueInfoT> ValueMap; + typedef typename llvm::remove_pointer<KeyT>::type KeySansPointerT; + + ValueMap *Map; + + ValueMapCallbackVH(KeyT Key, ValueMap *Map) + : CallbackVH(const_cast<Value*>(static_cast<const Value*>(Key))), + Map(Map) {} + +public: + KeyT Unwrap() const { return cast_or_null<KeySansPointerT>(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<KeySansPointerT>(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<KeySansPointerT>(new_key); + // Can destroy *this: + Config::onRAUW(Copy.Map->Data, Copy.Unwrap(), typed_new_key); + if (Config::FollowRAUW) { + typename ValueMap::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<typename KeyT, typename ValueT, typename Config, typename ValueInfoT> +struct DenseMapInfo<ValueMapCallbackVH<KeyT, ValueT, Config, ValueInfoT> > { + typedef ValueMapCallbackVH<KeyT, ValueT, Config, ValueInfoT> VH; + typedef DenseMapInfo<KeyT> 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<typename DenseMapT, typename KeyT> +class ValueMapIterator : + public std::iterator<std::forward_iterator_tag, + std::pair<KeyT, typename DenseMapT::mapped_type>, + 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<KeyT, ValueT>() 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<typename DenseMapT, typename KeyT> +class ValueMapConstIterator : + public std::iterator<std::forward_iterator_tag, + std::pair<KeyT, typename DenseMapT::mapped_type>, + 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<DenseMapT, KeyT> Other) + : I(Other.base()) {} + + BaseT base() const { return I; } + + struct ValueTypeProxy { + const KeyT first; + const ValueT& second; + ValueTypeProxy *operator->() { return this; } + operator std::pair<KeyT, ValueT>() 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 5f799b850d..cfaae4b841 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<Base>((Derived*)0)); }; +// remove_pointer - Metafunction to turn Foo* into Foo. Defined in +// C++0x [meta.trans.ptr]. +template <typename T> struct remove_pointer { typedef T type; }; +template <typename T> struct remove_pointer<T*> { typedef T type; }; +template <typename T> struct remove_pointer<T*const> { typedef T type; }; +template <typename T> struct remove_pointer<T*volatile> { typedef T type; }; +template <typename T> struct remove_pointer<T*const volatile> { + typedef T type; }; + } #endif diff --git a/unittests/ADT/ValueMapTest.cpp b/unittests/ADT/ValueMapTest.cpp new file mode 100644 index 0000000000..9de340c3f3 --- /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<typename T> +class ValueMapTest : public testing::Test { +protected: + Constant *ConstantV; + OwningPtr<BitCastInst> BitcastV; + OwningPtr<BinaryOperator> 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<Value, Instruction, const Instruction> KeyTypes; +TYPED_TEST_CASE(ValueMapTest, KeyTypes); + +TYPED_TEST(ValueMapTest, Null) { + ValueMap<TypeParam*, int> VM1; + VM1[NULL] = 7; + EXPECT_EQ(7, VM1.lookup(NULL)); +} + +TYPED_TEST(ValueMapTest, FollowsValue) { + ValueMap<TypeParam*, int> 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<TypeParam*, int> VM; + ValueMap<TypeParam*, int> VM2(16); + typename ValueMapConfig<TypeParam*>::ExtraData Data; + ValueMap<TypeParam*, int> VM3(Data, 16); + 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(true, 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(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<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) { + typedef char assert[is_same<ExpectedType, VarType>::value ? 1 : -1]; +} + +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; + 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; + 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(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<typename KeyT> +struct LockMutex : ValueMapConfig<KeyT> { + 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<TypeParam*>::ExtraData Data = + {&M, &CalledRAUW, &CalledDeleted}; + ValueMap<TypeParam*, int, LockMutex<TypeParam*> > VM(Data); + VM[this->BitcastV.get()] = 7; + this->BitcastV->replaceAllUsesWith(this->AddV.get()); + this->AddV.reset(); + EXPECT_TRUE(CalledRAUW); + EXPECT_TRUE(CalledDeleted); +} + +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(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<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 onDeleted(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 onDeleted(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_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())); +} + +} |