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