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