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