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