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