1 //===----------------------------------------------------------------------===//
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/Dialect/MemRef/IR/MemRef.h"
10 #include "mlir/Dialect/StandardOps/IR/Ops.h"
11 #include "mlir/Dialect/StandardOps/Utils/Utils.h"
12 #include "mlir/Dialect/Tensor/IR/Tensor.h"
13 #include "mlir/IR/AffineMap.h"
14 #include "mlir/IR/Builders.h"
15 #include "mlir/IR/BuiltinTypes.h"
16 #include "mlir/IR/Matchers.h"
17 #include "mlir/IR/PatternMatch.h"
18 #include "mlir/IR/TypeUtilities.h"
19 #include "llvm/ADT/STLExtras.h"
20 
21 using namespace mlir;
22 using namespace mlir::memref;
23 
24 /// Materialize a single constant operation from a given attribute value with
25 /// the desired resultant type.
26 Operation *MemRefDialect::materializeConstant(OpBuilder &builder,
27                                               Attribute value, Type type,
28                                               Location loc) {
29   return builder.create<mlir::ConstantOp>(loc, type, value);
30 }
31 
32 /// Extract int64_t values from the assumed ArrayAttr of IntegerAttr.
33 static SmallVector<int64_t, 4> extractFromI64ArrayAttr(Attribute attr) {
34   return llvm::to_vector<4>(
35       llvm::map_range(attr.cast<ArrayAttr>(), [](Attribute a) -> int64_t {
36         return a.cast<IntegerAttr>().getInt();
37       }));
38 }
39 
40 /// Helper function to dispatch an OpFoldResult into either the `dynamicVec` if
41 /// it is a Value or into `staticVec` if it is an IntegerAttr.
42 /// In the case of a Value, a copy of the `sentinel` value is also pushed to
43 /// `staticVec`. This is useful to extract mixed static and dynamic entries that
44 /// come from an AttrSizedOperandSegments trait.
45 static void dispatchIndexOpFoldResult(OpFoldResult ofr,
46                                       SmallVectorImpl<Value> &dynamicVec,
47                                       SmallVectorImpl<int64_t> &staticVec,
48                                       int64_t sentinel) {
49   if (auto v = ofr.dyn_cast<Value>()) {
50     dynamicVec.push_back(v);
51     staticVec.push_back(sentinel);
52     return;
53   }
54   APInt apInt = ofr.dyn_cast<Attribute>().cast<IntegerAttr>().getValue();
55   staticVec.push_back(apInt.getSExtValue());
56 }
57 
58 static void dispatchIndexOpFoldResults(ArrayRef<OpFoldResult> ofrs,
59                                        SmallVectorImpl<Value> &dynamicVec,
60                                        SmallVectorImpl<int64_t> &staticVec,
61                                        int64_t sentinel) {
62   for (auto ofr : ofrs)
63     dispatchIndexOpFoldResult(ofr, dynamicVec, staticVec, sentinel);
64 }
65 
66 //===----------------------------------------------------------------------===//
67 // Common canonicalization pattern support logic
68 //===----------------------------------------------------------------------===//
69 
70 /// This is a common class used for patterns of the form
71 /// "someop(memrefcast) -> someop".  It folds the source of any memref.cast
72 /// into the root operation directly.
73 static LogicalResult foldMemRefCast(Operation *op) {
74   bool folded = false;
75   for (OpOperand &operand : op->getOpOperands()) {
76     auto cast = operand.get().getDefiningOp<CastOp>();
77     if (cast && !cast.getOperand().getType().isa<UnrankedMemRefType>()) {
78       operand.set(cast.getOperand());
79       folded = true;
80     }
81   }
82   return success(folded);
83 }
84 
85 //===----------------------------------------------------------------------===//
86 // Helpers for GlobalOp
87 //===----------------------------------------------------------------------===//
88 
89 static Type getTensorTypeFromMemRefType(Type type) {
90   if (auto memref = type.dyn_cast<MemRefType>())
91     return RankedTensorType::get(memref.getShape(), memref.getElementType());
92   if (auto memref = type.dyn_cast<UnrankedMemRefType>())
93     return UnrankedTensorType::get(memref.getElementType());
94   return NoneType::get(type.getContext());
95 }
96 
97 //===----------------------------------------------------------------------===//
98 // AllocOp / AllocaOp
99 //===----------------------------------------------------------------------===//
100 
101 template <typename AllocLikeOp>
102 static LogicalResult verifyAllocLikeOp(AllocLikeOp op) {
103   static_assert(llvm::is_one_of<AllocLikeOp, AllocOp, AllocaOp>::value,
104                 "applies to only alloc or alloca");
105   auto memRefType = op.getResult().getType().template dyn_cast<MemRefType>();
106   if (!memRefType)
107     return op.emitOpError("result must be a memref");
108 
109   if (static_cast<int64_t>(op.dynamicSizes().size()) !=
110       memRefType.getNumDynamicDims())
111     return op.emitOpError("dimension operand count does not equal memref "
112                           "dynamic dimension count");
113 
114   unsigned numSymbols = 0;
115   if (!memRefType.getAffineMaps().empty())
116     numSymbols = memRefType.getAffineMaps().front().getNumSymbols();
117   if (op.symbolOperands().size() != numSymbols)
118     return op.emitOpError(
119         "symbol operand count does not equal memref symbol count");
120 
121   return success();
122 }
123 
124 static LogicalResult verify(AllocOp op) { return verifyAllocLikeOp(op); }
125 
126 static LogicalResult verify(AllocaOp op) {
127   // An alloca op needs to have an ancestor with an allocation scope trait.
128   if (!op->getParentWithTrait<OpTrait::AutomaticAllocationScope>())
129     return op.emitOpError(
130         "requires an ancestor op with AutomaticAllocationScope trait");
131 
132   return verifyAllocLikeOp(op);
133 }
134 
135 namespace {
136 /// Fold constant dimensions into an alloc like operation.
137 template <typename AllocLikeOp>
138 struct SimplifyAllocConst : public OpRewritePattern<AllocLikeOp> {
139   using OpRewritePattern<AllocLikeOp>::OpRewritePattern;
140 
141   LogicalResult matchAndRewrite(AllocLikeOp alloc,
142                                 PatternRewriter &rewriter) const override {
143     // Check to see if any dimensions operands are constants.  If so, we can
144     // substitute and drop them.
145     if (llvm::none_of(alloc.getOperands(), [](Value operand) {
146           return matchPattern(operand, matchConstantIndex());
147         }))
148       return failure();
149 
150     auto memrefType = alloc.getType();
151 
152     // Ok, we have one or more constant operands.  Collect the non-constant ones
153     // and keep track of the resultant memref type to build.
154     SmallVector<int64_t, 4> newShapeConstants;
155     newShapeConstants.reserve(memrefType.getRank());
156     SmallVector<Value, 4> newOperands;
157 
158     unsigned dynamicDimPos = 0;
159     for (unsigned dim = 0, e = memrefType.getRank(); dim < e; ++dim) {
160       int64_t dimSize = memrefType.getDimSize(dim);
161       // If this is already static dimension, keep it.
162       if (dimSize != -1) {
163         newShapeConstants.push_back(dimSize);
164         continue;
165       }
166       auto *defOp = alloc.getOperand(dynamicDimPos).getDefiningOp();
167       if (auto constantIndexOp = dyn_cast_or_null<ConstantIndexOp>(defOp)) {
168         // Dynamic shape dimension will be folded.
169         newShapeConstants.push_back(constantIndexOp.getValue());
170       } else {
171         // Dynamic shape dimension not folded; copy operand from old memref.
172         newShapeConstants.push_back(-1);
173         newOperands.push_back(alloc.getOperand(dynamicDimPos));
174       }
175       dynamicDimPos++;
176     }
177 
178     // Create new memref type (which will have fewer dynamic dimensions).
179     MemRefType newMemRefType =
180         MemRefType::Builder(memrefType).setShape(newShapeConstants);
181     assert(static_cast<int64_t>(newOperands.size()) ==
182            newMemRefType.getNumDynamicDims());
183 
184     // Create and insert the alloc op for the new memref.
185     auto newAlloc = rewriter.create<AllocLikeOp>(alloc.getLoc(), newMemRefType,
186                                                  newOperands, IntegerAttr());
187     // Insert a cast so we have the same type as the old alloc.
188     auto resultCast =
189         rewriter.create<CastOp>(alloc.getLoc(), newAlloc, alloc.getType());
190 
191     rewriter.replaceOp(alloc, {resultCast});
192     return success();
193   }
194 };
195 
196 /// Fold alloc operations with no uses. Alloc has side effects on the heap,
197 /// but can still be deleted if it has zero uses.
198 struct SimplifyDeadAlloc : public OpRewritePattern<AllocOp> {
199   using OpRewritePattern<AllocOp>::OpRewritePattern;
200 
201   LogicalResult matchAndRewrite(AllocOp alloc,
202                                 PatternRewriter &rewriter) const override {
203     if (alloc.use_empty()) {
204       rewriter.eraseOp(alloc);
205       return success();
206     }
207     return failure();
208   }
209 };
210 } // end anonymous namespace.
211 
212 void AllocOp::getCanonicalizationPatterns(OwningRewritePatternList &results,
213                                           MLIRContext *context) {
214   results.insert<SimplifyAllocConst<AllocOp>, SimplifyDeadAlloc>(context);
215 }
216 
217 void AllocaOp::getCanonicalizationPatterns(OwningRewritePatternList &results,
218                                            MLIRContext *context) {
219   results.insert<SimplifyAllocConst<AllocaOp>>(context);
220 }
221 
222 //===----------------------------------------------------------------------===//
223 // AssumeAlignmentOp
224 //===----------------------------------------------------------------------===//
225 
226 static LogicalResult verify(AssumeAlignmentOp op) {
227   unsigned alignment = op.alignment();
228   if (!llvm::isPowerOf2_32(alignment))
229     return op.emitOpError("alignment must be power of 2");
230   return success();
231 }
232 
233 //===----------------------------------------------------------------------===//
234 // BufferCastOp
235 //===----------------------------------------------------------------------===//
236 
237 OpFoldResult BufferCastOp::fold(ArrayRef<Attribute>) {
238   if (auto tensorLoad = tensor().getDefiningOp<TensorLoadOp>())
239     if (tensorLoad.memref().getType() == getType())
240       return tensorLoad.memref();
241   return {};
242 }
243 
244 namespace {
245 /// Replace tensor_cast + buffer_cast by buffer_cast + memref_cast.
246 struct BufferCast : public OpRewritePattern<BufferCastOp> {
247   using OpRewritePattern<BufferCastOp>::OpRewritePattern;
248 
249   LogicalResult matchAndRewrite(BufferCastOp bufferCast,
250                                 PatternRewriter &rewriter) const final {
251     auto tensorCastOperand =
252         bufferCast.getOperand().getDefiningOp<tensor::CastOp>();
253     if (!tensorCastOperand)
254       return failure();
255     auto srcTensorType =
256         tensorCastOperand.getOperand().getType().dyn_cast<RankedTensorType>();
257     if (!srcTensorType)
258       return failure();
259     auto memrefType = MemRefType::get(srcTensorType.getShape(),
260                                       srcTensorType.getElementType());
261     Value memref = rewriter.create<BufferCastOp>(
262         bufferCast.getLoc(), memrefType, tensorCastOperand.getOperand());
263     rewriter.replaceOpWithNewOp<CastOp>(bufferCast, bufferCast.getType(),
264                                         memref);
265     return success();
266   }
267 };
268 
269 /// Canonicalize memref.tensor_load + memref.buffer_cast to memref.cast when
270 /// type mismatches prevent `BufferCastOp::fold` to kick in.
271 struct TensorLoadToMemRef : public OpRewritePattern<BufferCastOp> {
272   using OpRewritePattern<BufferCastOp>::OpRewritePattern;
273 
274   LogicalResult matchAndRewrite(BufferCastOp bufferCast,
275                                 PatternRewriter &rewriter) const final {
276     auto tensorLoad = bufferCast.tensor().getDefiningOp<TensorLoadOp>();
277     // Bail unless we have a tensor_load + memref.buffer_cast with different
278     // types. `BufferCastOp::fold` handles the same type case.
279     if (!tensorLoad || tensorLoad.memref().getType() == bufferCast.getType())
280       return failure();
281     // If types are not cast-compatible, bail.
282     if (!CastOp::areCastCompatible(tensorLoad.memref().getType(),
283                                    bufferCast.getType()))
284       return failure();
285     rewriter.replaceOpWithNewOp<CastOp>(bufferCast, bufferCast.getType(),
286                                         tensorLoad.memref());
287     return success();
288   }
289 };
290 
291 } // namespace
292 
293 void BufferCastOp::getCanonicalizationPatterns(
294     OwningRewritePatternList &results, MLIRContext *context) {
295   results.insert<BufferCast, TensorLoadToMemRef>(context);
296 }
297 
298 //===----------------------------------------------------------------------===//
299 // CastOp
300 //===----------------------------------------------------------------------===//
301 
302 /// Determines whether MemRef_CastOp casts to a more dynamic version of the
303 /// source memref. This is useful to to fold a memref.cast into a consuming op
304 /// and implement canonicalization patterns for ops in different dialects that
305 /// may consume the results of memref.cast operations. Such foldable memref.cast
306 /// operations are typically inserted as `view` and `subview` ops are
307 /// canonicalized, to preserve the type compatibility of their uses.
308 ///
309 /// Returns true when all conditions are met:
310 /// 1. source and result are ranked memrefs with strided semantics and same
311 /// element type and rank.
312 /// 2. each of the source's size, offset or stride has more static information
313 /// than the corresponding result's size, offset or stride.
314 ///
315 /// Example 1:
316 /// ```mlir
317 ///   %1 = memref.cast %0 : memref<8x16xf32> to memref<?x?xf32>
318 ///   %2 = consumer %1 ... : memref<?x?xf32> ...
319 /// ```
320 ///
321 /// may fold into:
322 ///
323 /// ```mlir
324 ///   %2 = consumer %0 ... : memref<8x16xf32> ...
325 /// ```
326 ///
327 /// Example 2:
328 /// ```
329 ///   %1 = memref.cast %0 : memref<?x16xf32, affine_map<(i, j)->(16 * i + j)>>
330 ///          to memref<?x?xf32>
331 ///   consumer %1 : memref<?x?xf32> ...
332 /// ```
333 ///
334 /// may fold into:
335 ///
336 /// ```
337 ///   consumer %0 ... : memref<?x16xf32, affine_map<(i, j)->(16 * i + j)>>
338 /// ```
339 bool CastOp::canFoldIntoConsumerOp(CastOp castOp) {
340   MemRefType sourceType = castOp.source().getType().dyn_cast<MemRefType>();
341   MemRefType resultType = castOp.getType().dyn_cast<MemRefType>();
342 
343   // Requires ranked MemRefType.
344   if (!sourceType || !resultType)
345     return false;
346 
347   // Requires same elemental type.
348   if (sourceType.getElementType() != resultType.getElementType())
349     return false;
350 
351   // Requires same rank.
352   if (sourceType.getRank() != resultType.getRank())
353     return false;
354 
355   // Only fold casts between strided memref forms.
356   int64_t sourceOffset, resultOffset;
357   SmallVector<int64_t, 4> sourceStrides, resultStrides;
358   if (failed(getStridesAndOffset(sourceType, sourceStrides, sourceOffset)) ||
359       failed(getStridesAndOffset(resultType, resultStrides, resultOffset)))
360     return false;
361 
362   // If cast is towards more static sizes along any dimension, don't fold.
363   for (auto it : llvm::zip(sourceType.getShape(), resultType.getShape())) {
364     auto ss = std::get<0>(it), st = std::get<1>(it);
365     if (ss != st)
366       if (MemRefType::isDynamic(ss) && !MemRefType::isDynamic(st))
367         return false;
368   }
369 
370   // If cast is towards more static offset along any dimension, don't fold.
371   if (sourceOffset != resultOffset)
372     if (MemRefType::isDynamicStrideOrOffset(sourceOffset) &&
373         !MemRefType::isDynamicStrideOrOffset(resultOffset))
374       return false;
375 
376   // If cast is towards more static strides along any dimension, don't fold.
377   for (auto it : llvm::zip(sourceStrides, resultStrides)) {
378     auto ss = std::get<0>(it), st = std::get<1>(it);
379     if (ss != st)
380       if (MemRefType::isDynamicStrideOrOffset(ss) &&
381           !MemRefType::isDynamicStrideOrOffset(st))
382         return false;
383   }
384 
385   return true;
386 }
387 
388 bool CastOp::areCastCompatible(TypeRange inputs, TypeRange outputs) {
389   if (inputs.size() != 1 || outputs.size() != 1)
390     return false;
391   Type a = inputs.front(), b = outputs.front();
392   auto aT = a.dyn_cast<MemRefType>();
393   auto bT = b.dyn_cast<MemRefType>();
394 
395   auto uaT = a.dyn_cast<UnrankedMemRefType>();
396   auto ubT = b.dyn_cast<UnrankedMemRefType>();
397 
398   if (aT && bT) {
399     if (aT.getElementType() != bT.getElementType())
400       return false;
401     if (aT.getAffineMaps() != bT.getAffineMaps()) {
402       int64_t aOffset, bOffset;
403       SmallVector<int64_t, 4> aStrides, bStrides;
404       if (failed(getStridesAndOffset(aT, aStrides, aOffset)) ||
405           failed(getStridesAndOffset(bT, bStrides, bOffset)) ||
406           aStrides.size() != bStrides.size())
407         return false;
408 
409       // Strides along a dimension/offset are compatible if the value in the
410       // source memref is static and the value in the target memref is the
411       // same. They are also compatible if either one is dynamic (see
412       // description of MemRefCastOp for details).
413       auto checkCompatible = [](int64_t a, int64_t b) {
414         return (a == MemRefType::getDynamicStrideOrOffset() ||
415                 b == MemRefType::getDynamicStrideOrOffset() || a == b);
416       };
417       if (!checkCompatible(aOffset, bOffset))
418         return false;
419       for (auto aStride : enumerate(aStrides))
420         if (!checkCompatible(aStride.value(), bStrides[aStride.index()]))
421           return false;
422     }
423     if (aT.getMemorySpaceAsInt() != bT.getMemorySpaceAsInt())
424       return false;
425 
426     // They must have the same rank, and any specified dimensions must match.
427     if (aT.getRank() != bT.getRank())
428       return false;
429 
430     for (unsigned i = 0, e = aT.getRank(); i != e; ++i) {
431       int64_t aDim = aT.getDimSize(i), bDim = bT.getDimSize(i);
432       if (aDim != -1 && bDim != -1 && aDim != bDim)
433         return false;
434     }
435     return true;
436   } else {
437     if (!aT && !uaT)
438       return false;
439     if (!bT && !ubT)
440       return false;
441     // Unranked to unranked casting is unsupported
442     if (uaT && ubT)
443       return false;
444 
445     auto aEltType = (aT) ? aT.getElementType() : uaT.getElementType();
446     auto bEltType = (bT) ? bT.getElementType() : ubT.getElementType();
447     if (aEltType != bEltType)
448       return false;
449 
450     auto aMemSpace =
451         (aT) ? aT.getMemorySpaceAsInt() : uaT.getMemorySpaceAsInt();
452     auto bMemSpace =
453         (bT) ? bT.getMemorySpaceAsInt() : ubT.getMemorySpaceAsInt();
454     if (aMemSpace != bMemSpace)
455       return false;
456 
457     return true;
458   }
459 
460   return false;
461 }
462 
463 OpFoldResult CastOp::fold(ArrayRef<Attribute> operands) {
464   return succeeded(foldMemRefCast(*this)) ? getResult() : Value();
465 }
466 
467 //===----------------------------------------------------------------------===//
468 // DeallocOp
469 //===----------------------------------------------------------------------===//
470 namespace {
471 /// Fold Dealloc operations that are deallocating an AllocOp that is only used
472 /// by other Dealloc operations.
473 struct SimplifyDeadDealloc : public OpRewritePattern<DeallocOp> {
474   using OpRewritePattern<DeallocOp>::OpRewritePattern;
475 
476   LogicalResult matchAndRewrite(DeallocOp dealloc,
477                                 PatternRewriter &rewriter) const override {
478     // Check that the memref operand's defining operation is an AllocOp.
479     Value memref = dealloc.memref();
480     if (!isa_and_nonnull<AllocOp>(memref.getDefiningOp()))
481       return failure();
482 
483     // Check that all of the uses of the AllocOp are other DeallocOps.
484     for (auto *user : memref.getUsers())
485       if (!isa<DeallocOp>(user))
486         return failure();
487 
488     // Erase the dealloc operation.
489     rewriter.eraseOp(dealloc);
490     return success();
491   }
492 };
493 } // end anonymous namespace.
494 
495 static LogicalResult verify(DeallocOp op) {
496   if (!op.memref().getType().isa<MemRefType>())
497     return op.emitOpError("operand must be a memref");
498   return success();
499 }
500 
501 void DeallocOp::getCanonicalizationPatterns(OwningRewritePatternList &results,
502                                             MLIRContext *context) {
503   results.insert<SimplifyDeadDealloc>(context);
504 }
505 
506 LogicalResult DeallocOp::fold(ArrayRef<Attribute> cstOperands,
507                               SmallVectorImpl<OpFoldResult> &results) {
508   /// dealloc(memrefcast) -> dealloc
509   return foldMemRefCast(*this);
510 }
511 
512 //===----------------------------------------------------------------------===//
513 // DimOp
514 //===----------------------------------------------------------------------===//
515 
516 void DimOp::build(OpBuilder &builder, OperationState &result, Value memref,
517                   int64_t index) {
518   auto loc = result.location;
519   Value indexValue = builder.create<ConstantIndexOp>(loc, index);
520   build(builder, result, memref, indexValue);
521 }
522 
523 void DimOp::build(OpBuilder &builder, OperationState &result, Value memref,
524                   Value index) {
525   auto indexTy = builder.getIndexType();
526   build(builder, result, indexTy, memref, index);
527 }
528 
529 Optional<int64_t> DimOp::getConstantIndex() {
530   if (auto constantOp = index().getDefiningOp<ConstantOp>())
531     return constantOp.getValue().cast<IntegerAttr>().getInt();
532   return {};
533 }
534 
535 static LogicalResult verify(DimOp op) {
536   // Assume unknown index to be in range.
537   Optional<int64_t> index = op.getConstantIndex();
538   if (!index.hasValue())
539     return success();
540 
541   // Check that constant index is not knowingly out of range.
542   auto type = op.memrefOrTensor().getType();
543   if (auto memrefType = type.dyn_cast<MemRefType>()) {
544     if (index.getValue() >= memrefType.getRank())
545       return op.emitOpError("index is out of range");
546   } else if (auto tensorType = type.dyn_cast<RankedTensorType>()) {
547     if (index.getValue() >= tensorType.getRank())
548       return op.emitOpError("index is out of range");
549   } else if (type.isa<UnrankedMemRefType>() || type.isa<UnrankedTensorType>()) {
550     // Assume index to be in range.
551   } else {
552     llvm_unreachable("expected operand with memref type");
553   }
554   return success();
555 }
556 
557 OpFoldResult DimOp::fold(ArrayRef<Attribute> operands) {
558   auto index = operands[1].dyn_cast_or_null<IntegerAttr>();
559 
560   // All forms of folding require a known index.
561   if (!index)
562     return {};
563 
564   auto argTy = memrefOrTensor().getType();
565   // Fold if the shape extent along the given index is known.
566   if (auto shapedTy = argTy.dyn_cast<ShapedType>()) {
567     // Folding for unranked types (UnrankedMemRefType) is not supported.
568     if (!shapedTy.hasRank())
569       return {};
570     if (!shapedTy.isDynamicDim(index.getInt())) {
571       Builder builder(getContext());
572       return builder.getIndexAttr(shapedTy.getShape()[index.getInt()]);
573     }
574   }
575 
576   Operation *definingOp = memrefOrTensor().getDefiningOp();
577 
578   // dim(memref.tensor_load(memref)) -> dim(memref)
579   if (auto tensorLoadOp = dyn_cast_or_null<TensorLoadOp>(definingOp)) {
580     setOperand(0, tensorLoadOp.memref());
581     return getResult();
582   }
583 
584   // Fold dim to the operand of tensor.generate.
585   if (auto fromElements = dyn_cast_or_null<tensor::GenerateOp>(definingOp)) {
586     auto resultType =
587         fromElements.getResult().getType().cast<RankedTensorType>();
588     // The case where the type encodes the size of the dimension is handled
589     // above.
590     assert(resultType.getShape()[index.getInt()] ==
591            RankedTensorType::kDynamicSize);
592 
593     // Find the operand of the fromElements that corresponds to this index.
594     auto dynExtents = fromElements.dynamicExtents().begin();
595     for (auto dim : resultType.getShape().take_front(index.getInt()))
596       if (dim == RankedTensorType::kDynamicSize)
597         dynExtents++;
598 
599     return Value{*dynExtents};
600   }
601 
602   // The size at the given index is now known to be a dynamic size.
603   unsigned unsignedIndex = index.getValue().getZExtValue();
604 
605   if (auto subtensor = dyn_cast_or_null<mlir::SubTensorOp>(definingOp)) {
606     assert(subtensor.isDynamicSize(unsignedIndex) &&
607            "Expected dynamic subtensor size");
608     return subtensor.getDynamicSize(unsignedIndex);
609   }
610 
611   // Fold dim to the size argument for an `AllocOp`, `ViewOp`, or `SubViewOp`.
612   auto memrefType = argTy.dyn_cast<MemRefType>();
613   if (!memrefType)
614     return {};
615 
616   if (auto alloc = dyn_cast_or_null<AllocOp>(definingOp))
617     return *(alloc.getDynamicSizes().begin() +
618              memrefType.getDynamicDimIndex(unsignedIndex));
619 
620   if (auto view = dyn_cast_or_null<ViewOp>(definingOp))
621     return *(view.getDynamicSizes().begin() +
622              memrefType.getDynamicDimIndex(unsignedIndex));
623 
624   if (auto subview = dyn_cast_or_null<SubViewOp>(definingOp)) {
625     assert(subview.isDynamicSize(unsignedIndex) &&
626            "Expected dynamic subview size");
627     return subview.getDynamicSize(unsignedIndex);
628   }
629 
630   // dim(memrefcast) -> dim
631   if (succeeded(foldMemRefCast(*this)))
632     return getResult();
633 
634   return {};
635 }
636 
637 namespace {
638 /// Fold dim of a memref reshape operation to a load into the reshape's shape
639 /// operand.
640 struct DimOfMemRefReshape : public OpRewritePattern<DimOp> {
641   using OpRewritePattern<DimOp>::OpRewritePattern;
642 
643   LogicalResult matchAndRewrite(DimOp dim,
644                                 PatternRewriter &rewriter) const override {
645     auto reshape = dim.memrefOrTensor().getDefiningOp<ReshapeOp>();
646 
647     if (!reshape)
648       return failure();
649 
650     // Place the load directly after the reshape to ensure that the shape memref
651     // was not mutated.
652     rewriter.setInsertionPointAfter(reshape);
653     rewriter.replaceOpWithNewOp<LoadOp>(dim, reshape.shape(),
654                                         llvm::makeArrayRef({dim.index()}));
655     return success();
656   }
657 };
658 
659 /// Fold dim of a dim of a cast into the dim of the source of the tensor cast.
660 template <typename CastOpTy>
661 struct DimOfCastOp : public OpRewritePattern<DimOp> {
662   using OpRewritePattern<DimOp>::OpRewritePattern;
663 
664   LogicalResult matchAndRewrite(DimOp dimOp,
665                                 PatternRewriter &rewriter) const override {
666     auto castOp = dimOp.memrefOrTensor().getDefiningOp<CastOpTy>();
667     if (!castOp)
668       return failure();
669     Value newSource = castOp.getOperand();
670     rewriter.replaceOpWithNewOp<DimOp>(dimOp, newSource, dimOp.index());
671     return success();
672   }
673 };
674 } // end anonymous namespace.
675 
676 void DimOp::getCanonicalizationPatterns(OwningRewritePatternList &results,
677                                         MLIRContext *context) {
678   results.insert<DimOfMemRefReshape, DimOfCastOp<BufferCastOp>,
679                  DimOfCastOp<tensor::CastOp>>(context);
680 }
681 
682 // ---------------------------------------------------------------------------
683 // DmaStartOp
684 // ---------------------------------------------------------------------------
685 
686 void DmaStartOp::build(OpBuilder &builder, OperationState &result,
687                        Value srcMemRef, ValueRange srcIndices, Value destMemRef,
688                        ValueRange destIndices, Value numElements,
689                        Value tagMemRef, ValueRange tagIndices, Value stride,
690                        Value elementsPerStride) {
691   result.addOperands(srcMemRef);
692   result.addOperands(srcIndices);
693   result.addOperands(destMemRef);
694   result.addOperands(destIndices);
695   result.addOperands({numElements, tagMemRef});
696   result.addOperands(tagIndices);
697   if (stride)
698     result.addOperands({stride, elementsPerStride});
699 }
700 
701 void DmaStartOp::print(OpAsmPrinter &p) {
702   p << getOperationName() << " " << getSrcMemRef() << '[' << getSrcIndices()
703     << "], " << getDstMemRef() << '[' << getDstIndices() << "], "
704     << getNumElements() << ", " << getTagMemRef() << '[' << getTagIndices()
705     << ']';
706   if (isStrided())
707     p << ", " << getStride() << ", " << getNumElementsPerStride();
708 
709   p.printOptionalAttrDict((*this)->getAttrs());
710   p << " : " << getSrcMemRef().getType() << ", " << getDstMemRef().getType()
711     << ", " << getTagMemRef().getType();
712 }
713 
714 // Parse DmaStartOp.
715 // Ex:
716 //   %dma_id = dma_start %src[%i, %j], %dst[%k, %l], %size,
717 //                       %tag[%index], %stride, %num_elt_per_stride :
718 //                     : memref<3076 x f32, 0>,
719 //                       memref<1024 x f32, 2>,
720 //                       memref<1 x i32>
721 //
722 ParseResult DmaStartOp::parse(OpAsmParser &parser, OperationState &result) {
723   OpAsmParser::OperandType srcMemRefInfo;
724   SmallVector<OpAsmParser::OperandType, 4> srcIndexInfos;
725   OpAsmParser::OperandType dstMemRefInfo;
726   SmallVector<OpAsmParser::OperandType, 4> dstIndexInfos;
727   OpAsmParser::OperandType numElementsInfo;
728   OpAsmParser::OperandType tagMemrefInfo;
729   SmallVector<OpAsmParser::OperandType, 4> tagIndexInfos;
730   SmallVector<OpAsmParser::OperandType, 2> strideInfo;
731 
732   SmallVector<Type, 3> types;
733   auto indexType = parser.getBuilder().getIndexType();
734 
735   // Parse and resolve the following list of operands:
736   // *) source memref followed by its indices (in square brackets).
737   // *) destination memref followed by its indices (in square brackets).
738   // *) dma size in KiB.
739   if (parser.parseOperand(srcMemRefInfo) ||
740       parser.parseOperandList(srcIndexInfos, OpAsmParser::Delimiter::Square) ||
741       parser.parseComma() || parser.parseOperand(dstMemRefInfo) ||
742       parser.parseOperandList(dstIndexInfos, OpAsmParser::Delimiter::Square) ||
743       parser.parseComma() || parser.parseOperand(numElementsInfo) ||
744       parser.parseComma() || parser.parseOperand(tagMemrefInfo) ||
745       parser.parseOperandList(tagIndexInfos, OpAsmParser::Delimiter::Square))
746     return failure();
747 
748   // Parse optional stride and elements per stride.
749   if (parser.parseTrailingOperandList(strideInfo))
750     return failure();
751 
752   bool isStrided = strideInfo.size() == 2;
753   if (!strideInfo.empty() && !isStrided) {
754     return parser.emitError(parser.getNameLoc(),
755                             "expected two stride related operands");
756   }
757 
758   if (parser.parseColonTypeList(types))
759     return failure();
760   if (types.size() != 3)
761     return parser.emitError(parser.getNameLoc(), "fewer/more types expected");
762 
763   if (parser.resolveOperand(srcMemRefInfo, types[0], result.operands) ||
764       parser.resolveOperands(srcIndexInfos, indexType, result.operands) ||
765       parser.resolveOperand(dstMemRefInfo, types[1], result.operands) ||
766       parser.resolveOperands(dstIndexInfos, indexType, result.operands) ||
767       // size should be an index.
768       parser.resolveOperand(numElementsInfo, indexType, result.operands) ||
769       parser.resolveOperand(tagMemrefInfo, types[2], result.operands) ||
770       // tag indices should be index.
771       parser.resolveOperands(tagIndexInfos, indexType, result.operands))
772     return failure();
773 
774   if (isStrided) {
775     if (parser.resolveOperands(strideInfo, indexType, result.operands))
776       return failure();
777   }
778 
779   return success();
780 }
781 
782 LogicalResult DmaStartOp::verify() {
783   unsigned numOperands = getNumOperands();
784 
785   // Mandatory non-variadic operands are: src memref, dst memref, tag memref and
786   // the number of elements.
787   if (numOperands < 4)
788     return emitOpError("expected at least 4 operands");
789 
790   // Check types of operands. The order of these calls is important: the later
791   // calls rely on some type properties to compute the operand position.
792   // 1. Source memref.
793   if (!getSrcMemRef().getType().isa<MemRefType>())
794     return emitOpError("expected source to be of memref type");
795   if (numOperands < getSrcMemRefRank() + 4)
796     return emitOpError() << "expected at least " << getSrcMemRefRank() + 4
797                          << " operands";
798   if (!getSrcIndices().empty() &&
799       !llvm::all_of(getSrcIndices().getTypes(),
800                     [](Type t) { return t.isIndex(); }))
801     return emitOpError("expected source indices to be of index type");
802 
803   // 2. Destination memref.
804   if (!getDstMemRef().getType().isa<MemRefType>())
805     return emitOpError("expected destination to be of memref type");
806   unsigned numExpectedOperands = getSrcMemRefRank() + getDstMemRefRank() + 4;
807   if (numOperands < numExpectedOperands)
808     return emitOpError() << "expected at least " << numExpectedOperands
809                          << " operands";
810   if (!getDstIndices().empty() &&
811       !llvm::all_of(getDstIndices().getTypes(),
812                     [](Type t) { return t.isIndex(); }))
813     return emitOpError("expected destination indices to be of index type");
814 
815   // 3. Number of elements.
816   if (!getNumElements().getType().isIndex())
817     return emitOpError("expected num elements to be of index type");
818 
819   // 4. Tag memref.
820   if (!getTagMemRef().getType().isa<MemRefType>())
821     return emitOpError("expected tag to be of memref type");
822   numExpectedOperands += getTagMemRefRank();
823   if (numOperands < numExpectedOperands)
824     return emitOpError() << "expected at least " << numExpectedOperands
825                          << " operands";
826   if (!getTagIndices().empty() &&
827       !llvm::all_of(getTagIndices().getTypes(),
828                     [](Type t) { return t.isIndex(); }))
829     return emitOpError("expected tag indices to be of index type");
830 
831   // DMAs from different memory spaces supported.
832   if (getSrcMemorySpace() == getDstMemorySpace())
833     return emitOpError("DMA should be between different memory spaces");
834 
835   // Optional stride-related operands must be either both present or both
836   // absent.
837   if (numOperands != numExpectedOperands &&
838       numOperands != numExpectedOperands + 2)
839     return emitOpError("incorrect number of operands");
840 
841   // 5. Strides.
842   if (isStrided()) {
843     if (!getStride().getType().isIndex() ||
844         !getNumElementsPerStride().getType().isIndex())
845       return emitOpError(
846           "expected stride and num elements per stride to be of type index");
847   }
848 
849   return success();
850 }
851 
852 LogicalResult DmaStartOp::fold(ArrayRef<Attribute> cstOperands,
853                                SmallVectorImpl<OpFoldResult> &results) {
854   /// dma_start(memrefcast) -> dma_start
855   return foldMemRefCast(*this);
856 }
857 
858 // ---------------------------------------------------------------------------
859 // DmaWaitOp
860 // ---------------------------------------------------------------------------
861 
862 void DmaWaitOp::build(OpBuilder &builder, OperationState &result,
863                       Value tagMemRef, ValueRange tagIndices,
864                       Value numElements) {
865   result.addOperands(tagMemRef);
866   result.addOperands(tagIndices);
867   result.addOperands(numElements);
868 }
869 
870 void DmaWaitOp::print(OpAsmPrinter &p) {
871   p << getOperationName() << " " << getTagMemRef() << '[' << getTagIndices()
872     << "], " << getNumElements();
873   p.printOptionalAttrDict((*this)->getAttrs());
874   p << " : " << getTagMemRef().getType();
875 }
876 
877 // Parse DmaWaitOp.
878 // Eg:
879 //   dma_wait %tag[%index], %num_elements : memref<1 x i32, (d0) -> (d0), 4>
880 //
881 ParseResult DmaWaitOp::parse(OpAsmParser &parser, OperationState &result) {
882   OpAsmParser::OperandType tagMemrefInfo;
883   SmallVector<OpAsmParser::OperandType, 2> tagIndexInfos;
884   Type type;
885   auto indexType = parser.getBuilder().getIndexType();
886   OpAsmParser::OperandType numElementsInfo;
887 
888   // Parse tag memref, its indices, and dma size.
889   if (parser.parseOperand(tagMemrefInfo) ||
890       parser.parseOperandList(tagIndexInfos, OpAsmParser::Delimiter::Square) ||
891       parser.parseComma() || parser.parseOperand(numElementsInfo) ||
892       parser.parseColonType(type) ||
893       parser.resolveOperand(tagMemrefInfo, type, result.operands) ||
894       parser.resolveOperands(tagIndexInfos, indexType, result.operands) ||
895       parser.resolveOperand(numElementsInfo, indexType, result.operands))
896     return failure();
897 
898   return success();
899 }
900 
901 LogicalResult DmaWaitOp::fold(ArrayRef<Attribute> cstOperands,
902                               SmallVectorImpl<OpFoldResult> &results) {
903   /// dma_wait(memrefcast) -> dma_wait
904   return foldMemRefCast(*this);
905 }
906 
907 LogicalResult DmaWaitOp::verify() {
908   // Mandatory non-variadic operands are tag and the number of elements.
909   if (getNumOperands() < 2)
910     return emitOpError() << "expected at least 2 operands";
911 
912   // Check types of operands. The order of these calls is important: the later
913   // calls rely on some type properties to compute the operand position.
914   if (!getTagMemRef().getType().isa<MemRefType>())
915     return emitOpError() << "expected tag to be of memref type";
916 
917   if (getNumOperands() != 2 + getTagMemRefRank())
918     return emitOpError() << "expected " << 2 + getTagMemRefRank()
919                          << " operands";
920 
921   if (!getTagIndices().empty() &&
922       !llvm::all_of(getTagIndices().getTypes(),
923                     [](Type t) { return t.isIndex(); }))
924     return emitOpError() << "expected tag indices to be of index type";
925 
926   if (!getNumElements().getType().isIndex())
927     return emitOpError()
928            << "expected the number of elements to be of index type";
929 
930   return success();
931 }
932 
933 //===----------------------------------------------------------------------===//
934 // GlobalOp
935 //===----------------------------------------------------------------------===//
936 
937 static void printGlobalMemrefOpTypeAndInitialValue(OpAsmPrinter &p, GlobalOp op,
938                                                    TypeAttr type,
939                                                    Attribute initialValue) {
940   p << type;
941   if (!op.isExternal()) {
942     p << " = ";
943     if (op.isUninitialized())
944       p << "uninitialized";
945     else
946       p.printAttributeWithoutType(initialValue);
947   }
948 }
949 
950 static ParseResult
951 parseGlobalMemrefOpTypeAndInitialValue(OpAsmParser &parser, TypeAttr &typeAttr,
952                                        Attribute &initialValue) {
953   Type type;
954   if (parser.parseType(type))
955     return failure();
956 
957   auto memrefType = type.dyn_cast<MemRefType>();
958   if (!memrefType || !memrefType.hasStaticShape())
959     return parser.emitError(parser.getNameLoc())
960            << "type should be static shaped memref, but got " << type;
961   typeAttr = TypeAttr::get(type);
962 
963   if (parser.parseOptionalEqual())
964     return success();
965 
966   if (succeeded(parser.parseOptionalKeyword("uninitialized"))) {
967     initialValue = UnitAttr::get(parser.getBuilder().getContext());
968     return success();
969   }
970 
971   Type tensorType = getTensorTypeFromMemRefType(memrefType);
972   if (parser.parseAttribute(initialValue, tensorType))
973     return failure();
974   if (!initialValue.isa<ElementsAttr>())
975     return parser.emitError(parser.getNameLoc())
976            << "initial value should be a unit or elements attribute";
977   return success();
978 }
979 
980 static LogicalResult verify(GlobalOp op) {
981   auto memrefType = op.type().dyn_cast<MemRefType>();
982   if (!memrefType || !memrefType.hasStaticShape())
983     return op.emitOpError("type should be static shaped memref, but got ")
984            << op.type();
985 
986   // Verify that the initial value, if present, is either a unit attribute or
987   // an elements attribute.
988   if (op.initial_value().hasValue()) {
989     Attribute initValue = op.initial_value().getValue();
990     if (!initValue.isa<UnitAttr>() && !initValue.isa<ElementsAttr>())
991       return op.emitOpError("initial value should be a unit or elements "
992                             "attribute, but got ")
993              << initValue;
994 
995     // Check that the type of the initial value is compatible with the type of
996     // the global variable.
997     if (initValue.isa<ElementsAttr>()) {
998       Type initType = initValue.getType();
999       Type tensorType = getTensorTypeFromMemRefType(memrefType);
1000       if (initType != tensorType)
1001         return op.emitOpError("initial value expected to be of type ")
1002                << tensorType << ", but was of type " << initType;
1003     }
1004   }
1005 
1006   // TODO: verify visibility for declarations.
1007   return success();
1008 }
1009 
1010 //===----------------------------------------------------------------------===//
1011 // GetGlobalOp
1012 //===----------------------------------------------------------------------===//
1013 
1014 LogicalResult
1015 GetGlobalOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
1016   // Verify that the result type is same as the type of the referenced
1017   // memref.global op.
1018   auto global =
1019       symbolTable.lookupNearestSymbolFrom<GlobalOp>(*this, nameAttr());
1020   if (!global)
1021     return emitOpError("'")
1022            << name() << "' does not reference a valid global memref";
1023 
1024   Type resultType = result().getType();
1025   if (global.type() != resultType)
1026     return emitOpError("result type ")
1027            << resultType << " does not match type " << global.type()
1028            << " of the global memref @" << name();
1029   return success();
1030 }
1031 
1032 //===----------------------------------------------------------------------===//
1033 // LoadOp
1034 //===----------------------------------------------------------------------===//
1035 
1036 static LogicalResult verify(LoadOp op) {
1037   if (op.getNumOperands() != 1 + op.getMemRefType().getRank())
1038     return op.emitOpError("incorrect number of indices for load");
1039   return success();
1040 }
1041 
1042 OpFoldResult LoadOp::fold(ArrayRef<Attribute> cstOperands) {
1043   /// load(memrefcast) -> load
1044   if (succeeded(foldMemRefCast(*this)))
1045     return getResult();
1046   return OpFoldResult();
1047 }
1048 
1049 namespace {
1050 /// Fold a load on a buffer_cast operation into an tensor.extract on the
1051 /// corresponding tensor.
1052 struct LoadOfBufferCast : public OpRewritePattern<LoadOp> {
1053   using OpRewritePattern<LoadOp>::OpRewritePattern;
1054 
1055   LogicalResult matchAndRewrite(LoadOp load,
1056                                 PatternRewriter &rewriter) const override {
1057     auto buffercast = load.memref().getDefiningOp<BufferCastOp>();
1058     if (!buffercast)
1059       return failure();
1060 
1061     rewriter.replaceOpWithNewOp<tensor::ExtractOp>(load, buffercast.tensor(),
1062                                                    load.indices());
1063     return success();
1064   }
1065 };
1066 } // end anonymous namespace.
1067 
1068 void LoadOp::getCanonicalizationPatterns(OwningRewritePatternList &results,
1069                                          MLIRContext *context) {
1070   results.insert<LoadOfBufferCast>(context);
1071 }
1072 
1073 //===----------------------------------------------------------------------===//
1074 // PrefetchOp
1075 //===----------------------------------------------------------------------===//
1076 
1077 static void print(OpAsmPrinter &p, PrefetchOp op) {
1078   p << PrefetchOp::getOperationName() << " " << op.memref() << '[';
1079   p.printOperands(op.indices());
1080   p << ']' << ", " << (op.isWrite() ? "write" : "read");
1081   p << ", locality<" << op.localityHint();
1082   p << ">, " << (op.isDataCache() ? "data" : "instr");
1083   p.printOptionalAttrDict(
1084       op->getAttrs(),
1085       /*elidedAttrs=*/{"localityHint", "isWrite", "isDataCache"});
1086   p << " : " << op.getMemRefType();
1087 }
1088 
1089 static ParseResult parsePrefetchOp(OpAsmParser &parser,
1090                                    OperationState &result) {
1091   OpAsmParser::OperandType memrefInfo;
1092   SmallVector<OpAsmParser::OperandType, 4> indexInfo;
1093   IntegerAttr localityHint;
1094   MemRefType type;
1095   StringRef readOrWrite, cacheType;
1096 
1097   auto indexTy = parser.getBuilder().getIndexType();
1098   auto i32Type = parser.getBuilder().getIntegerType(32);
1099   if (parser.parseOperand(memrefInfo) ||
1100       parser.parseOperandList(indexInfo, OpAsmParser::Delimiter::Square) ||
1101       parser.parseComma() || parser.parseKeyword(&readOrWrite) ||
1102       parser.parseComma() || parser.parseKeyword("locality") ||
1103       parser.parseLess() ||
1104       parser.parseAttribute(localityHint, i32Type, "localityHint",
1105                             result.attributes) ||
1106       parser.parseGreater() || parser.parseComma() ||
1107       parser.parseKeyword(&cacheType) || parser.parseColonType(type) ||
1108       parser.resolveOperand(memrefInfo, type, result.operands) ||
1109       parser.resolveOperands(indexInfo, indexTy, result.operands))
1110     return failure();
1111 
1112   if (!readOrWrite.equals("read") && !readOrWrite.equals("write"))
1113     return parser.emitError(parser.getNameLoc(),
1114                             "rw specifier has to be 'read' or 'write'");
1115   result.addAttribute(
1116       PrefetchOp::getIsWriteAttrName(),
1117       parser.getBuilder().getBoolAttr(readOrWrite.equals("write")));
1118 
1119   if (!cacheType.equals("data") && !cacheType.equals("instr"))
1120     return parser.emitError(parser.getNameLoc(),
1121                             "cache type has to be 'data' or 'instr'");
1122 
1123   result.addAttribute(
1124       PrefetchOp::getIsDataCacheAttrName(),
1125       parser.getBuilder().getBoolAttr(cacheType.equals("data")));
1126 
1127   return success();
1128 }
1129 
1130 static LogicalResult verify(PrefetchOp op) {
1131   if (op.getNumOperands() != 1 + op.getMemRefType().getRank())
1132     return op.emitOpError("too few indices");
1133 
1134   return success();
1135 }
1136 
1137 LogicalResult PrefetchOp::fold(ArrayRef<Attribute> cstOperands,
1138                                SmallVectorImpl<OpFoldResult> &results) {
1139   // prefetch(memrefcast) -> prefetch
1140   return foldMemRefCast(*this);
1141 }
1142 
1143 //===----------------------------------------------------------------------===//
1144 // ReinterpretCastOp
1145 //===----------------------------------------------------------------------===//
1146 
1147 /// Build a ReinterpretCastOp with all dynamic entries: `staticOffsets`,
1148 /// `staticSizes` and `staticStrides` are automatically filled with
1149 /// source-memref-rank sentinel values that encode dynamic entries.
1150 void ReinterpretCastOp::build(OpBuilder &b, OperationState &result,
1151                               MemRefType resultType, Value source,
1152                               OpFoldResult offset, ArrayRef<OpFoldResult> sizes,
1153                               ArrayRef<OpFoldResult> strides,
1154                               ArrayRef<NamedAttribute> attrs) {
1155   SmallVector<int64_t> staticOffsets, staticSizes, staticStrides;
1156   SmallVector<Value> dynamicOffsets, dynamicSizes, dynamicStrides;
1157   dispatchIndexOpFoldResults(offset, dynamicOffsets, staticOffsets,
1158                              ShapedType::kDynamicStrideOrOffset);
1159   dispatchIndexOpFoldResults(sizes, dynamicSizes, staticSizes,
1160                              ShapedType::kDynamicSize);
1161   dispatchIndexOpFoldResults(strides, dynamicStrides, staticStrides,
1162                              ShapedType::kDynamicStrideOrOffset);
1163   build(b, result, resultType, source, dynamicOffsets, dynamicSizes,
1164         dynamicStrides, b.getI64ArrayAttr(staticOffsets),
1165         b.getI64ArrayAttr(staticSizes), b.getI64ArrayAttr(staticStrides));
1166   result.addAttributes(attrs);
1167 }
1168 
1169 void ReinterpretCastOp::build(OpBuilder &b, OperationState &result,
1170                               MemRefType resultType, Value source,
1171                               int64_t offset, ArrayRef<int64_t> sizes,
1172                               ArrayRef<int64_t> strides,
1173                               ArrayRef<NamedAttribute> attrs) {
1174   SmallVector<OpFoldResult> sizeValues =
1175       llvm::to_vector<4>(llvm::map_range(sizes, [&](int64_t v) -> OpFoldResult {
1176         return b.getI64IntegerAttr(v);
1177       }));
1178   SmallVector<OpFoldResult> strideValues = llvm::to_vector<4>(
1179       llvm::map_range(strides, [&](int64_t v) -> OpFoldResult {
1180         return b.getI64IntegerAttr(v);
1181       }));
1182   build(b, result, resultType, source, b.getI64IntegerAttr(offset), sizeValues,
1183         strideValues, attrs);
1184 }
1185 
1186 void ReinterpretCastOp::build(OpBuilder &b, OperationState &result,
1187                               MemRefType resultType, Value source, Value offset,
1188                               ValueRange sizes, ValueRange strides,
1189                               ArrayRef<NamedAttribute> attrs) {
1190   SmallVector<OpFoldResult> sizeValues = llvm::to_vector<4>(
1191       llvm::map_range(sizes, [](Value v) -> OpFoldResult { return v; }));
1192   SmallVector<OpFoldResult> strideValues = llvm::to_vector<4>(
1193       llvm::map_range(strides, [](Value v) -> OpFoldResult { return v; }));
1194   build(b, result, resultType, source, offset, sizeValues, strideValues, attrs);
1195 }
1196 
1197 // TODO: ponder whether we want to allow missing trailing sizes/strides that are
1198 // completed automatically, like we have for subview and subtensor.
1199 static LogicalResult verify(ReinterpretCastOp op) {
1200   // The source and result memrefs should be in the same memory space.
1201   auto srcType = op.source().getType().cast<BaseMemRefType>();
1202   auto resultType = op.getType().cast<MemRefType>();
1203   if (srcType.getMemorySpaceAsInt() != resultType.getMemorySpaceAsInt())
1204     return op.emitError("different memory spaces specified for source type ")
1205            << srcType << " and result memref type " << resultType;
1206   if (srcType.getElementType() != resultType.getElementType())
1207     return op.emitError("different element types specified for source type ")
1208            << srcType << " and result memref type " << resultType;
1209 
1210   // Match sizes in result memref type and in static_sizes attribute.
1211   for (auto &en :
1212        llvm::enumerate(llvm::zip(resultType.getShape(),
1213                                  extractFromI64ArrayAttr(op.static_sizes())))) {
1214     int64_t resultSize = std::get<0>(en.value());
1215     int64_t expectedSize = std::get<1>(en.value());
1216     if (resultSize != expectedSize)
1217       return op.emitError("expected result type with size = ")
1218              << expectedSize << " instead of " << resultSize
1219              << " in dim = " << en.index();
1220   }
1221 
1222   // Match offset and strides in static_offset and static_strides attributes if
1223   // result memref type has an affine map specified.
1224   if (!resultType.getAffineMaps().empty()) {
1225     int64_t resultOffset;
1226     SmallVector<int64_t, 4> resultStrides;
1227     if (failed(getStridesAndOffset(resultType, resultStrides, resultOffset)))
1228       return failure();
1229 
1230     // Match offset in result memref type and in static_offsets attribute.
1231     int64_t expectedOffset =
1232         extractFromI64ArrayAttr(op.static_offsets()).front();
1233     if (resultOffset != expectedOffset)
1234       return op.emitError("expected result type with offset = ")
1235              << resultOffset << " instead of " << expectedOffset;
1236 
1237     // Match strides in result memref type and in static_strides attribute.
1238     for (auto &en : llvm::enumerate(llvm::zip(
1239              resultStrides, extractFromI64ArrayAttr(op.static_strides())))) {
1240       int64_t resultStride = std::get<0>(en.value());
1241       int64_t expectedStride = std::get<1>(en.value());
1242       if (resultStride != expectedStride)
1243         return op.emitError("expected result type with stride = ")
1244                << expectedStride << " instead of " << resultStride
1245                << " in dim = " << en.index();
1246     }
1247   }
1248   return success();
1249 }
1250 
1251 //===----------------------------------------------------------------------===//
1252 // ReshapeOp
1253 //===----------------------------------------------------------------------===//
1254 
1255 static LogicalResult verify(ReshapeOp op) {
1256   Type operandType = op.source().getType();
1257   Type resultType = op.result().getType();
1258 
1259   Type operandElementType = operandType.cast<ShapedType>().getElementType();
1260   Type resultElementType = resultType.cast<ShapedType>().getElementType();
1261   if (operandElementType != resultElementType)
1262     return op.emitOpError("element types of source and destination memref "
1263                           "types should be the same");
1264 
1265   if (auto operandMemRefType = operandType.dyn_cast<MemRefType>())
1266     if (!operandMemRefType.getAffineMaps().empty())
1267       return op.emitOpError(
1268           "source memref type should have identity affine map");
1269 
1270   int64_t shapeSize = op.shape().getType().cast<MemRefType>().getDimSize(0);
1271   auto resultMemRefType = resultType.dyn_cast<MemRefType>();
1272   if (resultMemRefType) {
1273     if (!resultMemRefType.getAffineMaps().empty())
1274       return op.emitOpError(
1275           "result memref type should have identity affine map");
1276     if (shapeSize == ShapedType::kDynamicSize)
1277       return op.emitOpError("cannot use shape operand with dynamic length to "
1278                             "reshape to statically-ranked memref type");
1279     if (shapeSize != resultMemRefType.getRank())
1280       return op.emitOpError(
1281           "length of shape operand differs from the result's memref rank");
1282   }
1283   return success();
1284 }
1285 
1286 //===----------------------------------------------------------------------===//
1287 // StoreOp
1288 //===----------------------------------------------------------------------===//
1289 
1290 static LogicalResult verify(StoreOp op) {
1291   if (op.getNumOperands() != 2 + op.getMemRefType().getRank())
1292     return op.emitOpError("store index operand count not equal to memref rank");
1293 
1294   return success();
1295 }
1296 
1297 LogicalResult StoreOp::fold(ArrayRef<Attribute> cstOperands,
1298                             SmallVectorImpl<OpFoldResult> &results) {
1299   /// store(memrefcast) -> store
1300   return foldMemRefCast(*this);
1301 }
1302 
1303 //===----------------------------------------------------------------------===//
1304 // SubViewOp
1305 //===----------------------------------------------------------------------===//
1306 
1307 namespace {
1308 /// Helpers to write more idiomatic operations.
1309 namespace saturated_arith {
1310 struct Wrapper {
1311   explicit Wrapper(int64_t v) : v(v) {}
1312   operator int64_t() { return v; }
1313   int64_t v;
1314 };
1315 Wrapper operator+(Wrapper a, int64_t b) {
1316   if (ShapedType::isDynamicStrideOrOffset(a) ||
1317       ShapedType::isDynamicStrideOrOffset(b))
1318     return Wrapper(ShapedType::kDynamicStrideOrOffset);
1319   return Wrapper(a.v + b);
1320 }
1321 Wrapper operator*(Wrapper a, int64_t b) {
1322   if (ShapedType::isDynamicStrideOrOffset(a) ||
1323       ShapedType::isDynamicStrideOrOffset(b))
1324     return Wrapper(ShapedType::kDynamicStrideOrOffset);
1325   return Wrapper(a.v * b);
1326 }
1327 } // end namespace saturated_arith
1328 } // end namespace
1329 
1330 /// A subview result type can be fully inferred from the source type and the
1331 /// static representation of offsets, sizes and strides. Special sentinels
1332 /// encode the dynamic case.
1333 Type SubViewOp::inferResultType(MemRefType sourceMemRefType,
1334                                 ArrayRef<int64_t> leadingStaticOffsets,
1335                                 ArrayRef<int64_t> leadingStaticSizes,
1336                                 ArrayRef<int64_t> leadingStaticStrides) {
1337   // A subview may specify only a leading subset of offset/sizes/strides in
1338   // which case we complete with offset=0, sizes from memref type and strides=1.
1339   unsigned rank = sourceMemRefType.getRank();
1340   assert(leadingStaticOffsets.size() <= rank &&
1341          "unexpected leadingStaticOffsets overflow");
1342   assert(leadingStaticSizes.size() <= rank &&
1343          "unexpected leadingStaticSizes overflow");
1344   assert(leadingStaticStrides.size() <= rank &&
1345          "unexpected leadingStaticStrides overflow");
1346   auto staticOffsets = llvm::to_vector<4>(leadingStaticOffsets);
1347   auto staticSizes = llvm::to_vector<4>(leadingStaticSizes);
1348   auto staticStrides = llvm::to_vector<4>(leadingStaticStrides);
1349   unsigned numTrailingOffsets = rank - staticOffsets.size();
1350   unsigned numTrailingSizes = rank - staticSizes.size();
1351   unsigned numTrailingStrides = rank - staticStrides.size();
1352   staticOffsets.append(numTrailingOffsets, 0);
1353   llvm::append_range(staticSizes,
1354                      sourceMemRefType.getShape().take_back(numTrailingSizes));
1355   staticStrides.append(numTrailingStrides, 1);
1356 
1357   // Extract source offset and strides.
1358   int64_t sourceOffset;
1359   SmallVector<int64_t, 4> sourceStrides;
1360   auto res = getStridesAndOffset(sourceMemRefType, sourceStrides, sourceOffset);
1361   assert(succeeded(res) && "SubViewOp expected strided memref type");
1362   (void)res;
1363 
1364   // Compute target offset whose value is:
1365   //   `sourceOffset + sum_i(staticOffset_i * sourceStrides_i)`.
1366   int64_t targetOffset = sourceOffset;
1367   for (auto it : llvm::zip(staticOffsets, sourceStrides)) {
1368     auto staticOffset = std::get<0>(it), targetStride = std::get<1>(it);
1369     using namespace saturated_arith;
1370     targetOffset = Wrapper(targetOffset) + Wrapper(staticOffset) * targetStride;
1371   }
1372 
1373   // Compute target stride whose value is:
1374   //   `sourceStrides_i * staticStrides_i`.
1375   SmallVector<int64_t, 4> targetStrides;
1376   targetStrides.reserve(staticOffsets.size());
1377   for (auto it : llvm::zip(sourceStrides, staticStrides)) {
1378     auto sourceStride = std::get<0>(it), staticStride = std::get<1>(it);
1379     using namespace saturated_arith;
1380     targetStrides.push_back(Wrapper(sourceStride) * staticStride);
1381   }
1382 
1383   // The type is now known.
1384   return MemRefType::get(
1385       staticSizes, sourceMemRefType.getElementType(),
1386       makeStridedLinearLayoutMap(targetStrides, targetOffset,
1387                                  sourceMemRefType.getContext()),
1388       sourceMemRefType.getMemorySpaceAsInt());
1389 }
1390 
1391 Type SubViewOp::inferResultType(MemRefType sourceMemRefType,
1392                                 ArrayRef<OpFoldResult> leadingStaticOffsets,
1393                                 ArrayRef<OpFoldResult> leadingStaticSizes,
1394                                 ArrayRef<OpFoldResult> leadingStaticStrides) {
1395   SmallVector<int64_t> staticOffsets, staticSizes, staticStrides;
1396   SmallVector<Value> dynamicOffsets, dynamicSizes, dynamicStrides;
1397   dispatchIndexOpFoldResults(leadingStaticOffsets, dynamicOffsets,
1398                              staticOffsets, ShapedType::kDynamicStrideOrOffset);
1399   dispatchIndexOpFoldResults(leadingStaticSizes, dynamicSizes, staticSizes,
1400                              ShapedType::kDynamicSize);
1401   dispatchIndexOpFoldResults(leadingStaticStrides, dynamicStrides,
1402                              staticStrides, ShapedType::kDynamicStrideOrOffset);
1403   return SubViewOp::inferResultType(sourceMemRefType, staticOffsets,
1404                                     staticSizes, staticStrides)
1405       .cast<MemRefType>();
1406 }
1407 
1408 Type SubViewOp::inferRankReducedResultType(
1409     unsigned resultRank, MemRefType sourceRankedTensorType,
1410     ArrayRef<int64_t> leadingStaticOffsets,
1411     ArrayRef<int64_t> leadingStaticSizes,
1412     ArrayRef<int64_t> leadingStaticStrides) {
1413   auto inferredType =
1414       inferResultType(sourceRankedTensorType, leadingStaticOffsets,
1415                       leadingStaticSizes, leadingStaticStrides)
1416           .cast<MemRefType>();
1417   assert(inferredType.getRank() >= resultRank && "expected ");
1418   int rankDiff = inferredType.getRank() - resultRank;
1419   if (rankDiff > 0) {
1420     auto shape = inferredType.getShape();
1421     llvm::SmallDenseSet<unsigned> dimsToProject;
1422     mlir::getPositionsOfShapeOne(rankDiff, shape, dimsToProject);
1423     SmallVector<int64_t> projectedShape;
1424     for (unsigned pos = 0, e = shape.size(); pos < e; ++pos)
1425       if (!dimsToProject.contains(pos))
1426         projectedShape.push_back(shape[pos]);
1427 
1428     AffineMap map;
1429     auto maps = inferredType.getAffineMaps();
1430     if (!maps.empty() && maps.front())
1431       map = getProjectedMap(maps.front(), dimsToProject);
1432     inferredType =
1433         MemRefType::get(projectedShape, inferredType.getElementType(), map,
1434                         inferredType.getMemorySpaceAsInt());
1435   }
1436   return inferredType;
1437 }
1438 
1439 Type SubViewOp::inferRankReducedResultType(
1440     unsigned resultRank, MemRefType sourceRankedTensorType,
1441     ArrayRef<OpFoldResult> leadingStaticOffsets,
1442     ArrayRef<OpFoldResult> leadingStaticSizes,
1443     ArrayRef<OpFoldResult> leadingStaticStrides) {
1444   SmallVector<int64_t> staticOffsets, staticSizes, staticStrides;
1445   SmallVector<Value> dynamicOffsets, dynamicSizes, dynamicStrides;
1446   dispatchIndexOpFoldResults(leadingStaticOffsets, dynamicOffsets,
1447                              staticOffsets, ShapedType::kDynamicStrideOrOffset);
1448   dispatchIndexOpFoldResults(leadingStaticSizes, dynamicSizes, staticSizes,
1449                              ShapedType::kDynamicSize);
1450   dispatchIndexOpFoldResults(leadingStaticStrides, dynamicStrides,
1451                              staticStrides, ShapedType::kDynamicStrideOrOffset);
1452   return SubViewOp::inferRankReducedResultType(
1453       resultRank, sourceRankedTensorType, staticOffsets, staticSizes,
1454       staticStrides);
1455 }
1456 // Build a SubViewOp with mixed static and dynamic entries and custom result
1457 // type. If the type passed is nullptr, it is inferred.
1458 void SubViewOp::build(OpBuilder &b, OperationState &result,
1459                       MemRefType resultType, Value source,
1460                       ArrayRef<OpFoldResult> offsets,
1461                       ArrayRef<OpFoldResult> sizes,
1462                       ArrayRef<OpFoldResult> strides,
1463                       ArrayRef<NamedAttribute> attrs) {
1464   SmallVector<int64_t> staticOffsets, staticSizes, staticStrides;
1465   SmallVector<Value> dynamicOffsets, dynamicSizes, dynamicStrides;
1466   dispatchIndexOpFoldResults(offsets, dynamicOffsets, staticOffsets,
1467                              ShapedType::kDynamicStrideOrOffset);
1468   dispatchIndexOpFoldResults(sizes, dynamicSizes, staticSizes,
1469                              ShapedType::kDynamicSize);
1470   dispatchIndexOpFoldResults(strides, dynamicStrides, staticStrides,
1471                              ShapedType::kDynamicStrideOrOffset);
1472   auto sourceMemRefType = source.getType().cast<MemRefType>();
1473   // Structuring implementation this way avoids duplication between builders.
1474   if (!resultType) {
1475     resultType = SubViewOp::inferResultType(sourceMemRefType, staticOffsets,
1476                                             staticSizes, staticStrides)
1477                      .cast<MemRefType>();
1478   }
1479   build(b, result, resultType, source, dynamicOffsets, dynamicSizes,
1480         dynamicStrides, b.getI64ArrayAttr(staticOffsets),
1481         b.getI64ArrayAttr(staticSizes), b.getI64ArrayAttr(staticStrides));
1482   result.addAttributes(attrs);
1483 }
1484 
1485 // Build a SubViewOp with mixed static and dynamic entries and inferred result
1486 // type.
1487 void SubViewOp::build(OpBuilder &b, OperationState &result, Value source,
1488                       ArrayRef<OpFoldResult> offsets,
1489                       ArrayRef<OpFoldResult> sizes,
1490                       ArrayRef<OpFoldResult> strides,
1491                       ArrayRef<NamedAttribute> attrs) {
1492   build(b, result, MemRefType(), source, offsets, sizes, strides, attrs);
1493 }
1494 
1495 // Build a SubViewOp with static entries and inferred result type.
1496 void SubViewOp::build(OpBuilder &b, OperationState &result, Value source,
1497                       ArrayRef<int64_t> offsets, ArrayRef<int64_t> sizes,
1498                       ArrayRef<int64_t> strides,
1499                       ArrayRef<NamedAttribute> attrs) {
1500   SmallVector<OpFoldResult> offsetValues = llvm::to_vector<4>(
1501       llvm::map_range(offsets, [&](int64_t v) -> OpFoldResult {
1502         return b.getI64IntegerAttr(v);
1503       }));
1504   SmallVector<OpFoldResult> sizeValues =
1505       llvm::to_vector<4>(llvm::map_range(sizes, [&](int64_t v) -> OpFoldResult {
1506         return b.getI64IntegerAttr(v);
1507       }));
1508   SmallVector<OpFoldResult> strideValues = llvm::to_vector<4>(
1509       llvm::map_range(strides, [&](int64_t v) -> OpFoldResult {
1510         return b.getI64IntegerAttr(v);
1511       }));
1512   build(b, result, source, offsetValues, sizeValues, strideValues, attrs);
1513 }
1514 
1515 // Build a SubViewOp with dynamic entries and custom result type. If the
1516 // type passed is nullptr, it is inferred.
1517 void SubViewOp::build(OpBuilder &b, OperationState &result,
1518                       MemRefType resultType, Value source,
1519                       ArrayRef<int64_t> offsets, ArrayRef<int64_t> sizes,
1520                       ArrayRef<int64_t> strides,
1521                       ArrayRef<NamedAttribute> attrs) {
1522   SmallVector<OpFoldResult> offsetValues = llvm::to_vector<4>(
1523       llvm::map_range(offsets, [&](int64_t v) -> OpFoldResult {
1524         return b.getI64IntegerAttr(v);
1525       }));
1526   SmallVector<OpFoldResult> sizeValues =
1527       llvm::to_vector<4>(llvm::map_range(sizes, [&](int64_t v) -> OpFoldResult {
1528         return b.getI64IntegerAttr(v);
1529       }));
1530   SmallVector<OpFoldResult> strideValues = llvm::to_vector<4>(
1531       llvm::map_range(strides, [&](int64_t v) -> OpFoldResult {
1532         return b.getI64IntegerAttr(v);
1533       }));
1534   build(b, result, resultType, source, offsetValues, sizeValues, strideValues,
1535         attrs);
1536 }
1537 
1538 // Build a SubViewOp with dynamic entries and custom result type. If the type
1539 // passed is nullptr, it is inferred.
1540 void SubViewOp::build(OpBuilder &b, OperationState &result,
1541                       MemRefType resultType, Value source, ValueRange offsets,
1542                       ValueRange sizes, ValueRange strides,
1543                       ArrayRef<NamedAttribute> attrs) {
1544   SmallVector<OpFoldResult> offsetValues = llvm::to_vector<4>(
1545       llvm::map_range(offsets, [](Value v) -> OpFoldResult { return v; }));
1546   SmallVector<OpFoldResult> sizeValues = llvm::to_vector<4>(
1547       llvm::map_range(sizes, [](Value v) -> OpFoldResult { return v; }));
1548   SmallVector<OpFoldResult> strideValues = llvm::to_vector<4>(
1549       llvm::map_range(strides, [](Value v) -> OpFoldResult { return v; }));
1550   build(b, result, resultType, source, offsetValues, sizeValues, strideValues);
1551 }
1552 
1553 // Build a SubViewOp with dynamic entries and inferred result type.
1554 void SubViewOp::build(OpBuilder &b, OperationState &result, Value source,
1555                       ValueRange offsets, ValueRange sizes, ValueRange strides,
1556                       ArrayRef<NamedAttribute> attrs) {
1557   build(b, result, MemRefType(), source, offsets, sizes, strides, attrs);
1558 }
1559 
1560 /// For ViewLikeOpInterface.
1561 Value SubViewOp::getViewSource() { return source(); }
1562 
1563 enum SubViewVerificationResult {
1564   Success,
1565   RankTooLarge,
1566   SizeMismatch,
1567   ElemTypeMismatch,
1568   MemSpaceMismatch,
1569   AffineMapMismatch
1570 };
1571 
1572 /// Checks if `original` Type type can be rank reduced to `reduced` type.
1573 /// This function is slight variant of `is subsequence` algorithm where
1574 /// not matching dimension must be 1.
1575 static SubViewVerificationResult
1576 isRankReducedType(Type originalType, Type candidateReducedType,
1577                   std::string *errMsg = nullptr) {
1578   if (originalType == candidateReducedType)
1579     return SubViewVerificationResult::Success;
1580   if (!originalType.isa<MemRefType>())
1581     return SubViewVerificationResult::Success;
1582   if (originalType.isa<MemRefType>() && !candidateReducedType.isa<MemRefType>())
1583     return SubViewVerificationResult::Success;
1584 
1585   ShapedType originalShapedType = originalType.cast<ShapedType>();
1586   ShapedType candidateReducedShapedType =
1587       candidateReducedType.cast<ShapedType>();
1588 
1589   // Rank and size logic is valid for all ShapedTypes.
1590   ArrayRef<int64_t> originalShape = originalShapedType.getShape();
1591   ArrayRef<int64_t> candidateReducedShape =
1592       candidateReducedShapedType.getShape();
1593   unsigned originalRank = originalShape.size(),
1594            candidateReducedRank = candidateReducedShape.size();
1595   if (candidateReducedRank > originalRank)
1596     return SubViewVerificationResult::RankTooLarge;
1597 
1598   auto optionalUnusedDimsMask =
1599       computeRankReductionMask(originalShape, candidateReducedShape);
1600 
1601   // Sizes cannot be matched in case empty vector is returned.
1602   if (!optionalUnusedDimsMask.hasValue())
1603     return SubViewVerificationResult::SizeMismatch;
1604 
1605   if (originalShapedType.getElementType() !=
1606       candidateReducedShapedType.getElementType())
1607     return SubViewVerificationResult::ElemTypeMismatch;
1608 
1609   // Strided layout logic is relevant for MemRefType only.
1610   MemRefType original = originalType.cast<MemRefType>();
1611   MemRefType candidateReduced = candidateReducedType.cast<MemRefType>();
1612   if (original.getMemorySpaceAsInt() != candidateReduced.getMemorySpaceAsInt())
1613     return SubViewVerificationResult::MemSpaceMismatch;
1614 
1615   llvm::SmallDenseSet<unsigned> unusedDims = optionalUnusedDimsMask.getValue();
1616   auto inferredType =
1617       getProjectedMap(getStridedLinearLayoutMap(original), unusedDims);
1618   AffineMap candidateLayout;
1619   if (candidateReduced.getAffineMaps().empty())
1620     candidateLayout = getStridedLinearLayoutMap(candidateReduced);
1621   else
1622     candidateLayout = candidateReduced.getAffineMaps().front();
1623   assert(inferredType.getNumResults() == 1 &&
1624          candidateLayout.getNumResults() == 1);
1625   if (inferredType.getNumSymbols() != candidateLayout.getNumSymbols() ||
1626       inferredType.getNumDims() != candidateLayout.getNumDims()) {
1627     if (errMsg) {
1628       llvm::raw_string_ostream os(*errMsg);
1629       os << "inferred type: " << inferredType;
1630     }
1631     return SubViewVerificationResult::AffineMapMismatch;
1632   }
1633   // Check that the difference of the affine maps simplifies to 0.
1634   AffineExpr diffExpr =
1635       inferredType.getResult(0) - candidateLayout.getResult(0);
1636   diffExpr = simplifyAffineExpr(diffExpr, inferredType.getNumDims(),
1637                                 inferredType.getNumSymbols());
1638   auto cst = diffExpr.dyn_cast<AffineConstantExpr>();
1639   if (!(cst && cst.getValue() == 0)) {
1640     if (errMsg) {
1641       llvm::raw_string_ostream os(*errMsg);
1642       os << "inferred type: " << inferredType;
1643     }
1644     return SubViewVerificationResult::AffineMapMismatch;
1645   }
1646   return SubViewVerificationResult::Success;
1647 }
1648 
1649 template <typename OpTy>
1650 static LogicalResult produceSubViewErrorMsg(SubViewVerificationResult result,
1651                                             OpTy op, Type expectedType,
1652                                             StringRef errMsg = "") {
1653   auto memrefType = expectedType.cast<ShapedType>();
1654   switch (result) {
1655   case SubViewVerificationResult::Success:
1656     return success();
1657   case SubViewVerificationResult::RankTooLarge:
1658     return op.emitError("expected result rank to be smaller or equal to ")
1659            << "the source rank. " << errMsg;
1660   case SubViewVerificationResult::SizeMismatch:
1661     return op.emitError("expected result type to be ")
1662            << expectedType
1663            << " or a rank-reduced version. (mismatch of result sizes) "
1664            << errMsg;
1665   case SubViewVerificationResult::ElemTypeMismatch:
1666     return op.emitError("expected result element type to be ")
1667            << memrefType.getElementType() << errMsg;
1668   case SubViewVerificationResult::MemSpaceMismatch:
1669     return op.emitError("expected result and source memory spaces to match.")
1670            << errMsg;
1671   case SubViewVerificationResult::AffineMapMismatch:
1672     return op.emitError("expected result type to be ")
1673            << expectedType
1674            << " or a rank-reduced version. (mismatch of result affine map) "
1675            << errMsg;
1676   }
1677   llvm_unreachable("unexpected subview verification result");
1678 }
1679 
1680 /// Verifier for SubViewOp.
1681 static LogicalResult verify(SubViewOp op) {
1682   MemRefType baseType = op.getSourceType();
1683   MemRefType subViewType = op.getType();
1684 
1685   // The base memref and the view memref should be in the same memory space.
1686   if (baseType.getMemorySpaceAsInt() != subViewType.getMemorySpaceAsInt())
1687     return op.emitError("different memory spaces specified for base memref "
1688                         "type ")
1689            << baseType << " and subview memref type " << subViewType;
1690 
1691   // Verify that the base memref type has a strided layout map.
1692   if (!isStrided(baseType))
1693     return op.emitError("base type ") << baseType << " is not strided";
1694 
1695   // Verify result type against inferred type.
1696   auto expectedType = SubViewOp::inferResultType(
1697       baseType, extractFromI64ArrayAttr(op.static_offsets()),
1698       extractFromI64ArrayAttr(op.static_sizes()),
1699       extractFromI64ArrayAttr(op.static_strides()));
1700 
1701   std::string errMsg;
1702   auto result = isRankReducedType(expectedType, subViewType, &errMsg);
1703   return produceSubViewErrorMsg(result, op, expectedType, errMsg);
1704 }
1705 
1706 raw_ostream &mlir::operator<<(raw_ostream &os, Range &range) {
1707   return os << "range " << range.offset << ":" << range.size << ":"
1708             << range.stride;
1709 }
1710 
1711 /// Return the list of Range (i.e. offset, size, stride). Each Range
1712 /// entry contains either the dynamic value or a ConstantIndexOp constructed
1713 /// with `b` at location `loc`.
1714 SmallVector<Range, 8> mlir::getOrCreateRanges(OffsetSizeAndStrideOpInterface op,
1715                                               OpBuilder &b, Location loc) {
1716   std::array<unsigned, 3> ranks = op.getArrayAttrMaxRanks();
1717   assert(ranks[0] == ranks[1] && "expected offset and sizes of equal ranks");
1718   assert(ranks[1] == ranks[2] && "expected sizes and strides of equal ranks");
1719   SmallVector<Range, 8> res;
1720   unsigned rank = ranks[0];
1721   res.reserve(rank);
1722   for (unsigned idx = 0; idx < rank; ++idx) {
1723     Value offset =
1724         op.isDynamicOffset(idx)
1725             ? op.getDynamicOffset(idx)
1726             : b.create<ConstantIndexOp>(loc, op.getStaticOffset(idx));
1727     Value size = op.isDynamicSize(idx)
1728                      ? op.getDynamicSize(idx)
1729                      : b.create<ConstantIndexOp>(loc, op.getStaticSize(idx));
1730     Value stride =
1731         op.isDynamicStride(idx)
1732             ? op.getDynamicStride(idx)
1733             : b.create<ConstantIndexOp>(loc, op.getStaticStride(idx));
1734     res.emplace_back(Range{offset, size, stride});
1735   }
1736   return res;
1737 }
1738 
1739 namespace {
1740 /// Pattern to rewrite a subview op with MemRefCast arguments.
1741 /// This essentially pushes memref.cast past its consuming subview when
1742 /// `canFoldIntoConsumerOp` is true.
1743 ///
1744 /// Example:
1745 /// ```
1746 ///   %0 = memref.cast %V : memref<16x16xf32> to memref<?x?xf32>
1747 ///   %1 = memref.subview %0[0, 0][3, 4][1, 1] :
1748 ///     memref<?x?xf32> to memref<3x4xf32, offset:?, strides:[?, 1]>
1749 /// ```
1750 /// is rewritten into:
1751 /// ```
1752 ///   %0 = memref.subview %V: memref<16x16xf32> to memref<3x4xf32, #[[map0]]>
1753 ///   %1 = memref.cast %0: memref<3x4xf32, offset:0, strides:[16, 1]> to
1754 ///     memref<3x4xf32, offset:?, strides:[?, 1]>
1755 /// ```
1756 class SubViewOpMemRefCastFolder final : public OpRewritePattern<SubViewOp> {
1757 public:
1758   using OpRewritePattern<SubViewOp>::OpRewritePattern;
1759 
1760   LogicalResult matchAndRewrite(SubViewOp subViewOp,
1761                                 PatternRewriter &rewriter) const override {
1762     // Any constant operand, just return to let SubViewOpConstantFolder kick in.
1763     if (llvm::any_of(subViewOp.getOperands(), [](Value operand) {
1764           return matchPattern(operand, matchConstantIndex());
1765         }))
1766       return failure();
1767 
1768     auto castOp = subViewOp.source().getDefiningOp<CastOp>();
1769     if (!castOp)
1770       return failure();
1771 
1772     if (!CastOp::canFoldIntoConsumerOp(castOp))
1773       return failure();
1774 
1775     /// Deduce the resultType of the SubViewOp using `inferSubViewResultType` on
1776     /// the cast source operand type and the SubViewOp static information. This
1777     /// is the resulting type if the MemRefCastOp were folded.
1778     auto resultType = SubViewOp::inferRankReducedResultType(
1779         subViewOp.getType().getRank(),
1780         castOp.source().getType().cast<MemRefType>(),
1781         subViewOp.getMixedOffsets(), subViewOp.getMixedSizes(),
1782         subViewOp.getMixedStrides());
1783     Value newSubView = rewriter.create<SubViewOp>(
1784         subViewOp.getLoc(), resultType, castOp.source(), subViewOp.offsets(),
1785         subViewOp.sizes(), subViewOp.strides(), subViewOp.static_offsets(),
1786         subViewOp.static_sizes(), subViewOp.static_strides());
1787     rewriter.replaceOpWithNewOp<CastOp>(subViewOp, subViewOp.getType(),
1788                                         newSubView);
1789     return success();
1790   }
1791 };
1792 } // namespace
1793 
1794 /// A canonicalizer wrapper to replace SubViewOps.
1795 struct SubViewCanonicalizer {
1796   void operator()(PatternRewriter &rewriter, SubViewOp op, SubViewOp newOp) {
1797     rewriter.replaceOpWithNewOp<CastOp>(op, newOp, op.getType());
1798   }
1799 };
1800 
1801 void SubViewOp::getCanonicalizationPatterns(OwningRewritePatternList &results,
1802                                             MLIRContext *context) {
1803   results.insert<OpWithOffsetSizesAndStridesConstantArgumentFolder<
1804                      SubViewOp, SubViewCanonicalizer>,
1805                  SubViewOpMemRefCastFolder>(context);
1806 }
1807 
1808 OpFoldResult SubViewOp::fold(ArrayRef<Attribute> operands) {
1809   auto resultShapedType = getResult().getType().cast<ShapedType>();
1810   auto sourceShapedType = source().getType().cast<ShapedType>();
1811 
1812   if (resultShapedType.hasStaticShape() &&
1813       resultShapedType == sourceShapedType) {
1814     return getViewSource();
1815   }
1816 
1817   return {};
1818 }
1819 
1820 //===----------------------------------------------------------------------===//
1821 // TensorLoadOp
1822 //===----------------------------------------------------------------------===//
1823 
1824 OpFoldResult TensorLoadOp::fold(ArrayRef<Attribute>) {
1825   if (auto bufferCast = memref().getDefiningOp<BufferCastOp>())
1826     // Approximate alias analysis by conservatively folding only when no there
1827     // is no interleaved operation.
1828     if (bufferCast->getBlock() == this->getOperation()->getBlock() &&
1829         bufferCast->getNextNode() == this->getOperation())
1830       return bufferCast.tensor();
1831   return {};
1832 }
1833 
1834 //===----------------------------------------------------------------------===//
1835 // TransposeOp
1836 //===----------------------------------------------------------------------===//
1837 
1838 /// Build a strided memref type by applying `permutationMap` tp `memRefType`.
1839 static MemRefType inferTransposeResultType(MemRefType memRefType,
1840                                            AffineMap permutationMap) {
1841   auto rank = memRefType.getRank();
1842   auto originalSizes = memRefType.getShape();
1843   // Compute permuted sizes.
1844   SmallVector<int64_t, 4> sizes(rank, 0);
1845   for (auto en : llvm::enumerate(permutationMap.getResults()))
1846     sizes[en.index()] =
1847         originalSizes[en.value().cast<AffineDimExpr>().getPosition()];
1848 
1849   // Compute permuted strides.
1850   int64_t offset;
1851   SmallVector<int64_t, 4> strides;
1852   auto res = getStridesAndOffset(memRefType, strides, offset);
1853   assert(succeeded(res) && strides.size() == static_cast<unsigned>(rank));
1854   (void)res;
1855   auto map =
1856       makeStridedLinearLayoutMap(strides, offset, memRefType.getContext());
1857   map = permutationMap ? map.compose(permutationMap) : map;
1858   return MemRefType::Builder(memRefType).setShape(sizes).setAffineMaps(map);
1859 }
1860 
1861 void TransposeOp::build(OpBuilder &b, OperationState &result, Value in,
1862                         AffineMapAttr permutation,
1863                         ArrayRef<NamedAttribute> attrs) {
1864   auto permutationMap = permutation.getValue();
1865   assert(permutationMap);
1866 
1867   auto memRefType = in.getType().cast<MemRefType>();
1868   // Compute result type.
1869   MemRefType resultType = inferTransposeResultType(memRefType, permutationMap);
1870 
1871   build(b, result, resultType, in, attrs);
1872   result.addAttribute(TransposeOp::getPermutationAttrName(), permutation);
1873 }
1874 
1875 // transpose $in $permutation attr-dict : type($in) `to` type(results)
1876 static void print(OpAsmPrinter &p, TransposeOp op) {
1877   p << "memref.transpose " << op.in() << " " << op.permutation();
1878   p.printOptionalAttrDict(op->getAttrs(),
1879                           {TransposeOp::getPermutationAttrName()});
1880   p << " : " << op.in().getType() << " to " << op.getType();
1881 }
1882 
1883 static ParseResult parseTransposeOp(OpAsmParser &parser,
1884                                     OperationState &result) {
1885   OpAsmParser::OperandType in;
1886   AffineMap permutation;
1887   MemRefType srcType, dstType;
1888   if (parser.parseOperand(in) || parser.parseAffineMap(permutation) ||
1889       parser.parseOptionalAttrDict(result.attributes) ||
1890       parser.parseColonType(srcType) ||
1891       parser.resolveOperand(in, srcType, result.operands) ||
1892       parser.parseKeywordType("to", dstType) ||
1893       parser.addTypeToList(dstType, result.types))
1894     return failure();
1895 
1896   result.addAttribute(TransposeOp::getPermutationAttrName(),
1897                       AffineMapAttr::get(permutation));
1898   return success();
1899 }
1900 
1901 static LogicalResult verify(TransposeOp op) {
1902   if (!op.permutation().isPermutation())
1903     return op.emitOpError("expected a permutation map");
1904   if (op.permutation().getNumDims() != op.getShapedType().getRank())
1905     return op.emitOpError(
1906         "expected a permutation map of same rank as the input");
1907 
1908   auto srcType = op.in().getType().cast<MemRefType>();
1909   auto dstType = op.getType().cast<MemRefType>();
1910   auto transposedType = inferTransposeResultType(srcType, op.permutation());
1911   if (dstType != transposedType)
1912     return op.emitOpError("output type ")
1913            << dstType << " does not match transposed input type " << srcType
1914            << ", " << transposedType;
1915   return success();
1916 }
1917 
1918 OpFoldResult TransposeOp::fold(ArrayRef<Attribute>) {
1919   if (succeeded(foldMemRefCast(*this)))
1920     return getResult();
1921   return {};
1922 }
1923 
1924 //===----------------------------------------------------------------------===//
1925 // ViewOp
1926 //===----------------------------------------------------------------------===//
1927 
1928 static ParseResult parseViewOp(OpAsmParser &parser, OperationState &result) {
1929   OpAsmParser::OperandType srcInfo;
1930   SmallVector<OpAsmParser::OperandType, 1> offsetInfo;
1931   SmallVector<OpAsmParser::OperandType, 4> sizesInfo;
1932   auto indexType = parser.getBuilder().getIndexType();
1933   Type srcType, dstType;
1934   llvm::SMLoc offsetLoc;
1935   if (parser.parseOperand(srcInfo) || parser.getCurrentLocation(&offsetLoc) ||
1936       parser.parseOperandList(offsetInfo, OpAsmParser::Delimiter::Square))
1937     return failure();
1938 
1939   if (offsetInfo.size() != 1)
1940     return parser.emitError(offsetLoc) << "expects 1 offset operand";
1941 
1942   return failure(
1943       parser.parseOperandList(sizesInfo, OpAsmParser::Delimiter::Square) ||
1944       parser.parseOptionalAttrDict(result.attributes) ||
1945       parser.parseColonType(srcType) ||
1946       parser.resolveOperand(srcInfo, srcType, result.operands) ||
1947       parser.resolveOperands(offsetInfo, indexType, result.operands) ||
1948       parser.resolveOperands(sizesInfo, indexType, result.operands) ||
1949       parser.parseKeywordType("to", dstType) ||
1950       parser.addTypeToList(dstType, result.types));
1951 }
1952 
1953 static void print(OpAsmPrinter &p, ViewOp op) {
1954   p << op.getOperationName() << ' ' << op.getOperand(0) << '[';
1955   p.printOperand(op.byte_shift());
1956   p << "][" << op.sizes() << ']';
1957   p.printOptionalAttrDict(op->getAttrs());
1958   p << " : " << op.getOperand(0).getType() << " to " << op.getType();
1959 }
1960 
1961 static LogicalResult verify(ViewOp op) {
1962   auto baseType = op.getOperand(0).getType().cast<MemRefType>();
1963   auto viewType = op.getType();
1964 
1965   // The base memref should have identity layout map (or none).
1966   if (baseType.getAffineMaps().size() > 1 ||
1967       (baseType.getAffineMaps().size() == 1 &&
1968        !baseType.getAffineMaps()[0].isIdentity()))
1969     return op.emitError("unsupported map for base memref type ") << baseType;
1970 
1971   // The result memref should have identity layout map (or none).
1972   if (viewType.getAffineMaps().size() > 1 ||
1973       (viewType.getAffineMaps().size() == 1 &&
1974        !viewType.getAffineMaps()[0].isIdentity()))
1975     return op.emitError("unsupported map for result memref type ") << viewType;
1976 
1977   // The base memref and the view memref should be in the same memory space.
1978   if (baseType.getMemorySpaceAsInt() != viewType.getMemorySpaceAsInt())
1979     return op.emitError("different memory spaces specified for base memref "
1980                         "type ")
1981            << baseType << " and view memref type " << viewType;
1982 
1983   // Verify that we have the correct number of sizes for the result type.
1984   unsigned numDynamicDims = viewType.getNumDynamicDims();
1985   if (op.sizes().size() != numDynamicDims)
1986     return op.emitError("incorrect number of size operands for type ")
1987            << viewType;
1988 
1989   return success();
1990 }
1991 
1992 Value ViewOp::getViewSource() { return source(); }
1993 
1994 namespace {
1995 
1996 struct ViewOpShapeFolder : public OpRewritePattern<ViewOp> {
1997   using OpRewritePattern<ViewOp>::OpRewritePattern;
1998 
1999   LogicalResult matchAndRewrite(ViewOp viewOp,
2000                                 PatternRewriter &rewriter) const override {
2001     // Return if none of the operands are constants.
2002     if (llvm::none_of(viewOp.getOperands(), [](Value operand) {
2003           return matchPattern(operand, matchConstantIndex());
2004         }))
2005       return failure();
2006 
2007     // Get result memref type.
2008     auto memrefType = viewOp.getType();
2009 
2010     // Get offset from old memref view type 'memRefType'.
2011     int64_t oldOffset;
2012     SmallVector<int64_t, 4> oldStrides;
2013     if (failed(getStridesAndOffset(memrefType, oldStrides, oldOffset)))
2014       return failure();
2015     assert(oldOffset == 0 && "Expected 0 offset");
2016 
2017     SmallVector<Value, 4> newOperands;
2018 
2019     // Offset cannot be folded into result type.
2020 
2021     // Fold any dynamic dim operands which are produced by a constant.
2022     SmallVector<int64_t, 4> newShapeConstants;
2023     newShapeConstants.reserve(memrefType.getRank());
2024 
2025     unsigned dynamicDimPos = 0;
2026     unsigned rank = memrefType.getRank();
2027     for (unsigned dim = 0, e = rank; dim < e; ++dim) {
2028       int64_t dimSize = memrefType.getDimSize(dim);
2029       // If this is already static dimension, keep it.
2030       if (!ShapedType::isDynamic(dimSize)) {
2031         newShapeConstants.push_back(dimSize);
2032         continue;
2033       }
2034       auto *defOp = viewOp.sizes()[dynamicDimPos].getDefiningOp();
2035       if (auto constantIndexOp = dyn_cast_or_null<ConstantIndexOp>(defOp)) {
2036         // Dynamic shape dimension will be folded.
2037         newShapeConstants.push_back(constantIndexOp.getValue());
2038       } else {
2039         // Dynamic shape dimension not folded; copy operand from old memref.
2040         newShapeConstants.push_back(dimSize);
2041         newOperands.push_back(viewOp.sizes()[dynamicDimPos]);
2042       }
2043       dynamicDimPos++;
2044     }
2045 
2046     // Create new memref type with constant folded dims.
2047     MemRefType newMemRefType =
2048         MemRefType::Builder(memrefType).setShape(newShapeConstants);
2049     // Nothing new, don't fold.
2050     if (newMemRefType == memrefType)
2051       return failure();
2052 
2053     // Create new ViewOp.
2054     auto newViewOp = rewriter.create<ViewOp>(viewOp.getLoc(), newMemRefType,
2055                                              viewOp.getOperand(0),
2056                                              viewOp.byte_shift(), newOperands);
2057     // Insert a cast so we have the same type as the old memref type.
2058     rewriter.replaceOpWithNewOp<CastOp>(viewOp, newViewOp, viewOp.getType());
2059     return success();
2060   }
2061 };
2062 
2063 struct ViewOpMemrefCastFolder : public OpRewritePattern<ViewOp> {
2064   using OpRewritePattern<ViewOp>::OpRewritePattern;
2065 
2066   LogicalResult matchAndRewrite(ViewOp viewOp,
2067                                 PatternRewriter &rewriter) const override {
2068     Value memrefOperand = viewOp.getOperand(0);
2069     CastOp memrefCastOp = memrefOperand.getDefiningOp<CastOp>();
2070     if (!memrefCastOp)
2071       return failure();
2072     Value allocOperand = memrefCastOp.getOperand();
2073     AllocOp allocOp = allocOperand.getDefiningOp<AllocOp>();
2074     if (!allocOp)
2075       return failure();
2076     rewriter.replaceOpWithNewOp<ViewOp>(viewOp, viewOp.getType(), allocOperand,
2077                                         viewOp.byte_shift(), viewOp.sizes());
2078     return success();
2079   }
2080 };
2081 
2082 } // end anonymous namespace
2083 
2084 void ViewOp::getCanonicalizationPatterns(OwningRewritePatternList &results,
2085                                          MLIRContext *context) {
2086   results.insert<ViewOpShapeFolder, ViewOpMemrefCastFolder>(context);
2087 }
2088 
2089 //===----------------------------------------------------------------------===//
2090 // TableGen'd op method definitions
2091 //===----------------------------------------------------------------------===//
2092 
2093 #define GET_OP_CLASSES
2094 #include "mlir/Dialect/MemRef/IR/MemRefOps.cpp.inc"
2095