xref: /llvm-project/mlir/lib/Support/StorageUniquer.cpp (revision ceff9524f92445b3110fb79f0d6239713b5df153)
1cde4d5a6SJacques Pienaar //===- StorageUniquer.cpp - Common Storage Class Uniquer ------------------===//
2880df8f6SRiver Riddle //
330857107SMehdi Amini // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
456222a06SMehdi Amini // See https://llvm.org/LICENSE.txt for license information.
556222a06SMehdi Amini // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6880df8f6SRiver Riddle //
756222a06SMehdi Amini //===----------------------------------------------------------------------===//
8880df8f6SRiver Riddle 
9880df8f6SRiver Riddle #include "mlir/Support/StorageUniquer.h"
105b1345ffSMehdi Amini 
11880df8f6SRiver Riddle #include "mlir/Support/LLVM.h"
12f3df3b58SRiver Riddle #include "mlir/Support/ThreadLocalCache.h"
1386646be3SRiver Riddle #include "mlir/Support/TypeID.h"
14880df8f6SRiver Riddle #include "llvm/Support/RWMutex.h"
15880df8f6SRiver Riddle 
16880df8f6SRiver Riddle using namespace mlir;
17880df8f6SRiver Riddle using namespace mlir::detail;
18880df8f6SRiver Riddle 
1986646be3SRiver Riddle namespace {
20250f43d3SRiver Riddle /// This class represents a uniquer for storage instances of a specific type
21250f43d3SRiver Riddle /// that has parametric storage. It contains all of the necessary data to unique
22250f43d3SRiver Riddle /// storage instances in a thread safe way. This allows for the main uniquer to
23250f43d3SRiver Riddle /// bucket each of the individual sub-types removing the need to lock the main
24250f43d3SRiver Riddle /// uniquer itself.
2567f52f35SRiver Riddle class ParametricStorageUniquer {
2667f52f35SRiver Riddle public:
27880df8f6SRiver Riddle   using BaseStorage = StorageUniquer::BaseStorage;
28880df8f6SRiver Riddle   using StorageAllocator = StorageUniquer::StorageAllocator;
29880df8f6SRiver Riddle 
30880df8f6SRiver Riddle   /// A lookup key for derived instances of storage objects.
31880df8f6SRiver Riddle   struct LookupKey {
32880df8f6SRiver Riddle     /// The known hash value of the key.
33880df8f6SRiver Riddle     unsigned hashValue;
34880df8f6SRiver Riddle 
35880df8f6SRiver Riddle     /// An equality function for comparing with an existing storage instance.
364562e389SRiver Riddle     function_ref<bool(const BaseStorage *)> isEqual;
37880df8f6SRiver Riddle   };
38880df8f6SRiver Riddle 
3967f52f35SRiver Riddle private:
40880df8f6SRiver Riddle   /// A utility wrapper object representing a hashed storage object. This class
41880df8f6SRiver Riddle   /// contains a storage object and an existing computed hash value.
42880df8f6SRiver Riddle   struct HashedStorage {
HashedStorage__anon242314e60111::ParametricStorageUniquer::HashedStorage43f3df3b58SRiver Riddle     HashedStorage(unsigned hashValue = 0, BaseStorage *storage = nullptr)
44f3df3b58SRiver Riddle         : hashValue(hashValue), storage(storage) {}
45880df8f6SRiver Riddle     unsigned hashValue;
46880df8f6SRiver Riddle     BaseStorage *storage;
47880df8f6SRiver Riddle   };
48880df8f6SRiver Riddle 
49880df8f6SRiver Riddle   /// Storage info for derived TypeStorage objects.
507961511eSRiver Riddle   struct StorageKeyInfo {
getEmptyKey__anon242314e60111::ParametricStorageUniquer::StorageKeyInfo517961511eSRiver Riddle     static inline HashedStorage getEmptyKey() {
52f3df3b58SRiver Riddle       return HashedStorage(0, DenseMapInfo<BaseStorage *>::getEmptyKey());
53880df8f6SRiver Riddle     }
getTombstoneKey__anon242314e60111::ParametricStorageUniquer::StorageKeyInfo547961511eSRiver Riddle     static inline HashedStorage getTombstoneKey() {
55f3df3b58SRiver Riddle       return HashedStorage(0, DenseMapInfo<BaseStorage *>::getTombstoneKey());
56880df8f6SRiver Riddle     }
57880df8f6SRiver Riddle 
getHashValue__anon242314e60111::ParametricStorageUniquer::StorageKeyInfo587961511eSRiver Riddle     static inline unsigned getHashValue(const HashedStorage &key) {
59880df8f6SRiver Riddle       return key.hashValue;
60880df8f6SRiver Riddle     }
getHashValue__anon242314e60111::ParametricStorageUniquer::StorageKeyInfo617961511eSRiver Riddle     static inline unsigned getHashValue(const LookupKey &key) {
627961511eSRiver Riddle       return key.hashValue;
637961511eSRiver Riddle     }
64880df8f6SRiver Riddle 
isEqual__anon242314e60111::ParametricStorageUniquer::StorageKeyInfo657961511eSRiver Riddle     static inline bool isEqual(const HashedStorage &lhs,
667961511eSRiver Riddle                                const HashedStorage &rhs) {
67880df8f6SRiver Riddle       return lhs.storage == rhs.storage;
68880df8f6SRiver Riddle     }
isEqual__anon242314e60111::ParametricStorageUniquer::StorageKeyInfo697961511eSRiver Riddle     static inline bool isEqual(const LookupKey &lhs, const HashedStorage &rhs) {
70880df8f6SRiver Riddle       if (isEqual(rhs, getEmptyKey()) || isEqual(rhs, getTombstoneKey()))
71880df8f6SRiver Riddle         return false;
72250f43d3SRiver Riddle       // Invoke the equality function on the lookup key.
73250f43d3SRiver Riddle       return lhs.isEqual(rhs.storage);
74880df8f6SRiver Riddle     }
75880df8f6SRiver Riddle   };
764562e389SRiver Riddle   using StorageTypeSet = DenseSet<HashedStorage, StorageKeyInfo>;
7767f52f35SRiver Riddle 
7867f52f35SRiver Riddle   /// This class represents a single shard of the uniquer. The uniquer uses a
7967f52f35SRiver Riddle   /// set of shards to allow for multiple threads to create instances with less
8067f52f35SRiver Riddle   /// lock contention.
8167f52f35SRiver Riddle   struct Shard {
8267f52f35SRiver Riddle     /// The set containing the allocated storage instances.
83250f43d3SRiver Riddle     StorageTypeSet instances;
84880df8f6SRiver Riddle 
8567f52f35SRiver Riddle #if LLVM_ENABLE_THREADS != 0
8667f52f35SRiver Riddle     /// A mutex to keep uniquing thread-safe.
8767f52f35SRiver Riddle     llvm::sys::SmartRWMutex<true> mutex;
8867f52f35SRiver Riddle #endif
8967f52f35SRiver Riddle   };
9067f52f35SRiver Riddle 
9167f52f35SRiver Riddle   /// Get or create an instance of a param derived type in an thread-unsafe
9267f52f35SRiver Riddle   /// fashion.
getOrCreateUnsafe(Shard & shard,LookupKey & key,function_ref<BaseStorage * ()> ctorFn)939f991ed3SRiver Riddle   BaseStorage *getOrCreateUnsafe(Shard &shard, LookupKey &key,
949f991ed3SRiver Riddle                                  function_ref<BaseStorage *()> ctorFn) {
9567f52f35SRiver Riddle     auto existing = shard.instances.insert_as({key.hashValue}, key);
9667f52f35SRiver Riddle     BaseStorage *&storage = existing.first->storage;
9767f52f35SRiver Riddle     if (existing.second)
989f991ed3SRiver Riddle       storage = ctorFn();
9967f52f35SRiver Riddle     return storage;
10067f52f35SRiver Riddle   }
10167f52f35SRiver Riddle 
10231bb8efdSRiver Riddle   /// Destroy all of the storage instances within the given shard.
destroyShardInstances(Shard & shard)10331bb8efdSRiver Riddle   void destroyShardInstances(Shard &shard) {
10431bb8efdSRiver Riddle     if (!destructorFn)
10531bb8efdSRiver Riddle       return;
10631bb8efdSRiver Riddle     for (HashedStorage &instance : shard.instances)
10731bb8efdSRiver Riddle       destructorFn(instance.storage);
10831bb8efdSRiver Riddle   }
10931bb8efdSRiver Riddle 
11067f52f35SRiver Riddle public:
11167f52f35SRiver Riddle #if LLVM_ENABLE_THREADS != 0
11267f52f35SRiver Riddle   /// Initialize the storage uniquer with a given number of storage shards to
11331bb8efdSRiver Riddle   /// use. The provided shard number is required to be a valid power of 2. The
11431bb8efdSRiver Riddle   /// destructor function is used to destroy any allocated storage instances.
ParametricStorageUniquer(function_ref<void (BaseStorage *)> destructorFn,size_t numShards=8)11531bb8efdSRiver Riddle   ParametricStorageUniquer(function_ref<void(BaseStorage *)> destructorFn,
11631bb8efdSRiver Riddle                            size_t numShards = 8)
11731bb8efdSRiver Riddle       : shards(new std::atomic<Shard *>[numShards]), numShards(numShards),
11831bb8efdSRiver Riddle         destructorFn(destructorFn) {
11967f52f35SRiver Riddle     assert(llvm::isPowerOf2_64(numShards) &&
12067f52f35SRiver Riddle            "the number of shards is required to be a power of 2");
12167f52f35SRiver Riddle     for (size_t i = 0; i < numShards; i++)
12267f52f35SRiver Riddle       shards[i].store(nullptr, std::memory_order_relaxed);
12367f52f35SRiver Riddle   }
~ParametricStorageUniquer()12467f52f35SRiver Riddle   ~ParametricStorageUniquer() {
12567f52f35SRiver Riddle     // Free all of the allocated shards.
12631bb8efdSRiver Riddle     for (size_t i = 0; i != numShards; ++i) {
12731bb8efdSRiver Riddle       if (Shard *shard = shards[i].load()) {
12831bb8efdSRiver Riddle         destroyShardInstances(*shard);
12967f52f35SRiver Riddle         delete shard;
13067f52f35SRiver Riddle       }
13131bb8efdSRiver Riddle     }
13231bb8efdSRiver Riddle   }
13367f52f35SRiver Riddle   /// Get or create an instance of a parametric type.
getOrCreate(bool threadingIsEnabled,unsigned hashValue,function_ref<bool (const BaseStorage *)> isEqual,function_ref<BaseStorage * ()> ctorFn)1349f991ed3SRiver Riddle   BaseStorage *getOrCreate(bool threadingIsEnabled, unsigned hashValue,
13567f52f35SRiver Riddle                            function_ref<bool(const BaseStorage *)> isEqual,
1369f991ed3SRiver Riddle                            function_ref<BaseStorage *()> ctorFn) {
13767f52f35SRiver Riddle     Shard &shard = getShard(hashValue);
13867f52f35SRiver Riddle     ParametricStorageUniquer::LookupKey lookupKey{hashValue, isEqual};
13967f52f35SRiver Riddle     if (!threadingIsEnabled)
14067f52f35SRiver Riddle       return getOrCreateUnsafe(shard, lookupKey, ctorFn);
14167f52f35SRiver Riddle 
14267f52f35SRiver Riddle     // Check for a instance of this object in the local cache.
14367f52f35SRiver Riddle     auto localIt = localCache->insert_as({hashValue}, lookupKey);
14467f52f35SRiver Riddle     BaseStorage *&localInst = localIt.first->storage;
14567f52f35SRiver Riddle     if (localInst)
14667f52f35SRiver Riddle       return localInst;
14767f52f35SRiver Riddle 
14867f52f35SRiver Riddle     // Check for an existing instance in read-only mode.
14967f52f35SRiver Riddle     {
15067f52f35SRiver Riddle       llvm::sys::SmartScopedReader<true> typeLock(shard.mutex);
15167f52f35SRiver Riddle       auto it = shard.instances.find_as(lookupKey);
15267f52f35SRiver Riddle       if (it != shard.instances.end())
15367f52f35SRiver Riddle         return localInst = it->storage;
15467f52f35SRiver Riddle     }
15567f52f35SRiver Riddle 
15667f52f35SRiver Riddle     // Acquire a writer-lock so that we can safely create the new storage
15767f52f35SRiver Riddle     // instance.
15867f52f35SRiver Riddle     llvm::sys::SmartScopedWriter<true> typeLock(shard.mutex);
15967f52f35SRiver Riddle     return localInst = getOrCreateUnsafe(shard, lookupKey, ctorFn);
16067f52f35SRiver Riddle   }
1619f991ed3SRiver Riddle 
16267f52f35SRiver Riddle   /// Run a mutation function on the provided storage object in a thread-safe
16367f52f35SRiver Riddle   /// way.
mutate(bool threadingIsEnabled,BaseStorage * storage,function_ref<LogicalResult ()> mutationFn)1649f991ed3SRiver Riddle   LogicalResult mutate(bool threadingIsEnabled, BaseStorage *storage,
1659f991ed3SRiver Riddle                        function_ref<LogicalResult()> mutationFn) {
16667f52f35SRiver Riddle     if (!threadingIsEnabled)
1679f991ed3SRiver Riddle       return mutationFn();
16867f52f35SRiver Riddle 
1699f991ed3SRiver Riddle     // Get a shard to use for mutating this storage instance. It doesn't need to
1709f991ed3SRiver Riddle     // be the same shard as the original allocation, but does need to be
1719f991ed3SRiver Riddle     // deterministic.
1729f991ed3SRiver Riddle     Shard &shard = getShard(llvm::hash_value(storage));
17367f52f35SRiver Riddle     llvm::sys::SmartScopedWriter<true> lock(shard.mutex);
1749f991ed3SRiver Riddle     return mutationFn();
17567f52f35SRiver Riddle   }
17667f52f35SRiver Riddle 
17767f52f35SRiver Riddle private:
17867f52f35SRiver Riddle   /// Return the shard used for the given hash value.
getShard(unsigned hashValue)17967f52f35SRiver Riddle   Shard &getShard(unsigned hashValue) {
18067f52f35SRiver Riddle     // Get a shard number from the provided hashvalue.
18167f52f35SRiver Riddle     unsigned shardNum = hashValue & (numShards - 1);
18267f52f35SRiver Riddle 
18367f52f35SRiver Riddle     // Try to acquire an already initialized shard.
18467f52f35SRiver Riddle     Shard *shard = shards[shardNum].load(std::memory_order_acquire);
18567f52f35SRiver Riddle     if (shard)
18667f52f35SRiver Riddle       return *shard;
18767f52f35SRiver Riddle 
18867f52f35SRiver Riddle     // Otherwise, try to allocate a new shard.
18967f52f35SRiver Riddle     Shard *newShard = new Shard();
19067f52f35SRiver Riddle     if (shards[shardNum].compare_exchange_strong(shard, newShard))
19167f52f35SRiver Riddle       return *newShard;
19267f52f35SRiver Riddle 
19367f52f35SRiver Riddle     // If one was allocated before we can initialize ours, delete ours.
19467f52f35SRiver Riddle     delete newShard;
19567f52f35SRiver Riddle     return *shard;
19667f52f35SRiver Riddle   }
19767f52f35SRiver Riddle 
198f3df3b58SRiver Riddle   /// A thread local cache for storage objects. This helps to reduce the lock
199f3df3b58SRiver Riddle   /// contention when an object already existing in the cache.
200f3df3b58SRiver Riddle   ThreadLocalCache<StorageTypeSet> localCache;
201f3df3b58SRiver Riddle 
20267f52f35SRiver Riddle   /// A set of uniquer shards to allow for further bucketing accesses for
20367f52f35SRiver Riddle   /// instances of this storage type. Each shard is lazily initialized to reduce
20467f52f35SRiver Riddle   /// the overhead when only a small amount of shards are in use.
20567f52f35SRiver Riddle   std::unique_ptr<std::atomic<Shard *>[]> shards;
206880df8f6SRiver Riddle 
20767f52f35SRiver Riddle   /// The number of available shards.
20867f52f35SRiver Riddle   size_t numShards;
20967f52f35SRiver Riddle 
21031bb8efdSRiver Riddle   /// Function to used to destruct any allocated storage instances.
21131bb8efdSRiver Riddle   function_ref<void(BaseStorage *)> destructorFn;
21231bb8efdSRiver Riddle 
21367f52f35SRiver Riddle #else
21467f52f35SRiver Riddle   /// If multi-threading is disabled, ignore the shard parameter as we will
21531bb8efdSRiver Riddle   /// always use one shard. The destructor function is used to destroy any
21631bb8efdSRiver Riddle   /// allocated storage instances.
21731bb8efdSRiver Riddle   ParametricStorageUniquer(function_ref<void(BaseStorage *)> destructorFn,
21831bb8efdSRiver Riddle                            size_t numShards = 0)
21931bb8efdSRiver Riddle       : destructorFn(destructorFn) {}
22031bb8efdSRiver Riddle   ~ParametricStorageUniquer() { destroyShardInstances(shard); }
22167f52f35SRiver Riddle 
22267f52f35SRiver Riddle   /// Get or create an instance of a parametric type.
22367f52f35SRiver Riddle   BaseStorage *
22467f52f35SRiver Riddle   getOrCreate(bool threadingIsEnabled, unsigned hashValue,
22567f52f35SRiver Riddle               function_ref<bool(const BaseStorage *)> isEqual,
226*ceff9524SAlexandre Ganea               function_ref<BaseStorage *()> ctorFn) {
22767f52f35SRiver Riddle     ParametricStorageUniquer::LookupKey lookupKey{hashValue, isEqual};
22867f52f35SRiver Riddle     return getOrCreateUnsafe(shard, lookupKey, ctorFn);
22967f52f35SRiver Riddle   }
23067f52f35SRiver Riddle   /// Run a mutation function on the provided storage object in a thread-safe
23167f52f35SRiver Riddle   /// way.
23267f52f35SRiver Riddle   LogicalResult
23367f52f35SRiver Riddle   mutate(bool threadingIsEnabled, BaseStorage *storage,
234*ceff9524SAlexandre Ganea          function_ref<LogicalResult()> mutationFn) {
235*ceff9524SAlexandre Ganea     return mutationFn();
23667f52f35SRiver Riddle   }
23767f52f35SRiver Riddle 
23867f52f35SRiver Riddle private:
23967f52f35SRiver Riddle   /// The main uniquer shard that is used for allocating storage instances.
24067f52f35SRiver Riddle   Shard shard;
24131bb8efdSRiver Riddle 
24231bb8efdSRiver Riddle   /// Function to used to destruct any allocated storage instances.
24331bb8efdSRiver Riddle   function_ref<void(BaseStorage *)> destructorFn;
24467f52f35SRiver Riddle #endif
24586646be3SRiver Riddle };
246be0a7e9fSMehdi Amini } // namespace
24786646be3SRiver Riddle 
24886646be3SRiver Riddle namespace mlir {
24986646be3SRiver Riddle namespace detail {
25086646be3SRiver Riddle /// This is the implementation of the StorageUniquer class.
25186646be3SRiver Riddle struct StorageUniquerImpl {
25286646be3SRiver Riddle   using BaseStorage = StorageUniquer::BaseStorage;
25386646be3SRiver Riddle   using StorageAllocator = StorageUniquer::StorageAllocator;
25486646be3SRiver Riddle 
255250f43d3SRiver Riddle   //===--------------------------------------------------------------------===//
256250f43d3SRiver Riddle   // Parametric Storage
257250f43d3SRiver Riddle   //===--------------------------------------------------------------------===//
258250f43d3SRiver Riddle 
25997e77ac0SMehdi Amini   /// Check if an instance of a parametric storage class exists.
hasParametricStoragemlir::detail::StorageUniquerImpl26097e77ac0SMehdi Amini   bool hasParametricStorage(TypeID id) { return parametricUniquers.count(id); }
26197e77ac0SMehdi Amini 
262250f43d3SRiver Riddle   /// Get or create an instance of a parametric type.
26386646be3SRiver Riddle   BaseStorage *
getOrCreatemlir::detail::StorageUniquerImpl264250f43d3SRiver Riddle   getOrCreate(TypeID id, unsigned hashValue,
26586646be3SRiver Riddle               function_ref<bool(const BaseStorage *)> isEqual,
26686646be3SRiver Riddle               function_ref<BaseStorage *(StorageAllocator &)> ctorFn) {
267250f43d3SRiver Riddle     assert(parametricUniquers.count(id) &&
268250f43d3SRiver Riddle            "creating unregistered storage instance");
269250f43d3SRiver Riddle     ParametricStorageUniquer &storageUniquer = *parametricUniquers[id];
2709f991ed3SRiver Riddle     return storageUniquer.getOrCreate(
2719f991ed3SRiver Riddle         threadingIsEnabled, hashValue, isEqual,
2729f991ed3SRiver Riddle         [&] { return ctorFn(getThreadSafeAllocator()); });
27386646be3SRiver Riddle   }
27486646be3SRiver Riddle 
27567f52f35SRiver Riddle   /// Run a mutation function on the provided storage object in a thread-safe
27667f52f35SRiver Riddle   /// way.
27786646be3SRiver Riddle   LogicalResult
mutatemlir::detail::StorageUniquerImpl27867f52f35SRiver Riddle   mutate(TypeID id, BaseStorage *storage,
27986646be3SRiver Riddle          function_ref<LogicalResult(StorageAllocator &)> mutationFn) {
280250f43d3SRiver Riddle     assert(parametricUniquers.count(id) &&
281250f43d3SRiver Riddle            "mutating unregistered storage instance");
282250f43d3SRiver Riddle     ParametricStorageUniquer &storageUniquer = *parametricUniquers[id];
2839f991ed3SRiver Riddle     return storageUniquer.mutate(threadingIsEnabled, storage, [&] {
2849f991ed3SRiver Riddle       return mutationFn(getThreadSafeAllocator());
2859f991ed3SRiver Riddle     });
2869f991ed3SRiver Riddle   }
2879f991ed3SRiver Riddle 
2889f991ed3SRiver Riddle   /// Return an allocator that can be used to safely allocate instances on the
2899f991ed3SRiver Riddle   /// current thread.
getThreadSafeAllocatormlir::detail::StorageUniquerImpl2909f991ed3SRiver Riddle   StorageAllocator &getThreadSafeAllocator() {
2919f991ed3SRiver Riddle #if LLVM_ENABLE_THREADS != 0
2929f991ed3SRiver Riddle     if (!threadingIsEnabled)
2939f991ed3SRiver Riddle       return allocator;
2949f991ed3SRiver Riddle 
2959f991ed3SRiver Riddle     // If the allocator has not been initialized, create a new one.
2969f991ed3SRiver Riddle     StorageAllocator *&threadAllocator = threadSafeAllocator.get();
2979f991ed3SRiver Riddle     if (!threadAllocator) {
2989f991ed3SRiver Riddle       threadAllocator = new StorageAllocator();
2999f991ed3SRiver Riddle 
3009f991ed3SRiver Riddle       // Record this allocator, given that we don't want it to be destroyed when
3019f991ed3SRiver Riddle       // the thread dies.
3029f991ed3SRiver Riddle       llvm::sys::SmartScopedLock<true> lock(threadAllocatorMutex);
3039f991ed3SRiver Riddle       threadAllocators.push_back(
3049f991ed3SRiver Riddle           std::unique_ptr<StorageAllocator>(threadAllocator));
3059f991ed3SRiver Riddle     }
3069f991ed3SRiver Riddle 
3079f991ed3SRiver Riddle     return *threadAllocator;
3089f991ed3SRiver Riddle #else
3099f991ed3SRiver Riddle     return allocator;
3109f991ed3SRiver Riddle #endif
31186646be3SRiver Riddle   }
31286646be3SRiver Riddle 
31386646be3SRiver Riddle   //===--------------------------------------------------------------------===//
314250f43d3SRiver Riddle   // Singleton Storage
315250f43d3SRiver Riddle   //===--------------------------------------------------------------------===//
316250f43d3SRiver Riddle 
317250f43d3SRiver Riddle   /// Get or create an instance of a singleton storage class.
getSingletonmlir::detail::StorageUniquerImpl318250f43d3SRiver Riddle   BaseStorage *getSingleton(TypeID id) {
319250f43d3SRiver Riddle     BaseStorage *singletonInstance = singletonInstances[id];
320250f43d3SRiver Riddle     assert(singletonInstance && "expected singleton instance to exist");
321250f43d3SRiver Riddle     return singletonInstance;
322250f43d3SRiver Riddle   }
323250f43d3SRiver Riddle 
32497e77ac0SMehdi Amini   /// Check if an instance of a singleton storage class exists.
hasSingletonmlir::detail::StorageUniquerImpl32567f52f35SRiver Riddle   bool hasSingleton(TypeID id) const { return singletonInstances.count(id); }
32697e77ac0SMehdi Amini 
327250f43d3SRiver Riddle   //===--------------------------------------------------------------------===//
32886646be3SRiver Riddle   // Instance Storage
32986646be3SRiver Riddle   //===--------------------------------------------------------------------===//
33086646be3SRiver Riddle 
3319f991ed3SRiver Riddle #if LLVM_ENABLE_THREADS != 0
3329f991ed3SRiver Riddle   /// A thread local set of allocators used for uniquing parametric instances,
3339f991ed3SRiver Riddle   /// or other data allocated in thread volatile situations.
3349f991ed3SRiver Riddle   ThreadLocalCache<StorageAllocator *> threadSafeAllocator;
3359f991ed3SRiver Riddle 
3369f991ed3SRiver Riddle   /// All of the allocators that have been created for thread based allocation.
3379f991ed3SRiver Riddle   std::vector<std::unique_ptr<StorageAllocator>> threadAllocators;
3389f991ed3SRiver Riddle 
3399f991ed3SRiver Riddle   /// A mutex used for safely adding a new thread allocator.
3409f991ed3SRiver Riddle   llvm::sys::SmartMutex<true> threadAllocatorMutex;
3419f991ed3SRiver Riddle #endif
3429f991ed3SRiver Riddle 
3439f991ed3SRiver Riddle   /// Main allocator used for uniquing singleton instances, and other state when
3449f991ed3SRiver Riddle   /// thread safety is guaranteed.
3459f991ed3SRiver Riddle   StorageAllocator allocator;
3469f991ed3SRiver Riddle 
34786646be3SRiver Riddle   /// Map of type ids to the storage uniquer to use for registered objects.
348250f43d3SRiver Riddle   DenseMap<TypeID, std::unique_ptr<ParametricStorageUniquer>>
349250f43d3SRiver Riddle       parametricUniquers;
350250f43d3SRiver Riddle 
351250f43d3SRiver Riddle   /// Map of type ids to a singleton instance when the storage class is a
352250f43d3SRiver Riddle   /// singleton.
353250f43d3SRiver Riddle   DenseMap<TypeID, BaseStorage *> singletonInstances;
354250f43d3SRiver Riddle 
355cb9ae002SRiver Riddle   /// Flag specifying if multi-threading is enabled within the uniquer.
356cb9ae002SRiver Riddle   bool threadingIsEnabled = true;
357880df8f6SRiver Riddle };
358be0a7e9fSMehdi Amini } // namespace detail
359880df8f6SRiver Riddle } // namespace mlir
360880df8f6SRiver Riddle 
StorageUniquer()361880df8f6SRiver Riddle StorageUniquer::StorageUniquer() : impl(new StorageUniquerImpl()) {}
362e5639b3fSMehdi Amini StorageUniquer::~StorageUniquer() = default;
363880df8f6SRiver Riddle 
364cb9ae002SRiver Riddle /// Set the flag specifying if multi-threading is disabled within the uniquer.
disableMultithreading(bool disable)365cb9ae002SRiver Riddle void StorageUniquer::disableMultithreading(bool disable) {
366cb9ae002SRiver Riddle   impl->threadingIsEnabled = !disable;
367cb9ae002SRiver Riddle }
368cb9ae002SRiver Riddle 
369880df8f6SRiver Riddle /// Implementation for getting/creating an instance of a derived type with
370250f43d3SRiver Riddle /// parametric storage.
getParametricStorageTypeImpl(TypeID id,unsigned hashValue,function_ref<bool (const BaseStorage *)> isEqual,function_ref<BaseStorage * (StorageAllocator &)> ctorFn)371250f43d3SRiver Riddle auto StorageUniquer::getParametricStorageTypeImpl(
372250f43d3SRiver Riddle     TypeID id, unsigned hashValue,
3734562e389SRiver Riddle     function_ref<bool(const BaseStorage *)> isEqual,
3741fc6efafSRiver Riddle     function_ref<BaseStorage *(StorageAllocator &)> ctorFn) -> BaseStorage * {
375250f43d3SRiver Riddle   return impl->getOrCreate(id, hashValue, isEqual, ctorFn);
376880df8f6SRiver Riddle }
377880df8f6SRiver Riddle 
378250f43d3SRiver Riddle /// Implementation for registering an instance of a derived type with
379250f43d3SRiver Riddle /// parametric storage.
registerParametricStorageTypeImpl(TypeID id,function_ref<void (BaseStorage *)> destructorFn)38031bb8efdSRiver Riddle void StorageUniquer::registerParametricStorageTypeImpl(
38131bb8efdSRiver Riddle     TypeID id, function_ref<void(BaseStorage *)> destructorFn) {
382250f43d3SRiver Riddle   impl->parametricUniquers.try_emplace(
38331bb8efdSRiver Riddle       id, std::make_unique<ParametricStorageUniquer>(destructorFn));
384880df8f6SRiver Riddle }
3850f89ef30SRiver Riddle 
386250f43d3SRiver Riddle /// Implementation for getting an instance of a derived type with default
3870f89ef30SRiver Riddle /// storage.
getSingletonImpl(TypeID id)388250f43d3SRiver Riddle auto StorageUniquer::getSingletonImpl(TypeID id) -> BaseStorage * {
389250f43d3SRiver Riddle   return impl->getSingleton(id);
390250f43d3SRiver Riddle }
391250f43d3SRiver Riddle 
39297e77ac0SMehdi Amini /// Test is the storage singleton is initialized.
isSingletonStorageInitialized(TypeID id)39397e77ac0SMehdi Amini bool StorageUniquer::isSingletonStorageInitialized(TypeID id) {
39497e77ac0SMehdi Amini   return impl->hasSingleton(id);
39597e77ac0SMehdi Amini }
39697e77ac0SMehdi Amini 
39797e77ac0SMehdi Amini /// Test is the parametric storage is initialized.
isParametricStorageInitialized(TypeID id)39897e77ac0SMehdi Amini bool StorageUniquer::isParametricStorageInitialized(TypeID id) {
39997e77ac0SMehdi Amini   return impl->hasParametricStorage(id);
40097e77ac0SMehdi Amini }
40197e77ac0SMehdi Amini 
402250f43d3SRiver Riddle /// Implementation for registering an instance of a derived type with default
403250f43d3SRiver Riddle /// storage.
registerSingletonImpl(TypeID id,function_ref<BaseStorage * (StorageAllocator &)> ctorFn)404250f43d3SRiver Riddle void StorageUniquer::registerSingletonImpl(
405250f43d3SRiver Riddle     TypeID id, function_ref<BaseStorage *(StorageAllocator &)> ctorFn) {
406250f43d3SRiver Riddle   assert(!impl->singletonInstances.count(id) &&
407250f43d3SRiver Riddle          "storage class already registered");
4089f991ed3SRiver Riddle   impl->singletonInstances.try_emplace(id, ctorFn(impl->allocator));
409250f43d3SRiver Riddle }
410250f43d3SRiver Riddle 
411a5182991SAlex Zinenko /// Implementation for mutating an instance of a derived storage.
mutateImpl(TypeID id,BaseStorage * storage,function_ref<LogicalResult (StorageAllocator &)> mutationFn)412a5182991SAlex Zinenko LogicalResult StorageUniquer::mutateImpl(
41367f52f35SRiver Riddle     TypeID id, BaseStorage *storage,
41467f52f35SRiver Riddle     function_ref<LogicalResult(StorageAllocator &)> mutationFn) {
41567f52f35SRiver Riddle   return impl->mutate(id, storage, mutationFn);
416a5182991SAlex Zinenko }
417