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 *
getOrCreatemlir::detail::StorageUniquerImpl45 getOrCreate(unsigned kind, unsigned hashValue,
46 function_ref<bool(const BaseStorage *)> isEqual,
47 function_ref<BaseStorage *(StorageAllocator &)> ctorFn) {
48 LookupKey lookupKey{kind, hashValue, isEqual};
49 if (!threadingIsEnabled)
50 return getOrCreateUnsafe(kind, hashValue, lookupKey, ctorFn);
51
52 // Check for an existing instance in read-only mode.
53 {
54 llvm::sys::SmartScopedReader<true> typeLock(mutex);
55 auto it = storageTypes.find_as(lookupKey);
56 if (it != storageTypes.end())
57 return it->storage;
58 }
59
60 // Acquire a writer-lock so that we can safely create the new type instance.
61 llvm::sys::SmartScopedWriter<true> typeLock(mutex);
62 return getOrCreateUnsafe(kind, hashValue, lookupKey, ctorFn);
63 }
64 /// Get or create an instance of a complex derived type in an unsafe fashion.
65 BaseStorage *
getOrCreateUnsafemlir::detail::StorageUniquerImpl66 getOrCreateUnsafe(unsigned kind, unsigned hashValue, LookupKey &lookupKey,
67 function_ref<BaseStorage *(StorageAllocator &)> ctorFn) {
68 auto existing = storageTypes.insert_as({}, lookupKey);
69 if (!existing.second)
70 return existing.first->storage;
71
72 // Otherwise, construct and initialize the derived storage for this type
73 // instance.
74 BaseStorage *storage = initializeStorage(kind, ctorFn);
75 *existing.first = HashedStorage{hashValue, storage};
76 return storage;
77 }
78
79 /// Get or create an instance of a simple derived type.
80 BaseStorage *
getOrCreatemlir::detail::StorageUniquerImpl81 getOrCreate(unsigned kind,
82 function_ref<BaseStorage *(StorageAllocator &)> ctorFn) {
83 if (!threadingIsEnabled)
84 return getOrCreateUnsafe(kind, ctorFn);
85
86 // Check for an existing instance in read-only mode.
87 {
88 llvm::sys::SmartScopedReader<true> typeLock(mutex);
89 auto it = simpleTypes.find(kind);
90 if (it != simpleTypes.end())
91 return it->second;
92 }
93
94 // Acquire a writer-lock so that we can safely create the new type instance.
95 llvm::sys::SmartScopedWriter<true> typeLock(mutex);
96 return getOrCreateUnsafe(kind, ctorFn);
97 }
98 /// Get or create an instance of a simple derived type in an unsafe fashion.
99 BaseStorage *
getOrCreateUnsafemlir::detail::StorageUniquerImpl100 getOrCreateUnsafe(unsigned kind,
101 function_ref<BaseStorage *(StorageAllocator &)> ctorFn) {
102 auto &result = simpleTypes[kind];
103 if (result)
104 return result;
105
106 // Otherwise, create and return a new storage instance.
107 return result = initializeStorage(kind, ctorFn);
108 }
109
110 /// Erase an instance of a complex derived type.
erasemlir::detail::StorageUniquerImpl111 void erase(unsigned kind, unsigned hashValue,
112 function_ref<bool(const BaseStorage *)> isEqual,
113 function_ref<void(BaseStorage *)> cleanupFn) {
114 LookupKey lookupKey{kind, hashValue, isEqual};
115
116 // Acquire a writer-lock so that we can safely erase the type instance.
117 llvm::sys::SmartScopedWriter<true> typeLock(mutex);
118 auto existing = storageTypes.find_as(lookupKey);
119 if (existing == storageTypes.end())
120 return;
121
122 // Cleanup the storage and remove it from the map.
123 cleanupFn(existing->storage);
124 storageTypes.erase(existing);
125 }
126
127 //===--------------------------------------------------------------------===//
128 // Instance Storage
129 //===--------------------------------------------------------------------===//
130
131 /// Utility to create and initialize a storage instance.
132 BaseStorage *
initializeStoragemlir::detail::StorageUniquerImpl133 initializeStorage(unsigned kind,
134 function_ref<BaseStorage *(StorageAllocator &)> ctorFn) {
135 BaseStorage *storage = ctorFn(allocator);
136 storage->kind = kind;
137 return storage;
138 }
139
140 /// Storage info for derived TypeStorage objects.
141 struct StorageKeyInfo : DenseMapInfo<HashedStorage> {
getEmptyKeymlir::detail::StorageUniquerImpl::StorageKeyInfo142 static HashedStorage getEmptyKey() {
143 return HashedStorage{0, DenseMapInfo<BaseStorage *>::getEmptyKey()};
144 }
getTombstoneKeymlir::detail::StorageUniquerImpl::StorageKeyInfo145 static HashedStorage getTombstoneKey() {
146 return HashedStorage{0, DenseMapInfo<BaseStorage *>::getTombstoneKey()};
147 }
148
getHashValuemlir::detail::StorageUniquerImpl::StorageKeyInfo149 static unsigned getHashValue(const HashedStorage &key) {
150 return key.hashValue;
151 }
getHashValuemlir::detail::StorageUniquerImpl::StorageKeyInfo152 static unsigned getHashValue(LookupKey key) { return key.hashValue; }
153
isEqualmlir::detail::StorageUniquerImpl::StorageKeyInfo154 static bool isEqual(const HashedStorage &lhs, const HashedStorage &rhs) {
155 return lhs.storage == rhs.storage;
156 }
isEqualmlir::detail::StorageUniquerImpl::StorageKeyInfo157 static bool isEqual(const LookupKey &lhs, const HashedStorage &rhs) {
158 if (isEqual(rhs, getEmptyKey()) || isEqual(rhs, getTombstoneKey()))
159 return false;
160 // If the lookup kind matches the kind of the storage, then invoke the
161 // equality function on the lookup key.
162 return lhs.kind == rhs.storage->getKind() && lhs.isEqual(rhs.storage);
163 }
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
StorageUniquer()185 StorageUniquer::StorageUniquer() : impl(new StorageUniquerImpl()) {}
~StorageUniquer()186 StorageUniquer::~StorageUniquer() {}
187
188 /// Set the flag specifying if multi-threading is disabled within the uniquer.
disableMultithreading(bool disable)189 void StorageUniquer::disableMultithreading(bool disable) {
190 impl->threadingIsEnabled = !disable;
191 }
192
193 /// Implementation for getting/creating an instance of a derived type with
194 /// complex storage.
getImpl(unsigned kind,unsigned hashValue,function_ref<bool (const BaseStorage *)> isEqual,function_ref<BaseStorage * (StorageAllocator &)> ctorFn)195 auto StorageUniquer::getImpl(
196 unsigned kind, unsigned hashValue,
197 function_ref<bool(const BaseStorage *)> isEqual,
198 function_ref<BaseStorage *(StorageAllocator &)> ctorFn) -> BaseStorage * {
199 return impl->getOrCreate(kind, hashValue, isEqual, ctorFn);
200 }
201
202 /// Implementation for getting/creating an instance of a derived type with
203 /// default storage.
getImpl(unsigned kind,function_ref<BaseStorage * (StorageAllocator &)> ctorFn)204 auto StorageUniquer::getImpl(
205 unsigned kind, function_ref<BaseStorage *(StorageAllocator &)> ctorFn)
206 -> BaseStorage * {
207 return impl->getOrCreate(kind, ctorFn);
208 }
209
210 /// Implementation for erasing an instance of a derived type with complex
211 /// storage.
eraseImpl(unsigned kind,unsigned hashValue,function_ref<bool (const BaseStorage *)> isEqual,function_ref<void (BaseStorage *)> cleanupFn)212 void StorageUniquer::eraseImpl(unsigned kind, unsigned hashValue,
213 function_ref<bool(const BaseStorage *)> isEqual,
214 function_ref<void(BaseStorage *)> cleanupFn) {
215 impl->erase(kind, hashValue, isEqual, cleanupFn);
216 }
217