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