github.com/johnnyeven/libtools@v0.0.0-20191126065708-61829c1adf46/third_party/mlir/lib/Support/StorageUniquer.cpp (about) 1 //===- StorageUniquer.cpp - Common Storage Class Uniquer ------------------===// 2 // 3 // Copyright 2019 The MLIR Authors. 4 // 5 // Licensed under the Apache License, Version 2.0 (the "License"); 6 // you may not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, software 12 // distributed under the License is distributed on an "AS IS" BASIS, 13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 // See the License for the specific language governing permissions and 15 // limitations under the License. 16 // ============================================================================= 17 18 #include "mlir/Support/StorageUniquer.h" 19 #include "mlir/Support/LLVM.h" 20 #include "llvm/ADT/DenseMap.h" 21 #include "llvm/Support/Allocator.h" 22 #include "llvm/Support/RWMutex.h" 23 24 using namespace mlir; 25 using namespace mlir::detail; 26 27 namespace mlir { 28 namespace detail { 29 /// This is the implementation of the StorageUniquer class. 30 struct StorageUniquerImpl { 31 using BaseStorage = StorageUniquer::BaseStorage; 32 using StorageAllocator = StorageUniquer::StorageAllocator; 33 34 /// A lookup key for derived instances of storage objects. 35 struct LookupKey { 36 /// The known derived kind for the storage. 37 unsigned kind; 38 39 /// The known hash value of the key. 40 unsigned hashValue; 41 42 /// An equality function for comparing with an existing storage instance. 43 llvm::function_ref<bool(const BaseStorage *)> isEqual; 44 }; 45 46 /// A utility wrapper object representing a hashed storage object. This class 47 /// contains a storage object and an existing computed hash value. 48 struct HashedStorage { 49 unsigned hashValue; 50 BaseStorage *storage; 51 }; 52 53 /// Get or create an instance of a complex derived type. 54 BaseStorage * 55 getOrCreate(unsigned kind, unsigned hashValue, 56 llvm::function_ref<bool(const BaseStorage *)> isEqual, 57 llvm::function_ref<BaseStorage *(StorageAllocator &)> ctorFn) { 58 LookupKey lookupKey{kind, hashValue, isEqual}; 59 60 // Check for an existing instance in read-only mode. 61 { 62 llvm::sys::SmartScopedReader<true> typeLock(mutex); 63 auto it = storageTypes.find_as(lookupKey); 64 if (it != storageTypes.end()) 65 return it->storage; 66 } 67 68 // Acquire a writer-lock so that we can safely create the new type instance. 69 llvm::sys::SmartScopedWriter<true> typeLock(mutex); 70 71 // Check for an existing instance again here, because another writer thread 72 // may have already created one. 73 auto existing = storageTypes.insert_as({}, lookupKey); 74 if (!existing.second) 75 return existing.first->storage; 76 77 // Otherwise, construct and initialize the derived storage for this type 78 // instance. 79 BaseStorage *storage = initializeStorage(kind, ctorFn); 80 *existing.first = HashedStorage{hashValue, storage}; 81 return storage; 82 } 83 84 /// Get or create an instance of a simple derived type. 85 BaseStorage * 86 getOrCreate(unsigned kind, 87 llvm::function_ref<BaseStorage *(StorageAllocator &)> ctorFn) { 88 // Check for an existing instance in read-only mode. 89 { 90 llvm::sys::SmartScopedReader<true> typeLock(mutex); 91 auto it = simpleTypes.find(kind); 92 if (it != simpleTypes.end()) 93 return it->second; 94 } 95 96 // Acquire a writer-lock so that we can safely create the new type instance. 97 llvm::sys::SmartScopedWriter<true> typeLock(mutex); 98 99 // Check for an existing instance again here, because another writer thread 100 // may have already created one. 101 auto &result = simpleTypes[kind]; 102 if (result) 103 return result; 104 105 // Otherwise, create and return a new storage instance. 106 return result = initializeStorage(kind, ctorFn); 107 } 108 109 /// Erase an instance of a complex derived type. 110 void erase(unsigned kind, unsigned hashValue, 111 llvm::function_ref<bool(const BaseStorage *)> isEqual, 112 llvm::function_ref<void(BaseStorage *)> cleanupFn) { 113 LookupKey lookupKey{kind, hashValue, isEqual}; 114 115 // Acquire a writer-lock so that we can safely erase the type instance. 116 llvm::sys::SmartScopedWriter<true> typeLock(mutex); 117 auto existing = storageTypes.find_as(lookupKey); 118 if (existing == storageTypes.end()) 119 return; 120 121 // Cleanup the storage and remove it from the map. 122 cleanupFn(existing->storage); 123 storageTypes.erase(existing); 124 } 125 126 //===--------------------------------------------------------------------===// 127 // Instance Storage 128 //===--------------------------------------------------------------------===// 129 130 /// Utility to create and initialize a storage instance. 131 BaseStorage *initializeStorage( 132 unsigned kind, 133 llvm::function_ref<BaseStorage *(StorageAllocator &)> ctorFn) { 134 BaseStorage *storage = ctorFn(allocator); 135 storage->kind = kind; 136 return storage; 137 } 138 139 /// Storage info for derived TypeStorage objects. 140 struct StorageKeyInfo : DenseMapInfo<HashedStorage> { 141 static HashedStorage getEmptyKey() { 142 return HashedStorage{0, DenseMapInfo<BaseStorage *>::getEmptyKey()}; 143 } 144 static HashedStorage getTombstoneKey() { 145 return HashedStorage{0, DenseMapInfo<BaseStorage *>::getTombstoneKey()}; 146 } 147 148 static unsigned getHashValue(const HashedStorage &key) { 149 return key.hashValue; 150 } 151 static unsigned getHashValue(LookupKey key) { return key.hashValue; } 152 153 static bool isEqual(const HashedStorage &lhs, const HashedStorage &rhs) { 154 return lhs.storage == rhs.storage; 155 } 156 static bool isEqual(const LookupKey &lhs, const HashedStorage &rhs) { 157 if (isEqual(rhs, getEmptyKey()) || isEqual(rhs, getTombstoneKey())) 158 return false; 159 // If the lookup kind matches the kind of the storage, then invoke the 160 // equality function on the lookup key. 161 return lhs.kind == rhs.storage->getKind() && lhs.isEqual(rhs.storage); 162 } 163 }; 164 165 // Unique types with specific hashing or storage constraints. 166 using StorageTypeSet = llvm::DenseSet<HashedStorage, StorageKeyInfo>; 167 StorageTypeSet storageTypes; 168 169 // Unique types with just the kind. 170 llvm::DenseMap<unsigned, BaseStorage *> simpleTypes; 171 172 // Allocator to use when constructing derived type instances. 173 StorageUniquer::StorageAllocator allocator; 174 175 // A mutex to keep type uniquing thread-safe. 176 llvm::sys::SmartRWMutex<true> mutex; 177 }; 178 } // end namespace detail 179 } // namespace mlir 180 181 StorageUniquer::StorageUniquer() : impl(new StorageUniquerImpl()) {} 182 StorageUniquer::~StorageUniquer() {} 183 184 /// Implementation for getting/creating an instance of a derived type with 185 /// complex storage. 186 auto StorageUniquer::getImpl( 187 unsigned kind, unsigned hashValue, 188 llvm::function_ref<bool(const BaseStorage *)> isEqual, 189 std::function<BaseStorage *(StorageAllocator &)> ctorFn) -> BaseStorage * { 190 return impl->getOrCreate(kind, hashValue, isEqual, ctorFn); 191 } 192 193 /// Implementation for getting/creating an instance of a derived type with 194 /// default storage. 195 auto StorageUniquer::getImpl( 196 unsigned kind, std::function<BaseStorage *(StorageAllocator &)> ctorFn) 197 -> BaseStorage * { 198 return impl->getOrCreate(kind, ctorFn); 199 } 200 201 /// Implementation for erasing an instance of a derived type with complex 202 /// storage. 203 void StorageUniquer::eraseImpl( 204 unsigned kind, unsigned hashValue, 205 llvm::function_ref<bool(const BaseStorage *)> isEqual, 206 std::function<void(BaseStorage *)> cleanupFn) { 207 impl->erase(kind, hashValue, isEqual, cleanupFn); 208 }