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