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