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