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 "LocationDetail.h"
15 #include "TypeDetail.h"
16 #include "mlir/IR/AffineExpr.h"
17 #include "mlir/IR/AffineMap.h"
18 #include "mlir/IR/Attributes.h"
19 #include "mlir/IR/Diagnostics.h"
20 #include "mlir/IR/Dialect.h"
21 #include "mlir/IR/Function.h"
22 #include "mlir/IR/Identifier.h"
23 #include "mlir/IR/IntegerSet.h"
24 #include "mlir/IR/Location.h"
25 #include "mlir/IR/Module.h"
26 #include "mlir/IR/Types.h"
27 #include "mlir/Support/STLExtras.h"
28 #include "llvm/ADT/DenseMap.h"
29 #include "llvm/ADT/DenseSet.h"
30 #include "llvm/ADT/SetVector.h"
31 #include "llvm/ADT/StringMap.h"
32 #include "llvm/ADT/Twine.h"
33 #include "llvm/Support/Allocator.h"
34 #include "llvm/Support/CommandLine.h"
35 #include "llvm/Support/RWMutex.h"
36 #include "llvm/Support/raw_ostream.h"
37 #include <memory>
38 
39 using namespace mlir;
40 using namespace mlir::detail;
41 
42 using llvm::hash_combine;
43 using llvm::hash_combine_range;
44 
45 static llvm::cl::opt<bool> clPrintOpOnDiagnostic(
46     "mlir-print-op-on-diagnostic",
47     llvm::cl::desc("When a diagnostic is emitted on an operation, also print "
48                    "the operation as an attached note"),
49     llvm::cl::init(true));
50 
51 static llvm::cl::opt<bool> clPrintStackTraceOnDiagnostic(
52     "mlir-print-stacktrace-on-diagnostic",
53     llvm::cl::desc("When a diagnostic is emitted, also print the stack trace "
54                    "as an attached note"));
55 
56 /// A utility function to safely get or create a uniqued instance within the
57 /// given set container.
58 template <typename ValueT, typename DenseInfoT, typename KeyT,
59           typename ConstructorFn>
60 static ValueT safeGetOrCreate(DenseSet<ValueT, DenseInfoT> &container,
61                               KeyT &&key, llvm::sys::SmartRWMutex<true> &mutex,
62                               ConstructorFn &&constructorFn) {
63   { // Check for an existing instance in read-only mode.
64     llvm::sys::SmartScopedReader<true> instanceLock(mutex);
65     auto it = container.find_as(key);
66     if (it != container.end())
67       return *it;
68   }
69 
70   // Acquire a writer-lock so that we can safely create the new instance.
71   llvm::sys::SmartScopedWriter<true> instanceLock(mutex);
72 
73   // Check for an existing instance again here, because another writer thread
74   // may have already created one.
75   auto existing = container.insert_as(ValueT(), key);
76   if (!existing.second)
77     return *existing.first;
78 
79   // Otherwise, construct a new instance of the value.
80   return *existing.first = constructorFn();
81 }
82 
83 namespace {
84 /// A builtin dialect to define types/etc that are necessary for the validity of
85 /// the IR.
86 struct BuiltinDialect : public Dialect {
87   BuiltinDialect(MLIRContext *context) : Dialect(/*name=*/"", context) {
88     addAttributes<AffineMapAttr, ArrayAttr, BoolAttr, DenseElementsAttr,
89                   DictionaryAttr, FloatAttr, SymbolRefAttr, IntegerAttr,
90                   IntegerSetAttr, OpaqueAttr, OpaqueElementsAttr,
91                   SparseElementsAttr, StringAttr, TypeAttr, UnitAttr>();
92     addAttributes<CallSiteLoc, FileLineColLoc, FusedLoc, NameLoc, OpaqueLoc,
93                   UnknownLoc>();
94 
95     addTypes<ComplexType, FloatType, FunctionType, IndexType, IntegerType,
96              MemRefType, UnrankedMemRefType, NoneType, OpaqueType,
97              RankedTensorType, TupleType, UnrankedTensorType, VectorType>();
98 
99     // TODO: These operations should be moved to a different dialect when they
100     // have been fully decoupled from the core.
101     addOperations<FuncOp, ModuleOp, ModuleTerminatorOp>();
102   }
103 };
104 
105 struct AffineMapKeyInfo : DenseMapInfo<AffineMap> {
106   // Affine maps are uniqued based on their dim/symbol counts and affine
107   // expressions.
108   using KeyTy = std::tuple<unsigned, unsigned, ArrayRef<AffineExpr>>;
109   using DenseMapInfo<AffineMap>::isEqual;
110 
111   static unsigned getHashValue(const AffineMap &key) {
112     return getHashValue(
113         KeyTy(key.getNumDims(), key.getNumSymbols(), key.getResults()));
114   }
115 
116   static unsigned getHashValue(KeyTy key) {
117     return hash_combine(
118         std::get<0>(key), std::get<1>(key),
119         hash_combine_range(std::get<2>(key).begin(), std::get<2>(key).end()));
120   }
121 
122   static bool isEqual(const KeyTy &lhs, AffineMap rhs) {
123     if (rhs == getEmptyKey() || rhs == getTombstoneKey())
124       return false;
125     return lhs == std::make_tuple(rhs.getNumDims(), rhs.getNumSymbols(),
126                                   rhs.getResults());
127   }
128 };
129 
130 struct IntegerSetKeyInfo : DenseMapInfo<IntegerSet> {
131   // Integer sets are uniqued based on their dim/symbol counts, affine
132   // expressions appearing in the LHS of constraints, and eqFlags.
133   using KeyTy =
134       std::tuple<unsigned, unsigned, ArrayRef<AffineExpr>, ArrayRef<bool>>;
135   using DenseMapInfo<IntegerSet>::isEqual;
136 
137   static unsigned getHashValue(const IntegerSet &key) {
138     return getHashValue(KeyTy(key.getNumDims(), key.getNumSymbols(),
139                               key.getConstraints(), key.getEqFlags()));
140   }
141 
142   static unsigned getHashValue(KeyTy key) {
143     return hash_combine(
144         std::get<0>(key), std::get<1>(key),
145         hash_combine_range(std::get<2>(key).begin(), std::get<2>(key).end()),
146         hash_combine_range(std::get<3>(key).begin(), std::get<3>(key).end()));
147   }
148 
149   static bool isEqual(const KeyTy &lhs, IntegerSet rhs) {
150     if (rhs == getEmptyKey() || rhs == getTombstoneKey())
151       return false;
152     return lhs == std::make_tuple(rhs.getNumDims(), rhs.getNumSymbols(),
153                                   rhs.getConstraints(), rhs.getEqFlags());
154   }
155 };
156 } // end anonymous namespace.
157 
158 namespace mlir {
159 /// This is the implementation of the MLIRContext class, using the pImpl idiom.
160 /// This class is completely private to this file, so everything is public.
161 class MLIRContextImpl {
162 public:
163   //===--------------------------------------------------------------------===//
164   // Identifier uniquing
165   //===--------------------------------------------------------------------===//
166 
167   // Identifier allocator and mutex for thread safety.
168   llvm::BumpPtrAllocator identifierAllocator;
169   llvm::sys::SmartRWMutex<true> identifierMutex;
170 
171   //===--------------------------------------------------------------------===//
172   // Diagnostics
173   //===--------------------------------------------------------------------===//
174   DiagnosticEngine diagEngine;
175 
176   //===--------------------------------------------------------------------===//
177   // Options
178   //===--------------------------------------------------------------------===//
179 
180   /// In most cases, creating operation in unregistered dialect is not desired
181   /// and indicate a misconfiguration of the compiler. This option enables to
182   /// detect such use cases
183   bool allowUnregisteredDialects = false;
184 
185   /// If the operation should be attached to diagnostics printed via the
186   /// Operation::emit methods.
187   bool printOpOnDiagnostic;
188 
189   /// If the current stack trace should be attached when emitting diagnostics.
190   bool printStackTraceOnDiagnostic;
191 
192   //===--------------------------------------------------------------------===//
193   // Other
194   //===--------------------------------------------------------------------===//
195 
196   /// A general purpose mutex to lock access to parts of the context that do not
197   /// have a more specific mutex, e.g. registry operations.
198   llvm::sys::SmartRWMutex<true> contextMutex;
199 
200   /// This is a list of dialects that are created referring to this context.
201   /// The MLIRContext owns the objects.
202   std::vector<std::unique_ptr<Dialect>> dialects;
203 
204   /// This is a mapping from operation name to AbstractOperation for registered
205   /// operations.
206   llvm::StringMap<AbstractOperation> registeredOperations;
207 
208   /// This is a mapping from class identifier to Dialect for registered
209   /// attributes and types.
210   DenseMap<const ClassID *, Dialect *> registeredDialectSymbols;
211 
212   /// These are identifiers uniqued into this MLIRContext.
213   llvm::StringMap<char, llvm::BumpPtrAllocator &> identifiers;
214 
215   //===--------------------------------------------------------------------===//
216   // Affine uniquing
217   //===--------------------------------------------------------------------===//
218 
219   // Affine allocator and mutex for thread safety.
220   llvm::BumpPtrAllocator affineAllocator;
221   llvm::sys::SmartRWMutex<true> affineMutex;
222 
223   // Affine map uniquing.
224   using AffineMapSet = DenseSet<AffineMap, AffineMapKeyInfo>;
225   AffineMapSet affineMaps;
226 
227   // Integer set uniquing.
228   using IntegerSets = DenseSet<IntegerSet, IntegerSetKeyInfo>;
229   IntegerSets integerSets;
230 
231   // Affine expression uniquing.
232   StorageUniquer affineUniquer;
233 
234   //===--------------------------------------------------------------------===//
235   // Type uniquing
236   //===--------------------------------------------------------------------===//
237   StorageUniquer typeUniquer;
238 
239   /// Cached Type Instances.
240   FloatType bf16Ty, f16Ty, f32Ty, f64Ty;
241   IndexType indexTy;
242   IntegerType int1Ty, int8Ty, int16Ty, int32Ty, int64Ty, int128Ty;
243   NoneType noneType;
244 
245   //===--------------------------------------------------------------------===//
246   // Attribute uniquing
247   //===--------------------------------------------------------------------===//
248   StorageUniquer attributeUniquer;
249 
250   /// Cached Attribute Instances.
251   BoolAttr falseAttr, trueAttr;
252   UnitAttr unitAttr;
253   UnknownLoc unknownLocAttr;
254 
255 public:
256   MLIRContextImpl()
257       : printOpOnDiagnostic(clPrintOpOnDiagnostic),
258         printStackTraceOnDiagnostic(clPrintStackTraceOnDiagnostic),
259         identifiers(identifierAllocator) {}
260 };
261 } // end namespace mlir
262 
263 MLIRContext::MLIRContext() : impl(new MLIRContextImpl()) {
264   new BuiltinDialect(this);
265   registerAllDialects(this);
266 
267   // Initialize several common attributes and types to avoid the need to lock
268   // the context when accessing them.
269 
270   //// Types.
271   /// Floating-point Types.
272   impl->bf16Ty = TypeUniquer::get<FloatType>(this, StandardTypes::BF16);
273   impl->f16Ty = TypeUniquer::get<FloatType>(this, StandardTypes::F16);
274   impl->f32Ty = TypeUniquer::get<FloatType>(this, StandardTypes::F32);
275   impl->f64Ty = TypeUniquer::get<FloatType>(this, StandardTypes::F64);
276   /// Index Type.
277   impl->indexTy = TypeUniquer::get<IndexType>(this, StandardTypes::Index);
278   /// Integer Types.
279   impl->int1Ty = TypeUniquer::get<IntegerType>(this, StandardTypes::Integer, 1,
280                                                IntegerType::Signless);
281   impl->int8Ty = TypeUniquer::get<IntegerType>(this, StandardTypes::Integer, 8,
282                                                IntegerType::Signless);
283   impl->int16Ty = TypeUniquer::get<IntegerType>(this, StandardTypes::Integer,
284                                                 16, IntegerType::Signless);
285   impl->int32Ty = TypeUniquer::get<IntegerType>(this, StandardTypes::Integer,
286                                                 32, IntegerType::Signless);
287   impl->int64Ty = TypeUniquer::get<IntegerType>(this, StandardTypes::Integer,
288                                                 64, IntegerType::Signless);
289   impl->int128Ty = TypeUniquer::get<IntegerType>(this, StandardTypes::Integer,
290                                                  128, IntegerType::Signless);
291   /// None Type.
292   impl->noneType = TypeUniquer::get<NoneType>(this, StandardTypes::None);
293 
294   //// Attributes.
295   //// Note: These must be registered after the types as they may generate one
296   //// of the above types internally.
297   /// Bool Attributes.
298   // Note: The context is also used within the BoolAttrStorage.
299   impl->falseAttr = AttributeUniquer::get<BoolAttr>(
300       this, StandardAttributes::Bool, this, false);
301   impl->trueAttr = AttributeUniquer::get<BoolAttr>(
302       this, StandardAttributes::Bool, this, true);
303   /// Unit Attribute.
304   impl->unitAttr =
305       AttributeUniquer::get<UnitAttr>(this, StandardAttributes::Unit);
306   /// Unknown Location Attribute.
307   impl->unknownLocAttr = AttributeUniquer::get<UnknownLoc>(
308       this, StandardAttributes::UnknownLocation);
309 }
310 
311 MLIRContext::~MLIRContext() {}
312 
313 /// Copy the specified array of elements into memory managed by the provided
314 /// bump pointer allocator.  This assumes the elements are all PODs.
315 template <typename T>
316 static ArrayRef<T> copyArrayRefInto(llvm::BumpPtrAllocator &allocator,
317                                     ArrayRef<T> elements) {
318   auto result = allocator.Allocate<T>(elements.size());
319   std::uninitialized_copy(elements.begin(), elements.end(), result);
320   return ArrayRef<T>(result, elements.size());
321 }
322 
323 //===----------------------------------------------------------------------===//
324 // Diagnostic Handlers
325 //===----------------------------------------------------------------------===//
326 
327 /// Returns the diagnostic engine for this context.
328 DiagnosticEngine &MLIRContext::getDiagEngine() { return getImpl().diagEngine; }
329 
330 //===----------------------------------------------------------------------===//
331 // Dialect and Operation Registration
332 //===----------------------------------------------------------------------===//
333 
334 /// Return information about all registered IR dialects.
335 std::vector<Dialect *> MLIRContext::getRegisteredDialects() {
336   // Lock access to the context registry.
337   llvm::sys::SmartScopedReader<true> registryLock(getImpl().contextMutex);
338 
339   std::vector<Dialect *> result;
340   result.reserve(getImpl().dialects.size());
341   for (auto &dialect : getImpl().dialects)
342     result.push_back(dialect.get());
343   return result;
344 }
345 
346 /// Get a registered IR dialect with the given namespace. If none is found,
347 /// then return nullptr.
348 Dialect *MLIRContext::getRegisteredDialect(StringRef name) {
349   // Lock access to the context registry.
350   llvm::sys::SmartScopedReader<true> registryLock(getImpl().contextMutex);
351   for (auto &dialect : getImpl().dialects)
352     if (name == dialect->getNamespace())
353       return dialect.get();
354   return nullptr;
355 }
356 
357 /// Register this dialect object with the specified context.  The context
358 /// takes ownership of the heap allocated dialect.
359 void Dialect::registerDialect(MLIRContext *context) {
360   auto &impl = context->getImpl();
361   std::unique_ptr<Dialect> dialect(this);
362 
363   // Lock access to the context registry.
364   llvm::sys::SmartScopedWriter<true> registryLock(impl.contextMutex);
365 
366   // Get the correct insertion position sorted by namespace.
367   auto insertPt =
368       llvm::lower_bound(impl.dialects, dialect,
369                         [](const std::unique_ptr<Dialect> &lhs,
370                            const std::unique_ptr<Dialect> &rhs) {
371                           return lhs->getNamespace() < rhs->getNamespace();
372                         });
373 
374   // Abort if dialect with namespace has already been registered.
375   if (insertPt != impl.dialects.end() &&
376       (*insertPt)->getNamespace() == getNamespace()) {
377     llvm::report_fatal_error("a dialect with namespace '" + getNamespace() +
378                              "' has already been registered");
379   }
380   impl.dialects.insert(insertPt, std::move(dialect));
381 }
382 
383 bool MLIRContext::allowsUnregisteredDialects() {
384   return impl->allowUnregisteredDialects;
385 }
386 
387 void MLIRContext::allowUnregisteredDialects(bool allowing) {
388   impl->allowUnregisteredDialects = allowing;
389 }
390 
391 /// Return true if we should attach the operation to diagnostics emitted via
392 /// Operation::emit.
393 bool MLIRContext::shouldPrintOpOnDiagnostic() {
394   return impl->printOpOnDiagnostic;
395 }
396 
397 /// Set the flag specifying if we should attach the operation to diagnostics
398 /// emitted via Operation::emit.
399 void MLIRContext::printOpOnDiagnostic(bool enable) {
400   // Let the command line option take priority.
401   if (!clPrintOpOnDiagnostic.getNumOccurrences())
402     impl->printOpOnDiagnostic = enable;
403 }
404 
405 /// Return true if we should attach the current stacktrace to diagnostics when
406 /// emitted.
407 bool MLIRContext::shouldPrintStackTraceOnDiagnostic() {
408   return impl->printStackTraceOnDiagnostic;
409 }
410 
411 /// Set the flag specifying if we should attach the current stacktrace when
412 /// emitting diagnostics.
413 void MLIRContext::printStackTraceOnDiagnostic(bool enable) {
414   // Let the command line option take priority.
415   if (!clPrintStackTraceOnDiagnostic.getNumOccurrences())
416     impl->printStackTraceOnDiagnostic = enable;
417 }
418 
419 /// Return information about all registered operations.  This isn't very
420 /// efficient, typically you should ask the operations about their properties
421 /// directly.
422 std::vector<AbstractOperation *> MLIRContext::getRegisteredOperations() {
423   std::vector<std::pair<StringRef, AbstractOperation *>> opsToSort;
424 
425   { // Lock access to the context registry.
426     llvm::sys::SmartScopedReader<true> registryLock(getImpl().contextMutex);
427 
428     // We just have the operations in a non-deterministic hash table order. Dump
429     // into a temporary array, then sort it by operation name to get a stable
430     // ordering.
431     llvm::StringMap<AbstractOperation> &registeredOps =
432         getImpl().registeredOperations;
433 
434     opsToSort.reserve(registeredOps.size());
435     for (auto &elt : registeredOps)
436       opsToSort.push_back({elt.first(), &elt.second});
437   }
438 
439   llvm::array_pod_sort(opsToSort.begin(), opsToSort.end());
440 
441   std::vector<AbstractOperation *> result;
442   result.reserve(opsToSort.size());
443   for (auto &elt : opsToSort)
444     result.push_back(elt.second);
445   return result;
446 }
447 
448 void Dialect::addOperation(AbstractOperation opInfo) {
449   assert((getNamespace().empty() ||
450           opInfo.name.split('.').first == getNamespace()) &&
451          "op name doesn't start with dialect namespace");
452   assert(&opInfo.dialect == this && "Dialect object mismatch");
453   auto &impl = context->getImpl();
454 
455   // Lock access to the context registry.
456   llvm::sys::SmartScopedWriter<true> registryLock(impl.contextMutex);
457   if (!impl.registeredOperations.insert({opInfo.name, opInfo}).second) {
458     llvm::errs() << "error: operation named '" << opInfo.name
459                  << "' is already registered.\n";
460     abort();
461   }
462 }
463 
464 /// Register a dialect-specific symbol(e.g. type) with the current context.
465 void Dialect::addSymbol(const ClassID *const classID) {
466   auto &impl = context->getImpl();
467 
468   // Lock access to the context registry.
469   llvm::sys::SmartScopedWriter<true> registryLock(impl.contextMutex);
470   if (!impl.registeredDialectSymbols.insert({classID, this}).second) {
471     llvm::errs() << "error: dialect symbol already registered.\n";
472     abort();
473   }
474 }
475 
476 /// Look up the specified operation in the operation set and return a pointer
477 /// to it if present.  Otherwise, return a null pointer.
478 const AbstractOperation *AbstractOperation::lookup(StringRef opName,
479                                                    MLIRContext *context) {
480   auto &impl = context->getImpl();
481 
482   // Lock access to the context registry.
483   llvm::sys::SmartScopedReader<true> registryLock(impl.contextMutex);
484   auto it = impl.registeredOperations.find(opName);
485   if (it != impl.registeredOperations.end())
486     return &it->second;
487   return nullptr;
488 }
489 
490 //===----------------------------------------------------------------------===//
491 // Identifier uniquing
492 //===----------------------------------------------------------------------===//
493 
494 /// Return an identifier for the specified string.
495 Identifier Identifier::get(StringRef str, MLIRContext *context) {
496   assert(!str.empty() && "Cannot create an empty identifier");
497   assert(str.find('\0') == StringRef::npos &&
498          "Cannot create an identifier with a nul character");
499 
500   auto &impl = context->getImpl();
501 
502   { // Check for an existing identifier in read-only mode.
503     llvm::sys::SmartScopedReader<true> contextLock(impl.identifierMutex);
504     auto it = impl.identifiers.find(str);
505     if (it != impl.identifiers.end())
506       return Identifier(it->getKeyData());
507   }
508 
509   // Acquire a writer-lock so that we can safely create the new instance.
510   llvm::sys::SmartScopedWriter<true> contextLock(impl.identifierMutex);
511   auto it = impl.identifiers.insert({str, char()}).first;
512   return Identifier(it->getKeyData());
513 }
514 
515 //===----------------------------------------------------------------------===//
516 // Type uniquing
517 //===----------------------------------------------------------------------===//
518 
519 static Dialect &lookupDialectForSymbol(MLIRContext *ctx,
520                                        const ClassID *const classID) {
521   auto &impl = ctx->getImpl();
522   auto it = impl.registeredDialectSymbols.find(classID);
523   assert(it != impl.registeredDialectSymbols.end() &&
524          "symbol is not registered.");
525   return *it->second;
526 }
527 
528 /// Returns the storage uniquer used for constructing type storage instances.
529 /// This should not be used directly.
530 StorageUniquer &MLIRContext::getTypeUniquer() { return getImpl().typeUniquer; }
531 
532 /// Get the dialect that registered the type with the provided typeid.
533 Dialect &TypeUniquer::lookupDialectForType(MLIRContext *ctx,
534                                            const ClassID *const typeID) {
535   return lookupDialectForSymbol(ctx, typeID);
536 }
537 
538 FloatType FloatType::get(StandardTypes::Kind kind, MLIRContext *context) {
539   assert(kindof(kind) && "Not a FP kind.");
540   switch (kind) {
541   case StandardTypes::BF16:
542     return context->getImpl().bf16Ty;
543   case StandardTypes::F16:
544     return context->getImpl().f16Ty;
545   case StandardTypes::F32:
546     return context->getImpl().f32Ty;
547   case StandardTypes::F64:
548     return context->getImpl().f64Ty;
549   default:
550     llvm_unreachable("unexpected floating-point kind");
551   }
552 }
553 
554 /// Get an instance of the IndexType.
555 IndexType IndexType::get(MLIRContext *context) {
556   return context->getImpl().indexTy;
557 }
558 
559 /// Return an existing integer type instance if one is cached within the
560 /// context.
561 static IntegerType
562 getCachedIntegerType(unsigned width,
563                      IntegerType::SignednessSemantics signedness,
564                      MLIRContext *context) {
565   if (signedness != IntegerType::Signless)
566     return IntegerType();
567 
568   switch (width) {
569   case 1:
570     return context->getImpl().int1Ty;
571   case 8:
572     return context->getImpl().int8Ty;
573   case 16:
574     return context->getImpl().int16Ty;
575   case 32:
576     return context->getImpl().int32Ty;
577   case 64:
578     return context->getImpl().int64Ty;
579   case 128:
580     return context->getImpl().int128Ty;
581   default:
582     return IntegerType();
583   }
584 }
585 
586 IntegerType IntegerType::get(unsigned width, MLIRContext *context) {
587   return get(width, IntegerType::Signless, context);
588 }
589 
590 IntegerType IntegerType::get(unsigned width,
591                              IntegerType::SignednessSemantics signedness,
592                              MLIRContext *context) {
593   if (auto cached = getCachedIntegerType(width, signedness, context))
594     return cached;
595   return Base::get(context, StandardTypes::Integer, width, signedness);
596 }
597 
598 IntegerType IntegerType::getChecked(unsigned width, Location location) {
599   return getChecked(width, IntegerType::Signless, location);
600 }
601 
602 IntegerType IntegerType::getChecked(unsigned width,
603                                     SignednessSemantics signedness,
604                                     Location location) {
605   if (auto cached =
606           getCachedIntegerType(width, signedness, location->getContext()))
607     return cached;
608   return Base::getChecked(location, StandardTypes::Integer, width, signedness);
609 }
610 
611 /// Get an instance of the NoneType.
612 NoneType NoneType::get(MLIRContext *context) {
613   return context->getImpl().noneType;
614 }
615 
616 //===----------------------------------------------------------------------===//
617 // Attribute uniquing
618 //===----------------------------------------------------------------------===//
619 
620 /// Returns the storage uniquer used for constructing attribute storage
621 /// instances. This should not be used directly.
622 StorageUniquer &MLIRContext::getAttributeUniquer() {
623   return getImpl().attributeUniquer;
624 }
625 
626 /// Returns a functor used to initialize new attribute storage instances.
627 std::function<void(AttributeStorage *)>
628 AttributeUniquer::getInitFn(MLIRContext *ctx, const ClassID *const attrID) {
629   return [ctx, attrID](AttributeStorage *storage) {
630     storage->initializeDialect(lookupDialectForSymbol(ctx, attrID));
631 
632     // If the attribute did not provide a type, then default to NoneType.
633     if (!storage->getType())
634       storage->setType(NoneType::get(ctx));
635   };
636 }
637 
638 BoolAttr BoolAttr::get(bool value, MLIRContext *context) {
639   return value ? context->getImpl().trueAttr : context->getImpl().falseAttr;
640 }
641 
642 UnitAttr UnitAttr::get(MLIRContext *context) {
643   return context->getImpl().unitAttr;
644 }
645 
646 Location UnknownLoc::get(MLIRContext *context) {
647   return context->getImpl().unknownLocAttr;
648 }
649 
650 //===----------------------------------------------------------------------===//
651 // AffineMap uniquing
652 //===----------------------------------------------------------------------===//
653 
654 StorageUniquer &MLIRContext::getAffineUniquer() {
655   return getImpl().affineUniquer;
656 }
657 
658 AffineMap AffineMap::getImpl(unsigned dimCount, unsigned symbolCount,
659                              ArrayRef<AffineExpr> results,
660                              MLIRContext *context) {
661   auto &impl = context->getImpl();
662   auto key = std::make_tuple(dimCount, symbolCount, results);
663 
664   // Safely get or create an AffineMap instance.
665   return safeGetOrCreate(impl.affineMaps, key, impl.affineMutex, [&] {
666     auto *res = impl.affineAllocator.Allocate<detail::AffineMapStorage>();
667 
668     // Copy the results into the bump pointer.
669     results = copyArrayRefInto(impl.affineAllocator, results);
670 
671     // Initialize the memory using placement new.
672     new (res) detail::AffineMapStorage{dimCount, symbolCount, results, context};
673     return AffineMap(res);
674   });
675 }
676 
677 AffineMap AffineMap::get(MLIRContext *context) {
678   return getImpl(/*dimCount=*/0, /*symbolCount=*/0, /*results=*/{}, context);
679 }
680 
681 AffineMap AffineMap::get(unsigned dimCount, unsigned symbolCount,
682                          MLIRContext *context) {
683   return getImpl(dimCount, symbolCount, /*results=*/{}, context);
684 }
685 
686 AffineMap AffineMap::get(unsigned dimCount, unsigned symbolCount,
687                          ArrayRef<AffineExpr> results) {
688   // The number of results can't be zero.
689   assert(!results.empty());
690   return getImpl(dimCount, symbolCount, results, results[0].getContext());
691 }
692 
693 AffineMap AffineMap::get(unsigned dimCount, unsigned symbolCount,
694                          ArrayRef<AffineExpr> results, MLIRContext *context) {
695   return getImpl(dimCount, symbolCount, results, context);
696 }
697 
698 //===----------------------------------------------------------------------===//
699 // Integer Sets: these are allocated into the bump pointer, and are immutable.
700 // Unlike AffineMap's, these are uniqued only if they are small.
701 //===----------------------------------------------------------------------===//
702 
703 IntegerSet IntegerSet::get(unsigned dimCount, unsigned symbolCount,
704                            ArrayRef<AffineExpr> constraints,
705                            ArrayRef<bool> eqFlags) {
706   // The number of constraints can't be zero.
707   assert(!constraints.empty());
708   assert(constraints.size() == eqFlags.size());
709 
710   auto &impl = constraints[0].getContext()->getImpl();
711 
712   // A utility function to construct a new IntegerSetStorage instance.
713   auto constructorFn = [&] {
714     auto *res = impl.affineAllocator.Allocate<detail::IntegerSetStorage>();
715 
716     // Copy the results and equality flags into the bump pointer.
717     constraints = copyArrayRefInto(impl.affineAllocator, constraints);
718     eqFlags = copyArrayRefInto(impl.affineAllocator, eqFlags);
719 
720     // Initialize the memory using placement new.
721     new (res)
722         detail::IntegerSetStorage{dimCount, symbolCount, constraints, eqFlags};
723     return IntegerSet(res);
724   };
725 
726   // If this instance is uniqued, then we handle it separately so that multiple
727   // threads may simultaneously access existing instances.
728   if (constraints.size() < IntegerSet::kUniquingThreshold) {
729     auto key = std::make_tuple(dimCount, symbolCount, constraints, eqFlags);
730     return safeGetOrCreate(impl.integerSets, key, impl.affineMutex,
731                            constructorFn);
732   }
733 
734   // Otherwise, acquire a writer-lock so that we can safely create the new
735   // instance.
736   llvm::sys::SmartScopedWriter<true> affineLock(impl.affineMutex);
737   return constructorFn();
738 }
739 
740 //===----------------------------------------------------------------------===//
741 // StorageUniquerSupport
742 //===----------------------------------------------------------------------===//
743 
744 /// Utility method to generate a default location for use when checking the
745 /// construction invariants of a storage object. This is defined out-of-line to
746 /// avoid the need to include Location.h.
747 const AttributeStorage *
748 mlir::detail::generateUnknownStorageLocation(MLIRContext *ctx) {
749   return reinterpret_cast<const AttributeStorage *>(
750       ctx->getImpl().unknownLocAttr.getAsOpaquePointer());
751 }
752