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