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