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