1 //===- BuiltinTypes.cpp - MLIR Builtin 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/BuiltinTypes.h" 10 #include "TypeDetail.h" 11 #include "mlir/IR/AffineExpr.h" 12 #include "mlir/IR/AffineMap.h" 13 #include "mlir/IR/BuiltinAttributes.h" 14 #include "mlir/IR/BuiltinDialect.h" 15 #include "mlir/IR/Diagnostics.h" 16 #include "mlir/IR/Dialect.h" 17 #include "llvm/ADT/APFloat.h" 18 #include "llvm/ADT/BitVector.h" 19 #include "llvm/ADT/Sequence.h" 20 #include "llvm/ADT/Twine.h" 21 #include "llvm/ADT/TypeSwitch.h" 22 23 using namespace mlir; 24 using namespace mlir::detail; 25 26 //===----------------------------------------------------------------------===// 27 /// Tablegen Type Definitions 28 //===----------------------------------------------------------------------===// 29 30 #define GET_TYPEDEF_CLASSES 31 #include "mlir/IR/BuiltinTypes.cpp.inc" 32 33 //===----------------------------------------------------------------------===// 34 // BuiltinDialect 35 //===----------------------------------------------------------------------===// 36 37 void BuiltinDialect::registerTypes() { 38 addTypes< 39 #define GET_TYPEDEF_LIST 40 #include "mlir/IR/BuiltinTypes.cpp.inc" 41 >(); 42 } 43 44 //===----------------------------------------------------------------------===// 45 /// ComplexType 46 //===----------------------------------------------------------------------===// 47 48 /// Verify the construction of an integer type. 49 LogicalResult ComplexType::verify(function_ref<InFlightDiagnostic()> emitError, 50 Type elementType) { 51 if (!elementType.isIntOrFloat()) 52 return emitError() << "invalid element type for complex"; 53 return success(); 54 } 55 56 //===----------------------------------------------------------------------===// 57 // Integer Type 58 //===----------------------------------------------------------------------===// 59 60 // static constexpr must have a definition (until in C++17 and inline variable). 61 constexpr unsigned IntegerType::kMaxWidth; 62 63 /// Verify the construction of an integer type. 64 LogicalResult IntegerType::verify(function_ref<InFlightDiagnostic()> emitError, 65 unsigned width, 66 SignednessSemantics signedness) { 67 if (width > IntegerType::kMaxWidth) { 68 return emitError() << "integer bitwidth is limited to " 69 << IntegerType::kMaxWidth << " bits"; 70 } 71 return success(); 72 } 73 74 unsigned IntegerType::getWidth() const { return getImpl()->width; } 75 76 IntegerType::SignednessSemantics IntegerType::getSignedness() const { 77 return getImpl()->signedness; 78 } 79 80 IntegerType IntegerType::scaleElementBitwidth(unsigned scale) { 81 if (!scale) 82 return IntegerType(); 83 return IntegerType::get(getContext(), scale * getWidth(), getSignedness()); 84 } 85 86 //===----------------------------------------------------------------------===// 87 // Float Type 88 //===----------------------------------------------------------------------===// 89 90 unsigned FloatType::getWidth() { 91 if (isa<Float16Type, BFloat16Type>()) 92 return 16; 93 if (isa<Float32Type>()) 94 return 32; 95 if (isa<Float64Type>()) 96 return 64; 97 if (isa<Float80Type>()) 98 return 80; 99 if (isa<Float128Type>()) 100 return 128; 101 llvm_unreachable("unexpected float type"); 102 } 103 104 /// Returns the floating semantics for the given type. 105 const llvm::fltSemantics &FloatType::getFloatSemantics() { 106 if (isa<BFloat16Type>()) 107 return APFloat::BFloat(); 108 if (isa<Float16Type>()) 109 return APFloat::IEEEhalf(); 110 if (isa<Float32Type>()) 111 return APFloat::IEEEsingle(); 112 if (isa<Float64Type>()) 113 return APFloat::IEEEdouble(); 114 if (isa<Float80Type>()) 115 return APFloat::x87DoubleExtended(); 116 if (isa<Float128Type>()) 117 return APFloat::IEEEquad(); 118 llvm_unreachable("non-floating point type used"); 119 } 120 121 FloatType FloatType::scaleElementBitwidth(unsigned scale) { 122 if (!scale) 123 return FloatType(); 124 MLIRContext *ctx = getContext(); 125 if (isF16() || isBF16()) { 126 if (scale == 2) 127 return FloatType::getF32(ctx); 128 if (scale == 4) 129 return FloatType::getF64(ctx); 130 } 131 if (isF32()) 132 if (scale == 2) 133 return FloatType::getF64(ctx); 134 return FloatType(); 135 } 136 137 //===----------------------------------------------------------------------===// 138 // FunctionType 139 //===----------------------------------------------------------------------===// 140 141 unsigned FunctionType::getNumInputs() const { return getImpl()->numInputs; } 142 143 ArrayRef<Type> FunctionType::getInputs() const { 144 return getImpl()->getInputs(); 145 } 146 147 unsigned FunctionType::getNumResults() const { return getImpl()->numResults; } 148 149 ArrayRef<Type> FunctionType::getResults() const { 150 return getImpl()->getResults(); 151 } 152 153 /// Helper to call a callback once on each index in the range 154 /// [0, `totalIndices`), *except* for the indices given in `indices`. 155 /// `indices` is allowed to have duplicates and can be in any order. 156 inline void iterateIndicesExcept(unsigned totalIndices, 157 ArrayRef<unsigned> indices, 158 function_ref<void(unsigned)> callback) { 159 llvm::BitVector skipIndices(totalIndices); 160 for (unsigned i : indices) 161 skipIndices.set(i); 162 163 for (unsigned i = 0; i < totalIndices; ++i) 164 if (!skipIndices.test(i)) 165 callback(i); 166 } 167 168 /// Returns a new function type without the specified arguments and results. 169 FunctionType 170 FunctionType::getWithoutArgsAndResults(ArrayRef<unsigned> argIndices, 171 ArrayRef<unsigned> resultIndices) { 172 ArrayRef<Type> newInputTypes = getInputs(); 173 SmallVector<Type, 4> newInputTypesBuffer; 174 if (!argIndices.empty()) { 175 unsigned originalNumArgs = getNumInputs(); 176 iterateIndicesExcept(originalNumArgs, argIndices, [&](unsigned i) { 177 newInputTypesBuffer.emplace_back(getInput(i)); 178 }); 179 newInputTypes = newInputTypesBuffer; 180 } 181 182 ArrayRef<Type> newResultTypes = getResults(); 183 SmallVector<Type, 4> newResultTypesBuffer; 184 if (!resultIndices.empty()) { 185 unsigned originalNumResults = getNumResults(); 186 iterateIndicesExcept(originalNumResults, resultIndices, [&](unsigned i) { 187 newResultTypesBuffer.emplace_back(getResult(i)); 188 }); 189 newResultTypes = newResultTypesBuffer; 190 } 191 192 return get(getContext(), newInputTypes, newResultTypes); 193 } 194 195 //===----------------------------------------------------------------------===// 196 // OpaqueType 197 //===----------------------------------------------------------------------===// 198 199 /// Verify the construction of an opaque type. 200 LogicalResult OpaqueType::verify(function_ref<InFlightDiagnostic()> emitError, 201 Identifier dialect, StringRef typeData) { 202 if (!Dialect::isValidNamespace(dialect.strref())) 203 return emitError() << "invalid dialect namespace '" << dialect << "'"; 204 return success(); 205 } 206 207 //===----------------------------------------------------------------------===// 208 // ShapedType 209 //===----------------------------------------------------------------------===// 210 constexpr int64_t ShapedType::kDynamicSize; 211 constexpr int64_t ShapedType::kDynamicStrideOrOffset; 212 213 ShapedType ShapedType::clone(ArrayRef<int64_t> shape, Type elementType) { 214 if (auto other = dyn_cast<MemRefType>()) { 215 MemRefType::Builder b(other); 216 b.setShape(shape); 217 b.setElementType(elementType); 218 return b; 219 } 220 221 if (auto other = dyn_cast<UnrankedMemRefType>()) { 222 MemRefType::Builder b(shape, elementType); 223 b.setMemorySpace(other.getMemorySpace()); 224 return b; 225 } 226 227 if (isa<TensorType>()) 228 return RankedTensorType::get(shape, elementType); 229 230 if (isa<VectorType>()) 231 return VectorType::get(shape, elementType); 232 233 llvm_unreachable("Unhandled ShapedType clone case"); 234 } 235 236 ShapedType ShapedType::clone(ArrayRef<int64_t> shape) { 237 if (auto other = dyn_cast<MemRefType>()) { 238 MemRefType::Builder b(other); 239 b.setShape(shape); 240 return b; 241 } 242 243 if (auto other = dyn_cast<UnrankedMemRefType>()) { 244 MemRefType::Builder b(shape, other.getElementType()); 245 b.setShape(shape); 246 b.setMemorySpace(other.getMemorySpace()); 247 return b; 248 } 249 250 if (isa<TensorType>()) 251 return RankedTensorType::get(shape, getElementType()); 252 253 if (isa<VectorType>()) 254 return VectorType::get(shape, getElementType()); 255 256 llvm_unreachable("Unhandled ShapedType clone case"); 257 } 258 259 ShapedType ShapedType::clone(Type elementType) { 260 if (auto other = dyn_cast<MemRefType>()) { 261 MemRefType::Builder b(other); 262 b.setElementType(elementType); 263 return b; 264 } 265 266 if (auto other = dyn_cast<UnrankedMemRefType>()) { 267 return UnrankedMemRefType::get(elementType, other.getMemorySpace()); 268 } 269 270 if (isa<TensorType>()) { 271 if (hasRank()) 272 return RankedTensorType::get(getShape(), elementType); 273 return UnrankedTensorType::get(elementType); 274 } 275 276 if (isa<VectorType>()) 277 return VectorType::get(getShape(), elementType); 278 279 llvm_unreachable("Unhandled ShapedType clone hit"); 280 } 281 282 Type ShapedType::getElementType() const { 283 return TypeSwitch<Type, Type>(*this) 284 .Case<VectorType, RankedTensorType, UnrankedTensorType, MemRefType, 285 UnrankedMemRefType>([](auto ty) { return ty.getElementType(); }); 286 } 287 288 unsigned ShapedType::getElementTypeBitWidth() const { 289 return getElementType().getIntOrFloatBitWidth(); 290 } 291 292 int64_t ShapedType::getNumElements() const { 293 assert(hasStaticShape() && "cannot get element count of dynamic shaped type"); 294 auto shape = getShape(); 295 int64_t num = 1; 296 for (auto dim : shape) { 297 num *= dim; 298 assert(num >= 0 && "integer overflow in element count computation"); 299 } 300 return num; 301 } 302 303 int64_t ShapedType::getRank() const { 304 assert(hasRank() && "cannot query rank of unranked shaped type"); 305 return getShape().size(); 306 } 307 308 bool ShapedType::hasRank() const { 309 return !isa<UnrankedMemRefType, UnrankedTensorType>(); 310 } 311 312 int64_t ShapedType::getDimSize(unsigned idx) const { 313 assert(idx < getRank() && "invalid index for shaped type"); 314 return getShape()[idx]; 315 } 316 317 bool ShapedType::isDynamicDim(unsigned idx) const { 318 assert(idx < getRank() && "invalid index for shaped type"); 319 return isDynamic(getShape()[idx]); 320 } 321 322 unsigned ShapedType::getDynamicDimIndex(unsigned index) const { 323 assert(index < getRank() && "invalid index"); 324 assert(ShapedType::isDynamic(getDimSize(index)) && "invalid index"); 325 return llvm::count_if(getShape().take_front(index), ShapedType::isDynamic); 326 } 327 328 /// Get the number of bits require to store a value of the given shaped type. 329 /// Compute the value recursively since tensors are allowed to have vectors as 330 /// elements. 331 int64_t ShapedType::getSizeInBits() const { 332 assert(hasStaticShape() && 333 "cannot get the bit size of an aggregate with a dynamic shape"); 334 335 auto elementType = getElementType(); 336 if (elementType.isIntOrFloat()) 337 return elementType.getIntOrFloatBitWidth() * getNumElements(); 338 339 if (auto complexType = elementType.dyn_cast<ComplexType>()) { 340 elementType = complexType.getElementType(); 341 return elementType.getIntOrFloatBitWidth() * getNumElements() * 2; 342 } 343 344 // Tensors can have vectors and other tensors as elements, other shaped types 345 // cannot. 346 assert(isa<TensorType>() && "unsupported element type"); 347 assert((elementType.isa<VectorType, TensorType>()) && 348 "unsupported tensor element type"); 349 return getNumElements() * elementType.cast<ShapedType>().getSizeInBits(); 350 } 351 352 ArrayRef<int64_t> ShapedType::getShape() const { 353 if (auto vectorType = dyn_cast<VectorType>()) 354 return vectorType.getShape(); 355 if (auto tensorType = dyn_cast<RankedTensorType>()) 356 return tensorType.getShape(); 357 return cast<MemRefType>().getShape(); 358 } 359 360 int64_t ShapedType::getNumDynamicDims() const { 361 return llvm::count_if(getShape(), isDynamic); 362 } 363 364 bool ShapedType::hasStaticShape() const { 365 return hasRank() && llvm::none_of(getShape(), isDynamic); 366 } 367 368 bool ShapedType::hasStaticShape(ArrayRef<int64_t> shape) const { 369 return hasStaticShape() && getShape() == shape; 370 } 371 372 //===----------------------------------------------------------------------===// 373 // VectorType 374 //===----------------------------------------------------------------------===// 375 376 LogicalResult VectorType::verify(function_ref<InFlightDiagnostic()> emitError, 377 ArrayRef<int64_t> shape, Type elementType) { 378 if (shape.empty()) 379 return emitError() << "vector types must have at least one dimension"; 380 381 if (!isValidElementType(elementType)) 382 return emitError() << "vector elements must be int or float type"; 383 384 if (any_of(shape, [](int64_t i) { return i <= 0; })) 385 return emitError() << "vector types must have positive constant sizes"; 386 387 return success(); 388 } 389 390 VectorType VectorType::scaleElementBitwidth(unsigned scale) { 391 if (!scale) 392 return VectorType(); 393 if (auto et = getElementType().dyn_cast<IntegerType>()) 394 if (auto scaledEt = et.scaleElementBitwidth(scale)) 395 return VectorType::get(getShape(), scaledEt); 396 if (auto et = getElementType().dyn_cast<FloatType>()) 397 if (auto scaledEt = et.scaleElementBitwidth(scale)) 398 return VectorType::get(getShape(), scaledEt); 399 return VectorType(); 400 } 401 402 //===----------------------------------------------------------------------===// 403 // TensorType 404 //===----------------------------------------------------------------------===// 405 406 // Check if "elementType" can be an element type of a tensor. 407 static LogicalResult 408 checkTensorElementType(function_ref<InFlightDiagnostic()> emitError, 409 Type elementType) { 410 if (!TensorType::isValidElementType(elementType)) 411 return emitError() << "invalid tensor element type: " << elementType; 412 return success(); 413 } 414 415 /// Return true if the specified element type is ok in a tensor. 416 bool TensorType::isValidElementType(Type type) { 417 // Note: Non standard/builtin types are allowed to exist within tensor 418 // types. Dialects are expected to verify that tensor types have a valid 419 // element type within that dialect. 420 return type.isa<ComplexType, FloatType, IntegerType, OpaqueType, VectorType, 421 IndexType>() || 422 !type.getDialect().getNamespace().empty(); 423 } 424 425 //===----------------------------------------------------------------------===// 426 // RankedTensorType 427 //===----------------------------------------------------------------------===// 428 429 LogicalResult 430 RankedTensorType::verify(function_ref<InFlightDiagnostic()> emitError, 431 ArrayRef<int64_t> shape, Type elementType) { 432 for (int64_t s : shape) 433 if (s < -1) 434 return emitError() << "invalid tensor dimension size"; 435 return checkTensorElementType(emitError, elementType); 436 } 437 438 //===----------------------------------------------------------------------===// 439 // UnrankedTensorType 440 //===----------------------------------------------------------------------===// 441 442 LogicalResult 443 UnrankedTensorType::verify(function_ref<InFlightDiagnostic()> emitError, 444 Type elementType) { 445 return checkTensorElementType(emitError, elementType); 446 } 447 448 //===----------------------------------------------------------------------===// 449 // BaseMemRefType 450 //===----------------------------------------------------------------------===// 451 452 Attribute BaseMemRefType::getMemorySpace() const { 453 if (auto rankedMemRefTy = dyn_cast<MemRefType>()) 454 return rankedMemRefTy.getMemorySpace(); 455 return cast<UnrankedMemRefType>().getMemorySpace(); 456 } 457 458 unsigned BaseMemRefType::getMemorySpaceAsInt() const { 459 if (auto rankedMemRefTy = dyn_cast<MemRefType>()) 460 return rankedMemRefTy.getMemorySpaceAsInt(); 461 return cast<UnrankedMemRefType>().getMemorySpaceAsInt(); 462 } 463 464 //===----------------------------------------------------------------------===// 465 // MemRefType 466 //===----------------------------------------------------------------------===// 467 468 bool mlir::detail::isSupportedMemorySpace(Attribute memorySpace) { 469 // Empty attribute is allowed as default memory space. 470 if (!memorySpace) 471 return true; 472 473 // Supported built-in attributes. 474 if (memorySpace.isa<IntegerAttr, StringAttr, DictionaryAttr>()) 475 return true; 476 477 // Allow custom dialect attributes. 478 if (!::mlir::isa<BuiltinDialect>(memorySpace.getDialect())) 479 return true; 480 481 return false; 482 } 483 484 Attribute mlir::detail::wrapIntegerMemorySpace(unsigned memorySpace, 485 MLIRContext *ctx) { 486 if (memorySpace == 0) 487 return nullptr; 488 489 return IntegerAttr::get(IntegerType::get(ctx, 64), memorySpace); 490 } 491 492 Attribute mlir::detail::skipDefaultMemorySpace(Attribute memorySpace) { 493 IntegerAttr intMemorySpace = memorySpace.dyn_cast_or_null<IntegerAttr>(); 494 if (intMemorySpace && intMemorySpace.getValue() == 0) 495 return nullptr; 496 497 return memorySpace; 498 } 499 500 unsigned mlir::detail::getMemorySpaceAsInt(Attribute memorySpace) { 501 if (!memorySpace) 502 return 0; 503 504 assert(memorySpace.isa<IntegerAttr>() && 505 "Using `getMemorySpaceInteger` with non-Integer attribute"); 506 507 return static_cast<unsigned>(memorySpace.cast<IntegerAttr>().getInt()); 508 } 509 510 MemRefType::Builder & 511 MemRefType::Builder::setMemorySpace(unsigned newMemorySpace) { 512 memorySpace = 513 wrapIntegerMemorySpace(newMemorySpace, elementType.getContext()); 514 return *this; 515 } 516 517 unsigned MemRefType::getMemorySpaceAsInt() const { 518 return detail::getMemorySpaceAsInt(getMemorySpace()); 519 } 520 521 LogicalResult MemRefType::verify(function_ref<InFlightDiagnostic()> emitError, 522 ArrayRef<int64_t> shape, Type elementType, 523 ArrayRef<AffineMap> affineMapComposition, 524 Attribute memorySpace) { 525 if (!BaseMemRefType::isValidElementType(elementType)) 526 return emitError() << "invalid memref element type"; 527 528 // Negative sizes are not allowed except for `-1` that means dynamic size. 529 for (int64_t s : shape) 530 if (s < -1) 531 return emitError() << "invalid memref size"; 532 533 // Check that the structure of the composition is valid, i.e. that each 534 // subsequent affine map has as many inputs as the previous map has results. 535 // Take the dimensionality of the MemRef for the first map. 536 size_t dim = shape.size(); 537 for (auto it : llvm::enumerate(affineMapComposition)) { 538 AffineMap map = it.value(); 539 if (map.getNumDims() == dim) { 540 dim = map.getNumResults(); 541 continue; 542 } 543 return emitError() << "memref affine map dimension mismatch between " 544 << (it.index() == 0 ? Twine("memref rank") 545 : "affine map " + Twine(it.index())) 546 << " and affine map" << it.index() + 1 << ": " << dim 547 << " != " << map.getNumDims(); 548 } 549 550 if (!isSupportedMemorySpace(memorySpace)) { 551 return emitError() << "unsupported memory space Attribute"; 552 } 553 554 return success(); 555 } 556 557 //===----------------------------------------------------------------------===// 558 // UnrankedMemRefType 559 //===----------------------------------------------------------------------===// 560 561 unsigned UnrankedMemRefType::getMemorySpaceAsInt() const { 562 return detail::getMemorySpaceAsInt(getMemorySpace()); 563 } 564 565 LogicalResult 566 UnrankedMemRefType::verify(function_ref<InFlightDiagnostic()> emitError, 567 Type elementType, Attribute memorySpace) { 568 if (!BaseMemRefType::isValidElementType(elementType)) 569 return emitError() << "invalid memref element type"; 570 571 if (!isSupportedMemorySpace(memorySpace)) 572 return emitError() << "unsupported memory space Attribute"; 573 574 return success(); 575 } 576 577 // Fallback cases for terminal dim/sym/cst that are not part of a binary op ( 578 // i.e. single term). Accumulate the AffineExpr into the existing one. 579 static void extractStridesFromTerm(AffineExpr e, 580 AffineExpr multiplicativeFactor, 581 MutableArrayRef<AffineExpr> strides, 582 AffineExpr &offset) { 583 if (auto dim = e.dyn_cast<AffineDimExpr>()) 584 strides[dim.getPosition()] = 585 strides[dim.getPosition()] + multiplicativeFactor; 586 else 587 offset = offset + e * multiplicativeFactor; 588 } 589 590 /// Takes a single AffineExpr `e` and populates the `strides` array with the 591 /// strides expressions for each dim position. 592 /// The convention is that the strides for dimensions d0, .. dn appear in 593 /// order to make indexing intuitive into the result. 594 static LogicalResult extractStrides(AffineExpr e, 595 AffineExpr multiplicativeFactor, 596 MutableArrayRef<AffineExpr> strides, 597 AffineExpr &offset) { 598 auto bin = e.dyn_cast<AffineBinaryOpExpr>(); 599 if (!bin) { 600 extractStridesFromTerm(e, multiplicativeFactor, strides, offset); 601 return success(); 602 } 603 604 if (bin.getKind() == AffineExprKind::CeilDiv || 605 bin.getKind() == AffineExprKind::FloorDiv || 606 bin.getKind() == AffineExprKind::Mod) 607 return failure(); 608 609 if (bin.getKind() == AffineExprKind::Mul) { 610 auto dim = bin.getLHS().dyn_cast<AffineDimExpr>(); 611 if (dim) { 612 strides[dim.getPosition()] = 613 strides[dim.getPosition()] + bin.getRHS() * multiplicativeFactor; 614 return success(); 615 } 616 // LHS and RHS may both contain complex expressions of dims. Try one path 617 // and if it fails try the other. This is guaranteed to succeed because 618 // only one path may have a `dim`, otherwise this is not an AffineExpr in 619 // the first place. 620 if (bin.getLHS().isSymbolicOrConstant()) 621 return extractStrides(bin.getRHS(), multiplicativeFactor * bin.getLHS(), 622 strides, offset); 623 return extractStrides(bin.getLHS(), multiplicativeFactor * bin.getRHS(), 624 strides, offset); 625 } 626 627 if (bin.getKind() == AffineExprKind::Add) { 628 auto res1 = 629 extractStrides(bin.getLHS(), multiplicativeFactor, strides, offset); 630 auto res2 = 631 extractStrides(bin.getRHS(), multiplicativeFactor, strides, offset); 632 return success(succeeded(res1) && succeeded(res2)); 633 } 634 635 llvm_unreachable("unexpected binary operation"); 636 } 637 638 LogicalResult mlir::getStridesAndOffset(MemRefType t, 639 SmallVectorImpl<AffineExpr> &strides, 640 AffineExpr &offset) { 641 auto affineMaps = t.getAffineMaps(); 642 // For now strides are only computed on a single affine map with a single 643 // result (i.e. the closed subset of linearization maps that are compatible 644 // with striding semantics). 645 // TODO: support more forms on a per-need basis. 646 if (affineMaps.size() > 1) 647 return failure(); 648 if (affineMaps.size() == 1 && affineMaps[0].getNumResults() != 1) 649 return failure(); 650 651 auto zero = getAffineConstantExpr(0, t.getContext()); 652 auto one = getAffineConstantExpr(1, t.getContext()); 653 offset = zero; 654 strides.assign(t.getRank(), zero); 655 656 AffineMap m; 657 if (!affineMaps.empty()) { 658 m = affineMaps.front(); 659 assert(!m.isIdentity() && "unexpected identity map"); 660 } 661 662 // Canonical case for empty map. 663 if (!m) { 664 // 0-D corner case, offset is already 0. 665 if (t.getRank() == 0) 666 return success(); 667 auto stridedExpr = 668 makeCanonicalStridedLayoutExpr(t.getShape(), t.getContext()); 669 if (succeeded(extractStrides(stridedExpr, one, strides, offset))) 670 return success(); 671 assert(false && "unexpected failure: extract strides in canonical layout"); 672 } 673 674 // Non-canonical case requires more work. 675 auto stridedExpr = 676 simplifyAffineExpr(m.getResult(0), m.getNumDims(), m.getNumSymbols()); 677 if (failed(extractStrides(stridedExpr, one, strides, offset))) { 678 offset = AffineExpr(); 679 strides.clear(); 680 return failure(); 681 } 682 683 // Simplify results to allow folding to constants and simple checks. 684 unsigned numDims = m.getNumDims(); 685 unsigned numSymbols = m.getNumSymbols(); 686 offset = simplifyAffineExpr(offset, numDims, numSymbols); 687 for (auto &stride : strides) 688 stride = simplifyAffineExpr(stride, numDims, numSymbols); 689 690 /// In practice, a strided memref must be internally non-aliasing. Test 691 /// against 0 as a proxy. 692 /// TODO: static cases can have more advanced checks. 693 /// TODO: dynamic cases would require a way to compare symbolic 694 /// expressions and would probably need an affine set context propagated 695 /// everywhere. 696 if (llvm::any_of(strides, [](AffineExpr e) { 697 return e == getAffineConstantExpr(0, e.getContext()); 698 })) { 699 offset = AffineExpr(); 700 strides.clear(); 701 return failure(); 702 } 703 704 return success(); 705 } 706 707 LogicalResult mlir::getStridesAndOffset(MemRefType t, 708 SmallVectorImpl<int64_t> &strides, 709 int64_t &offset) { 710 AffineExpr offsetExpr; 711 SmallVector<AffineExpr, 4> strideExprs; 712 if (failed(::getStridesAndOffset(t, strideExprs, offsetExpr))) 713 return failure(); 714 if (auto cst = offsetExpr.dyn_cast<AffineConstantExpr>()) 715 offset = cst.getValue(); 716 else 717 offset = ShapedType::kDynamicStrideOrOffset; 718 for (auto e : strideExprs) { 719 if (auto c = e.dyn_cast<AffineConstantExpr>()) 720 strides.push_back(c.getValue()); 721 else 722 strides.push_back(ShapedType::kDynamicStrideOrOffset); 723 } 724 return success(); 725 } 726 727 //===----------------------------------------------------------------------===// 728 /// TupleType 729 //===----------------------------------------------------------------------===// 730 731 /// Return the elements types for this tuple. 732 ArrayRef<Type> TupleType::getTypes() const { return getImpl()->getTypes(); } 733 734 /// Accumulate the types contained in this tuple and tuples nested within it. 735 /// Note that this only flattens nested tuples, not any other container type, 736 /// e.g. a tuple<i32, tensor<i32>, tuple<f32, tuple<i64>>> is flattened to 737 /// (i32, tensor<i32>, f32, i64) 738 void TupleType::getFlattenedTypes(SmallVectorImpl<Type> &types) { 739 for (Type type : getTypes()) { 740 if (auto nestedTuple = type.dyn_cast<TupleType>()) 741 nestedTuple.getFlattenedTypes(types); 742 else 743 types.push_back(type); 744 } 745 } 746 747 /// Return the number of element types. 748 size_t TupleType::size() const { return getImpl()->size(); } 749 750 //===----------------------------------------------------------------------===// 751 // Type Utilities 752 //===----------------------------------------------------------------------===// 753 754 AffineMap mlir::makeStridedLinearLayoutMap(ArrayRef<int64_t> strides, 755 int64_t offset, 756 MLIRContext *context) { 757 AffineExpr expr; 758 unsigned nSymbols = 0; 759 760 // AffineExpr for offset. 761 // Static case. 762 if (offset != MemRefType::getDynamicStrideOrOffset()) { 763 auto cst = getAffineConstantExpr(offset, context); 764 expr = cst; 765 } else { 766 // Dynamic case, new symbol for the offset. 767 auto sym = getAffineSymbolExpr(nSymbols++, context); 768 expr = sym; 769 } 770 771 // AffineExpr for strides. 772 for (auto en : llvm::enumerate(strides)) { 773 auto dim = en.index(); 774 auto stride = en.value(); 775 assert(stride != 0 && "Invalid stride specification"); 776 auto d = getAffineDimExpr(dim, context); 777 AffineExpr mult; 778 // Static case. 779 if (stride != MemRefType::getDynamicStrideOrOffset()) 780 mult = getAffineConstantExpr(stride, context); 781 else 782 // Dynamic case, new symbol for each new stride. 783 mult = getAffineSymbolExpr(nSymbols++, context); 784 expr = expr + d * mult; 785 } 786 787 return AffineMap::get(strides.size(), nSymbols, expr); 788 } 789 790 /// Return a version of `t` with identity layout if it can be determined 791 /// statically that the layout is the canonical contiguous strided layout. 792 /// Otherwise pass `t`'s layout into `simplifyAffineMap` and return a copy of 793 /// `t` with simplified layout. 794 /// If `t` has multiple layout maps or a multi-result layout, just return `t`. 795 MemRefType mlir::canonicalizeStridedLayout(MemRefType t) { 796 auto affineMaps = t.getAffineMaps(); 797 // Already in canonical form. 798 if (affineMaps.empty()) 799 return t; 800 801 // Can't reduce to canonical identity form, return in canonical form. 802 if (affineMaps.size() > 1 || affineMaps[0].getNumResults() > 1) 803 return t; 804 805 // Corner-case for 0-D affine maps. 806 auto m = affineMaps[0]; 807 if (m.getNumDims() == 0 && m.getNumSymbols() == 0) { 808 if (auto cst = m.getResult(0).dyn_cast<AffineConstantExpr>()) 809 if (cst.getValue() == 0) 810 return MemRefType::Builder(t).setAffineMaps({}); 811 return t; 812 } 813 814 // 0-D corner case for empty shape that still have an affine map. Example: 815 // `memref<f32, affine_map<()[s0] -> (s0)>>`. This is a 1 element memref whose 816 // offset needs to remain, just return t. 817 if (t.getShape().empty()) 818 return t; 819 820 // If the canonical strided layout for the sizes of `t` is equal to the 821 // simplified layout of `t` we can just return an empty layout. Otherwise, 822 // just simplify the existing layout. 823 AffineExpr expr = 824 makeCanonicalStridedLayoutExpr(t.getShape(), t.getContext()); 825 auto simplifiedLayoutExpr = 826 simplifyAffineExpr(m.getResult(0), m.getNumDims(), m.getNumSymbols()); 827 if (expr != simplifiedLayoutExpr) 828 return MemRefType::Builder(t).setAffineMaps({AffineMap::get( 829 m.getNumDims(), m.getNumSymbols(), simplifiedLayoutExpr)}); 830 return MemRefType::Builder(t).setAffineMaps({}); 831 } 832 833 AffineExpr mlir::makeCanonicalStridedLayoutExpr(ArrayRef<int64_t> sizes, 834 ArrayRef<AffineExpr> exprs, 835 MLIRContext *context) { 836 assert(!sizes.empty() && !exprs.empty() && 837 "expected non-empty sizes and exprs"); 838 839 // Size 0 corner case is useful for canonicalizations. 840 if (llvm::is_contained(sizes, 0)) 841 return getAffineConstantExpr(0, context); 842 843 auto maps = AffineMap::inferFromExprList(exprs); 844 assert(!maps.empty() && "Expected one non-empty map"); 845 unsigned numDims = maps[0].getNumDims(), nSymbols = maps[0].getNumSymbols(); 846 847 AffineExpr expr; 848 bool dynamicPoisonBit = false; 849 int64_t runningSize = 1; 850 for (auto en : llvm::zip(llvm::reverse(exprs), llvm::reverse(sizes))) { 851 int64_t size = std::get<1>(en); 852 // Degenerate case, no size =-> no stride 853 if (size == 0) 854 continue; 855 AffineExpr dimExpr = std::get<0>(en); 856 AffineExpr stride = dynamicPoisonBit 857 ? getAffineSymbolExpr(nSymbols++, context) 858 : getAffineConstantExpr(runningSize, context); 859 expr = expr ? expr + dimExpr * stride : dimExpr * stride; 860 if (size > 0) { 861 runningSize *= size; 862 assert(runningSize > 0 && "integer overflow in size computation"); 863 } else { 864 dynamicPoisonBit = true; 865 } 866 } 867 return simplifyAffineExpr(expr, numDims, nSymbols); 868 } 869 870 /// Return a version of `t` with a layout that has all dynamic offset and 871 /// strides. This is used to erase the static layout. 872 MemRefType mlir::eraseStridedLayout(MemRefType t) { 873 auto val = ShapedType::kDynamicStrideOrOffset; 874 return MemRefType::Builder(t).setAffineMaps(makeStridedLinearLayoutMap( 875 SmallVector<int64_t, 4>(t.getRank(), val), val, t.getContext())); 876 } 877 878 AffineExpr mlir::makeCanonicalStridedLayoutExpr(ArrayRef<int64_t> sizes, 879 MLIRContext *context) { 880 SmallVector<AffineExpr, 4> exprs; 881 exprs.reserve(sizes.size()); 882 for (auto dim : llvm::seq<unsigned>(0, sizes.size())) 883 exprs.push_back(getAffineDimExpr(dim, context)); 884 return makeCanonicalStridedLayoutExpr(sizes, exprs, context); 885 } 886 887 /// Return true if the layout for `t` is compatible with strided semantics. 888 bool mlir::isStrided(MemRefType t) { 889 int64_t offset; 890 SmallVector<int64_t, 4> strides; 891 auto res = getStridesAndOffset(t, strides, offset); 892 return succeeded(res); 893 } 894 895 /// Return the layout map in strided linear layout AffineMap form. 896 /// Return null if the layout is not compatible with a strided layout. 897 AffineMap mlir::getStridedLinearLayoutMap(MemRefType t) { 898 int64_t offset; 899 SmallVector<int64_t, 4> strides; 900 if (failed(getStridesAndOffset(t, strides, offset))) 901 return AffineMap(); 902 return makeStridedLinearLayoutMap(strides, offset, t.getContext()); 903 } 904