1 //===- MLIRContext.cpp - MLIR Type Classes --------------------------------===//
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/IR/MLIRContext.h"
10 #include "AffineExprDetail.h"
11 #include "AffineMapDetail.h"
12 #include "AttributeDetail.h"
13 #include "IntegerSetDetail.h"
14 #include "TypeDetail.h"
15 #include "mlir/IR/AffineExpr.h"
16 #include "mlir/IR/AffineMap.h"
17 #include "mlir/IR/Attributes.h"
18 #include "mlir/IR/BuiltinDialect.h"
19 #include "mlir/IR/Diagnostics.h"
20 #include "mlir/IR/Dialect.h"
21 #include "mlir/IR/Identifier.h"
22 #include "mlir/IR/IntegerSet.h"
23 #include "mlir/IR/Location.h"
24 #include "mlir/IR/OpImplementation.h"
25 #include "mlir/IR/Types.h"
26 #include "mlir/Support/DebugAction.h"
27 #include "llvm/ADT/DenseMap.h"
28 #include "llvm/ADT/DenseSet.h"
29 #include "llvm/ADT/SetVector.h"
30 #include "llvm/ADT/SmallString.h"
31 #include "llvm/ADT/StringSet.h"
32 #include "llvm/ADT/Twine.h"
33 #include "llvm/Support/Allocator.h"
34 #include "llvm/Support/CommandLine.h"
35 #include "llvm/Support/Debug.h"
36 #include "llvm/Support/RWMutex.h"
37 #include "llvm/Support/ThreadPool.h"
38 #include "llvm/Support/raw_ostream.h"
39 #include <memory>
40 
41 #define DEBUG_TYPE "mlircontext"
42 
43 using namespace mlir;
44 using namespace mlir::detail;
45 
46 using llvm::hash_combine;
47 using llvm::hash_combine_range;
48 
49 //===----------------------------------------------------------------------===//
50 // MLIRContext CommandLine Options
51 //===----------------------------------------------------------------------===//
52 
53 namespace {
54 /// This struct contains command line options that can be used to initialize
55 /// various bits of an MLIRContext. This uses a struct wrapper to avoid the need
56 /// for global command line options.
57 struct MLIRContextOptions {
58   llvm::cl::opt<bool> disableThreading{
59       "mlir-disable-threading",
60       llvm::cl::desc("Disabling multi-threading within MLIR")};
61 
62   llvm::cl::opt<bool> printOpOnDiagnostic{
63       "mlir-print-op-on-diagnostic",
64       llvm::cl::desc("When a diagnostic is emitted on an operation, also print "
65                      "the operation as an attached note"),
66       llvm::cl::init(true)};
67 
68   llvm::cl::opt<bool> printStackTraceOnDiagnostic{
69       "mlir-print-stacktrace-on-diagnostic",
70       llvm::cl::desc("When a diagnostic is emitted, also print the stack trace "
71                      "as an attached note")};
72 };
73 } // end anonymous namespace
74 
75 static llvm::ManagedStatic<MLIRContextOptions> clOptions;
76 
77 /// Register a set of useful command-line options that can be used to configure
78 /// various flags within the MLIRContext. These flags are used when constructing
79 /// an MLIR context for initialization.
80 void mlir::registerMLIRContextCLOptions() {
81   // Make sure that the options struct has been initialized.
82   *clOptions;
83 }
84 
85 //===----------------------------------------------------------------------===//
86 // Locking Utilities
87 //===----------------------------------------------------------------------===//
88 
89 namespace {
90 /// Utility reader lock that takes a runtime flag that specifies if we really
91 /// need to lock.
92 struct ScopedReaderLock {
93   ScopedReaderLock(llvm::sys::SmartRWMutex<true> &mutexParam, bool shouldLock)
94       : mutex(shouldLock ? &mutexParam : nullptr) {
95     if (mutex)
96       mutex->lock_shared();
97   }
98   ~ScopedReaderLock() {
99     if (mutex)
100       mutex->unlock_shared();
101   }
102   llvm::sys::SmartRWMutex<true> *mutex;
103 };
104 /// Utility writer lock that takes a runtime flag that specifies if we really
105 /// need to lock.
106 struct ScopedWriterLock {
107   ScopedWriterLock(llvm::sys::SmartRWMutex<true> &mutexParam, bool shouldLock)
108       : mutex(shouldLock ? &mutexParam : nullptr) {
109     if (mutex)
110       mutex->lock();
111   }
112   ~ScopedWriterLock() {
113     if (mutex)
114       mutex->unlock();
115   }
116   llvm::sys::SmartRWMutex<true> *mutex;
117 };
118 } // end anonymous namespace.
119 
120 //===----------------------------------------------------------------------===//
121 // AffineMap and IntegerSet hashing
122 //===----------------------------------------------------------------------===//
123 
124 /// A utility function to safely get or create a uniqued instance within the
125 /// given set container.
126 template <typename ValueT, typename DenseInfoT, typename KeyT,
127           typename ConstructorFn>
128 static ValueT safeGetOrCreate(DenseSet<ValueT, DenseInfoT> &container,
129                               KeyT &&key, llvm::sys::SmartRWMutex<true> &mutex,
130                               bool threadingIsEnabled,
131                               ConstructorFn &&constructorFn) {
132   // Check for an existing instance in read-only mode.
133   if (threadingIsEnabled) {
134     llvm::sys::SmartScopedReader<true> instanceLock(mutex);
135     auto it = container.find_as(key);
136     if (it != container.end())
137       return *it;
138   }
139 
140   // Acquire a writer-lock so that we can safely create the new instance.
141   ScopedWriterLock instanceLock(mutex, threadingIsEnabled);
142 
143   // Check for an existing instance again here, because another writer thread
144   // may have already created one. Otherwise, construct a new instance.
145   auto existing = container.insert_as(ValueT(), key);
146   if (existing.second)
147     return *existing.first = constructorFn();
148   return *existing.first;
149 }
150 
151 namespace {
152 struct AffineMapKeyInfo : DenseMapInfo<AffineMap> {
153   // Affine maps are uniqued based on their dim/symbol counts and affine
154   // expressions.
155   using KeyTy = std::tuple<unsigned, unsigned, ArrayRef<AffineExpr>>;
156   using DenseMapInfo<AffineMap>::isEqual;
157 
158   static unsigned getHashValue(const AffineMap &key) {
159     return getHashValue(
160         KeyTy(key.getNumDims(), key.getNumSymbols(), key.getResults()));
161   }
162 
163   static unsigned getHashValue(KeyTy key) {
164     return hash_combine(
165         std::get<0>(key), std::get<1>(key),
166         hash_combine_range(std::get<2>(key).begin(), std::get<2>(key).end()));
167   }
168 
169   static bool isEqual(const KeyTy &lhs, AffineMap rhs) {
170     if (rhs == getEmptyKey() || rhs == getTombstoneKey())
171       return false;
172     return lhs == std::make_tuple(rhs.getNumDims(), rhs.getNumSymbols(),
173                                   rhs.getResults());
174   }
175 };
176 
177 struct IntegerSetKeyInfo : DenseMapInfo<IntegerSet> {
178   // Integer sets are uniqued based on their dim/symbol counts, affine
179   // expressions appearing in the LHS of constraints, and eqFlags.
180   using KeyTy =
181       std::tuple<unsigned, unsigned, ArrayRef<AffineExpr>, ArrayRef<bool>>;
182   using DenseMapInfo<IntegerSet>::isEqual;
183 
184   static unsigned getHashValue(const IntegerSet &key) {
185     return getHashValue(KeyTy(key.getNumDims(), key.getNumSymbols(),
186                               key.getConstraints(), key.getEqFlags()));
187   }
188 
189   static unsigned getHashValue(KeyTy key) {
190     return hash_combine(
191         std::get<0>(key), std::get<1>(key),
192         hash_combine_range(std::get<2>(key).begin(), std::get<2>(key).end()),
193         hash_combine_range(std::get<3>(key).begin(), std::get<3>(key).end()));
194   }
195 
196   static bool isEqual(const KeyTy &lhs, IntegerSet rhs) {
197     if (rhs == getEmptyKey() || rhs == getTombstoneKey())
198       return false;
199     return lhs == std::make_tuple(rhs.getNumDims(), rhs.getNumSymbols(),
200                                   rhs.getConstraints(), rhs.getEqFlags());
201   }
202 };
203 } // end anonymous namespace.
204 
205 //===----------------------------------------------------------------------===//
206 // MLIRContextImpl
207 //===----------------------------------------------------------------------===//
208 
209 namespace mlir {
210 /// This is the implementation of the MLIRContext class, using the pImpl idiom.
211 /// This class is completely private to this file, so everything is public.
212 class MLIRContextImpl {
213 public:
214   //===--------------------------------------------------------------------===//
215   // Debugging
216   //===--------------------------------------------------------------------===//
217 
218   /// An action manager for use within the context.
219   DebugActionManager debugActionManager;
220 
221   //===--------------------------------------------------------------------===//
222   // Identifier uniquing
223   //===--------------------------------------------------------------------===//
224 
225   // Identifier allocator and mutex for thread safety.
226   llvm::BumpPtrAllocator identifierAllocator;
227   llvm::sys::SmartRWMutex<true> identifierMutex;
228 
229   //===--------------------------------------------------------------------===//
230   // Diagnostics
231   //===--------------------------------------------------------------------===//
232   DiagnosticEngine diagEngine;
233 
234   //===--------------------------------------------------------------------===//
235   // Options
236   //===--------------------------------------------------------------------===//
237 
238   /// In most cases, creating operation in unregistered dialect is not desired
239   /// and indicate a misconfiguration of the compiler. This option enables to
240   /// detect such use cases
241   bool allowUnregisteredDialects = false;
242 
243   /// Enable support for multi-threading within MLIR.
244   bool threadingIsEnabled = true;
245 
246   /// Track if we are currently executing in a threaded execution environment
247   /// (like the pass-manager): this is only a debugging feature to help reducing
248   /// the chances of data races one some context APIs.
249 #ifndef NDEBUG
250   std::atomic<int> multiThreadedExecutionContext{0};
251 #endif
252 
253   /// If the operation should be attached to diagnostics printed via the
254   /// Operation::emit methods.
255   bool printOpOnDiagnostic = true;
256 
257   /// If the current stack trace should be attached when emitting diagnostics.
258   bool printStackTraceOnDiagnostic = false;
259 
260   //===--------------------------------------------------------------------===//
261   // Other
262   //===--------------------------------------------------------------------===//
263 
264   /// This points to the ThreadPool used when processing MLIR tasks in parallel.
265   /// It can't be nullptr when multi-threading is enabled. Otherwise if
266   /// multi-threading is disabled, and the threadpool wasn't externally provided
267   /// using `setThreadPool`, this will be nullptr.
268   llvm::ThreadPool *threadPool = nullptr;
269 
270   /// In case where the thread pool is owned by the context, this ensures
271   /// destruction with the context.
272   std::unique_ptr<llvm::ThreadPool> ownedThreadPool;
273 
274   /// This is a list of dialects that are created referring to this context.
275   /// The MLIRContext owns the objects.
276   DenseMap<StringRef, std::unique_ptr<Dialect>> loadedDialects;
277   DialectRegistry dialectsRegistry;
278 
279   /// This is a mapping from operation name to AbstractOperation for registered
280   /// operations.
281   llvm::StringMap<AbstractOperation> registeredOperations;
282 
283   /// Identifiers are uniqued by string value and use the internal string set
284   /// for storage.
285   llvm::StringMap<PointerUnion<Dialect *, MLIRContext *>,
286                   llvm::BumpPtrAllocator &>
287       identifiers;
288 
289   /// An allocator used for AbstractAttribute and AbstractType objects.
290   llvm::BumpPtrAllocator abstractDialectSymbolAllocator;
291 
292   //===--------------------------------------------------------------------===//
293   // Affine uniquing
294   //===--------------------------------------------------------------------===//
295 
296   // Affine allocator and mutex for thread safety.
297   llvm::BumpPtrAllocator affineAllocator;
298   llvm::sys::SmartRWMutex<true> affineMutex;
299 
300   // Affine map uniquing.
301   using AffineMapSet = DenseSet<AffineMap, AffineMapKeyInfo>;
302   AffineMapSet affineMaps;
303 
304   // Integer set uniquing.
305   using IntegerSets = DenseSet<IntegerSet, IntegerSetKeyInfo>;
306   IntegerSets integerSets;
307 
308   // Affine expression uniquing.
309   StorageUniquer affineUniquer;
310 
311   //===--------------------------------------------------------------------===//
312   // Type uniquing
313   //===--------------------------------------------------------------------===//
314 
315   DenseMap<TypeID, AbstractType *> registeredTypes;
316   StorageUniquer typeUniquer;
317 
318   /// Cached Type Instances.
319   BFloat16Type bf16Ty;
320   Float16Type f16Ty;
321   Float32Type f32Ty;
322   Float64Type f64Ty;
323   Float80Type f80Ty;
324   Float128Type f128Ty;
325   IndexType indexTy;
326   IntegerType int1Ty, int8Ty, int16Ty, int32Ty, int64Ty, int128Ty;
327   NoneType noneType;
328 
329   //===--------------------------------------------------------------------===//
330   // Attribute uniquing
331   //===--------------------------------------------------------------------===//
332 
333   DenseMap<TypeID, AbstractAttribute *> registeredAttributes;
334   StorageUniquer attributeUniquer;
335 
336   /// Cached Attribute Instances.
337   BoolAttr falseAttr, trueAttr;
338   UnitAttr unitAttr;
339   UnknownLoc unknownLocAttr;
340   DictionaryAttr emptyDictionaryAttr;
341   StringAttr emptyStringAttr;
342 
343 public:
344   MLIRContextImpl(bool threadingIsEnabled)
345       : threadingIsEnabled(threadingIsEnabled),
346         identifiers(identifierAllocator) {
347     if (threadingIsEnabled) {
348       ownedThreadPool = std::make_unique<llvm::ThreadPool>();
349       threadPool = ownedThreadPool.get();
350     }
351   }
352   ~MLIRContextImpl() {
353     for (auto typeMapping : registeredTypes)
354       typeMapping.second->~AbstractType();
355     for (auto attrMapping : registeredAttributes)
356       attrMapping.second->~AbstractAttribute();
357   }
358 };
359 } // end namespace mlir
360 
361 MLIRContext::MLIRContext(Threading setting)
362     : MLIRContext(DialectRegistry(), setting) {}
363 
364 MLIRContext::MLIRContext(const DialectRegistry &registry, Threading setting)
365     : impl(new MLIRContextImpl(setting == Threading::ENABLED)) {
366   // Initialize values based on the command line flags if they were provided.
367   if (clOptions.isConstructed()) {
368     disableMultithreading(clOptions->disableThreading);
369     printOpOnDiagnostic(clOptions->printOpOnDiagnostic);
370     printStackTraceOnDiagnostic(clOptions->printStackTraceOnDiagnostic);
371   }
372 
373   // Pre-populate the registry.
374   registry.appendTo(impl->dialectsRegistry);
375 
376   // Ensure the builtin dialect is always pre-loaded.
377   getOrLoadDialect<BuiltinDialect>();
378 
379   // Initialize several common attributes and types to avoid the need to lock
380   // the context when accessing them.
381 
382   //// Types.
383   /// Floating-point Types.
384   impl->bf16Ty = TypeUniquer::get<BFloat16Type>(this);
385   impl->f16Ty = TypeUniquer::get<Float16Type>(this);
386   impl->f32Ty = TypeUniquer::get<Float32Type>(this);
387   impl->f64Ty = TypeUniquer::get<Float64Type>(this);
388   impl->f80Ty = TypeUniquer::get<Float80Type>(this);
389   impl->f128Ty = TypeUniquer::get<Float128Type>(this);
390   /// Index Type.
391   impl->indexTy = TypeUniquer::get<IndexType>(this);
392   /// Integer Types.
393   impl->int1Ty = TypeUniquer::get<IntegerType>(this, 1, IntegerType::Signless);
394   impl->int8Ty = TypeUniquer::get<IntegerType>(this, 8, IntegerType::Signless);
395   impl->int16Ty =
396       TypeUniquer::get<IntegerType>(this, 16, IntegerType::Signless);
397   impl->int32Ty =
398       TypeUniquer::get<IntegerType>(this, 32, IntegerType::Signless);
399   impl->int64Ty =
400       TypeUniquer::get<IntegerType>(this, 64, IntegerType::Signless);
401   impl->int128Ty =
402       TypeUniquer::get<IntegerType>(this, 128, IntegerType::Signless);
403   /// None Type.
404   impl->noneType = TypeUniquer::get<NoneType>(this);
405 
406   //// Attributes.
407   //// Note: These must be registered after the types as they may generate one
408   //// of the above types internally.
409   /// Unknown Location Attribute.
410   impl->unknownLocAttr = AttributeUniquer::get<UnknownLoc>(this);
411   /// Bool Attributes.
412   impl->falseAttr = IntegerAttr::getBoolAttrUnchecked(impl->int1Ty, false);
413   impl->trueAttr = IntegerAttr::getBoolAttrUnchecked(impl->int1Ty, true);
414   /// Unit Attribute.
415   impl->unitAttr = AttributeUniquer::get<UnitAttr>(this);
416   /// The empty dictionary attribute.
417   impl->emptyDictionaryAttr = DictionaryAttr::getEmptyUnchecked(this);
418   /// The empty string attribute.
419   impl->emptyStringAttr = StringAttr::getEmptyStringAttrUnchecked(this);
420 
421   // Register the affine storage objects with the uniquer.
422   impl->affineUniquer
423       .registerParametricStorageType<AffineBinaryOpExprStorage>();
424   impl->affineUniquer
425       .registerParametricStorageType<AffineConstantExprStorage>();
426   impl->affineUniquer.registerParametricStorageType<AffineDimExprStorage>();
427 }
428 
429 MLIRContext::~MLIRContext() {}
430 
431 /// Copy the specified array of elements into memory managed by the provided
432 /// bump pointer allocator.  This assumes the elements are all PODs.
433 template <typename T>
434 static ArrayRef<T> copyArrayRefInto(llvm::BumpPtrAllocator &allocator,
435                                     ArrayRef<T> elements) {
436   auto result = allocator.Allocate<T>(elements.size());
437   std::uninitialized_copy(elements.begin(), elements.end(), result);
438   return ArrayRef<T>(result, elements.size());
439 }
440 
441 //===----------------------------------------------------------------------===//
442 // Debugging
443 //===----------------------------------------------------------------------===//
444 
445 DebugActionManager &MLIRContext::getDebugActionManager() {
446   return getImpl().debugActionManager;
447 }
448 
449 //===----------------------------------------------------------------------===//
450 // Diagnostic Handlers
451 //===----------------------------------------------------------------------===//
452 
453 /// Returns the diagnostic engine for this context.
454 DiagnosticEngine &MLIRContext::getDiagEngine() { return getImpl().diagEngine; }
455 
456 //===----------------------------------------------------------------------===//
457 // Dialect and Operation Registration
458 //===----------------------------------------------------------------------===//
459 
460 void MLIRContext::appendDialectRegistry(const DialectRegistry &registry) {
461   registry.appendTo(impl->dialectsRegistry);
462 
463   // For the already loaded dialects, register the interfaces immediately.
464   for (const auto &kvp : impl->loadedDialects)
465     registry.registerDelayedInterfaces(kvp.second.get());
466 }
467 
468 const DialectRegistry &MLIRContext::getDialectRegistry() {
469   return impl->dialectsRegistry;
470 }
471 
472 /// Return information about all registered IR dialects.
473 std::vector<Dialect *> MLIRContext::getLoadedDialects() {
474   std::vector<Dialect *> result;
475   result.reserve(impl->loadedDialects.size());
476   for (auto &dialect : impl->loadedDialects)
477     result.push_back(dialect.second.get());
478   llvm::array_pod_sort(result.begin(), result.end(),
479                        [](Dialect *const *lhs, Dialect *const *rhs) -> int {
480                          return (*lhs)->getNamespace() < (*rhs)->getNamespace();
481                        });
482   return result;
483 }
484 std::vector<StringRef> MLIRContext::getAvailableDialects() {
485   std::vector<StringRef> result;
486   for (auto dialect : impl->dialectsRegistry.getDialectNames())
487     result.push_back(dialect);
488   return result;
489 }
490 
491 /// Get a registered IR dialect with the given namespace. If none is found,
492 /// then return nullptr.
493 Dialect *MLIRContext::getLoadedDialect(StringRef name) {
494   // Dialects are sorted by name, so we can use binary search for lookup.
495   auto it = impl->loadedDialects.find(name);
496   return (it != impl->loadedDialects.end()) ? it->second.get() : nullptr;
497 }
498 
499 Dialect *MLIRContext::getOrLoadDialect(StringRef name) {
500   Dialect *dialect = getLoadedDialect(name);
501   if (dialect)
502     return dialect;
503   DialectAllocatorFunctionRef allocator =
504       impl->dialectsRegistry.getDialectAllocator(name);
505   return allocator ? allocator(this) : nullptr;
506 }
507 
508 /// Get a dialect for the provided namespace and TypeID: abort the program if a
509 /// dialect exist for this namespace with different TypeID. Returns a pointer to
510 /// the dialect owned by the context.
511 Dialect *
512 MLIRContext::getOrLoadDialect(StringRef dialectNamespace, TypeID dialectID,
513                               function_ref<std::unique_ptr<Dialect>()> ctor) {
514   auto &impl = getImpl();
515   // Get the correct insertion position sorted by namespace.
516   std::unique_ptr<Dialect> &dialect = impl.loadedDialects[dialectNamespace];
517 
518   if (!dialect) {
519     LLVM_DEBUG(llvm::dbgs()
520                << "Load new dialect in Context " << dialectNamespace << "\n");
521 #ifndef NDEBUG
522     if (impl.multiThreadedExecutionContext != 0)
523       llvm::report_fatal_error(
524           "Loading a dialect (" + dialectNamespace +
525           ") while in a multi-threaded execution context (maybe "
526           "the PassManager): this can indicate a "
527           "missing `dependentDialects` in a pass for example.");
528 #endif
529     dialect = ctor();
530     assert(dialect && "dialect ctor failed");
531 
532     // Refresh all the identifiers dialect field, this catches cases where a
533     // dialect may be loaded after identifier prefixed with this dialect name
534     // were already created.
535     llvm::SmallString<32> dialectPrefix(dialectNamespace);
536     dialectPrefix.push_back('.');
537     for (auto &identifierEntry : impl.identifiers)
538       if (identifierEntry.second.is<MLIRContext *>() &&
539           identifierEntry.first().startswith(dialectPrefix))
540         identifierEntry.second = dialect.get();
541 
542     // Actually register the interfaces with delayed registration.
543     impl.dialectsRegistry.registerDelayedInterfaces(dialect.get());
544     return dialect.get();
545   }
546 
547   // Abort if dialect with namespace has already been registered.
548   if (dialect->getTypeID() != dialectID)
549     llvm::report_fatal_error("a dialect with namespace '" + dialectNamespace +
550                              "' has already been registered");
551 
552   return dialect.get();
553 }
554 
555 void MLIRContext::loadAllAvailableDialects() {
556   for (StringRef name : getAvailableDialects())
557     getOrLoadDialect(name);
558 }
559 
560 llvm::hash_code MLIRContext::getRegistryHash() {
561   llvm::hash_code hash(0);
562   // Factor in number of loaded dialects, attributes, operations, types.
563   hash = llvm::hash_combine(hash, impl->loadedDialects.size());
564   hash = llvm::hash_combine(hash, impl->registeredAttributes.size());
565   hash = llvm::hash_combine(hash, impl->registeredOperations.size());
566   hash = llvm::hash_combine(hash, impl->registeredTypes.size());
567   return hash;
568 }
569 
570 bool MLIRContext::allowsUnregisteredDialects() {
571   return impl->allowUnregisteredDialects;
572 }
573 
574 void MLIRContext::allowUnregisteredDialects(bool allowing) {
575   impl->allowUnregisteredDialects = allowing;
576 }
577 
578 /// Return true if multi-threading is enabled by the context.
579 bool MLIRContext::isMultithreadingEnabled() {
580   return impl->threadingIsEnabled && llvm::llvm_is_multithreaded();
581 }
582 
583 /// Set the flag specifying if multi-threading is disabled by the context.
584 void MLIRContext::disableMultithreading(bool disable) {
585   impl->threadingIsEnabled = !disable;
586 
587   // Update the threading mode for each of the uniquers.
588   impl->affineUniquer.disableMultithreading(disable);
589   impl->attributeUniquer.disableMultithreading(disable);
590   impl->typeUniquer.disableMultithreading(disable);
591 
592   // Destroy thread pool (stop all threads) if it is no longer needed, or create
593   // a new one if multithreading was re-enabled.
594   if (disable) {
595     // If the thread pool is owned, explicitly set it to nullptr to avoid
596     // keeping a dangling pointer around. If the thread pool is externally
597     // owned, we don't do anything.
598     if (impl->ownedThreadPool) {
599       assert(impl->threadPool);
600       impl->threadPool = nullptr;
601       impl->ownedThreadPool.reset();
602     }
603   } else if (!impl->threadPool) {
604     // The thread pool isn't externally provided.
605     assert(!impl->ownedThreadPool);
606     impl->ownedThreadPool = std::make_unique<llvm::ThreadPool>();
607     impl->threadPool = impl->ownedThreadPool.get();
608   }
609 }
610 
611 void MLIRContext::setThreadPool(llvm::ThreadPool &pool) {
612   assert(!isMultithreadingEnabled() &&
613          "expected multi-threading to be disabled when setting a ThreadPool");
614   impl->threadPool = &pool;
615   impl->ownedThreadPool.reset();
616   enableMultithreading();
617 }
618 
619 llvm::ThreadPool &MLIRContext::getThreadPool() {
620   assert(isMultithreadingEnabled() &&
621          "expected multi-threading to be enabled within the context");
622   assert(impl->threadPool &&
623          "multi-threading is enabled but threadpool not set");
624   return *impl->threadPool;
625 }
626 
627 void MLIRContext::enterMultiThreadedExecution() {
628 #ifndef NDEBUG
629   ++impl->multiThreadedExecutionContext;
630 #endif
631 }
632 void MLIRContext::exitMultiThreadedExecution() {
633 #ifndef NDEBUG
634   --impl->multiThreadedExecutionContext;
635 #endif
636 }
637 
638 /// Return true if we should attach the operation to diagnostics emitted via
639 /// Operation::emit.
640 bool MLIRContext::shouldPrintOpOnDiagnostic() {
641   return impl->printOpOnDiagnostic;
642 }
643 
644 /// Set the flag specifying if we should attach the operation to diagnostics
645 /// emitted via Operation::emit.
646 void MLIRContext::printOpOnDiagnostic(bool enable) {
647   impl->printOpOnDiagnostic = enable;
648 }
649 
650 /// Return true if we should attach the current stacktrace to diagnostics when
651 /// emitted.
652 bool MLIRContext::shouldPrintStackTraceOnDiagnostic() {
653   return impl->printStackTraceOnDiagnostic;
654 }
655 
656 /// Set the flag specifying if we should attach the current stacktrace when
657 /// emitting diagnostics.
658 void MLIRContext::printStackTraceOnDiagnostic(bool enable) {
659   impl->printStackTraceOnDiagnostic = enable;
660 }
661 
662 /// Return information about all registered operations.  This isn't very
663 /// efficient, typically you should ask the operations about their properties
664 /// directly.
665 std::vector<AbstractOperation *> MLIRContext::getRegisteredOperations() {
666   // We just have the operations in a non-deterministic hash table order. Dump
667   // into a temporary array, then sort it by operation name to get a stable
668   // ordering.
669   llvm::StringMap<AbstractOperation> &registeredOps =
670       impl->registeredOperations;
671 
672   std::vector<AbstractOperation *> result;
673   result.reserve(registeredOps.size());
674   for (auto &elt : registeredOps)
675     result.push_back(&elt.second);
676   llvm::array_pod_sort(
677       result.begin(), result.end(),
678       [](AbstractOperation *const *lhs, AbstractOperation *const *rhs) {
679         return (*lhs)->name.compare((*rhs)->name);
680       });
681 
682   return result;
683 }
684 
685 bool MLIRContext::isOperationRegistered(StringRef name) {
686   return impl->registeredOperations.count(name);
687 }
688 
689 void Dialect::addType(TypeID typeID, AbstractType &&typeInfo) {
690   auto &impl = context->getImpl();
691   assert(impl.multiThreadedExecutionContext == 0 &&
692          "Registering a new type kind while in a multi-threaded execution "
693          "context");
694   auto *newInfo =
695       new (impl.abstractDialectSymbolAllocator.Allocate<AbstractType>())
696           AbstractType(std::move(typeInfo));
697   if (!impl.registeredTypes.insert({typeID, newInfo}).second)
698     llvm::report_fatal_error("Dialect Type already registered.");
699 }
700 
701 void Dialect::addAttribute(TypeID typeID, AbstractAttribute &&attrInfo) {
702   auto &impl = context->getImpl();
703   assert(impl.multiThreadedExecutionContext == 0 &&
704          "Registering a new attribute kind while in a multi-threaded execution "
705          "context");
706   auto *newInfo =
707       new (impl.abstractDialectSymbolAllocator.Allocate<AbstractAttribute>())
708           AbstractAttribute(std::move(attrInfo));
709   if (!impl.registeredAttributes.insert({typeID, newInfo}).second)
710     llvm::report_fatal_error("Dialect Attribute already registered.");
711 }
712 
713 //===----------------------------------------------------------------------===//
714 // AbstractAttribute
715 //===----------------------------------------------------------------------===//
716 
717 /// Get the dialect that registered the attribute with the provided typeid.
718 const AbstractAttribute &AbstractAttribute::lookup(TypeID typeID,
719                                                    MLIRContext *context) {
720   const AbstractAttribute *abstract = lookupMutable(typeID, context);
721   if (!abstract)
722     llvm::report_fatal_error("Trying to create an Attribute that was not "
723                              "registered in this MLIRContext.");
724   return *abstract;
725 }
726 
727 AbstractAttribute *AbstractAttribute::lookupMutable(TypeID typeID,
728                                                     MLIRContext *context) {
729   auto &impl = context->getImpl();
730   auto it = impl.registeredAttributes.find(typeID);
731   if (it == impl.registeredAttributes.end())
732     return nullptr;
733   return it->second;
734 }
735 
736 //===----------------------------------------------------------------------===//
737 // AbstractOperation
738 //===----------------------------------------------------------------------===//
739 
740 ParseResult AbstractOperation::parseAssembly(OpAsmParser &parser,
741                                              OperationState &result) const {
742   return parseAssemblyFn(parser, result);
743 }
744 
745 /// Look up the specified operation in the operation set and return a pointer
746 /// to it if present. Otherwise, return a null pointer.
747 AbstractOperation *AbstractOperation::lookupMutable(StringRef opName,
748                                                     MLIRContext *context) {
749   auto &impl = context->getImpl();
750   auto it = impl.registeredOperations.find(opName);
751   if (it != impl.registeredOperations.end())
752     return &it->second;
753   return nullptr;
754 }
755 
756 void AbstractOperation::insert(
757     StringRef name, Dialect &dialect, TypeID typeID,
758     ParseAssemblyFn &&parseAssembly, PrintAssemblyFn &&printAssembly,
759     VerifyInvariantsFn &&verifyInvariants, FoldHookFn &&foldHook,
760     GetCanonicalizationPatternsFn &&getCanonicalizationPatterns,
761     detail::InterfaceMap &&interfaceMap, HasTraitFn &&hasTrait,
762     ArrayRef<StringRef> attrNames) {
763   MLIRContext *ctx = dialect.getContext();
764   auto &impl = ctx->getImpl();
765   assert(impl.multiThreadedExecutionContext == 0 &&
766          "Registering a new operation kind while in a multi-threaded execution "
767          "context");
768 
769   // Register the attribute names of this operation.
770   MutableArrayRef<Identifier> cachedAttrNames;
771   if (!attrNames.empty()) {
772     cachedAttrNames = MutableArrayRef<Identifier>(
773         impl.identifierAllocator.Allocate<Identifier>(attrNames.size()),
774         attrNames.size());
775     for (unsigned i : llvm::seq<unsigned>(0, attrNames.size()))
776       new (&cachedAttrNames[i]) Identifier(Identifier::get(attrNames[i], ctx));
777   }
778 
779   // Register the information for this operation.
780   AbstractOperation opInfo(
781       name, dialect, typeID, std::move(parseAssembly), std::move(printAssembly),
782       std::move(verifyInvariants), std::move(foldHook),
783       std::move(getCanonicalizationPatterns), std::move(interfaceMap),
784       std::move(hasTrait), cachedAttrNames);
785   if (!impl.registeredOperations.insert({name, std::move(opInfo)}).second) {
786     llvm::errs() << "error: operation named '" << name
787                  << "' is already registered.\n";
788     abort();
789   }
790 }
791 
792 AbstractOperation::AbstractOperation(
793     StringRef name, Dialect &dialect, TypeID typeID,
794     ParseAssemblyFn &&parseAssembly, PrintAssemblyFn &&printAssembly,
795     VerifyInvariantsFn &&verifyInvariants, FoldHookFn &&foldHook,
796     GetCanonicalizationPatternsFn &&getCanonicalizationPatterns,
797     detail::InterfaceMap &&interfaceMap, HasTraitFn &&hasTrait,
798     ArrayRef<Identifier> attrNames)
799     : name(Identifier::get(name, dialect.getContext())), dialect(dialect),
800       typeID(typeID), interfaceMap(std::move(interfaceMap)),
801       foldHookFn(std::move(foldHook)),
802       getCanonicalizationPatternsFn(std::move(getCanonicalizationPatterns)),
803       hasTraitFn(std::move(hasTrait)),
804       parseAssemblyFn(std::move(parseAssembly)),
805       printAssemblyFn(std::move(printAssembly)),
806       verifyInvariantsFn(std::move(verifyInvariants)),
807       attributeNames(attrNames) {}
808 
809 //===----------------------------------------------------------------------===//
810 // AbstractType
811 //===----------------------------------------------------------------------===//
812 
813 const AbstractType &AbstractType::lookup(TypeID typeID, MLIRContext *context) {
814   const AbstractType *type = lookupMutable(typeID, context);
815   if (!type)
816     llvm::report_fatal_error(
817         "Trying to create a Type that was not registered in this MLIRContext.");
818   return *type;
819 }
820 
821 AbstractType *AbstractType::lookupMutable(TypeID typeID, MLIRContext *context) {
822   auto &impl = context->getImpl();
823   auto it = impl.registeredTypes.find(typeID);
824   if (it == impl.registeredTypes.end())
825     return nullptr;
826   return it->second;
827 }
828 
829 //===----------------------------------------------------------------------===//
830 // Identifier uniquing
831 //===----------------------------------------------------------------------===//
832 
833 /// Return an identifier for the specified string.
834 Identifier Identifier::get(const Twine &string, MLIRContext *context) {
835   SmallString<32> tempStr;
836   StringRef str = string.toStringRef(tempStr);
837 
838   // Check invariants after seeing if we already have something in the
839   // identifier table - if we already had it in the table, then it already
840   // passed invariant checks.
841   assert(!str.empty() && "Cannot create an empty identifier");
842   assert(str.find('\0') == StringRef::npos &&
843          "Cannot create an identifier with a nul character");
844 
845   auto getDialectOrContext = [&]() {
846     PointerUnion<Dialect *, MLIRContext *> dialectOrContext = context;
847     auto dialectNamePair = str.split('.');
848     if (!dialectNamePair.first.empty())
849       if (Dialect *dialect = context->getLoadedDialect(dialectNamePair.first))
850         dialectOrContext = dialect;
851     return dialectOrContext;
852   };
853 
854   auto &impl = context->getImpl();
855   if (!context->isMultithreadingEnabled()) {
856     auto insertedIt = impl.identifiers.insert({str, nullptr});
857     if (insertedIt.second)
858       insertedIt.first->second = getDialectOrContext();
859     return Identifier(&*insertedIt.first);
860   }
861 
862   // Check for an existing identifier in read-only mode.
863   {
864     llvm::sys::SmartScopedReader<true> contextLock(impl.identifierMutex);
865     auto it = impl.identifiers.find(str);
866     if (it != impl.identifiers.end())
867       return Identifier(&*it);
868   }
869 
870   // Acquire a writer-lock so that we can safely create the new instance.
871   llvm::sys::SmartScopedWriter<true> contextLock(impl.identifierMutex);
872   auto it = impl.identifiers.insert({str, getDialectOrContext()}).first;
873   return Identifier(&*it);
874 }
875 
876 Dialect *Identifier::getDialect() {
877   return entry->second.dyn_cast<Dialect *>();
878 }
879 
880 MLIRContext *Identifier::getContext() {
881   if (Dialect *dialect = getDialect())
882     return dialect->getContext();
883   return entry->second.get<MLIRContext *>();
884 }
885 
886 //===----------------------------------------------------------------------===//
887 // Type uniquing
888 //===----------------------------------------------------------------------===//
889 
890 /// Returns the storage uniquer used for constructing type storage instances.
891 /// This should not be used directly.
892 StorageUniquer &MLIRContext::getTypeUniquer() { return getImpl().typeUniquer; }
893 
894 BFloat16Type BFloat16Type::get(MLIRContext *context) {
895   return context->getImpl().bf16Ty;
896 }
897 Float16Type Float16Type::get(MLIRContext *context) {
898   return context->getImpl().f16Ty;
899 }
900 Float32Type Float32Type::get(MLIRContext *context) {
901   return context->getImpl().f32Ty;
902 }
903 Float64Type Float64Type::get(MLIRContext *context) {
904   return context->getImpl().f64Ty;
905 }
906 Float80Type Float80Type::get(MLIRContext *context) {
907   return context->getImpl().f80Ty;
908 }
909 Float128Type Float128Type::get(MLIRContext *context) {
910   return context->getImpl().f128Ty;
911 }
912 
913 /// Get an instance of the IndexType.
914 IndexType IndexType::get(MLIRContext *context) {
915   return context->getImpl().indexTy;
916 }
917 
918 /// Return an existing integer type instance if one is cached within the
919 /// context.
920 static IntegerType
921 getCachedIntegerType(unsigned width,
922                      IntegerType::SignednessSemantics signedness,
923                      MLIRContext *context) {
924   if (signedness != IntegerType::Signless)
925     return IntegerType();
926 
927   switch (width) {
928   case 1:
929     return context->getImpl().int1Ty;
930   case 8:
931     return context->getImpl().int8Ty;
932   case 16:
933     return context->getImpl().int16Ty;
934   case 32:
935     return context->getImpl().int32Ty;
936   case 64:
937     return context->getImpl().int64Ty;
938   case 128:
939     return context->getImpl().int128Ty;
940   default:
941     return IntegerType();
942   }
943 }
944 
945 IntegerType IntegerType::get(MLIRContext *context, unsigned width,
946                              IntegerType::SignednessSemantics signedness) {
947   if (auto cached = getCachedIntegerType(width, signedness, context))
948     return cached;
949   return Base::get(context, width, signedness);
950 }
951 
952 IntegerType
953 IntegerType::getChecked(function_ref<InFlightDiagnostic()> emitError,
954                         MLIRContext *context, unsigned width,
955                         SignednessSemantics signedness) {
956   if (auto cached = getCachedIntegerType(width, signedness, context))
957     return cached;
958   return Base::getChecked(emitError, context, width, signedness);
959 }
960 
961 /// Get an instance of the NoneType.
962 NoneType NoneType::get(MLIRContext *context) {
963   if (NoneType cachedInst = context->getImpl().noneType)
964     return cachedInst;
965   // Note: May happen when initializing the singleton attributes of the builtin
966   // dialect.
967   return Base::get(context);
968 }
969 
970 //===----------------------------------------------------------------------===//
971 // Attribute uniquing
972 //===----------------------------------------------------------------------===//
973 
974 /// Returns the storage uniquer used for constructing attribute storage
975 /// instances. This should not be used directly.
976 StorageUniquer &MLIRContext::getAttributeUniquer() {
977   return getImpl().attributeUniquer;
978 }
979 
980 /// Initialize the given attribute storage instance.
981 void AttributeUniquer::initializeAttributeStorage(AttributeStorage *storage,
982                                                   MLIRContext *ctx,
983                                                   TypeID attrID) {
984   storage->initialize(AbstractAttribute::lookup(attrID, ctx));
985 
986   // If the attribute did not provide a type, then default to NoneType.
987   if (!storage->getType())
988     storage->setType(NoneType::get(ctx));
989 }
990 
991 BoolAttr BoolAttr::get(MLIRContext *context, bool value) {
992   return value ? context->getImpl().trueAttr : context->getImpl().falseAttr;
993 }
994 
995 UnitAttr UnitAttr::get(MLIRContext *context) {
996   return context->getImpl().unitAttr;
997 }
998 
999 UnknownLoc UnknownLoc::get(MLIRContext *context) {
1000   return context->getImpl().unknownLocAttr;
1001 }
1002 
1003 /// Return empty dictionary.
1004 DictionaryAttr DictionaryAttr::getEmpty(MLIRContext *context) {
1005   return context->getImpl().emptyDictionaryAttr;
1006 }
1007 
1008 /// Return an empty string.
1009 StringAttr StringAttr::get(MLIRContext *context) {
1010   return context->getImpl().emptyStringAttr;
1011 }
1012 
1013 //===----------------------------------------------------------------------===//
1014 // AffineMap uniquing
1015 //===----------------------------------------------------------------------===//
1016 
1017 StorageUniquer &MLIRContext::getAffineUniquer() {
1018   return getImpl().affineUniquer;
1019 }
1020 
1021 AffineMap AffineMap::getImpl(unsigned dimCount, unsigned symbolCount,
1022                              ArrayRef<AffineExpr> results,
1023                              MLIRContext *context) {
1024   auto &impl = context->getImpl();
1025   auto key = std::make_tuple(dimCount, symbolCount, results);
1026 
1027   // Safely get or create an AffineMap instance.
1028   return safeGetOrCreate(
1029       impl.affineMaps, key, impl.affineMutex, impl.threadingIsEnabled, [&] {
1030         auto *res = impl.affineAllocator.Allocate<detail::AffineMapStorage>();
1031 
1032         // Copy the results into the bump pointer.
1033         results = copyArrayRefInto(impl.affineAllocator, results);
1034 
1035         // Initialize the memory using placement new.
1036         new (res)
1037             detail::AffineMapStorage{dimCount, symbolCount, results, context};
1038         return AffineMap(res);
1039       });
1040 }
1041 
1042 AffineMap AffineMap::get(MLIRContext *context) {
1043   return getImpl(/*dimCount=*/0, /*symbolCount=*/0, /*results=*/{}, context);
1044 }
1045 
1046 AffineMap AffineMap::get(unsigned dimCount, unsigned symbolCount,
1047                          MLIRContext *context) {
1048   return getImpl(dimCount, symbolCount, /*results=*/{}, context);
1049 }
1050 
1051 AffineMap AffineMap::get(unsigned dimCount, unsigned symbolCount,
1052                          AffineExpr result) {
1053   return getImpl(dimCount, symbolCount, {result}, result.getContext());
1054 }
1055 
1056 AffineMap AffineMap::get(unsigned dimCount, unsigned symbolCount,
1057                          ArrayRef<AffineExpr> results, MLIRContext *context) {
1058   return getImpl(dimCount, symbolCount, results, context);
1059 }
1060 
1061 //===----------------------------------------------------------------------===//
1062 // Integer Sets: these are allocated into the bump pointer, and are immutable.
1063 // Unlike AffineMap's, these are uniqued only if they are small.
1064 //===----------------------------------------------------------------------===//
1065 
1066 IntegerSet IntegerSet::get(unsigned dimCount, unsigned symbolCount,
1067                            ArrayRef<AffineExpr> constraints,
1068                            ArrayRef<bool> eqFlags) {
1069   // The number of constraints can't be zero.
1070   assert(!constraints.empty());
1071   assert(constraints.size() == eqFlags.size());
1072 
1073   auto &impl = constraints[0].getContext()->getImpl();
1074 
1075   // A utility function to construct a new IntegerSetStorage instance.
1076   auto constructorFn = [&] {
1077     auto *res = impl.affineAllocator.Allocate<detail::IntegerSetStorage>();
1078 
1079     // Copy the results and equality flags into the bump pointer.
1080     constraints = copyArrayRefInto(impl.affineAllocator, constraints);
1081     eqFlags = copyArrayRefInto(impl.affineAllocator, eqFlags);
1082 
1083     // Initialize the memory using placement new.
1084     new (res)
1085         detail::IntegerSetStorage{dimCount, symbolCount, constraints, eqFlags};
1086     return IntegerSet(res);
1087   };
1088 
1089   // If this instance is uniqued, then we handle it separately so that multiple
1090   // threads may simultaneously access existing instances.
1091   if (constraints.size() < IntegerSet::kUniquingThreshold) {
1092     auto key = std::make_tuple(dimCount, symbolCount, constraints, eqFlags);
1093     return safeGetOrCreate(impl.integerSets, key, impl.affineMutex,
1094                            impl.threadingIsEnabled, constructorFn);
1095   }
1096 
1097   // Otherwise, acquire a writer-lock so that we can safely create the new
1098   // instance.
1099   ScopedWriterLock affineLock(impl.affineMutex, impl.threadingIsEnabled);
1100   return constructorFn();
1101 }
1102 
1103 //===----------------------------------------------------------------------===//
1104 // StorageUniquerSupport
1105 //===----------------------------------------------------------------------===//
1106 
1107 /// Utility method to generate a callback that can be used to generate a
1108 /// diagnostic when checking the construction invariants of a storage object.
1109 /// This is defined out-of-line to avoid the need to include Location.h.
1110 llvm::unique_function<InFlightDiagnostic()>
1111 mlir::detail::getDefaultDiagnosticEmitFn(MLIRContext *ctx) {
1112   return [ctx] { return emitError(UnknownLoc::get(ctx)); };
1113 }
1114 llvm::unique_function<InFlightDiagnostic()>
1115 mlir::detail::getDefaultDiagnosticEmitFn(const Location &loc) {
1116   return [=] { return emitError(loc); };
1117 }
1118