/home/arjun/llvm-project/mlir/lib/Support/StorageUniquer.cpp
| Line | Count | Source (jump to first uncovered line) | 
| 1 |  | //===- StorageUniquer.cpp - Common Storage Class Uniquer ------------------===// | 
| 2 |  | // | 
| 3 |  | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
| 4 |  | // See https://llvm.org/LICENSE.txt for license information. | 
| 5 |  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
| 6 |  | // | 
| 7 |  | //===----------------------------------------------------------------------===// | 
| 8 |  |  | 
| 9 |  | #include "mlir/Support/StorageUniquer.h" | 
| 10 |  |  | 
| 11 |  | #include "mlir/Support/LLVM.h" | 
| 12 |  | #include "llvm/Support/RWMutex.h" | 
| 13 |  |  | 
| 14 |  | using namespace mlir; | 
| 15 |  | using namespace mlir::detail; | 
| 16 |  |  | 
| 17 |  | namespace mlir { | 
| 18 |  | namespace detail { | 
| 19 |  | /// This is the implementation of the StorageUniquer class. | 
| 20 |  | struct StorageUniquerImpl { | 
| 21 |  |   using BaseStorage = StorageUniquer::BaseStorage; | 
| 22 |  |   using StorageAllocator = StorageUniquer::StorageAllocator; | 
| 23 |  |  | 
| 24 |  |   /// A lookup key for derived instances of storage objects. | 
| 25 |  |   struct LookupKey { | 
| 26 |  |     /// The known derived kind for the storage. | 
| 27 |  |     unsigned kind; | 
| 28 |  |  | 
| 29 |  |     /// The known hash value of the key. | 
| 30 |  |     unsigned hashValue; | 
| 31 |  |  | 
| 32 |  |     /// An equality function for comparing with an existing storage instance. | 
| 33 |  |     function_ref<bool(const BaseStorage *)> isEqual; | 
| 34 |  |   }; | 
| 35 |  |  | 
| 36 |  |   /// A utility wrapper object representing a hashed storage object. This class | 
| 37 |  |   /// contains a storage object and an existing computed hash value. | 
| 38 |  |   struct HashedStorage { | 
| 39 |  |     unsigned hashValue; | 
| 40 |  |     BaseStorage *storage; | 
| 41 |  |   }; | 
| 42 |  |  | 
| 43 |  |   /// Get or create an instance of a complex derived type. | 
| 44 |  |   BaseStorage * | 
| 45 |  |   getOrCreate(unsigned kind, unsigned hashValue, | 
| 46 |  |               function_ref<bool(const BaseStorage *)> isEqual, | 
| 47 | 0 |               function_ref<BaseStorage *(StorageAllocator &)> ctorFn) { | 
| 48 | 0 |     LookupKey lookupKey{kind, hashValue, isEqual}; | 
| 49 | 0 |     if (!threadingIsEnabled) | 
| 50 | 0 |       return getOrCreateUnsafe(kind, hashValue, lookupKey, ctorFn); | 
| 51 | 0 |  | 
| 52 | 0 |     // Check for an existing instance in read-only mode. | 
| 53 | 0 |     { | 
| 54 | 0 |       llvm::sys::SmartScopedReader<true> typeLock(mutex); | 
| 55 | 0 |       auto it = storageTypes.find_as(lookupKey); | 
| 56 | 0 |       if (it != storageTypes.end()) | 
| 57 | 0 |         return it->storage; | 
| 58 | 0 |     } | 
| 59 | 0 |  | 
| 60 | 0 |     // Acquire a writer-lock so that we can safely create the new type instance. | 
| 61 | 0 |     llvm::sys::SmartScopedWriter<true> typeLock(mutex); | 
| 62 | 0 |     return getOrCreateUnsafe(kind, hashValue, lookupKey, ctorFn); | 
| 63 | 0 |   } | 
| 64 |  |   /// Get or create an instance of a complex derived type in an unsafe fashion. | 
| 65 |  |   BaseStorage * | 
| 66 |  |   getOrCreateUnsafe(unsigned kind, unsigned hashValue, LookupKey &lookupKey, | 
| 67 | 0 |                     function_ref<BaseStorage *(StorageAllocator &)> ctorFn) { | 
| 68 | 0 |     auto existing = storageTypes.insert_as({}, lookupKey); | 
| 69 | 0 |     if (!existing.second) | 
| 70 | 0 |       return existing.first->storage; | 
| 71 | 0 |  | 
| 72 | 0 |     // Otherwise, construct and initialize the derived storage for this type | 
| 73 | 0 |     // instance. | 
| 74 | 0 |     BaseStorage *storage = initializeStorage(kind, ctorFn); | 
| 75 | 0 |     *existing.first = HashedStorage{hashValue, storage}; | 
| 76 | 0 |     return storage; | 
| 77 | 0 |   } | 
| 78 |  |  | 
| 79 |  |   /// Get or create an instance of a simple derived type. | 
| 80 |  |   BaseStorage * | 
| 81 |  |   getOrCreate(unsigned kind, | 
| 82 | 0 |               function_ref<BaseStorage *(StorageAllocator &)> ctorFn) { | 
| 83 | 0 |     if (!threadingIsEnabled) | 
| 84 | 0 |       return getOrCreateUnsafe(kind, ctorFn); | 
| 85 | 0 |  | 
| 86 | 0 |     // Check for an existing instance in read-only mode. | 
| 87 | 0 |     { | 
| 88 | 0 |       llvm::sys::SmartScopedReader<true> typeLock(mutex); | 
| 89 | 0 |       auto it = simpleTypes.find(kind); | 
| 90 | 0 |       if (it != simpleTypes.end()) | 
| 91 | 0 |         return it->second; | 
| 92 | 0 |     } | 
| 93 | 0 |  | 
| 94 | 0 |     // Acquire a writer-lock so that we can safely create the new type instance. | 
| 95 | 0 |     llvm::sys::SmartScopedWriter<true> typeLock(mutex); | 
| 96 | 0 |     return getOrCreateUnsafe(kind, ctorFn); | 
| 97 | 0 |   } | 
| 98 |  |   /// Get or create an instance of a simple derived type in an unsafe fashion. | 
| 99 |  |   BaseStorage * | 
| 100 |  |   getOrCreateUnsafe(unsigned kind, | 
| 101 | 0 |                     function_ref<BaseStorage *(StorageAllocator &)> ctorFn) { | 
| 102 | 0 |     auto &result = simpleTypes[kind]; | 
| 103 | 0 |     if (result) | 
| 104 | 0 |       return result; | 
| 105 | 0 |  | 
| 106 | 0 |     // Otherwise, create and return a new storage instance. | 
| 107 | 0 |     return result = initializeStorage(kind, ctorFn); | 
| 108 | 0 |   } | 
| 109 |  |  | 
| 110 |  |   /// Erase an instance of a complex derived type. | 
| 111 |  |   void erase(unsigned kind, unsigned hashValue, | 
| 112 |  |              function_ref<bool(const BaseStorage *)> isEqual, | 
| 113 | 0 |              function_ref<void(BaseStorage *)> cleanupFn) { | 
| 114 | 0 |     LookupKey lookupKey{kind, hashValue, isEqual}; | 
| 115 | 0 | 
 | 
| 116 | 0 |     // Acquire a writer-lock so that we can safely erase the type instance. | 
| 117 | 0 |     llvm::sys::SmartScopedWriter<true> typeLock(mutex); | 
| 118 | 0 |     auto existing = storageTypes.find_as(lookupKey); | 
| 119 | 0 |     if (existing == storageTypes.end()) | 
| 120 | 0 |       return; | 
| 121 | 0 |  | 
| 122 | 0 |     // Cleanup the storage and remove it from the map. | 
| 123 | 0 |     cleanupFn(existing->storage); | 
| 124 | 0 |     storageTypes.erase(existing); | 
| 125 | 0 |   } | 
| 126 |  |  | 
| 127 |  |   //===--------------------------------------------------------------------===// | 
| 128 |  |   // Instance Storage | 
| 129 |  |   //===--------------------------------------------------------------------===// | 
| 130 |  |  | 
| 131 |  |   /// Utility to create and initialize a storage instance. | 
| 132 |  |   BaseStorage * | 
| 133 |  |   initializeStorage(unsigned kind, | 
| 134 | 0 |                     function_ref<BaseStorage *(StorageAllocator &)> ctorFn) { | 
| 135 | 0 |     BaseStorage *storage = ctorFn(allocator); | 
| 136 | 0 |     storage->kind = kind; | 
| 137 | 0 |     return storage; | 
| 138 | 0 |   } | 
| 139 |  |  | 
| 140 |  |   /// Storage info for derived TypeStorage objects. | 
| 141 |  |   struct StorageKeyInfo : DenseMapInfo<HashedStorage> { | 
| 142 | 0 |     static HashedStorage getEmptyKey() { | 
| 143 | 0 |       return HashedStorage{0, DenseMapInfo<BaseStorage *>::getEmptyKey()}; | 
| 144 | 0 |     } | 
| 145 | 0 |     static HashedStorage getTombstoneKey() { | 
| 146 | 0 |       return HashedStorage{0, DenseMapInfo<BaseStorage *>::getTombstoneKey()}; | 
| 147 | 0 |     } | 
| 148 |  |  | 
| 149 | 0 |     static unsigned getHashValue(const HashedStorage &key) { | 
| 150 | 0 |       return key.hashValue; | 
| 151 | 0 |     } | 
| 152 | 0 |     static unsigned getHashValue(LookupKey key) { return key.hashValue; } | 
| 153 |  |  | 
| 154 | 0 |     static bool isEqual(const HashedStorage &lhs, const HashedStorage &rhs) { | 
| 155 | 0 |       return lhs.storage == rhs.storage; | 
| 156 | 0 |     } | 
| 157 | 0 |     static bool isEqual(const LookupKey &lhs, const HashedStorage &rhs) { | 
| 158 | 0 |       if (isEqual(rhs, getEmptyKey()) || isEqual(rhs, getTombstoneKey())) | 
| 159 | 0 |         return false; | 
| 160 | 0 |       // If the lookup kind matches the kind of the storage, then invoke the | 
| 161 | 0 |       // equality function on the lookup key. | 
| 162 | 0 |       return lhs.kind == rhs.storage->getKind() && lhs.isEqual(rhs.storage); | 
| 163 | 0 |     } | 
| 164 |  |   }; | 
| 165 |  |  | 
| 166 |  |   /// Unique types with specific hashing or storage constraints. | 
| 167 |  |   using StorageTypeSet = DenseSet<HashedStorage, StorageKeyInfo>; | 
| 168 |  |   StorageTypeSet storageTypes; | 
| 169 |  |  | 
| 170 |  |   /// Unique types with just the kind. | 
| 171 |  |   DenseMap<unsigned, BaseStorage *> simpleTypes; | 
| 172 |  |  | 
| 173 |  |   /// Allocator to use when constructing derived type instances. | 
| 174 |  |   StorageUniquer::StorageAllocator allocator; | 
| 175 |  |  | 
| 176 |  |   /// A mutex to keep type uniquing thread-safe. | 
| 177 |  |   llvm::sys::SmartRWMutex<true> mutex; | 
| 178 |  |  | 
| 179 |  |   /// Flag specifying if multi-threading is enabled within the uniquer. | 
| 180 |  |   bool threadingIsEnabled = true; | 
| 181 |  | }; | 
| 182 |  | } // end namespace detail | 
| 183 |  | } // namespace mlir | 
| 184 |  |  | 
| 185 | 0 | StorageUniquer::StorageUniquer() : impl(new StorageUniquerImpl()) {} | 
| 186 | 0 | StorageUniquer::~StorageUniquer() {} | 
| 187 |  |  | 
| 188 |  | /// Set the flag specifying if multi-threading is disabled within the uniquer. | 
| 189 | 0 | void StorageUniquer::disableMultithreading(bool disable) { | 
| 190 | 0 |   impl->threadingIsEnabled = !disable; | 
| 191 | 0 | } | 
| 192 |  |  | 
| 193 |  | /// Implementation for getting/creating an instance of a derived type with | 
| 194 |  | /// complex storage. | 
| 195 |  | auto StorageUniquer::getImpl( | 
| 196 |  |     unsigned kind, unsigned hashValue, | 
| 197 |  |     function_ref<bool(const BaseStorage *)> isEqual, | 
| 198 | 0 |     function_ref<BaseStorage *(StorageAllocator &)> ctorFn) -> BaseStorage * { | 
| 199 | 0 |   return impl->getOrCreate(kind, hashValue, isEqual, ctorFn); | 
| 200 | 0 | } | 
| 201 |  |  | 
| 202 |  | /// Implementation for getting/creating an instance of a derived type with | 
| 203 |  | /// default storage. | 
| 204 |  | auto StorageUniquer::getImpl( | 
| 205 |  |     unsigned kind, function_ref<BaseStorage *(StorageAllocator &)> ctorFn) | 
| 206 | 0 |     -> BaseStorage * { | 
| 207 | 0 |   return impl->getOrCreate(kind, ctorFn); | 
| 208 | 0 | } | 
| 209 |  |  | 
| 210 |  | /// Implementation for erasing an instance of a derived type with complex | 
| 211 |  | /// storage. | 
| 212 |  | void StorageUniquer::eraseImpl(unsigned kind, unsigned hashValue, | 
| 213 |  |                                function_ref<bool(const BaseStorage *)> isEqual, | 
| 214 | 0 |                                function_ref<void(BaseStorage *)> cleanupFn) { | 
| 215 | 0 |   impl->erase(kind, hashValue, isEqual, cleanupFn); | 
| 216 | 0 | } |