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