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