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