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