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