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