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::addType(TypeID typeID, AbstractType &&typeInfo) { 625 auto &impl = context->getImpl(); 626 assert(impl.multiThreadedExecutionContext == 0 && 627 "Registering a new type kind while in a multi-threaded execution " 628 "context"); 629 auto *newInfo = 630 new (impl.abstractDialectSymbolAllocator.Allocate<AbstractType>()) 631 AbstractType(std::move(typeInfo)); 632 if (!impl.registeredTypes.insert({typeID, newInfo}).second) 633 llvm::report_fatal_error("Dialect Type already registered."); 634 } 635 636 void Dialect::addAttribute(TypeID typeID, AbstractAttribute &&attrInfo) { 637 auto &impl = context->getImpl(); 638 assert(impl.multiThreadedExecutionContext == 0 && 639 "Registering a new attribute kind while in a multi-threaded execution " 640 "context"); 641 auto *newInfo = 642 new (impl.abstractDialectSymbolAllocator.Allocate<AbstractAttribute>()) 643 AbstractAttribute(std::move(attrInfo)); 644 if (!impl.registeredAttributes.insert({typeID, newInfo}).second) 645 llvm::report_fatal_error("Dialect Attribute already registered."); 646 } 647 648 //===----------------------------------------------------------------------===// 649 // AbstractAttribute 650 //===----------------------------------------------------------------------===// 651 652 /// Get the dialect that registered the attribute with the provided typeid. 653 const AbstractAttribute &AbstractAttribute::lookup(TypeID typeID, 654 MLIRContext *context) { 655 auto &impl = context->getImpl(); 656 auto it = impl.registeredAttributes.find(typeID); 657 if (it == impl.registeredAttributes.end()) 658 llvm::report_fatal_error("Trying to create an Attribute that was not " 659 "registered in this MLIRContext."); 660 return *it->second; 661 } 662 663 //===----------------------------------------------------------------------===// 664 // AbstractOperation 665 //===----------------------------------------------------------------------===// 666 667 ParseResult AbstractOperation::parseAssembly(OpAsmParser &parser, 668 OperationState &result) const { 669 return parseAssemblyFn(parser, result); 670 } 671 672 /// Look up the specified operation in the operation set and return a pointer 673 /// to it if present. Otherwise, return a null pointer. 674 const AbstractOperation *AbstractOperation::lookup(StringRef opName, 675 MLIRContext *context) { 676 auto &impl = context->getImpl(); 677 auto it = impl.registeredOperations.find(opName); 678 if (it != impl.registeredOperations.end()) 679 return &it->second; 680 return nullptr; 681 } 682 683 void AbstractOperation::insert( 684 StringRef name, Dialect &dialect, OperationProperties opProperties, 685 TypeID typeID, ParseAssemblyFn parseAssembly, PrintAssemblyFn printAssembly, 686 VerifyInvariantsFn verifyInvariants, FoldHookFn foldHook, 687 GetCanonicalizationPatternsFn getCanonicalizationPatterns, 688 detail::InterfaceMap &&interfaceMap, HasTraitFn hasTrait) { 689 AbstractOperation opInfo(name, dialect, opProperties, typeID, parseAssembly, 690 printAssembly, verifyInvariants, foldHook, 691 getCanonicalizationPatterns, std::move(interfaceMap), 692 hasTrait); 693 694 auto &impl = dialect.getContext()->getImpl(); 695 assert(impl.multiThreadedExecutionContext == 0 && 696 "Registering a new operation kind while in a multi-threaded execution " 697 "context"); 698 if (!impl.registeredOperations.insert({name, std::move(opInfo)}).second) { 699 llvm::errs() << "error: operation named '" << name 700 << "' is already registered.\n"; 701 abort(); 702 } 703 } 704 705 AbstractOperation::AbstractOperation( 706 StringRef name, Dialect &dialect, OperationProperties opProperties, 707 TypeID typeID, ParseAssemblyFn parseAssembly, PrintAssemblyFn printAssembly, 708 VerifyInvariantsFn verifyInvariants, FoldHookFn foldHook, 709 GetCanonicalizationPatternsFn getCanonicalizationPatterns, 710 detail::InterfaceMap &&interfaceMap, HasTraitFn hasTrait) 711 : name(Identifier::get(name, dialect.getContext())), dialect(dialect), 712 typeID(typeID), opProperties(opProperties), 713 interfaceMap(std::move(interfaceMap)), foldHookFn(foldHook), 714 getCanonicalizationPatternsFn(getCanonicalizationPatterns), 715 hasTraitFn(hasTrait), parseAssemblyFn(parseAssembly), 716 printAssemblyFn(printAssembly), verifyInvariantsFn(verifyInvariants) {} 717 718 //===----------------------------------------------------------------------===// 719 // AbstractType 720 //===----------------------------------------------------------------------===// 721 722 const AbstractType &AbstractType::lookup(TypeID typeID, MLIRContext *context) { 723 auto &impl = context->getImpl(); 724 auto it = impl.registeredTypes.find(typeID); 725 if (it == impl.registeredTypes.end()) 726 llvm::report_fatal_error( 727 "Trying to create a Type that was not registered in this MLIRContext."); 728 return *it->second; 729 } 730 731 //===----------------------------------------------------------------------===// 732 // Identifier uniquing 733 //===----------------------------------------------------------------------===// 734 735 /// Return an identifier for the specified string. 736 Identifier Identifier::get(StringRef str, MLIRContext *context) { 737 // Check invariants after seeing if we already have something in the 738 // identifier table - if we already had it in the table, then it already 739 // passed invariant checks. 740 assert(!str.empty() && "Cannot create an empty identifier"); 741 assert(str.find('\0') == StringRef::npos && 742 "Cannot create an identifier with a nul character"); 743 744 auto &impl = context->getImpl(); 745 if (!context->isMultithreadingEnabled()) 746 return Identifier(&*impl.identifiers.insert(str).first); 747 748 // Check for an existing instance in the local cache. 749 auto *&localEntry = (*impl.localIdentifierCache)[str]; 750 if (localEntry) 751 return Identifier(localEntry); 752 753 // Check for an existing identifier in read-only mode. 754 { 755 llvm::sys::SmartScopedReader<true> contextLock(impl.identifierMutex); 756 auto it = impl.identifiers.find(str); 757 if (it != impl.identifiers.end()) { 758 localEntry = &*it; 759 return Identifier(localEntry); 760 } 761 } 762 763 // Acquire a writer-lock so that we can safely create the new instance. 764 llvm::sys::SmartScopedWriter<true> contextLock(impl.identifierMutex); 765 auto it = impl.identifiers.insert(str).first; 766 localEntry = &*it; 767 return Identifier(localEntry); 768 } 769 770 //===----------------------------------------------------------------------===// 771 // Type uniquing 772 //===----------------------------------------------------------------------===// 773 774 /// Returns the storage uniquer used for constructing type storage instances. 775 /// This should not be used directly. 776 StorageUniquer &MLIRContext::getTypeUniquer() { return getImpl().typeUniquer; } 777 778 BFloat16Type BFloat16Type::get(MLIRContext *context) { 779 return context->getImpl().bf16Ty; 780 } 781 Float16Type Float16Type::get(MLIRContext *context) { 782 return context->getImpl().f16Ty; 783 } 784 Float32Type Float32Type::get(MLIRContext *context) { 785 return context->getImpl().f32Ty; 786 } 787 Float64Type Float64Type::get(MLIRContext *context) { 788 return context->getImpl().f64Ty; 789 } 790 791 /// Get an instance of the IndexType. 792 IndexType IndexType::get(MLIRContext *context) { 793 return context->getImpl().indexTy; 794 } 795 796 /// Return an existing integer type instance if one is cached within the 797 /// context. 798 static IntegerType 799 getCachedIntegerType(unsigned width, 800 IntegerType::SignednessSemantics signedness, 801 MLIRContext *context) { 802 if (signedness != IntegerType::Signless) 803 return IntegerType(); 804 805 switch (width) { 806 case 1: 807 return context->getImpl().int1Ty; 808 case 8: 809 return context->getImpl().int8Ty; 810 case 16: 811 return context->getImpl().int16Ty; 812 case 32: 813 return context->getImpl().int32Ty; 814 case 64: 815 return context->getImpl().int64Ty; 816 case 128: 817 return context->getImpl().int128Ty; 818 default: 819 return IntegerType(); 820 } 821 } 822 823 IntegerType IntegerType::get(unsigned width, MLIRContext *context) { 824 return get(width, IntegerType::Signless, context); 825 } 826 827 IntegerType IntegerType::get(unsigned width, 828 IntegerType::SignednessSemantics signedness, 829 MLIRContext *context) { 830 if (auto cached = getCachedIntegerType(width, signedness, context)) 831 return cached; 832 return Base::get(context, width, signedness); 833 } 834 835 IntegerType IntegerType::getChecked(unsigned width, Location location) { 836 return getChecked(width, IntegerType::Signless, location); 837 } 838 839 IntegerType IntegerType::getChecked(unsigned width, 840 SignednessSemantics signedness, 841 Location location) { 842 if (auto cached = 843 getCachedIntegerType(width, signedness, location->getContext())) 844 return cached; 845 return Base::getChecked(location, width, signedness); 846 } 847 848 /// Get an instance of the NoneType. 849 NoneType NoneType::get(MLIRContext *context) { 850 if (NoneType cachedInst = context->getImpl().noneType) 851 return cachedInst; 852 // Note: May happen when initializing the singleton attributes of the builtin 853 // dialect. 854 return Base::get(context); 855 } 856 857 //===----------------------------------------------------------------------===// 858 // Attribute uniquing 859 //===----------------------------------------------------------------------===// 860 861 /// Returns the storage uniquer used for constructing attribute storage 862 /// instances. This should not be used directly. 863 StorageUniquer &MLIRContext::getAttributeUniquer() { 864 return getImpl().attributeUniquer; 865 } 866 867 /// Initialize the given attribute storage instance. 868 void AttributeUniquer::initializeAttributeStorage(AttributeStorage *storage, 869 MLIRContext *ctx, 870 TypeID attrID) { 871 storage->initialize(AbstractAttribute::lookup(attrID, ctx)); 872 873 // If the attribute did not provide a type, then default to NoneType. 874 if (!storage->getType()) 875 storage->setType(NoneType::get(ctx)); 876 } 877 878 BoolAttr BoolAttr::get(bool value, MLIRContext *context) { 879 return value ? context->getImpl().trueAttr : context->getImpl().falseAttr; 880 } 881 882 UnitAttr UnitAttr::get(MLIRContext *context) { 883 return context->getImpl().unitAttr; 884 } 885 886 Location UnknownLoc::get(MLIRContext *context) { 887 return context->getImpl().unknownLocAttr; 888 } 889 890 /// Return empty dictionary. 891 DictionaryAttr DictionaryAttr::getEmpty(MLIRContext *context) { 892 return context->getImpl().emptyDictionaryAttr; 893 } 894 895 //===----------------------------------------------------------------------===// 896 // AffineMap uniquing 897 //===----------------------------------------------------------------------===// 898 899 StorageUniquer &MLIRContext::getAffineUniquer() { 900 return getImpl().affineUniquer; 901 } 902 903 AffineMap AffineMap::getImpl(unsigned dimCount, unsigned symbolCount, 904 ArrayRef<AffineExpr> results, 905 MLIRContext *context) { 906 auto &impl = context->getImpl(); 907 auto key = std::make_tuple(dimCount, symbolCount, results); 908 909 // Safely get or create an AffineMap instance. 910 return safeGetOrCreate( 911 impl.affineMaps, key, impl.affineMutex, impl.threadingIsEnabled, [&] { 912 auto *res = impl.affineAllocator.Allocate<detail::AffineMapStorage>(); 913 914 // Copy the results into the bump pointer. 915 results = copyArrayRefInto(impl.affineAllocator, results); 916 917 // Initialize the memory using placement new. 918 new (res) 919 detail::AffineMapStorage{dimCount, symbolCount, results, context}; 920 return AffineMap(res); 921 }); 922 } 923 924 AffineMap AffineMap::get(MLIRContext *context) { 925 return getImpl(/*dimCount=*/0, /*symbolCount=*/0, /*results=*/{}, context); 926 } 927 928 AffineMap AffineMap::get(unsigned dimCount, unsigned symbolCount, 929 MLIRContext *context) { 930 return getImpl(dimCount, symbolCount, /*results=*/{}, context); 931 } 932 933 AffineMap AffineMap::get(unsigned dimCount, unsigned symbolCount, 934 AffineExpr result) { 935 return getImpl(dimCount, symbolCount, {result}, result.getContext()); 936 } 937 938 AffineMap AffineMap::get(unsigned dimCount, unsigned symbolCount, 939 ArrayRef<AffineExpr> results, MLIRContext *context) { 940 return getImpl(dimCount, symbolCount, results, context); 941 } 942 943 //===----------------------------------------------------------------------===// 944 // Integer Sets: these are allocated into the bump pointer, and are immutable. 945 // Unlike AffineMap's, these are uniqued only if they are small. 946 //===----------------------------------------------------------------------===// 947 948 IntegerSet IntegerSet::get(unsigned dimCount, unsigned symbolCount, 949 ArrayRef<AffineExpr> constraints, 950 ArrayRef<bool> eqFlags) { 951 // The number of constraints can't be zero. 952 assert(!constraints.empty()); 953 assert(constraints.size() == eqFlags.size()); 954 955 auto &impl = constraints[0].getContext()->getImpl(); 956 957 // A utility function to construct a new IntegerSetStorage instance. 958 auto constructorFn = [&] { 959 auto *res = impl.affineAllocator.Allocate<detail::IntegerSetStorage>(); 960 961 // Copy the results and equality flags into the bump pointer. 962 constraints = copyArrayRefInto(impl.affineAllocator, constraints); 963 eqFlags = copyArrayRefInto(impl.affineAllocator, eqFlags); 964 965 // Initialize the memory using placement new. 966 new (res) 967 detail::IntegerSetStorage{dimCount, symbolCount, constraints, eqFlags}; 968 return IntegerSet(res); 969 }; 970 971 // If this instance is uniqued, then we handle it separately so that multiple 972 // threads may simultaneously access existing instances. 973 if (constraints.size() < IntegerSet::kUniquingThreshold) { 974 auto key = std::make_tuple(dimCount, symbolCount, constraints, eqFlags); 975 return safeGetOrCreate(impl.integerSets, key, impl.affineMutex, 976 impl.threadingIsEnabled, constructorFn); 977 } 978 979 // Otherwise, acquire a writer-lock so that we can safely create the new 980 // instance. 981 ScopedWriterLock affineLock(impl.affineMutex, impl.threadingIsEnabled); 982 return constructorFn(); 983 } 984 985 //===----------------------------------------------------------------------===// 986 // StorageUniquerSupport 987 //===----------------------------------------------------------------------===// 988 989 /// Utility method to generate a default location for use when checking the 990 /// construction invariants of a storage object. This is defined out-of-line to 991 /// avoid the need to include Location.h. 992 const AttributeStorage * 993 mlir::detail::generateUnknownStorageLocation(MLIRContext *ctx) { 994 return reinterpret_cast<const AttributeStorage *>( 995 ctx->getImpl().unknownLocAttr.getAsOpaquePointer()); 996 } 997