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