9
9
#include " mlir/Support/StorageUniquer.h"
10
10
11
11
#include " mlir/Support/LLVM.h"
12
+ #include " mlir/Support/ThreadLocalCache.h"
12
13
#include " mlir/Support/TypeID.h"
13
14
#include " llvm/Support/RWMutex.h"
14
15
@@ -37,17 +38,19 @@ struct ParametricStorageUniquer {
37
38
// / A utility wrapper object representing a hashed storage object. This class
38
39
// / contains a storage object and an existing computed hash value.
39
40
struct HashedStorage {
41
+ HashedStorage (unsigned hashValue = 0 , BaseStorage *storage = nullptr )
42
+ : hashValue(hashValue), storage(storage) {}
40
43
unsigned hashValue;
41
44
BaseStorage *storage;
42
45
};
43
46
44
47
// / Storage info for derived TypeStorage objects.
45
48
struct StorageKeyInfo : DenseMapInfo<HashedStorage> {
46
49
static HashedStorage getEmptyKey () {
47
- return HashedStorage{ 0 , DenseMapInfo<BaseStorage *>::getEmptyKey ()} ;
50
+ return HashedStorage ( 0 , DenseMapInfo<BaseStorage *>::getEmptyKey ()) ;
48
51
}
49
52
static HashedStorage getTombstoneKey () {
50
- return HashedStorage{ 0 , DenseMapInfo<BaseStorage *>::getTombstoneKey ()} ;
53
+ return HashedStorage ( 0 , DenseMapInfo<BaseStorage *>::getTombstoneKey ()) ;
51
54
}
52
55
53
56
static unsigned getHashValue (const HashedStorage &key) {
@@ -70,6 +73,10 @@ struct ParametricStorageUniquer {
70
73
using StorageTypeSet = DenseSet<HashedStorage, StorageKeyInfo>;
71
74
StorageTypeSet instances;
72
75
76
+ // / A thread local cache for storage objects. This helps to reduce the lock
77
+ // / contention when an object already existing in the cache.
78
+ ThreadLocalCache<StorageTypeSet> localCache;
79
+
73
80
// / Allocator to use when constructing derived instances.
74
81
StorageAllocator allocator;
75
82
@@ -104,56 +111,42 @@ struct StorageUniquerImpl {
104
111
if (!threadingIsEnabled)
105
112
return getOrCreateUnsafe (storageUniquer, lookupKey, ctorFn);
106
113
114
+ // Check for a instance of this object in the local cache.
115
+ auto localIt = storageUniquer.localCache ->insert_as ({hashValue}, lookupKey);
116
+ BaseStorage *&localInst = localIt.first ->storage ;
117
+ if (localInst)
118
+ return localInst;
119
+
107
120
// Check for an existing instance in read-only mode.
108
121
{
109
122
llvm::sys::SmartScopedReader<true > typeLock (storageUniquer.mutex );
110
123
auto it = storageUniquer.instances .find_as (lookupKey);
111
124
if (it != storageUniquer.instances .end ())
112
- return it->storage ;
125
+ return localInst = it->storage ;
113
126
}
114
127
115
128
// Acquire a writer-lock so that we can safely create the new type instance.
116
129
llvm::sys::SmartScopedWriter<true > typeLock (storageUniquer.mutex );
117
- return getOrCreateUnsafe (storageUniquer, lookupKey, ctorFn);
130
+ return localInst = getOrCreateUnsafe (storageUniquer, lookupKey, ctorFn);
118
131
}
119
- // / Get or create an instance of a complex derived type in an thread-unsafe
132
+ // / Get or create an instance of a param derived type in an thread-unsafe
120
133
// / fashion.
121
134
BaseStorage *
122
135
getOrCreateUnsafe (ParametricStorageUniquer &storageUniquer,
123
- ParametricStorageUniquer::LookupKey &lookupKey ,
136
+ ParametricStorageUniquer::LookupKey &key ,
124
137
function_ref<BaseStorage *(StorageAllocator &)> ctorFn) {
125
- auto existing = storageUniquer.instances .insert_as ({}, lookupKey );
138
+ auto existing = storageUniquer.instances .insert_as ({key. hashValue }, key );
126
139
if (!existing.second )
127
140
return existing.first ->storage ;
128
141
129
142
// Otherwise, construct and initialize the derived storage for this type
130
143
// instance.
131
144
BaseStorage *storage = ctorFn (storageUniquer.allocator );
132
145
*existing.first =
133
- ParametricStorageUniquer::HashedStorage{lookupKey .hashValue , storage};
146
+ ParametricStorageUniquer::HashedStorage{key .hashValue , storage};
134
147
return storage;
135
148
}
136
149
137
- // / Erase an instance of a parametric derived type.
138
- void erase (TypeID id, unsigned hashValue,
139
- function_ref<bool (const BaseStorage *)> isEqual,
140
- function_ref<void(BaseStorage *)> cleanupFn) {
141
- assert (parametricUniquers.count (id) &&
142
- " erasing unregistered storage instance" );
143
- ParametricStorageUniquer &storageUniquer = *parametricUniquers[id];
144
- ParametricStorageUniquer::LookupKey lookupKey{hashValue, isEqual};
145
-
146
- // Acquire a writer-lock so that we can safely erase the type instance.
147
- llvm::sys::SmartScopedWriter<true > lock (storageUniquer.mutex );
148
- auto existing = storageUniquer.instances .find_as (lookupKey);
149
- if (existing == storageUniquer.instances .end ())
150
- return ;
151
-
152
- // Cleanup the storage and remove it from the map.
153
- cleanupFn (existing->storage );
154
- storageUniquer.instances .erase (existing);
155
- }
156
-
157
150
// / Mutates an instance of a derived storage in a thread-safe way.
158
151
LogicalResult
159
152
mutate (TypeID id,
@@ -252,14 +245,6 @@ void StorageUniquer::registerSingletonImpl(
252
245
impl->singletonInstances .try_emplace (id, ctorFn (impl->singletonAllocator ));
253
246
}
254
247
255
- // / Implementation for erasing an instance of a derived type with parametric
256
- // / storage.
257
- void StorageUniquer::eraseImpl (TypeID id, unsigned hashValue,
258
- function_ref<bool (const BaseStorage *)> isEqual,
259
- function_ref<void(BaseStorage *)> cleanupFn) {
260
- impl->erase (id, hashValue, isEqual, cleanupFn);
261
- }
262
-
263
248
// / Implementation for mutating an instance of a derived storage.
264
249
LogicalResult StorageUniquer::mutateImpl (
265
250
TypeID id, function_ref<LogicalResult(StorageAllocator &)> mutationFn) {
0 commit comments