1 //===- LinalgOps.cpp - Implementation of the linalg operations ------------===//
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 // This file implements the Linalg operations.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "mlir/Dialect/Linalg/IR/Linalg.h"
14 
15 #include "mlir/Dialect/SCF/SCF.h"
16 #include "mlir/Dialect/StandardOps/Utils/Utils.h"
17 #include "mlir/Dialect/Utils/ReshapeOpsUtils.h"
18 #include "mlir/Dialect/Utils/StaticValueUtils.h"
19 #include "mlir/IR/AffineExprVisitor.h"
20 #include "mlir/IR/Matchers.h"
21 #include "mlir/IR/OpImplementation.h"
22 #include "mlir/IR/PatternMatch.h"
23 #include "mlir/Interfaces/InferTypeOpInterface.h"
24 #include "mlir/Parser.h"
25 
26 #include "llvm/ADT/DenseMap.h"
27 #include "llvm/ADT/SetVector.h"
28 #include "llvm/ADT/SmallSet.h"
29 #include "llvm/ADT/StringSet.h"
30 #include "llvm/ADT/TypeSwitch.h"
31 #include "llvm/Support/FormatVariadic.h"
32 #include "llvm/Support/MathExtras.h"
33 #include "llvm/Support/raw_ostream.h"
34 
35 using namespace mlir;
36 using namespace mlir::linalg;
37 
38 #include "mlir/Dialect/Linalg/IR/LinalgOpsDialect.cpp.inc"
39 
40 /// Forward declarations.
41 
42 /// Generic entry point to create the block for the region of a LinalgOp.
43 /// This is used by both named structured ops created by ods-gen and by manually
44 /// defined C++ ops.
45 /// This is used by both builders and parsers.
46 /// This function creates the block in the region with arguments corresponding
47 /// to the elemental types of `inputTypes` and `outputTypes`. The latter are
48 /// asserted to be of ShapedType.
49 template <typename NamedStructuredOpType>
50 static void fillStructuredOpRegion(
51     OpBuilder &opBuilder, Region &region, TypeRange inputTypes,
52     TypeRange outputTypes,
53     llvm::function_ref<void(unsigned, unsigned)> errorHandler = nullptr);
54 
55 /// Generic entry point to create both the region and the block of a LinalgOp.
56 template <typename NamedStructuredOpType>
57 static void
58 createAndFillStructuredOpRegion(OpBuilder &opBuilder, OperationState &result,
59                                 TypeRange inputTypes, TypeRange outputTypes);
60 
61 /// Common parsing and printing used for both named structured ops created by
62 /// ods-gen and by manually defined C++ ops. Does not handle regions.
63 static ParseResult
64 parseCommonStructuredOpParts(OpAsmParser &parser, OperationState &result,
65                              SmallVectorImpl<Type> &inputTypes,
66                              SmallVectorImpl<Type> &outputTypes);
67 template <typename NamedStructuredOpType>
68 static void printCommonStructuredOpParts(OpAsmPrinter &p,
69                                          NamedStructuredOpType op);
70 
71 /// Specific parsing and printing for named structured ops created by ods-gen.
72 template <typename NamedStructuredOpType>
73 static ParseResult
74 parseNamedStructuredOpRegion(OpAsmParser &parser, Region &region,
75                              TypeRange inputTypes, TypeRange outputTypes);
76 
77 static ParseResult
78 parseNamedStructuredOpResults(OpAsmParser &parser,
79                               SmallVectorImpl<Type> &resultTypes);
80 
81 template <typename NamedStructuredOpType>
82 static ParseResult parseNamedStructuredOp(OpAsmParser &parser,
83                                           OperationState &result);
84 
85 static void printNamedStructuredOpResults(OpAsmPrinter &p,
86                                           TypeRange resultTypes);
87 
88 template <typename NamedStructuredOpType>
89 static void printNamedStructuredOp(OpAsmPrinter &p, NamedStructuredOpType op);
90 
91 /// This is a common class used for patterns of the form
92 /// ```
93 ///    someop(memrefcast(%src)) -> someop(%src)
94 /// ```
95 /// It folds the source of the memref.cast into the root operation directly.
96 static LogicalResult foldMemRefCast(Operation *op) {
97   bool folded = false;
98   for (OpOperand &operand : op->getOpOperands()) {
99     auto castOp = operand.get().getDefiningOp<memref::CastOp>();
100     if (castOp && memref::CastOp::canFoldIntoConsumerOp(castOp)) {
101       operand.set(castOp.getOperand());
102       folded = true;
103     }
104   }
105   return success(folded);
106 }
107 
108 /// This is a specialization of `foldMemRefCast` used for patterns of the form
109 /// ```
110 ///    tiled_loop(memrefcast(%src)) -> tiled_loop(%src)
111 /// ```
112 /// It folds the source of the memref.cast into the root operation directly.
113 static LogicalResult foldMemRefCastInTiledLoopOp(TiledLoopOp op) {
114   bool folded = false;
115   Location loc = op->getLoc();
116 
117   Block *body = op.getBody();
118   OpBuilder b = OpBuilder::atBlockBegin(body);
119 
120   // Update `input` and `output` operands and block arguments if necessary.
121   // Operands list: [lbs, ubs, steps, inputs, outputs].
122   // Block args list: [ivs, inputs, outputs].
123   for (size_t operandIndex = op.getNumControlOperands(),
124               bbArgIndex = op.getNumLoops(), e = op.getNumOperands();
125        operandIndex < e; ++operandIndex, ++bbArgIndex) {
126     OpOperand &operand = op->getOpOperand(operandIndex);
127 
128     auto castOp = operand.get().getDefiningOp<memref::CastOp>();
129     if (castOp && memref::CastOp::canFoldIntoConsumerOp(castOp)) {
130       operand.set(castOp.getOperand());
131       BlockArgument newBbArg = body->insertArgument(
132           bbArgIndex, castOp.getOperand().getType(), op.getLoc());
133       BlockArgument oldBbArg = body->getArgument(newBbArg.getArgNumber() + 1);
134 
135       // Insert memref.cast back to the original type.
136       oldBbArg.replaceAllUsesWith(
137           b.create<memref::CastOp>(loc, oldBbArg.getType(), newBbArg));
138       body->eraseArgument(oldBbArg.getArgNumber());
139 
140       folded = true;
141     }
142   }
143   return success(folded);
144 }
145 
146 //===----------------------------------------------------------------------===//
147 // Region builder helper.
148 // TODO: Move this to a utility library.
149 // The public methods on this class are referenced directly from generated code
150 // and bind by name to math and type conversion functions in the DSL as:
151 //   `arithfn__{fnName}`
152 //   `typefn__{fnName}`
153 // Examples:
154 //   `arithfn__add`
155 //   `arithfn__mul`
156 //   `typefn__cast`
157 // The naming convention is intentional in order to match snake-cased DSL names.
158 // See mlir-linalg-ods-yaml-gen.cpp for the code that mates to this class.
159 //
160 // Implementations of the math functions must be polymorphic over numeric types,
161 // internally performing necessary casts. If the function application makes no
162 // sense, then the only recourse is to assert and return nullptr. This can be
163 // extended later if it becomes possible to fail construction of the region. The
164 // invariant should be enforced at a higher level.
165 //
166 // TODO: These helpers are currently type polymorphic over the class of integer
167 // and floating point types, but they will not internally cast within bit
168 // widths of a class (mixed precision such as i8->i32) or across classes
169 // (i.e. mixed float and integer). Many such combinations are ambiguous or need
170 // to be handled with care and work is being considered to extend the op
171 // language to make such cases explicit. In the mean-time, violating this will
172 // fail verification, which is deemed acceptable.
173 //===----------------------------------------------------------------------===//
174 
175 namespace {
176 
177 class RegionBuilderHelper {
178 public:
179   RegionBuilderHelper(MLIRContext *context, Block &block)
180       : context(context), block(block) {}
181 
182   // Generates operations to cast the given operand to a specified type.
183   // If the cast cannot be performed, a warning will be issued and the
184   // operand returned as-is (which will presumably yield a verification
185   // issue downstream).
186   Value cast(Type toType, Value operand, bool isUnsignedCast) {
187     OpBuilder builder = getBuilder();
188     auto loc = operand.getLoc();
189 
190     if (operand.getType() == toType)
191       return operand;
192     if (auto toIntType = toType.dyn_cast<IntegerType>()) {
193       // If operand is floating point, cast directly to the int type.
194       if (operand.getType().isa<FloatType>()) {
195         if (isUnsignedCast)
196           return builder.create<arith::FPToUIOp>(loc, toType, operand);
197         return builder.create<arith::FPToSIOp>(loc, toType, operand);
198       }
199       // Cast index operands directly to the int type.
200       if (operand.getType().isIndex())
201         return builder.create<arith::IndexCastOp>(loc, toType, operand);
202       if (auto fromIntType = operand.getType().dyn_cast<IntegerType>()) {
203         // Either extend or truncate.
204         if (toIntType.getWidth() > fromIntType.getWidth()) {
205           if (isUnsignedCast)
206             return builder.create<arith::ExtUIOp>(loc, toType, operand);
207           return builder.create<arith::ExtSIOp>(loc, toType, operand);
208         }
209         if (toIntType.getWidth() < fromIntType.getWidth())
210           return builder.create<arith::TruncIOp>(loc, toType, operand);
211       }
212     } else if (auto toFloatType = toType.dyn_cast<FloatType>()) {
213       // If operand is integer, cast directly to the float type.
214       // Note that it is unclear how to cast from BF16<->FP16.
215       if (operand.getType().isa<IntegerType>()) {
216         if (isUnsignedCast)
217           return builder.create<arith::UIToFPOp>(loc, toFloatType, operand);
218         return builder.create<arith::SIToFPOp>(loc, toFloatType, operand);
219       }
220       if (auto fromFloatType = operand.getType().dyn_cast<FloatType>()) {
221         if (toFloatType.getWidth() > fromFloatType.getWidth())
222           return builder.create<arith::ExtFOp>(loc, toFloatType, operand);
223         if (toFloatType.getWidth() < fromFloatType.getWidth())
224           return builder.create<arith::TruncFOp>(loc, toFloatType, operand);
225       }
226     }
227 
228     emitWarning(operand.getLoc()) << "could not cast operand of type "
229                                   << operand.getType() << " to " << toType;
230     return operand;
231   }
232 
233   // NOLINTNEXTLINE(*-identifier-naming): externally called.
234   Value typefn__cast(Type toType, Value operand) {
235     return cast(toType, operand, false);
236   }
237 
238   // NOLINTNEXTLINE(*-identifier-naming): externally called.
239   Value typefn__cast_unsigned(Type toType, Value operand) {
240     return cast(toType, operand, true);
241   }
242 
243   // NOLINTNEXTLINE(*-identifier-naming): externally called.
244   Value arithfn__add(Value lhs, Value rhs) {
245     OpBuilder builder = getBuilder();
246     if (isFloatingPoint(lhs))
247       return builder.create<arith::AddFOp>(lhs.getLoc(), lhs, rhs);
248     if (isInteger(lhs))
249       return builder.create<arith::AddIOp>(lhs.getLoc(), lhs, rhs);
250     llvm_unreachable("unsupported non numeric type");
251   }
252 
253   // NOLINTNEXTLINE(*-identifier-naming): externally called.
254   Value arithfn__exp(Value x) {
255     OpBuilder builder = getBuilder();
256     if (isFloatingPoint(x))
257       return builder.create<math::ExpOp>(x.getLoc(), x);
258     llvm_unreachable("unsupported non numeric type");
259   }
260 
261   // NOLINTNEXTLINE(*-identifier-naming): externally called.
262   Value arithfn__log(Value x) {
263     OpBuilder builder = getBuilder();
264     if (isFloatingPoint(x))
265       return builder.create<math::LogOp>(x.getLoc(), x);
266     llvm_unreachable("unsupported non numeric type");
267   }
268 
269   // NOLINTNEXTLINE(*-identifier-naming): externally called.
270   Value arithfn__sub(Value lhs, Value rhs) {
271     OpBuilder builder = getBuilder();
272     if (isFloatingPoint(lhs))
273       return builder.create<arith::SubFOp>(lhs.getLoc(), lhs, rhs);
274     if (isInteger(lhs))
275       return builder.create<arith::SubIOp>(lhs.getLoc(), lhs, rhs);
276     llvm_unreachable("unsupported non numeric type");
277   }
278 
279   // NOLINTNEXTLINE(*-identifier-naming): externally called.
280   Value arithfn__mul(Value lhs, Value rhs) {
281     OpBuilder builder = getBuilder();
282     if (isFloatingPoint(lhs))
283       return builder.create<arith::MulFOp>(lhs.getLoc(), lhs, rhs);
284     if (isInteger(lhs))
285       return builder.create<arith::MulIOp>(lhs.getLoc(), lhs, rhs);
286     llvm_unreachable("unsupported non numeric type");
287   }
288 
289   // NOLINTNEXTLINE(*-identifier-naming): externally called.
290   Value arithfn__max(Value lhs, Value rhs) {
291     OpBuilder builder = getBuilder();
292     if (isFloatingPoint(lhs))
293       return builder.create<arith::MaxFOp>(lhs.getLoc(), lhs, rhs);
294     if (isInteger(lhs))
295       return builder.create<arith::MaxSIOp>(lhs.getLoc(), lhs, rhs);
296     llvm_unreachable("unsupported non numeric type");
297   }
298 
299   // NOLINTNEXTLINE(*-identifier-naming): externally called.
300   Value arithfn__max_unsigned(Value lhs, Value rhs) {
301     OpBuilder builder = getBuilder();
302     if (isFloatingPoint(lhs))
303       return builder.create<arith::MaxFOp>(lhs.getLoc(), lhs, rhs);
304     if (isInteger(lhs))
305       return builder.create<arith::MaxUIOp>(lhs.getLoc(), lhs, rhs);
306     llvm_unreachable("unsupported non numeric type");
307   }
308 
309   // NOLINTNEXTLINE(*-identifier-naming): externally called.
310   Value arithfn__min(Value lhs, Value rhs) {
311     OpBuilder builder = getBuilder();
312     if (isFloatingPoint(lhs))
313       return builder.create<arith::MinFOp>(lhs.getLoc(), lhs, rhs);
314     if (isInteger(lhs))
315       return builder.create<arith::MinSIOp>(lhs.getLoc(), lhs, rhs);
316     llvm_unreachable("unsupported non numeric type");
317   }
318 
319   // NOLINTNEXTLINE(*-identifier-naming): externally called.
320   Value arithfn__min_unsigned(Value lhs, Value rhs) {
321     OpBuilder builder = getBuilder();
322     if (isFloatingPoint(lhs))
323       return builder.create<arith::MinFOp>(lhs.getLoc(), lhs, rhs);
324     if (isInteger(lhs))
325       return builder.create<arith::MinUIOp>(lhs.getLoc(), lhs, rhs);
326     llvm_unreachable("unsupported non numeric type");
327   }
328 
329   void yieldOutputs(ValueRange values) {
330     assert(!values.empty() && "linalg ops must yield outputs");
331     if (values.empty())
332       return;
333     Value first = values.front();
334     OpBuilder builder = getBuilder();
335     builder.create<YieldOp>(first.getLoc(), values);
336   }
337 
338   Value constant(const std::string &value) {
339     OpBuilder builder = getBuilder();
340     Location loc = builder.getUnknownLoc();
341     Attribute valueAttr = parseAttribute(value, builder.getContext());
342     return builder.create<arith::ConstantOp>(loc, valueAttr.getType(),
343                                              valueAttr);
344   }
345 
346   Value index(int64_t dim) {
347     OpBuilder builder = getBuilder();
348     return builder.create<IndexOp>(builder.getUnknownLoc(), dim);
349   }
350 
351   Type getIntegerType(unsigned width) {
352     return IntegerType::get(context, width);
353   }
354 
355   Type getFloat32Type() { return Float32Type::get(context); }
356 
357   Type getFloat64Type() { return Float64Type::get(context); }
358 
359 private:
360   MLIRContext *context;
361   Block &block;
362 
363   bool isFloatingPoint(Value value) { return value.getType().isa<FloatType>(); }
364   bool isInteger(Value value) { return value.getType().isa<IntegerType>(); }
365 
366   OpBuilder getBuilder() {
367     OpBuilder builder(context);
368     builder.setInsertionPointToEnd(&block);
369     return builder;
370   }
371 };
372 
373 } // namespace
374 
375 //===----------------------------------------------------------------------===//
376 // CopyOp
377 //===----------------------------------------------------------------------===//
378 void CopyOp::regionBuilder(ImplicitLocOpBuilder &b, Block &block) {
379   assert(block.getNumArguments() == 2 && "CopyOp regionBuilder expects 2 args");
380   b.create<linalg::YieldOp>(block.getArgument(0));
381 }
382 
383 void CopyOp::build(OpBuilder &builder, OperationState &result, Value input,
384                    Value output, AffineMap inputPermutation,
385                    AffineMap outputPermutation,
386                    ArrayRef<NamedAttribute> namedAttrs) {
387   result.addOperands({input, output});
388   result.addAttributes(namedAttrs);
389   if (inputPermutation)
390     result.addAttribute("inputPermutation",
391                         AffineMapAttr::get(inputPermutation));
392   if (outputPermutation)
393     result.addAttribute("outputPermutation",
394                         AffineMapAttr::get(outputPermutation));
395   result.addRegion();
396   fillStructuredOpRegion<CopyOp>(builder, *result.regions.front(),
397                                  TypeRange{input.getType()},
398                                  TypeRange{output.getType()});
399 }
400 
401 ParseResult parseCopyOpRegion(OpAsmParser &parser, Region &r, Type inputType,
402                               Type outputType) {
403   OpBuilder opBuilder(parser.getContext());
404   fillStructuredOpRegion<CopyOp>(opBuilder, r, TypeRange{inputType},
405                                  TypeRange{outputType});
406   return success();
407 }
408 
409 /// CopyOp region is elided when printing.
410 void printCopyOpRegion(OpAsmPrinter &, Operation *, Region &, Type, Type) {}
411 
412 static LogicalResult verify(CopyOp op) {
413   OpOperand *output = op.getOutputOperand(0);
414   OpOperand *input = op.getInputOperand(0);
415   if (getElementTypeOrSelf(input->get()) != getElementTypeOrSelf(output->get()))
416     return op.emitOpError("expects views of the same type");
417   if (op.getRank(input) != op.getRank(output))
418     return op.emitOpError("expects views of the same rank");
419   auto rank = op.getNumParallelLoops();
420   auto inputPermutationMap = op.inputPermutation();
421   if (inputPermutationMap) {
422     if (inputPermutationMap->getNumInputs() != rank)
423       return op.emitOpError("expects optional input_permutation map of rank ")
424              << rank;
425     if (!inputPermutationMap->isPermutation())
426       return op.emitOpError(
427           "expects optional input_permutation map to be a permutation");
428   }
429   auto outputPermutationMap = op.outputPermutation();
430   if (outputPermutationMap) {
431     if (outputPermutationMap->getNumInputs() != rank)
432       return op.emitOpError("expects optional output_permutation map of rank ")
433              << rank;
434     if (!outputPermutationMap->isPermutation())
435       return op.emitOpError(
436           "expects optional output_permutation map to be a permutation");
437   }
438   if (rank == 0 && inputPermutationMap)
439     return op.emitOpError("expected no input permutation when rank == 0");
440   if (rank == 0 && outputPermutationMap)
441     return op.emitOpError("expected no output permutation when rank == 0");
442   return success();
443 }
444 
445 void CopyOp::getEffects(
446     SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
447         &effects) {
448   effects.emplace_back(MemoryEffects::Read::get(), input(),
449                        SideEffects::DefaultResource::get());
450   effects.emplace_back(MemoryEffects::Write::get(), output(),
451                        SideEffects::DefaultResource::get());
452 }
453 
454 namespace {
455 /// Remove copy operations that copy data inplace. Requirements are:
456 /// 1) The input and output values are identical.
457 /// 2) The input and output permutation maps are identical.
458 struct EraseIdentityCopyOp : public OpRewritePattern<CopyOp> {
459   using OpRewritePattern<CopyOp>::OpRewritePattern;
460 
461   LogicalResult matchAndRewrite(CopyOp copyOp,
462                                 PatternRewriter &rewriter) const override {
463     assert(copyOp.hasBufferSemantics());
464     if (copyOp.input() == copyOp.output() &&
465         copyOp.inputPermutation() == copyOp.outputPermutation()) {
466       rewriter.eraseOp(copyOp);
467       return success();
468     }
469     return failure();
470   }
471 };
472 } // namespace
473 
474 void CopyOp::getCanonicalizationPatterns(RewritePatternSet &results,
475                                          MLIRContext *context) {
476   results.add<EraseIdentityCopyOp>(context);
477 }
478 
479 //===----------------------------------------------------------------------===//
480 // FillOp
481 //===----------------------------------------------------------------------===//
482 void FillOp::regionBuilder(ImplicitLocOpBuilder &b, Block &block) {
483   assert(block.getNumArguments() == 2 && "FillOp regionBuilder expects 2 args");
484   b.create<linalg::YieldOp>(block.getArgument(0));
485 }
486 
487 void FillOp::build(OpBuilder &builder, OperationState &result, Value value,
488                    Value output) {
489   build(builder, result, output.getType().dyn_cast<RankedTensorType>(), value,
490         output);
491   fillStructuredOpRegion<FillOp>(builder, *result.regions.front(),
492                                  TypeRange{value.getType()},
493                                  TypeRange{output.getType()}, {});
494 }
495 
496 ParseResult parseFillOpRegion(OpAsmParser &parser, Region &r, Type valueType,
497                               Type outputType) {
498   OpBuilder opBuilder(parser.getContext());
499   fillStructuredOpRegion<FillOp>(opBuilder, r, TypeRange{valueType},
500                                  TypeRange{outputType});
501   return success();
502 }
503 
504 /// FillOp region is elided when printing.
505 void printFillOpRegion(OpAsmPrinter &, Operation *, Region &, Type, Type) {}
506 
507 static LogicalResult verify(FillOp op) {
508   OpOperand *output = op.getOutputOperand(0);
509   Type fillType = op.value().getType();
510   if (getElementTypeOrSelf(output->get()) != fillType)
511     return op.emitOpError("expects fill type to match view elemental type");
512   return success();
513 }
514 
515 void FillOp::getEffects(
516     SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
517         &effects) {
518   if (output().getType().isa<MemRefType>())
519     effects.emplace_back(MemoryEffects::Write::get(), output(),
520                          SideEffects::DefaultResource::get());
521 }
522 
523 namespace {
524 
525 /// Fold linalg.fill -> tensor.expand/collapse_shape chain.
526 ///
527 /// For such op chains, we can create new linalg.fill ops with the result
528 /// type of the tensor.expand/collapse_shape op.
529 template <typename TensorReshapeOp>
530 struct FoldFillWithTensorReshape : OpRewritePattern<TensorReshapeOp> {
531   using OpRewritePattern<TensorReshapeOp>::OpRewritePattern;
532   LogicalResult matchAndRewrite(TensorReshapeOp reshapeOp,
533                                 PatternRewriter &rewriter) const override {
534     auto oldFill = reshapeOp.src().template getDefiningOp<FillOp>();
535     if (!oldFill)
536       return failure();
537 
538     Location loc = oldFill.getLoc();
539     auto newInit = rewriter.create<TensorReshapeOp>(
540         loc, reshapeOp.getResultType(), oldFill.output(),
541         reshapeOp.reassociation());
542     rewriter.replaceOpWithNewOp<FillOp>(reshapeOp, oldFill.value(), newInit);
543 
544     return success();
545   }
546 };
547 
548 } // namespace
549 
550 void FillOp::getCanonicalizationPatterns(RewritePatternSet &results,
551                                          MLIRContext *context) {
552   results.add<FoldFillWithTensorReshape<tensor::CollapseShapeOp>,
553               FoldFillWithTensorReshape<tensor::ExpandShapeOp>>(context);
554 }
555 
556 //===----------------------------------------------------------------------===//
557 // GenericOps
558 //===----------------------------------------------------------------------===//
559 void GenericOp::build(
560     OpBuilder &builder, OperationState &result, TypeRange resultTensorTypes,
561     ValueRange inputs, ValueRange outputs, ArrayRef<AffineMap> indexingMaps,
562     ArrayRef<StringRef> iteratorTypes, StringRef doc, StringRef libraryCall,
563     function_ref<void(OpBuilder &, Location, ValueRange)> bodyBuild,
564     ArrayRef<NamedAttribute> attributes) {
565   build(builder, result, resultTensorTypes, inputs, outputs,
566         builder.getAffineMapArrayAttr(indexingMaps),
567         builder.getStrArrayAttr(iteratorTypes),
568         doc.empty() ? StringAttr() : builder.getStringAttr(doc),
569         libraryCall.empty() ? StringAttr()
570                             : builder.getStringAttr(libraryCall));
571   result.addAttributes(attributes);
572   if (!bodyBuild)
573     return;
574 
575   SmallVector<Type, 4> blockArgTypes;
576   SmallVector<Location, 4> blockArgLocs;
577   for (ValueRange container : {inputs, outputs}) {
578     for (Value v : container) {
579       blockArgTypes.push_back(getElementTypeOrSelf(v));
580       blockArgLocs.push_back(v.getLoc());
581     }
582   }
583 
584   OpBuilder::InsertionGuard guard(builder);
585   auto &region = *result.regions.front();
586   Block *bodyBlock =
587       builder.createBlock(&region, region.end(), blockArgTypes, blockArgLocs);
588   bodyBuild(builder, result.location, bodyBlock->getArguments());
589 }
590 
591 void GenericOp::build(
592     OpBuilder &builder, OperationState &result, ValueRange inputs,
593     ValueRange outputs, ArrayRef<AffineMap> indexingMaps,
594     ArrayRef<StringRef> iteratorTypes, StringRef doc, StringRef libraryCall,
595     function_ref<void(OpBuilder &, Location, ValueRange)> bodyBuild,
596     ArrayRef<NamedAttribute> attributes) {
597   build(builder, result, TypeRange{}, inputs, outputs, indexingMaps,
598         iteratorTypes, doc, libraryCall, bodyBuild, attributes);
599 }
600 
601 void GenericOp::build(
602     OpBuilder &builder, OperationState &result, ValueRange inputs,
603     ValueRange outputs, ArrayRef<AffineMap> indexingMaps,
604     ArrayRef<StringRef> iteratorTypes,
605     function_ref<void(OpBuilder &, Location, ValueRange)> bodyBuild,
606     ArrayRef<NamedAttribute> attributes) {
607   build(builder, result, inputs, outputs, indexingMaps, iteratorTypes,
608         /*doc=*/"",
609         /*libraryCall=*/"", bodyBuild, attributes);
610 }
611 
612 void GenericOp::build(
613     OpBuilder &builder, OperationState &result, TypeRange resultTensorTypes,
614     ValueRange inputs, ValueRange outputs, ArrayRef<AffineMap> indexingMaps,
615     ArrayRef<StringRef> iteratorTypes,
616     function_ref<void(OpBuilder &, Location, ValueRange)> bodyBuild,
617     ArrayRef<NamedAttribute> attributes) {
618   build(builder, result, resultTensorTypes, inputs, outputs, indexingMaps,
619         iteratorTypes,
620         /*doc=*/"",
621         /*libraryCall=*/"", bodyBuild, attributes);
622 }
623 
624 static void print(OpAsmPrinter &p, GenericOp op) {
625   p << " ";
626 
627   // Print extra attributes.
628   auto genericAttrNames = op.linalgTraitAttrNames();
629 
630   llvm::StringSet<> genericAttrNamesSet;
631   genericAttrNamesSet.insert(genericAttrNames.begin(), genericAttrNames.end());
632   SmallVector<NamedAttribute, 8> genericAttrs;
633   for (auto attr : op->getAttrs())
634     if (genericAttrNamesSet.count(attr.getName().strref()) > 0)
635       genericAttrs.push_back(attr);
636   if (!genericAttrs.empty()) {
637     auto genericDictAttr = DictionaryAttr::get(op.getContext(), genericAttrs);
638     p << genericDictAttr;
639   }
640 
641   // Printing is shared with named ops, except for the region and attributes
642   printCommonStructuredOpParts(p, op);
643 
644   genericAttrNames.push_back("operand_segment_sizes");
645   genericAttrNamesSet.insert(genericAttrNames.back());
646 
647   bool hasExtraAttrs = false;
648   for (NamedAttribute n : op->getAttrs()) {
649     if ((hasExtraAttrs = !genericAttrNamesSet.contains(n.getName().strref())))
650       break;
651   }
652   if (hasExtraAttrs) {
653     p << " attrs = ";
654     p.printOptionalAttrDict(op->getAttrs(), /*elidedAttrs=*/genericAttrNames);
655   }
656 
657   // Print region.
658   if (!op.region().empty()) {
659     p << ' ';
660     p.printRegion(op.region());
661   }
662 
663   // Print results.
664   printNamedStructuredOpResults(p, op.result_tensors().getTypes());
665 }
666 
667 static ParseResult parseGenericOp(OpAsmParser &parser, OperationState &result) {
668   DictionaryAttr dictAttr;
669   // Parse the core linalg traits that must check into a dictAttr.
670   // The name is unimportant as we will overwrite result.attributes.
671   // The core linalg traits must contain the information necessary to pass the
672   // verifier.
673   if (parser.parseAttribute(dictAttr, "_", result.attributes))
674     return failure();
675   result.attributes.assign(dictAttr.getValue().begin(),
676                            dictAttr.getValue().end());
677 
678   // Parsing is shared with named ops, except for the region.
679   SmallVector<Type, 1> inputTypes, outputTypes;
680   if (parseCommonStructuredOpParts(parser, result, inputTypes, outputTypes))
681     return failure();
682 
683   // Optional attributes may be added.
684   if (succeeded(parser.parseOptionalKeyword("attrs")))
685     if (failed(parser.parseEqual()) ||
686         failed(parser.parseOptionalAttrDict(result.attributes)))
687       return failure();
688 
689   SmallVector<OpAsmParser::OperandType, 8> regionOperands;
690   std::unique_ptr<Region> region = std::make_unique<Region>();
691   SmallVector<Type, 8> operandTypes, regionTypes;
692   if (parser.parseRegion(*region, regionOperands, regionTypes))
693     return failure();
694   result.addRegion(std::move(region));
695 
696   // Generic ops may specify that a subset of its outputs are tensors. Such
697   // outputs are specified in the result type.
698   // TODO: may need to move output parsing before region parsing.
699   // Need to wait for declarative assembly resolution to decide.
700   SmallVector<Type, 1> outputTensorsTypes;
701   if (parseNamedStructuredOpResults(parser, outputTensorsTypes))
702     return failure();
703   result.addTypes(outputTensorsTypes);
704 
705   return success();
706 }
707 
708 static void getGenericEffectsImpl(
709     SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
710         &effects,
711     ValueRange results, ValueRange inputBuffers, ValueRange outputs) {
712   for (Value value : results) {
713     effects.emplace_back(MemoryEffects::Allocate::get(), value,
714                          SideEffects::DefaultResource::get());
715   }
716   for (Value value : inputBuffers) {
717     effects.emplace_back(MemoryEffects::Read::get(), value,
718                          SideEffects::DefaultResource::get());
719   }
720   for (Value value : outputs) {
721     effects.emplace_back(MemoryEffects::Read::get(), value,
722                          SideEffects::DefaultResource::get());
723     effects.emplace_back(MemoryEffects::Write::get(), value,
724                          SideEffects::DefaultResource::get());
725   }
726 }
727 
728 void GenericOp::getEffects(
729     SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
730         &effects) {
731   SmallVector<Value> inputBuffers = getInputBufferOperands();
732   SmallVector<Value> outputBuffers = getOutputBufferOperands();
733   getGenericEffectsImpl(effects, getOperation()->getResults(), inputBuffers,
734                         outputBuffers);
735 }
736 
737 template <typename GenericOpType>
738 static LogicalResult verifyGenericOp(GenericOpType op) {
739   return success();
740 }
741 
742 static LogicalResult verify(GenericOp op) { return verifyGenericOp(op); }
743 
744 namespace {
745 // Deduplicate redundant args of a linalg generic op.
746 // An arg is redundant if it has the same Value and indexing map as another.
747 struct DeduplicateGenericOpInputs : public OpRewritePattern<GenericOp> {
748   using OpRewritePattern<GenericOp>::OpRewritePattern;
749 
750   LogicalResult matchAndRewrite(GenericOp genericOp,
751                                 PatternRewriter &rewriter) const override {
752     // Associate each input to an equivalent "canonical" input that has the same
753     // Value and indexing map.
754     //
755     // In the non-duplicate case, input `i` will have canonical input `i`. But
756     // in the case of duplicated inputs, the canonical input could be some other
757     // input `< i`. That is, a later input will have some earlier input as its
758     // canonical input.
759     llvm::SmallDenseMap<std::pair<Value, AffineMap>, unsigned> canonicalInput;
760     // For later remapping tasks like deduplicating payload block arguments,
761     // having a simple "inputIndex -> canonicalInputIndex" integer mapping is
762     // convenient.
763     SmallVector<unsigned> canonicalInputIndices;
764     for (OpOperand *opOperand : genericOp.getInputOperands()) {
765       AffineMap indexingMap = genericOp.getTiedIndexingMap(opOperand);
766       // STL-like maps have a convenient behavior for our use case here. In the
767       // case of duplicate keys, the insertion is rejected, and the returned
768       // iterator gives access to the value already in the map.
769       auto pair = canonicalInput.insert(
770           {{opOperand->get(), indexingMap}, opOperand->getOperandNumber()});
771       canonicalInputIndices.push_back(pair.first->second);
772     }
773 
774     // If there are no duplicate args, then bail out.
775     if (canonicalInput.size() == genericOp.getNumInputs())
776       return failure();
777 
778     // The operands for the newly canonicalized op.
779     SmallVector<Value> newInputOperands;
780     for (OpOperand *opOperand : genericOp.getInputOperands())
781       if (canonicalInputIndices[opOperand->getOperandNumber()] ==
782           opOperand->getOperandNumber())
783         newInputOperands.push_back(opOperand->get());
784 
785     // Repair the indexing maps by filtering out the ones that have been
786     // eliminated.
787     SmallVector<AffineMap> newIndexingMaps;
788     for (OpOperand *opOperand : genericOp.getInputOperands())
789       if (canonicalInputIndices[opOperand->getOperandNumber()] ==
790           opOperand->getOperandNumber())
791         newIndexingMaps.push_back(genericOp.getTiedIndexingMap(opOperand));
792     for (OpOperand *opOperand : genericOp.getOutputOperands())
793       newIndexingMaps.push_back(genericOp.getTiedIndexingMap(opOperand));
794 
795     // Clone the old op with new operands.
796     SmallVector<Value> outputOperands = genericOp.getOutputOperands();
797     auto newOp = rewriter.create<GenericOp>(
798         genericOp.getLoc(), genericOp->getResultTypes(), newInputOperands,
799         outputOperands, rewriter.getAffineMapArrayAttr(newIndexingMaps),
800         genericOp.iterator_types(), genericOp.docAttr(),
801         genericOp.library_callAttr());
802 
803     // Copy over unknown attributes. They might be load bearing for some flow.
804     ArrayRef<StringRef> odsAttrs = genericOp.getAttributeNames();
805     for (NamedAttribute kv : genericOp->getAttrs()) {
806       if (!llvm::is_contained(odsAttrs, kv.getName().getValue())) {
807         newOp->setAttr(kv.getName(), kv.getValue());
808       }
809     }
810 
811     rewriter.inlineRegionBefore(genericOp.region(), newOp.region(),
812                                 newOp.region().begin());
813 
814     // Repair the payload entry block by RAUW'ing redundant arguments and
815     // erasing them.
816     Block &payload = newOp.region().front();
817     SmallVector<OpOperand *> inputOperands = genericOp.getInputOperands();
818     for (OpOperand *opOperand : llvm::reverse(inputOperands)) {
819       // Iterate in reverse, so that we erase later args first, preventing the
820       // argument list from shifting unexpectedly and invalidating all our
821       // indices.
822       unsigned operandNumber = opOperand->getOperandNumber();
823       if (canonicalInputIndices[operandNumber] == operandNumber)
824         continue;
825       payload.getArgument(operandNumber)
826           .replaceAllUsesWith(
827               payload.getArgument(canonicalInputIndices[operandNumber]));
828       payload.eraseArgument(operandNumber);
829     }
830 
831     rewriter.replaceOp(genericOp, newOp->getResults());
832     return success();
833   }
834 };
835 
836 /// Remove generic operations (on tensors) that are just copying
837 /// the values from inputs to the results. Requirements are
838 /// 1) All iterator types are parallel
839 /// 2) The body contains just a yield operation with the yielded values being
840 ///    the arguments corresponding to the operands.
841 struct EraseIdentityGenericOp : public OpRewritePattern<GenericOp> {
842   using OpRewritePattern<GenericOp>::OpRewritePattern;
843 
844   LogicalResult matchAndRewrite(GenericOp genericOp,
845                                 PatternRewriter &rewriter) const override {
846     if (!genericOp.hasTensorSemantics())
847       return failure();
848     // Check all indexing maps are identity.
849     if (llvm::any_of(genericOp.getIndexingMaps(),
850                      [](AffineMap map) { return !map.isIdentity(); }))
851       return failure();
852 
853     // Check that the body of the linalg operation is just a linalg.yield
854     // operation.
855     Block &body = genericOp.region().front();
856     if (!llvm::hasSingleElement(body))
857       return failure();
858     auto yieldOp = dyn_cast<linalg::YieldOp>(body.getTerminator());
859     if (!yieldOp)
860       return failure();
861 
862     // Get the argument number of the returned values. That is the operand
863     // number to use for replacing uses of this operation.
864     SmallVector<Value> returnedArgs;
865     for (const auto &yieldVal : llvm::enumerate(yieldOp.values())) {
866       auto yieldArg = yieldVal.value().dyn_cast<BlockArgument>();
867       if (!yieldArg || yieldArg.getOwner() != &body)
868         return failure();
869       unsigned argumentNumber = yieldArg.getArgNumber();
870       Value returnedArg = genericOp->getOperand(argumentNumber);
871       Type resultType = genericOp->getResult(yieldVal.index()).getType();
872       // The input can have a different type than the result, e.g. a dynamic
873       // input dimension can be turned into a static output dimension.
874       if (returnedArg.getType() != resultType)
875         returnedArg = rewriter.create<tensor::CastOp>(genericOp.getLoc(),
876                                                       resultType, returnedArg);
877       returnedArgs.push_back(returnedArg);
878     }
879     if (returnedArgs.size() != genericOp->getNumResults())
880       return failure();
881     rewriter.replaceOp(genericOp, returnedArgs);
882     return success();
883   }
884 };
885 } // namespace
886 
887 void GenericOp::getCanonicalizationPatterns(RewritePatternSet &results,
888                                             MLIRContext *context) {
889   results.add<DeduplicateGenericOpInputs, EraseIdentityGenericOp>(context);
890 }
891 
892 //===----------------------------------------------------------------------===//
893 // InitTensorOp
894 //===----------------------------------------------------------------------===//
895 
896 void InitTensorOp::build(OpBuilder &b, OperationState &result,
897                          ArrayRef<OpFoldResult> sizes, Type elementType,
898                          ArrayRef<NamedAttribute> attrs) {
899   SmallVector<Value, 4> dynamicSizes;
900   SmallVector<int64_t, 4> staticSizes;
901   dispatchIndexOpFoldResults(sizes, dynamicSizes, staticSizes,
902                              ShapedType::kDynamicSize);
903   auto resultType = RankedTensorType ::get(staticSizes, elementType);
904   build(b, result, resultType, dynamicSizes, b.getI64ArrayAttr(staticSizes));
905   result.addAttributes(attrs);
906 }
907 
908 static LogicalResult verify(InitTensorOp op) {
909   RankedTensorType resultType = op.getType();
910   SmallVector<int64_t, 4> staticSizes = llvm::to_vector<4>(llvm::map_range(
911       op.static_sizes().cast<ArrayAttr>(),
912       [](Attribute a) -> int64_t { return a.cast<IntegerAttr>().getInt(); }));
913 
914   if (failed(verifyListOfOperandsOrIntegers(op, "sizes", resultType.getRank(),
915                                             op.static_sizes(), op.sizes(),
916                                             ShapedType::isDynamic)))
917     return failure();
918 
919   if (op.static_sizes().size() != static_cast<unsigned>(resultType.getRank()))
920     return op->emitError("expected ")
921            << resultType.getRank() << " sizes values";
922 
923   Type expectedType = InitTensorOp::inferResultType(
924       staticSizes, resultType.getElementType(), resultType.getEncoding());
925   if (resultType != expectedType) {
926     return op.emitError("specified type ")
927            << resultType << " does not match the inferred type "
928            << expectedType;
929   }
930   return success();
931 }
932 
933 Type InitTensorOp::inferResultType(ArrayRef<int64_t> staticSizes,
934                                    Type elementType, Attribute encoding) {
935   return RankedTensorType::get(staticSizes, elementType, encoding);
936 }
937 
938 namespace {
939 /// Change the type of the result of a `linalg.init_tensor` by making the result
940 /// type statically sized along dimension that in the original operation where
941 /// defined as dynamic, but the size was defined using a `constant` op. For
942 /// example
943 ///
944 ///  %c5 = arith.constant 5: index
945 ///  %0 = linalg.init_tensor [%arg0, %c5] : tensor<?x?xf32>
946 ///
947 ///  to
948 ///
949 ///  %0 = linalg.init_tensor [%arg0, 5] : tensor<?x5xf32>
950 struct ReplaceStaticShapeDims : OpRewritePattern<InitTensorOp> {
951   using OpRewritePattern<InitTensorOp>::OpRewritePattern;
952 
953   LogicalResult matchAndRewrite(InitTensorOp op,
954                                 PatternRewriter &rewriter) const override {
955     SmallVector<Value, 4> dynamicSizes;
956     SmallVector<int64_t, 4> staticSizes;
957     for (unsigned i = 0, e = op.getType().getRank(); i != e; ++i) {
958       // If the size is already static, nothing to do.
959       if (!op.isDynamicSize(i)) {
960         staticSizes.push_back(op.getStaticSize(i));
961         continue;
962       }
963 
964       // If the size is dynamic but defined using a `constant` op, get the
965       // constant value to find the static size to use.
966       unsigned operandNum = op.getIndexOfDynamicSize(i);
967       Value sizeOperand = op.getOperand(operandNum);
968       if (auto constantIndexOp =
969               sizeOperand.getDefiningOp<arith::ConstantIndexOp>()) {
970         staticSizes.push_back(constantIndexOp.value());
971         continue;
972       }
973 
974       // Fallback case. Keep the size dynamic.
975       dynamicSizes.push_back(sizeOperand);
976       staticSizes.push_back(ShapedType::kDynamicSize);
977     }
978     RankedTensorType newType =
979         RankedTensorType::get(staticSizes, op.getType().getElementType());
980     if (newType == op.getType())
981       return failure();
982     auto newOp =
983         rewriter.create<InitTensorOp>(op.getLoc(), newType, dynamicSizes,
984                                       rewriter.getI64ArrayAttr(staticSizes));
985     rewriter.replaceOpWithNewOp<tensor::CastOp>(op, op.getType(), newOp);
986     return success();
987   }
988 };
989 } // namespace
990 
991 namespace {
992 /// Since `init_tensor` operation creates a tensor needed only for its shape, a
993 /// slice of this is also needed only for its shape. The result can be
994 /// replaced by a new init_tensor operation of the same size as the extract
995 /// slice op.
996 struct FoldInitTensorWithExtractSliceOp
997     : public OpRewritePattern<tensor::ExtractSliceOp> {
998   using OpRewritePattern<tensor::ExtractSliceOp>::OpRewritePattern;
999 
1000   LogicalResult matchAndRewrite(tensor::ExtractSliceOp sliceOp,
1001                                 PatternRewriter &rewriter) const override {
1002     if (!sliceOp.source().getDefiningOp<linalg::InitTensorOp>())
1003       return failure();
1004     // ExtractSliceOp may be rank-reducing; its dynamic sizes must be preserved
1005     // as well as its result type.
1006     rewriter.replaceOpWithNewOp<linalg::InitTensorOp>(
1007         sliceOp, sliceOp.sizes(),
1008         sliceOp.result().getType().cast<RankedTensorType>().getShape(),
1009         sliceOp.getSourceType().getElementType());
1010     return success();
1011   }
1012 };
1013 
1014 template <typename TensorReshapeOp>
1015 struct FoldInitTensorWithTensorReshapeOp
1016     : public OpRewritePattern<TensorReshapeOp> {
1017   using OpRewritePattern<TensorReshapeOp>::OpRewritePattern;
1018 
1019   LogicalResult matchAndRewrite(TensorReshapeOp reshapeOp,
1020                                 PatternRewriter &rewriter) const override {
1021     if (!reshapeOp.src().template getDefiningOp<InitTensorOp>())
1022       return failure();
1023     Location loc = reshapeOp.getLoc();
1024     ReifiedRankedShapedTypeDims resultShapes;
1025     ReifyRankedShapedTypeOpInterface reifyShapedTypeInterface =
1026         cast<ReifyRankedShapedTypeOpInterface>(reshapeOp.getOperation());
1027     if (failed(reifyShapedTypeInterface.reifyResultShapes(rewriter,
1028                                                           resultShapes)) ||
1029         !llvm::hasSingleElement(resultShapes))
1030       return failure();
1031     Value initTensor = rewriter.create<InitTensorOp>(
1032         loc, getAsOpFoldResult(resultShapes[0]),
1033         reshapeOp.getResultType().getElementType());
1034     if (initTensor.getType() != reshapeOp.getResultType()) {
1035       rewriter.replaceOpWithNewOp<tensor::CastOp>(
1036           reshapeOp, reshapeOp.getResultType(), initTensor);
1037     } else {
1038       rewriter.replaceOp(reshapeOp, initTensor);
1039     }
1040     return success();
1041   }
1042 };
1043 
1044 struct FoldInitTensorWithDimOp : public OpRewritePattern<tensor::DimOp> {
1045   using OpRewritePattern<tensor::DimOp>::OpRewritePattern;
1046 
1047   LogicalResult matchAndRewrite(tensor::DimOp dimOp,
1048                                 PatternRewriter &rewriter) const override {
1049     Optional<int64_t> maybeConstantIndex = dimOp.getConstantIndex();
1050     auto initTensorOp = dimOp.source().getDefiningOp<linalg::InitTensorOp>();
1051     if (!initTensorOp || !maybeConstantIndex)
1052       return failure();
1053     if (!initTensorOp.isDynamicSize(*maybeConstantIndex))
1054       return failure();
1055     rewriter.replaceOp(dimOp, initTensorOp.getDynamicSize(*maybeConstantIndex));
1056     return success();
1057   }
1058 };
1059 } // namespace
1060 
1061 void InitTensorOp::getCanonicalizationPatterns(RewritePatternSet &results,
1062                                                MLIRContext *context) {
1063   results.add<FoldInitTensorWithDimOp, FoldInitTensorWithExtractSliceOp,
1064               FoldInitTensorWithTensorReshapeOp<tensor::ExpandShapeOp>,
1065               FoldInitTensorWithTensorReshapeOp<tensor::CollapseShapeOp>,
1066               ReplaceStaticShapeDims>(context);
1067 }
1068 
1069 LogicalResult InitTensorOp::reifyResultShapes(
1070     OpBuilder &builder, ReifiedRankedShapedTypeDims &reifiedReturnShapes) {
1071   auto shapes = llvm::to_vector<4>(llvm::map_range(
1072       llvm::seq<int64_t>(0, getType().getRank()), [&](int64_t dim) -> Value {
1073         if (isDynamicSize(dim))
1074           return getDynamicSize(dim);
1075         return builder.create<arith::ConstantIndexOp>(getLoc(),
1076                                                       getStaticSize(dim));
1077       }));
1078   reifiedReturnShapes.emplace_back(std::move(shapes));
1079   return success();
1080 }
1081 
1082 //===----------------------------------------------------------------------===//
1083 // PadTensorOp
1084 //===----------------------------------------------------------------------===//
1085 
1086 // TODO: Replace custom<InferType> directive with AllTypesMatch as soon as it
1087 // supports optional types.
1088 void printInferType(OpAsmPrinter &printer, Operation *op, Value optOperand,
1089                     Type typeToInfer, Type typeToInferFrom) {}
1090 
1091 ParseResult parseInferType(OpAsmParser &parser,
1092                            Optional<OpAsmParser::OperandType> optOperand,
1093                            Type &typeToInfer, Type typeToInferFrom) {
1094   if (optOperand)
1095     typeToInfer = typeToInferFrom;
1096   return success();
1097 }
1098 
1099 static LogicalResult verify(PadTensorOp op) {
1100   auto sourceType = op.source().getType().cast<RankedTensorType>();
1101   auto resultType = op.result().getType().cast<RankedTensorType>();
1102   auto expectedType = PadTensorOp::inferResultType(
1103       sourceType, extractFromI64ArrayAttr(op.static_low()),
1104       extractFromI64ArrayAttr(op.static_high()));
1105   for (int i = 0, e = sourceType.getRank(); i < e; ++i) {
1106     if (resultType.getDimSize(i) == expectedType.getDimSize(i))
1107       continue;
1108     if (expectedType.isDynamicDim(i))
1109       continue;
1110     return op.emitError("specified type ")
1111            << resultType << " does not match the inferred type "
1112            << expectedType;
1113   }
1114 
1115   auto &region = op.region();
1116   unsigned rank = resultType.getRank();
1117   Block &block = region.front();
1118   if (block.getNumArguments() != rank)
1119     return op.emitError("expected the block to have ") << rank << " arguments";
1120 
1121   // Note: the number and type of yield values are checked in the YieldOp.
1122   for (const auto &en : llvm::enumerate(block.getArgumentTypes())) {
1123     if (!en.value().isIndex())
1124       return op.emitOpError("expected block argument ")
1125              << (en.index() + 1) << " to be an index";
1126   }
1127 
1128   return success();
1129 }
1130 
1131 RankedTensorType PadTensorOp::inferResultType(RankedTensorType sourceType,
1132                                               ArrayRef<int64_t> staticLow,
1133                                               ArrayRef<int64_t> staticHigh,
1134                                               ArrayRef<int64_t> resultShape) {
1135   unsigned rank = sourceType.getRank();
1136   assert(staticLow.size() == rank && "unexpected staticLow size mismatch");
1137   assert(staticHigh.size() == rank && "unexpected staticHigh size mismatch");
1138   assert((resultShape.empty() || resultShape.size() == rank) &&
1139          "unexpected resultShape size mismatch");
1140 
1141   SmallVector<int64_t, 4> inferredShape;
1142   for (auto i : llvm::seq<unsigned>(0, rank)) {
1143     if (sourceType.isDynamicDim(i) ||
1144         staticLow[i] == ShapedType::kDynamicSize ||
1145         staticHigh[i] == ShapedType::kDynamicSize) {
1146       inferredShape.push_back(resultShape.empty() ? ShapedType::kDynamicSize
1147                                                   : resultShape[i]);
1148     } else {
1149       int64_t size = sourceType.getDimSize(i) + staticLow[i] + staticHigh[i];
1150       assert((resultShape.empty() || size == resultShape[i] ||
1151               resultShape[i] == ShapedType::kDynamicSize) &&
1152              "mismatch between inferred shape and result shape");
1153       inferredShape.push_back(size);
1154     }
1155   }
1156 
1157   return RankedTensorType::get(inferredShape, sourceType.getElementType());
1158 }
1159 
1160 void PadTensorOp::build(OpBuilder &b, OperationState &result, Value source,
1161                         ArrayRef<int64_t> staticLow,
1162                         ArrayRef<int64_t> staticHigh, ValueRange low,
1163                         ValueRange high, bool nofold,
1164                         ArrayRef<NamedAttribute> attrs) {
1165   auto sourceType = source.getType().cast<RankedTensorType>();
1166   auto resultType = inferResultType(sourceType, staticLow, staticHigh);
1167   build(b, result, resultType, source, low, high, b.getI64ArrayAttr(staticLow),
1168         b.getI64ArrayAttr(staticHigh), nofold ? b.getUnitAttr() : UnitAttr());
1169   result.addAttributes(attrs);
1170 }
1171 
1172 void PadTensorOp::build(OpBuilder &b, OperationState &result, Value source,
1173                         ValueRange low, ValueRange high, bool nofold,
1174                         ArrayRef<NamedAttribute> attrs) {
1175   auto sourceType = source.getType().cast<RankedTensorType>();
1176   unsigned rank = sourceType.getRank();
1177   SmallVector<int64_t, 4> staticVector(rank, ShapedType::kDynamicSize);
1178   build(b, result, source, staticVector, staticVector, low, high, nofold,
1179         attrs);
1180 }
1181 
1182 void PadTensorOp::build(OpBuilder &b, OperationState &result, Type resultType,
1183                         Value source, ArrayRef<OpFoldResult> low,
1184                         ArrayRef<OpFoldResult> high, bool nofold,
1185                         ArrayRef<NamedAttribute> attrs) {
1186   assert(resultType.isa<RankedTensorType>());
1187   auto sourceType = source.getType().cast<RankedTensorType>();
1188   SmallVector<Value, 4> dynamicLow, dynamicHigh;
1189   SmallVector<int64_t, 4> staticLow, staticHigh;
1190   // staticLow and staticHigh have full information of the padding config.
1191   // This will grow staticLow and staticHigh with 1 value. If the config is
1192   // dynamic (ie not a constant), dynamicLow and dynamicHigh will grow with 1
1193   // value as well.
1194   dispatchIndexOpFoldResults(low, dynamicLow, staticLow,
1195                              ShapedType::kDynamicSize);
1196   dispatchIndexOpFoldResults(high, dynamicHigh, staticHigh,
1197                              ShapedType::kDynamicSize);
1198   if (!resultType) {
1199     resultType =
1200         PadTensorOp::inferResultType(sourceType, staticLow, staticHigh);
1201   }
1202   build(b, result, resultType, source, dynamicLow, dynamicHigh,
1203         b.getI64ArrayAttr(staticLow), b.getI64ArrayAttr(staticHigh),
1204         nofold ? b.getUnitAttr() : UnitAttr());
1205   result.addAttributes(attrs);
1206 }
1207 
1208 PadTensorOp PadTensorOp::createPadScalarOp(Type type, Value source, Value pad,
1209                                            ArrayRef<OpFoldResult> low,
1210                                            ArrayRef<OpFoldResult> high,
1211                                            bool nofold, Location loc,
1212                                            OpBuilder &builder) {
1213   auto padTensorOp =
1214       builder.create<linalg::PadTensorOp>(loc, type, source, low, high, nofold);
1215   int rank = padTensorOp.getResultType().getRank();
1216   SmallVector<Type, 4> blockArgTypes(rank, builder.getIndexType());
1217   SmallVector<Location, 4> blockArgLocs(rank, loc);
1218   auto &region = padTensorOp.region();
1219   // `builder.createBlock` changes the insertion point within the block. Create
1220   // a guard to reset the insertion point of the builder after it is destroyed.
1221   OpBuilder::InsertionGuard guard(builder);
1222   builder.createBlock(&region, region.end(), blockArgTypes, blockArgLocs);
1223   builder.create<linalg::YieldOp>(loc, pad);
1224   return padTensorOp;
1225 }
1226 
1227 PadTensorOp PadTensorOp::createPadHighOp(Type type, Value source, Value pad,
1228                                          bool nofold, Location loc,
1229                                          OpBuilder &b) {
1230   SmallVector<OpFoldResult, 4> low, high;
1231   auto rankedTensorType = type.cast<RankedTensorType>();
1232   assert(rankedTensorType.hasStaticShape());
1233   for (const auto &en : enumerate(rankedTensorType.getShape())) {
1234     AffineExpr d0;
1235     bindDims(b.getContext(), d0);
1236     auto dimOp = b.createOrFold<tensor::DimOp>(loc, source, en.index());
1237     Value paddingWidth =
1238         makeComposedAffineApply(b, loc, en.value() - d0, {dimOp});
1239     high.push_back(paddingWidth);
1240     low.push_back(b.createOrFold<arith::ConstantIndexOp>(loc, 0));
1241   }
1242   return PadTensorOp::createPadScalarOp(type, source, pad, low, high, nofold,
1243                                         loc, b);
1244 }
1245 
1246 LogicalResult PadTensorOp::reifyResultShapes(
1247     OpBuilder &b, ReifiedRankedShapedTypeDims &reifiedReturnShapes) {
1248   Location loc = getLoc();
1249   auto lowPad = getMixedLowPad();
1250   auto highPad = getMixedHighPad();
1251   SmallVector<Value> shapes;
1252   for (auto dim : llvm::seq<int64_t>(0, getSourceType().getRank())) {
1253     // Shape along each dimension is source dim + low pad + high pad.
1254     SmallVector<Value> mapOperands;
1255     mapOperands.push_back(b.createOrFold<tensor::DimOp>(loc, source(), dim));
1256     AffineExpr expr = b.getAffineDimExpr(0);
1257     unsigned numSymbols = 0;
1258     auto addOpFoldResult = [&](OpFoldResult valueOrAttr) {
1259       if (Value v = valueOrAttr.dyn_cast<Value>()) {
1260         expr = expr + b.getAffineSymbolExpr(numSymbols++);
1261         mapOperands.push_back(v);
1262         return;
1263       }
1264       int64_t staticValue =
1265           valueOrAttr.get<Attribute>().cast<IntegerAttr>().getInt();
1266       expr = expr + staticValue;
1267     };
1268     addOpFoldResult(lowPad[dim]);
1269     addOpFoldResult(highPad[dim]);
1270     shapes.push_back(applyMapToValues(
1271         b, loc, AffineMap::get(1, numSymbols, expr), mapOperands)[0]);
1272   }
1273   reifiedReturnShapes.emplace_back(std::move(shapes));
1274   return success();
1275 }
1276 
1277 //===----------------------------------------------------------------------===//
1278 // Methods related to PadTensor tiling.
1279 //===----------------------------------------------------------------------===//
1280 
1281 SmallVector<Value> PadTensorOp::getDestinationOperands(OpBuilder &b) {
1282   ReifiedRankedShapedTypeDims reifiedShapes;
1283   (void)reifyResultShapes(b, reifiedShapes);
1284   SmallVector<OpFoldResult> mixedSizes = getAsOpFoldResult(reifiedShapes[0]);
1285   Value initTensor = b.create<InitTensorOp>(getLoc(), mixedSizes,
1286                                             getResultType().getElementType());
1287   return {initTensor};
1288 }
1289 
1290 SmallVector<StringRef> PadTensorOp::getLoopIteratorTypes() {
1291   SmallVector<StringRef> iteratorTypes(getResultType().getRank(),
1292                                        getParallelIteratorTypeName());
1293   return iteratorTypes;
1294 }
1295 
1296 SmallVector<Range> PadTensorOp::getIterationDomain(OpBuilder &b) {
1297   ReifiedRankedShapedTypeDims reifiedShapes;
1298   (void)reifyResultShapes(b, reifiedShapes);
1299   Value zero = b.create<arith::ConstantIndexOp>(getLoc(), 0);
1300   Value one = b.create<arith::ConstantIndexOp>(getLoc(), 1);
1301   // Initialize all the ranges to {zero, one, one}. All the `ub`s are
1302   // overwritten.
1303   SmallVector<Range> loopRanges(reifiedShapes[0].size(), {zero, one, one});
1304   for (const auto &ub : enumerate(reifiedShapes[0]))
1305     loopRanges[ub.index()].size = ub.value();
1306   return loopRanges;
1307 }
1308 
1309 SmallVector<Operation *> PadTensorOp::getTiledImplementation(
1310     OpBuilder &b, ValueRange dest, ArrayRef<OpFoldResult> offsets,
1311     ArrayRef<OpFoldResult> sizes, bool /*tileDestOperands*/) {
1312   // Only constant padding value supported.
1313   Value padValue = getConstantPaddingValue();
1314   if (!padValue)
1315     return {};
1316 
1317   // Helper variables and functions for various arithmetic operations. These are
1318   // used extensively for computing new offset/length and padding values.
1319   Location loc = getLoc();
1320   AffineExpr dim0, dim1;
1321   bindDims(b.getContext(), dim0, dim1);
1322   // Add two integers.
1323   auto addMap = AffineMap::get(2, 0, {dim0 + dim1});
1324   auto add = [&](Value v1, Value v2) {
1325     return b.createOrFold<AffineApplyOp>(loc, addMap, ValueRange{v1, v2});
1326   };
1327   // Subtract two integers.
1328   auto subMap = AffineMap::get(2, 0, {dim0 - dim1});
1329   auto sub = [&](Value v1, Value v2) {
1330     return b.createOrFold<AffineApplyOp>(loc, subMap, ValueRange{v1, v2});
1331   };
1332   // Take the minimum of two integers.
1333   auto idMap = AffineMap::getMultiDimIdentityMap(2, b.getContext());
1334   auto min = [&](Value v1, Value v2) {
1335     return b.createOrFold<AffineMinOp>(loc, idMap, ValueRange{v1, v2});
1336   };
1337   // Take the maximum of two integers.
1338   auto max = [&](Value v1, Value v2) {
1339     return b.createOrFold<AffineMaxOp>(loc, idMap, ValueRange{v1, v2});
1340   };
1341   // Zero index-typed integer.
1342   auto zero = b.create<arith::ConstantIndexOp>(loc, 0);
1343 
1344   // Helper function for filling static/dynamic low/high padding indices vectors
1345   // of PadTensorOp.
1346   auto appendIndex = [&](Value val, SmallVector<Value> &dynIndices,
1347                          SmallVector<int64_t> &staticIndices) {
1348     if (auto constInt = getConstantIntValue(val)) {
1349       staticIndices.push_back(*constInt);
1350     } else {
1351       staticIndices.push_back(ShapedType::kDynamicSize);
1352       dynIndices.push_back(val);
1353     }
1354   };
1355 
1356   // Compute new offsets, lengths, low padding, high padding.
1357   SmallVector<OpFoldResult> newOffsets, newLengths, newStrides;
1358   SmallVector<Value> newLows, newHighs;
1359   SmallVector<int64_t> staticNewLows, staticNewHighs;
1360   // Set to true if the original data source is not read at all.
1361   bool hasZeroLen = false;
1362   // Same as hasZeroLen, but for dynamic dimension sizes. This condition
1363   // is true if the original data source turns out to be unused at runtime.
1364   Value dynHasZeroLenCond;
1365 
1366   int64_t rank = getSourceType().getRank();
1367   for (unsigned dim = 0; dim < rank; ++dim) {
1368     auto low = getValueOrCreateConstantIndexOp(b, loc, getMixedLowPad()[dim]);
1369     bool hasLowPad = getConstantIntValue(low) != static_cast<int64_t>(0);
1370     auto high = getValueOrCreateConstantIndexOp(b, loc, getMixedHighPad()[dim]);
1371     bool hasHighPad = getConstantIntValue(high) != static_cast<int64_t>(0);
1372     auto offset = getValueOrCreateConstantIndexOp(b, loc, offsets[dim]);
1373     auto length = getValueOrCreateConstantIndexOp(b, loc, sizes[dim]);
1374     auto srcSize = b.createOrFold<tensor::DimOp>(loc, source(), dim);
1375 
1376     // The new amount of low padding is `low - offset`. Except for the case
1377     // where none of the low padding is read. In that case, the new amount of
1378     // low padding is zero.
1379     //
1380     // Optimization: If low = 0, then newLow = 0.
1381     Value newLow = hasLowPad ? max(zero, sub(low, offset)) : zero;
1382     appendIndex(newLow, newLows, staticNewLows);
1383 
1384     // Start reading the data from position `offset - low`. Since the original
1385     // read may have started in the low padding zone, this value could be
1386     // negative. Therefore, start reading from:
1387     //
1388     // max(offset - low, 0)
1389     //
1390     // The original read could also have started in the high padding zone.
1391     // In that case, set the offset to the end of source tensor. The new
1392     // ExtractSliceOp length will be zero in that case. (Effectively reading no
1393     // data from the source.)
1394     //
1395     // Optimization: If low = 0, then the formula can be simplified.
1396     Value newOffset = hasLowPad ? min(max(sub(offset, low), zero), srcSize)
1397                                 : min(offset, srcSize);
1398     newOffsets.push_back(getAsOpFoldResult(newOffset));
1399 
1400     // The original ExtractSliceOp was reading until position `offset + length`.
1401     // Therefore, the corresponding position within the source tensor is:
1402     //
1403     // offset + length - low
1404     //
1405     // In case the original ExtractSliceOp stopped reading within the low
1406     // padding zone, this value can be negative. In that case, the end position
1407     // of the read should be zero. (Similar to newOffset.)
1408     //
1409     // The original read could also have stopped in the high padding zone.
1410     // In that case, set the end positition of the read should be the end of the
1411     // source tensor. (Similar to newOffset.)
1412     //
1413     // endLoc = min(max(offset - low + length, 0), srcSize)
1414     //
1415     // The new ExtractSliceOp length is `endLoc - newOffset`.
1416     //
1417     // Optimization: If low = 0, then the formula can be simplified.
1418     Value endLoc = hasLowPad
1419                        ? min(max(add(sub(offset, low), length), zero), srcSize)
1420                        : min(add(offset, length), srcSize);
1421     Value newLength = sub(endLoc, newOffset);
1422     newLengths.push_back(getAsOpFoldResult(newLength));
1423 
1424     // Check if newLength is zero. In that case, no SubTensorOp should be
1425     // executed.
1426     if (auto newLengthInt = getConstantIntValue(newLength)) {
1427       hasZeroLen |= *newLengthInt == 0;
1428     } else {
1429       Value check = b.create<arith::CmpIOp>(loc, arith::CmpIPredicate::eq,
1430                                             newLength, zero);
1431       dynHasZeroLenCond =
1432           dynHasZeroLenCond
1433               ? b.create<arith::OrIOp>(loc, check, dynHasZeroLenCond)
1434               : check;
1435     }
1436 
1437     // The amount of high padding is simply the number of elements remaining,
1438     // so that the result has the same length as the original ExtractSliceOp.
1439     // As an optimization, if the original high padding is zero, then the new
1440     // high padding must also be zero.
1441     Value newHigh = hasHighPad ? sub(sub(length, newLength), newLow) : zero;
1442     appendIndex(newHigh, newHighs, staticNewHighs);
1443 
1444     // Only unit stride supported.
1445     newStrides.push_back(b.getIndexAttr(1));
1446   }
1447 
1448   // The shape of the result can be obtained from the sizes passed in.
1449   SmallVector<Value> dynDims;
1450   SmallVector<int64_t> shape;
1451   dispatchIndexOpFoldResults(sizes, dynDims, shape, ShapedType::kDynamicSize);
1452   RankedTensorType resultType =
1453       RankedTensorType::get(shape, getResultType().getElementType());
1454 
1455   // Insert cast to ensure that types match. (May be folded away.)
1456   auto castResult = [&](Value val) -> Operation * {
1457     auto castOp = b.create<tensor::CastOp>(loc, resultType, val);
1458     return castOp;
1459   };
1460 
1461   // In cases where the original data source is unused: Emit a GenerateOp and
1462   // do not generate a SliceOp. (The result shape of the SliceOp would
1463   // have a dimension of size 0, the semantics of which is unclear.)
1464   auto createGenerateOp = [&]() {
1465     // Create GenerateOp.
1466     auto generateOp = b.create<tensor::GenerateOp>(
1467         loc, resultType, dynDims,
1468         [&](OpBuilder &builder, Location gLoc, ValueRange indices) {
1469           builder.create<tensor::YieldOp>(gLoc, padValue);
1470         });
1471     return castResult(generateOp);
1472   };
1473 
1474   // Emit a SliceOp and a PadTensorOp. Should not be used in cases where
1475   // the result shape of the new SliceOp has a zero dimension.
1476   auto createPadTensorOfSubTensor = [&]() {
1477     // Create pad_tensor(subtensor(x)).
1478     auto newSliceOp = b.create<tensor::ExtractSliceOp>(
1479         loc, source(), newOffsets, newLengths, newStrides);
1480     auto newPadTensorOp = b.create<PadTensorOp>(
1481         loc, newSliceOp, staticNewLows, staticNewHighs, newLows, newHighs);
1482 
1483     // Copy region to new PadTensorOp.
1484     BlockAndValueMapping bvm;
1485     region().cloneInto(&newPadTensorOp.getRegion(), bvm);
1486 
1487     // Cast result and return.
1488     return castResult(newPadTensorOp);
1489   };
1490 
1491   // Rewrite subtensor(pad_tensor(x)) into a GenerateOp it is statically known
1492   // that the original data source x is not used.
1493   if (hasZeroLen) {
1494     return {createGenerateOp()};
1495   }
1496 
1497   // If there are dynamic dimensions: Generate an scf.if check to avoid creating
1498   // SliceOps with result dimensions of size 0 at runtime.
1499   if (dynHasZeroLenCond) {
1500     auto result = b.create<scf::IfOp>(
1501         loc, resultType, dynHasZeroLenCond,
1502         /*thenBuilder=*/
1503         [&](OpBuilder &b, Location loc) {
1504           b.create<scf::YieldOp>(loc, createGenerateOp()->getResult(0));
1505         },
1506         /*elseBuilder=*/
1507         [&](OpBuilder &b, Location loc) {
1508           b.create<scf::YieldOp>(loc,
1509                                  createPadTensorOfSubTensor()->getResult(0));
1510         });
1511     return {result};
1512   }
1513   return {createPadTensorOfSubTensor()};
1514 }
1515 
1516 namespace {
1517 // Folds linalg.pad_tensor when padding is static zeros and the attribute
1518 // doesn't request otherwise.
1519 struct FoldStaticZeroPadding : public OpRewritePattern<PadTensorOp> {
1520   using OpRewritePattern<PadTensorOp>::OpRewritePattern;
1521 
1522   LogicalResult matchAndRewrite(PadTensorOp padTensorOp,
1523                                 PatternRewriter &rewriter) const override {
1524     if (!padTensorOp.hasZeroLowPad() || !padTensorOp.hasZeroHighPad())
1525       return failure();
1526     if (padTensorOp.nofold())
1527       return failure();
1528     rewriter.replaceOpWithNewOp<tensor::CastOp>(
1529         padTensorOp, padTensorOp.result().getType(), padTensorOp.source());
1530     return success();
1531   }
1532 };
1533 
1534 // Fold CastOp into PadTensorOp when adding static information.
1535 struct FoldSourceTensorCast : public OpRewritePattern<PadTensorOp> {
1536   using OpRewritePattern<PadTensorOp>::OpRewritePattern;
1537 
1538   LogicalResult matchAndRewrite(PadTensorOp padTensorOp,
1539                                 PatternRewriter &rewriter) const override {
1540     auto castOp = padTensorOp.source().getDefiningOp<tensor::CastOp>();
1541     if (!tensor::canFoldIntoConsumerOp(castOp))
1542       return failure();
1543 
1544     auto newResultType = PadTensorOp::inferResultType(
1545         castOp.source().getType().cast<RankedTensorType>(),
1546         extractFromI64ArrayAttr(padTensorOp.static_low()),
1547         extractFromI64ArrayAttr(padTensorOp.static_high()),
1548         padTensorOp.getResultType().getShape());
1549 
1550     if (newResultType == padTensorOp.getResultType()) {
1551       rewriter.updateRootInPlace(padTensorOp, [&]() {
1552         padTensorOp.sourceMutable().assign(castOp.source());
1553       });
1554     } else {
1555       auto newOp = rewriter.create<PadTensorOp>(
1556           padTensorOp->getLoc(), newResultType, padTensorOp.source(),
1557           padTensorOp.low(), padTensorOp.high(), padTensorOp.static_low(),
1558           padTensorOp.static_high(), padTensorOp.nofold());
1559       BlockAndValueMapping mapper;
1560       padTensorOp.getRegion().cloneInto(&newOp.getRegion(), mapper);
1561 
1562       rewriter.replaceOpWithNewOp<tensor::CastOp>(
1563           padTensorOp, padTensorOp.getResultType(), newOp);
1564     }
1565     return success();
1566   }
1567 };
1568 
1569 // Fold CastOp using the result of PadTensorOp back into the latter if it adds
1570 // static information.
1571 struct FoldTargetTensorCast : public OpRewritePattern<PadTensorOp> {
1572   using OpRewritePattern<PadTensorOp>::OpRewritePattern;
1573 
1574   LogicalResult matchAndRewrite(PadTensorOp padTensorOp,
1575                                 PatternRewriter &rewriter) const override {
1576     if (!padTensorOp.result().hasOneUse())
1577       return failure();
1578     auto tensorCastOp =
1579         dyn_cast<tensor::CastOp>(*padTensorOp->getUsers().begin());
1580     if (!tensorCastOp)
1581       return failure();
1582     if (!tensor::preservesStaticInformation(padTensorOp.result().getType(),
1583                                             tensorCastOp.dest().getType()))
1584       return failure();
1585 
1586     auto replacementOp = rewriter.create<PadTensorOp>(
1587         padTensorOp.getLoc(), tensorCastOp.dest().getType(),
1588         padTensorOp.source(), padTensorOp.low(), padTensorOp.high(),
1589         padTensorOp.static_low(), padTensorOp.static_high(),
1590         padTensorOp.nofold());
1591     replacementOp.region().takeBody(padTensorOp.region());
1592 
1593     rewriter.replaceOp(padTensorOp, replacementOp.result());
1594     rewriter.replaceOp(tensorCastOp, replacementOp.result());
1595     return success();
1596   }
1597 };
1598 } // namespace
1599 
1600 void PadTensorOp::getCanonicalizationPatterns(RewritePatternSet &results,
1601                                               MLIRContext *context) {
1602   results.add<FoldStaticZeroPadding, FoldSourceTensorCast>(context);
1603   results.add<FoldTargetTensorCast>(context);
1604 }
1605 
1606 /// Return the padding value of the PadTensorOp if it constant. In this context,
1607 /// "constant" means an actual constant or "defined outside of the block".
1608 ///
1609 /// Values are considered constant in three cases:
1610 ///  - A ConstantLike value.
1611 ///  - A basic block argument from a different block.
1612 ///  - A value defined outside of the block.
1613 ///
1614 /// If the padding value is not constant, an empty Value is returned.
1615 Value PadTensorOp::getConstantPaddingValue() {
1616   auto yieldOp = dyn_cast<YieldOp>(getRegion().front().getTerminator());
1617   if (!yieldOp || yieldOp.values().size() != 1)
1618     return {};
1619   Value padValue = yieldOp.values().front();
1620   // Check if yield value is a constant.
1621   if (matchPattern(padValue, m_Constant()))
1622     return padValue;
1623   // Check if yield value is defined inside the PadTensorOp block.
1624   if (padValue.getParentBlock() == &getRegion().front())
1625     return {};
1626   // Else: Yield value defined outside of the PadTensorOp block.
1627   return padValue;
1628 }
1629 
1630 OpFoldResult PadTensorOp::fold(ArrayRef<Attribute>) {
1631   if (getResultType().hasStaticShape() && getResultType() == getSourceType() &&
1632       !nofold())
1633     return source();
1634   return {};
1635 }
1636 
1637 //===----------------------------------------------------------------------===//
1638 // YieldOp
1639 //===----------------------------------------------------------------------===//
1640 
1641 static void print(OpAsmPrinter &p, linalg::YieldOp op) {
1642   if (op.getNumOperands() > 0)
1643     p << ' ' << op.getOperands();
1644   p.printOptionalAttrDict(op->getAttrs());
1645   if (op.getNumOperands() > 0)
1646     p << " : " << op.getOperandTypes();
1647 }
1648 
1649 static ParseResult parseYieldOp(OpAsmParser &parser, OperationState &result) {
1650   SmallVector<OpAsmParser::OperandType, 2> opInfo;
1651   SmallVector<Type, 2> types;
1652   llvm::SMLoc loc = parser.getCurrentLocation();
1653   return failure(parser.parseOperandList(opInfo) ||
1654                  parser.parseOptionalAttrDict(result.attributes) ||
1655                  (!opInfo.empty() && parser.parseColonTypeList(types)) ||
1656                  parser.resolveOperands(opInfo, types, loc, result.operands));
1657 }
1658 
1659 // Check the operand number and types must match the element types of the
1660 // LinalgOp interface's shaped operands.
1661 static LogicalResult verifyYield(linalg::YieldOp op, LinalgOp linalgOp) {
1662   if (op.getNumOperands() != linalgOp.getNumOutputs())
1663     return op.emitOpError("expected number of yield values (")
1664            << linalgOp.getNumOutputs()
1665            << ") to match the number of operands of the enclosing "
1666            << "LinalgOp (" << op.getNumOperands() << ")";
1667 
1668   for (OpOperand &opOperand : op->getOpOperands()) {
1669     OpOperand *outputOperand =
1670         linalgOp.getOutputOperand(opOperand.getOperandNumber());
1671     Type elementType = getElementTypeOrSelf(outputOperand->get().getType());
1672     if (opOperand.get().getType() != elementType)
1673       return op.emitOpError("type of yield operand ")
1674              << (opOperand.getOperandNumber() + 1) << " ("
1675              << opOperand.get().getType() << ") doesn't match "
1676              << "the element type of the enclosing linalg.generic op ("
1677              << elementType << ")";
1678   }
1679   return success();
1680 }
1681 
1682 static LogicalResult verify(linalg::YieldOp op) {
1683   auto *parentOp = op->getParentOp();
1684   if (parentOp->getNumRegions() != 1 || parentOp->getRegion(0).empty())
1685     return op.emitOpError("expected single non-empty parent region");
1686 
1687   if (auto linalgOp = dyn_cast<LinalgOp>(parentOp))
1688     return verifyYield(op, cast<LinalgOp>(parentOp));
1689 
1690   if (auto padTensorOp = dyn_cast<linalg::PadTensorOp>(parentOp)) {
1691     if (op.getNumOperands() != 1)
1692       return op.emitOpError("expected single yield operand (got ")
1693              << op->getNumOperands() << ")";
1694     if (op.getOperand(0).getType() !=
1695         padTensorOp.getType().cast<ShapedType>().getElementType())
1696       return op.emitOpError("expected yield type to match shape element type");
1697     return success();
1698   }
1699 
1700   if (auto tiledLoopOp = dyn_cast<linalg::TiledLoopOp>(parentOp)) {
1701     // Check if output args with tensor types match results types.
1702     SmallVector<Value, 2> tensorOuts;
1703     llvm::copy_if(
1704         tiledLoopOp.outputs(), std::back_inserter(tensorOuts),
1705         [&](Value out) { return out.getType().isa<RankedTensorType>(); });
1706     if (tensorOuts.size() != op.values().size())
1707       return op.emitOpError("expected number of tensor output args = ")
1708              << tensorOuts.size() << " to match the number of yield operands = "
1709              << op.values().size();
1710 
1711     TypeRange tensorTypes(llvm::makeArrayRef(tensorOuts));
1712     for (auto &item :
1713          llvm::enumerate(llvm::zip(tensorTypes, op.getOperandTypes()))) {
1714       Type outType, resultType;
1715       unsigned index = item.index();
1716       std::tie(outType, resultType) = item.value();
1717       if (outType != resultType)
1718         return op.emitOpError("expected yield operand ")
1719                << index << " with type = " << resultType
1720                << " to match output arg type = " << outType;
1721     }
1722     return success();
1723   }
1724   return op.emitOpError("expected parent op with LinalgOp interface");
1725 }
1726 
1727 //===----------------------------------------------------------------------===//
1728 // TiledLoopOp
1729 //===----------------------------------------------------------------------===//
1730 
1731 void TiledLoopOp::build(OpBuilder &builder, OperationState &result,
1732                         ValueRange lowerBounds, ValueRange upperBounds,
1733                         ValueRange steps, ValueRange inputs, ValueRange outputs,
1734                         ArrayAttr iteratorTypes,
1735                         function_ref<void(OpBuilder &, Location, ValueRange,
1736                                           ValueRange, ValueRange)>
1737                             bodyBuilderFn) {
1738   build(builder, result, lowerBounds, upperBounds, steps, inputs, outputs,
1739         iteratorTypes, llvm::None, bodyBuilderFn);
1740 }
1741 
1742 void TiledLoopOp::build(OpBuilder &builder, OperationState &result,
1743                         ValueRange lowerBounds, ValueRange upperBounds,
1744                         ValueRange steps, ValueRange inputs, ValueRange outputs,
1745                         ArrayAttr iteratorTypes,
1746                         Optional<ArrayAttr> distributionTypes,
1747                         function_ref<void(OpBuilder &, Location, ValueRange,
1748                                           ValueRange, ValueRange)>
1749                             bodyBuilderFn) {
1750   result.addOperands(lowerBounds);
1751   result.addOperands(upperBounds);
1752   result.addOperands(steps);
1753   result.addOperands(inputs);
1754   result.addOperands(outputs);
1755   result.addAttribute(
1756       TiledLoopOp::getOperandSegmentSizeAttr(),
1757       builder.getI32VectorAttr({static_cast<int32_t>(lowerBounds.size()),
1758                                 static_cast<int32_t>(upperBounds.size()),
1759                                 static_cast<int32_t>(steps.size()),
1760                                 static_cast<int32_t>(inputs.size()),
1761                                 static_cast<int32_t>(outputs.size())}));
1762   result.addAttribute(getIteratorTypesAttrName(), iteratorTypes);
1763 
1764   if (distributionTypes.hasValue())
1765     result.addAttribute(getDistributionTypesAttrName(),
1766                         distributionTypes.getValue());
1767 
1768   // Add output types for `RankedTensorType` output arguments.
1769   for (Value output : outputs) {
1770     Type outputType = output.getType();
1771     if (outputType.isa<RankedTensorType>())
1772       result.addTypes(outputType);
1773   }
1774 
1775   OpBuilder::InsertionGuard guard(builder);
1776   unsigned numIVs = steps.size();
1777   SmallVector<Type, 8> argTypes(numIVs, builder.getIndexType());
1778   SmallVector<Location, 8> argLocs(numIVs, result.location);
1779   for (Value input : inputs) {
1780     argTypes.push_back(input.getType());
1781     argLocs.push_back(input.getLoc());
1782   }
1783   for (Value output : outputs) {
1784     argTypes.push_back(output.getType());
1785     argLocs.push_back(output.getLoc());
1786   }
1787   Region *bodyRegion = result.addRegion();
1788   Block *bodyBlock = builder.createBlock(bodyRegion, {}, argTypes, argLocs);
1789 
1790   if (bodyBuilderFn) {
1791     builder.setInsertionPointToStart(bodyBlock);
1792     bodyBuilderFn(builder, result.location,
1793                   bodyBlock->getArguments().take_front(numIVs),
1794                   bodyBlock->getArguments().slice(numIVs, inputs.size()),
1795                   bodyBlock->getArguments().take_back(outputs.size()));
1796     TiledLoopOp::ensureTerminator(*bodyRegion, builder, result.location);
1797   }
1798 }
1799 
1800 static void print(OpAsmPrinter &p, TiledLoopOp op) {
1801   p << " (" << op.getInductionVars() << ") = (" << op.lowerBound() << ") to ("
1802     << op.upperBound() << ") step (" << op.step() << ")";
1803 
1804   if (!op.inputs().empty()) {
1805     p << " ins (";
1806     llvm::interleaveComma(llvm::zip(op.getRegionInputArgs(), op.inputs()), p,
1807                           [&](auto it) {
1808                             p << std::get<0>(it) << " = " << std::get<1>(it)
1809                               << ": " << std::get<1>(it).getType();
1810                           });
1811     p << ")";
1812   }
1813   if (!op.outputs().empty()) {
1814     p << " outs (";
1815     llvm::interleaveComma(llvm::zip(op.getRegionOutputArgs(), op.outputs()), p,
1816                           [&](auto it) {
1817                             p << std::get<0>(it) << " = " << std::get<1>(it)
1818                               << ": " << std::get<1>(it).getType();
1819                           });
1820     p << ")";
1821   }
1822 
1823   if (llvm::any_of(op.iterator_types(), [](Attribute attr) {
1824         return attr.cast<StringAttr>().getValue() !=
1825                getParallelIteratorTypeName();
1826       }))
1827     p << " iterators" << op.iterator_types();
1828 
1829   if (op.distribution_types().hasValue())
1830     p << " distribution" << op.distribution_types().getValue();
1831 
1832   p << ' ';
1833   p.printRegion(op.region(), /*printEntryBlockArgs=*/false);
1834   p.printOptionalAttrDict(
1835       op->getAttrs(), /*elidedAttrs=*/{TiledLoopOp::getOperandSegmentSizeAttr(),
1836                                        getIteratorTypesAttrName(),
1837                                        getDistributionTypesAttrName()});
1838 }
1839 
1840 static ParseResult parseTiledLoopOp(OpAsmParser &parser,
1841                                     OperationState &result) {
1842   auto &builder = parser.getBuilder();
1843   // Parse an opening `(` followed by induction variables followed by `)`
1844   SmallVector<OpAsmParser::OperandType, 4> ivs;
1845   if (parser.parseRegionArgumentList(ivs, /*requiredOperandCount=*/-1,
1846                                      OpAsmParser::Delimiter::Paren))
1847     return failure();
1848 
1849   // Parse loop bounds.
1850   SmallVector<OpAsmParser::OperandType, 4> lower;
1851   if (parser.parseEqual() ||
1852       parser.parseOperandList(lower, ivs.size(),
1853                               OpAsmParser::Delimiter::Paren) ||
1854       parser.resolveOperands(lower, builder.getIndexType(), result.operands))
1855     return failure();
1856 
1857   SmallVector<OpAsmParser::OperandType, 4> upper;
1858   if (parser.parseKeyword("to") ||
1859       parser.parseOperandList(upper, ivs.size(),
1860                               OpAsmParser::Delimiter::Paren) ||
1861       parser.resolveOperands(upper, builder.getIndexType(), result.operands))
1862     return failure();
1863 
1864   // Parse step values.
1865   SmallVector<OpAsmParser::OperandType, 4> steps;
1866   if (parser.parseKeyword("step") ||
1867       parser.parseOperandList(steps, ivs.size(),
1868                               OpAsmParser::Delimiter::Paren) ||
1869       parser.resolveOperands(steps, builder.getIndexType(), result.operands))
1870     return failure();
1871 
1872   // Parse input tensors.
1873   SmallVector<OpAsmParser::OperandType, 4> inputs, inputRegionArgs;
1874   SmallVector<Type, 4> inputTypes;
1875   if (succeeded(parser.parseOptionalKeyword("ins"))) {
1876     llvm::SMLoc inputsOperandsLoc = parser.getCurrentLocation();
1877 
1878     if (parser.parseAssignmentListWithTypes(inputRegionArgs, inputs,
1879                                             inputTypes))
1880       return failure();
1881 
1882     if (parser.resolveOperands(inputs, inputTypes, inputsOperandsLoc,
1883                                result.operands))
1884       return failure();
1885   }
1886 
1887   // Parse output tensors.
1888   SmallVector<OpAsmParser::OperandType, 4> outputs, outputRegionArgs;
1889   SmallVector<Type, 4> outputTypes;
1890   if (succeeded(parser.parseOptionalKeyword("outs"))) {
1891     llvm::SMLoc outputsOperandsLoc = parser.getCurrentLocation();
1892 
1893     if (parser.parseAssignmentListWithTypes(outputRegionArgs, outputs,
1894                                             outputTypes))
1895       return failure();
1896 
1897     if (parser.resolveOperands(outputs, outputTypes, outputsOperandsLoc,
1898                                result.operands))
1899       return failure();
1900     for (Type outputType : outputTypes)
1901       if (outputType.isa<RankedTensorType>())
1902         result.addTypes(outputType);
1903   }
1904 
1905   // Parse attributes.
1906   SmallVector<Attribute, 4> iterTypes, distributionTypes;
1907   auto parseAttr = [&](StringRef keyword, SmallVector<Attribute, 4> *attrs) {
1908     if (succeeded(parser.parseOptionalKeyword(keyword))) {
1909       StringAttr attr;
1910 
1911       if (parser.parseLSquare() || parser.parseAttribute(attr))
1912         return failure();
1913       attrs->push_back(attr);
1914       for (int i = 1, e = ivs.size(); i < e; ++i) {
1915         if (parser.parseComma() || parser.parseAttribute(attr))
1916           return failure();
1917         attrs->push_back(attr);
1918       }
1919       if (parser.parseRSquare())
1920         return failure();
1921     }
1922     return success();
1923   };
1924   if (failed(parseAttr("iterators", &iterTypes)) ||
1925       failed(parseAttr("distribution", &distributionTypes)))
1926     return failure();
1927 
1928   // Set all loop iterator types to "parallel" if they are not printed in IR.
1929   if (iterTypes.empty()) {
1930     auto parallelIter = builder.getStringAttr(getParallelIteratorTypeName());
1931     iterTypes = SmallVector<Attribute, 4>(ivs.size(), parallelIter);
1932   }
1933   result.addAttribute(getIteratorTypesAttrName(),
1934                       builder.getArrayAttr(iterTypes));
1935   if (!distributionTypes.empty())
1936     result.addAttribute(getDistributionTypesAttrName(),
1937                         builder.getArrayAttr(distributionTypes));
1938   result.addAttribute(
1939       TiledLoopOp::getOperandSegmentSizeAttr(),
1940       builder.getI32VectorAttr({static_cast<int32_t>(lower.size()),
1941                                 static_cast<int32_t>(upper.size()),
1942                                 static_cast<int32_t>(steps.size()),
1943                                 static_cast<int32_t>(inputs.size()),
1944                                 static_cast<int32_t>(outputs.size())}));
1945 
1946   // Parse the body.
1947   Region *body = result.addRegion();
1948 
1949   SmallVector<Type, 4> regionTypes(ivs.size(), builder.getIndexType());
1950   regionTypes.append(inputTypes);
1951   regionTypes.append(outputTypes);
1952 
1953   SmallVector<OpAsmParser::OperandType, 4> regionArgs(ivs);
1954   regionArgs.append(inputRegionArgs);
1955   regionArgs.append(outputRegionArgs);
1956 
1957   if (parser.parseRegion(*body, regionArgs, regionTypes))
1958     return failure();
1959 
1960   // Parse optional attributes.
1961   parser.parseOptionalAttrDict(result.attributes);
1962 
1963   return success();
1964 }
1965 
1966 Region &TiledLoopOp::getLoopBody() { return region(); }
1967 
1968 LogicalResult TiledLoopOp::moveOutOfLoop(ArrayRef<Operation *> ops) {
1969   for (auto *op : ops)
1970     op->moveBefore(*this);
1971   return success();
1972 }
1973 
1974 bool TiledLoopOp::isDefinedOutsideOfLoop(Value value) {
1975   return !region().isAncestor(value.getParentRegion());
1976 }
1977 
1978 static LogicalResult verify(TiledLoopOp op) {
1979   // Check if iterator types are provided for every loop dimension.
1980   if (op.iterator_types().size() != op.getNumLoops())
1981     return op.emitOpError("expected iterator types array attribute size = ")
1982            << op.iterator_types().size()
1983            << " to match the number of loops = " << op.getNumLoops();
1984 
1985   // Check if types of input arguments match region args types.
1986   for (auto &item :
1987        llvm::enumerate(llvm::zip(op.inputs(), op.getRegionInputArgs()))) {
1988     Value input, inputRegionArg;
1989     unsigned index = item.index();
1990     std::tie(input, inputRegionArg) = item.value();
1991     if (input.getType() != inputRegionArg.getType())
1992       return op.emitOpError("expected input arg ")
1993              << index << " with type = " << input.getType()
1994              << " to match region arg " << index + op.getNumLoops()
1995              << " type = " << inputRegionArg.getType();
1996   }
1997 
1998   // Check if types of input arguments match region args types.
1999   for (auto &item :
2000        llvm::enumerate(llvm::zip(op.outputs(), op.getRegionOutputArgs()))) {
2001     Value output, outputRegionArg;
2002     unsigned index = item.index();
2003     std::tie(output, outputRegionArg) = item.value();
2004     if (output.getType() != outputRegionArg.getType())
2005       return op.emitOpError("expected output arg ")
2006              << index << " with type = " << output.getType()
2007              << " to match region arg "
2008              << index + op.getNumLoops() + op.inputs().size()
2009              << " type = " << outputRegionArg.getType();
2010   }
2011   return success();
2012 }
2013 
2014 namespace {
2015 
2016 static constexpr int64_t kNoMatch = -1;
2017 
2018 // Folds away TiledLoopOp inputs if they have no uses within the body.
2019 //
2020 // Example:
2021 //
2022 // %0 = linalg.tiled_loop ...  ins (%in_ = %in: tensor<...>,
2023 //                                  %in_buf_ = %in_buf: memref<...>) {...}
2024 // Becomes
2025 //
2026 // linalg.tiled_loop ...  ins (%in_buf_ = %in_buf: memref<...>) {...}
2027 struct TiledLoopInputsFolder : public OpRewritePattern<linalg::TiledLoopOp> {
2028   using OpRewritePattern<linalg::TiledLoopOp>::OpRewritePattern;
2029 
2030   LogicalResult matchAndRewrite(linalg::TiledLoopOp tiledLoop,
2031                                 PatternRewriter &rewriter) const final {
2032     SmallVector<Value, 2> newInputs, regionInputTensorArgs;
2033     // Store ids of the corresponding old and new input operands.
2034     SmallVector<int64_t, 2> oldInputIdToNew(tiledLoop.inputs().size(),
2035                                             kNoMatch);
2036     for (const auto &en : llvm::enumerate(
2037              llvm::zip(tiledLoop.inputs(), tiledLoop.getRegionInputArgs()))) {
2038       Value in, bbArg;
2039       size_t index = en.index();
2040       std::tie(in, bbArg) = en.value();
2041       if (!bbArg.use_empty()) {
2042         oldInputIdToNew[index] = newInputs.size();
2043         newInputs.push_back(in);
2044       }
2045     }
2046     if (newInputs.size() == tiledLoop.inputs().size())
2047       return failure();
2048     Location loc = tiledLoop.getLoc();
2049     auto newTiledLoop = rewriter.create<TiledLoopOp>(
2050         loc, tiledLoop.lowerBound(), tiledLoop.upperBound(), tiledLoop.step(),
2051         newInputs, tiledLoop.outputs(), tiledLoop.iterator_types(),
2052         tiledLoop.distribution_types());
2053 
2054     // Clone the region.
2055     BlockAndValueMapping bvm;
2056     bvm.map(tiledLoop.getInductionVars(), newTiledLoop.getInductionVars());
2057     bvm.map(tiledLoop.getRegionOutputArgs(),
2058             newTiledLoop.getRegionOutputArgs());
2059     for (const auto &en : llvm::enumerate(oldInputIdToNew))
2060       if (en.value() != kNoMatch)
2061         bvm.map(tiledLoop.getRegionInputArgs()[en.index()],
2062                 newTiledLoop.getRegionInputArgs()[en.value()]);
2063     OpBuilder innerBuilder =
2064         OpBuilder::atBlockEnd(newTiledLoop.getBody(), rewriter.getListener());
2065     for (auto &op : *tiledLoop.getBody())
2066       innerBuilder.clone(op, bvm);
2067     rewriter.replaceOp(tiledLoop, newTiledLoop.getResults());
2068 
2069     return success();
2070   }
2071 };
2072 
2073 } // namespace
2074 
2075 /// A simple, conservative analysis to determine if the loop is shape
2076 /// conserving. I.e., the type of the arg-th yielded value is the same as the
2077 /// type of the corresponding basic block argument of the loop.
2078 /// Note: This function handles only simple cases. Expand as needed.
2079 static bool isShapePreserving(TiledLoopOp loopOp, int64_t arg) {
2080   auto yieldOp = cast<YieldOp>(loopOp.getLoopBody().front().getTerminator());
2081   if (yieldOp.values().empty())
2082     // Tiled loop either has no outputs or is a "memref-based version". In
2083     // either case, the loop is shape conserving.
2084     return true;
2085   assert(arg < static_cast<int64_t>(yieldOp.values().size()) &&
2086          "arg is out of bounds");
2087   Value value = yieldOp.values()[arg];
2088   while (value) {
2089     if (value == loopOp.getRegionOutputArgs()[arg])
2090       return true;
2091     OpResult opResult = value.dyn_cast<OpResult>();
2092     if (!opResult)
2093       return false;
2094 
2095     using tensor::InsertSliceOp;
2096     value = llvm::TypeSwitch<Operation *, Value>(opResult.getOwner())
2097                 .template Case<InsertSliceOp>(
2098                     [&](InsertSliceOp op) { return op.dest(); })
2099                 .template Case<TiledLoopOp>([&](TiledLoopOp loopOp) {
2100                   return isShapePreserving(loopOp, opResult.getResultNumber())
2101                              ? loopOp.outputs()[opResult.getResultNumber()]
2102                              : Value();
2103                 })
2104                 .Default([&](auto op) { return Value(); });
2105   }
2106   return false;
2107 }
2108 
2109 namespace {
2110 
2111 /// Fold dim(x) where `x` is an input/output argument of a TiledLoopOp block
2112 /// to dim(y) where `y` is the initial input/output value of the argument.
2113 ///
2114 /// E.g.:
2115 /// %y = ... : tensor<...>
2116 /// linalg.tiled_loop ... ins(%x = %y : tensor<...>) {
2117 ///   tensor.dim %x, %c0 : tensor<...>
2118 /// }
2119 ///
2120 /// is folded to:
2121 /// %y = ... : tensor<...>
2122 /// linalg.tiled_loop ... ins(%x = %y : tensor<...>) {
2123 ///   tensor.dim %y, %c0 : tensor<...>
2124 /// }
2125 ///
2126 /// Note: Dim ops are folded only if it can be proven that the runtime type of
2127 /// the yielded value (in case of outputs) does not change with loop iterations.
2128 template <typename OpTy>
2129 struct DimOfTiledLoopInsOutsFolder : public OpRewritePattern<OpTy> {
2130   using OpRewritePattern<OpTy>::OpRewritePattern;
2131 
2132   LogicalResult matchAndRewrite(OpTy dimOp,
2133                                 PatternRewriter &rewriter) const final {
2134     auto src = dimOp.source().template dyn_cast<BlockArgument>();
2135     if (!src)
2136       return failure();
2137     auto loopOp =
2138         dyn_cast<TiledLoopOp>(src.getOwner()->getParent()->getParentOp());
2139     if (!loopOp)
2140       return failure();
2141     unsigned numLoops = loopOp.getNumLoops();
2142     unsigned numInputArgs = loopOp.getRegionInputArgs().size();
2143     if (src.getArgNumber() >= numInputArgs + numLoops &&
2144         !isShapePreserving(loopOp,
2145                            src.getArgNumber() - numInputArgs - numLoops))
2146       return failure();
2147 
2148     auto inputArgs = loopOp.getRegionInputArgs();
2149     auto it1 = llvm::find(inputArgs, src);
2150     if (it1 != inputArgs.end()) {
2151       rewriter.updateRootInPlace(dimOp, [&] {
2152         dimOp.sourceMutable().assign(loopOp.inputs()[it1 - inputArgs.begin()]);
2153       });
2154       return success();
2155     }
2156 
2157     auto outputArgs = loopOp.getRegionOutputArgs();
2158     auto it2 = llvm::find(outputArgs, src);
2159     if (it2 != outputArgs.end()) {
2160       rewriter.updateRootInPlace(dimOp, [&] {
2161         dimOp.sourceMutable().assign(
2162             loopOp.outputs()[it2 - outputArgs.begin()]);
2163       });
2164       return success();
2165     }
2166 
2167     return failure();
2168   }
2169 };
2170 
2171 /// Fold dim(r) where `r` is the result of a TiledLoopOp to dim(y) where `y`
2172 /// is the initial output value of the loop.
2173 ///
2174 /// E.g.:
2175 /// %y = ... : tensor<...>
2176 /// %r = linalg.tiled_loop ... outs(%i = %y : tensor<...>) {
2177 ///   ...
2178 /// }
2179 /// %0 = tensor.dim %r, %c0 : tensor<...>
2180 ///
2181 /// is folded to:
2182 /// %y = ... : tensor<...>
2183 /// linalg.tiled_loop ... outs(%i = %y : tensor<...>) {
2184 ///   ...
2185 /// }
2186 /// %0 = tensor.dim %y, %c0 : tensor<...>
2187 ///
2188 /// Note: Dim ops are folded only if it can be proven that the runtime type of
2189 /// the yielded value (in case of outputs) does not change with loop iterations.
2190 template <typename OpTy>
2191 struct DimOfTiledLoopResultFolder : public OpRewritePattern<OpTy> {
2192   using OpRewritePattern<OpTy>::OpRewritePattern;
2193 
2194   LogicalResult matchAndRewrite(OpTy dimOp,
2195                                 PatternRewriter &rewriter) const final {
2196     auto loopOp = dimOp.source().template getDefiningOp<TiledLoopOp>();
2197     if (!loopOp)
2198       return failure();
2199     auto opResult = dimOp.source().template cast<OpResult>();
2200     unsigned resultNumber = opResult.getResultNumber();
2201     if (!isShapePreserving(loopOp, resultNumber))
2202       return failure();
2203     rewriter.updateRootInPlace(dimOp, [&]() {
2204       dimOp.sourceMutable().assign(loopOp.outputs()[resultNumber]);
2205     });
2206     return success();
2207   }
2208 };
2209 
2210 // Folds away TiledLoopOp output tensors when the following conditions are met:
2211 // * result of `linalg.tiled_loop` has no uses
2212 // * output tensor is the argument of `linalg.yield`
2213 //
2214 // Example:
2215 //
2216 // %0 = linalg.tiled_loop ...  outs (%o_ = %out: tensor<...>,
2217 //                                   %obuf_ = %out_buf: memref<...>) {
2218 //   ...
2219 //   linalg.yield %o_ : tensor ...
2220 // }
2221 //
2222 // Becomes
2223 //
2224 // linalg.tiled_loop ...  outs (%obuf_ = %out_buf: memref<...>) {
2225 //   ...
2226 //   linalg.yield
2227 // }
2228 struct TiledLoopResultsFolder : public OpRewritePattern<linalg::TiledLoopOp> {
2229   using OpRewritePattern<linalg::TiledLoopOp>::OpRewritePattern;
2230 
2231   LogicalResult matchAndRewrite(linalg::TiledLoopOp tiledLoop,
2232                                 PatternRewriter &rewriter) const final {
2233     if (tiledLoop.getNumResults() == 0)
2234       return failure();
2235 
2236     Block *block = tiledLoop.getBody();
2237     auto yieldOp = cast<linalg::YieldOp>(block->getTerminator());
2238 
2239     // Match the pattern and collect output buffers that will replace the output
2240     // tensors and also the ops that will be ignored when cloning the body.
2241     SmallVector<Value, 2> newOutputOperands, newYieldArgs;
2242     int resultId = 0;
2243     // Store ids of the corresponding old and new output operands.
2244     SmallVector<int64_t, 2> oldOutputIdToNew(tiledLoop.outputs().size(),
2245                                              kNoMatch);
2246     // Store ids of the corresponding old and new results.
2247     SmallVector<int64_t, 2> oldResultIdToNew(tiledLoop.getNumResults(),
2248                                              kNoMatch);
2249     SmallVector<Value, 2> resultReplacement(tiledLoop.getNumResults());
2250     for (const auto &en : llvm::enumerate(
2251              llvm::zip(tiledLoop.outputs(), tiledLoop.getRegionOutputArgs()))) {
2252       size_t index = en.index();
2253       Value out = std::get<0>(en.value());
2254       Value outRegionArg = std::get<1>(en.value());
2255 
2256       if (!out.getType().isa<RankedTensorType>()) {
2257         oldOutputIdToNew[index] = newOutputOperands.size();
2258         newOutputOperands.push_back(out);
2259         continue;
2260       }
2261       Value result = tiledLoop.getResult(resultId);
2262       Value yieldArg = yieldOp.getOperand(resultId);
2263       if (yieldArg != outRegionArg || !result.use_empty()) {
2264         oldOutputIdToNew[index] = newOutputOperands.size();
2265         oldResultIdToNew[resultId] = newYieldArgs.size();
2266         resultReplacement[resultId] = out;
2267         newOutputOperands.push_back(out);
2268         newYieldArgs.push_back(yieldArg);
2269       }
2270       ++resultId;
2271     }
2272     if (newOutputOperands.size() == tiledLoop.outputs().size())
2273       return failure();
2274 
2275     Location loc = tiledLoop.getLoc();
2276     auto newTiledLoop = rewriter.create<TiledLoopOp>(
2277         loc, tiledLoop.lowerBound(), tiledLoop.upperBound(), tiledLoop.step(),
2278         tiledLoop.inputs(), newOutputOperands, tiledLoop.iterator_types(),
2279         tiledLoop.distribution_types());
2280 
2281     // Clone the region.
2282     BlockAndValueMapping bvm;
2283     bvm.map(tiledLoop.getInductionVars(), newTiledLoop.getInductionVars());
2284     bvm.map(tiledLoop.getRegionInputArgs(), newTiledLoop.getRegionInputArgs());
2285     for (const auto &en : llvm::enumerate(oldOutputIdToNew)) {
2286       if (en.value() != kNoMatch)
2287         bvm.map(tiledLoop.getRegionOutputArgs()[en.index()],
2288                 newTiledLoop.getRegionOutputArgs()[en.value()]);
2289       else
2290         bvm.map(tiledLoop.getRegionOutputArgs()[en.index()],
2291                 tiledLoop.outputs()[en.index()]);
2292     }
2293     OpBuilder innerBuilder =
2294         OpBuilder::atBlockEnd(newTiledLoop.getBody(), rewriter.getListener());
2295     for (auto &op : tiledLoop.getBody()->without_terminator())
2296       innerBuilder.clone(op, bvm);
2297     innerBuilder.create<linalg::YieldOp>(
2298         loc, llvm::to_vector<2>(llvm::map_range(
2299                  newYieldArgs, [&](Value arg) { return bvm.lookup(arg); })));
2300 
2301     for (const auto &en : llvm::enumerate(oldResultIdToNew))
2302       if (en.value() != kNoMatch)
2303         resultReplacement[en.index()] = newTiledLoop.getResult(en.value());
2304     rewriter.replaceOp(tiledLoop, resultReplacement);
2305 
2306     return success();
2307   }
2308 };
2309 } // namespace
2310 
2311 void TiledLoopOp::getCanonicalizationPatterns(OwningRewritePatternList &results,
2312                                               MLIRContext *context) {
2313   results.insert<TiledLoopInputsFolder, TiledLoopResultsFolder,
2314                  DimOfTiledLoopInsOutsFolder<tensor::DimOp>,
2315                  DimOfTiledLoopInsOutsFolder<memref::DimOp>,
2316                  DimOfTiledLoopResultFolder<tensor::DimOp>,
2317                  DimOfTiledLoopResultFolder<memref::DimOp>>(context);
2318 }
2319 
2320 LogicalResult TiledLoopOp::fold(ArrayRef<Attribute>,
2321                                 SmallVectorImpl<OpFoldResult> &) {
2322   return foldMemRefCastInTiledLoopOp(*this);
2323 }
2324 
2325 //===----------------------------------------------------------------------===//
2326 // IndexOp
2327 //===----------------------------------------------------------------------===//
2328 
2329 static LogicalResult verify(IndexOp op) {
2330   auto linalgOp = dyn_cast<LinalgOp>(op->getParentOp());
2331   if (!linalgOp)
2332     return op.emitOpError("expected parent op with LinalgOp interface");
2333   if (linalgOp.getNumLoops() <= op.dim())
2334     return op.emitOpError("expected dim (")
2335            << op.dim() << ") to be lower than the number of loops ("
2336            << linalgOp.getNumLoops() << ") of the enclosing LinalgOp";
2337   return success();
2338 }
2339 
2340 /////// Operations corresponding to library calls defined with Tablegen ////////
2341 
2342 #include "mlir/Dialect/Linalg/IR/LinalgNamedStructuredOps.yamlgen.cpp.inc"
2343 
2344 #define GET_OP_CLASSES
2345 #include "mlir/Dialect/Linalg/IR/LinalgOps.cpp.inc"
2346 
2347 #define GET_OP_CLASSES
2348 #include "mlir/Dialect/Linalg/IR/LinalgStructuredOps.cpp.inc"
2349 
2350 /// Return the dims that are `iteratorTypeName` loops in the LinalgOp `op`.
2351 /// Assumes `op` is a LinalgOp.
2352 void mlir::linalg::getDimsOfType(Operation *op, StringRef iteratorTypeName,
2353                                  SmallVectorImpl<unsigned> &res) {
2354   if (!cast<LinalgOp>(op).iterator_types())
2355     return;
2356 
2357   unsigned dim = 0;
2358   for (auto tn :
2359        cast<LinalgOp>(op).iterator_types().getAsValueRange<StringAttr>()) {
2360     if (tn == iteratorTypeName)
2361       res.push_back(dim);
2362     ++dim;
2363   }
2364 }
2365 
2366 AffineMap mlir::linalg::extractOrIdentityMap(Optional<AffineMap> maybeMap,
2367                                              unsigned rank,
2368                                              MLIRContext *context) {
2369   if (maybeMap)
2370     return maybeMap.getValue();
2371   if (rank == 0)
2372     return AffineMap::get(context);
2373   return AffineMap::getMultiDimIdentityMap(rank, context);
2374 }
2375 
2376 SmallVector<AffineExpr, 4>
2377 mlir::linalg::makeAffineDimExprs(unsigned num, unsigned &startIdx,
2378                                  MLIRContext *context) {
2379   SmallVector<AffineExpr, 4> res;
2380   res.reserve(num);
2381   for (unsigned i = 0; i < num; ++i)
2382     res.push_back(getAffineDimExpr(startIdx++, context));
2383   return res;
2384 }
2385 
2386 SmallVector<AffineExpr, 4> mlir::linalg::concat(ArrayRef<AffineExpr> a,
2387                                                 ArrayRef<AffineExpr> b) {
2388   auto rangeA = llvm::make_range(a.begin(), a.end());
2389   auto rangeB = llvm::make_range(b.begin(), b.end());
2390   auto concatRanges = llvm::concat<const AffineExpr>(rangeA, rangeB);
2391   return llvm::to_vector<4>(concatRanges);
2392 }
2393 
2394 static void appendMangledType(llvm::raw_string_ostream &ss, Type t) {
2395   if (auto memref = t.dyn_cast<MemRefType>()) {
2396     ss << "view";
2397     for (auto size : memref.getShape())
2398       if (size < 0)
2399         ss << "sx";
2400       else
2401         ss << size << "x";
2402     appendMangledType(ss, memref.getElementType());
2403   } else if (auto vec = t.dyn_cast<VectorType>()) {
2404     ss << "vector";
2405     llvm::interleave(
2406         vec.getShape(), [&](int64_t i) { ss << i; }, [&]() { ss << "x"; });
2407     appendMangledType(ss, vec.getElementType());
2408   } else if (t.isSignlessIntOrIndexOrFloat()) {
2409     ss << t;
2410   } else {
2411     llvm_unreachable("Invalid type for linalg library name mangling");
2412   }
2413 }
2414 
2415 std::string mlir::linalg::generateLibraryCallName(Operation *op) {
2416   assert(isa<LinalgOp>(op));
2417   std::string name(op->getName().getStringRef().str());
2418   name.reserve(128);
2419   std::replace(name.begin(), name.end(), '.', '_');
2420   llvm::raw_string_ostream ss(name);
2421   ss << "_";
2422   auto types = op->getOperandTypes();
2423   llvm::interleave(
2424       types.begin(), types.end(), [&](Type t) { appendMangledType(ss, t); },
2425       [&]() { ss << "_"; });
2426   return ss.str();
2427 }
2428 
2429 //===----------------------------------------------------------------------===//
2430 // Support for named Linalg ops defined in ods-gen.
2431 //===----------------------------------------------------------------------===//
2432 
2433 /// Generic entry point to create the block for the region of a LinalgOp.
2434 /// This is used by both named structured ops created by ods-gen and by manually
2435 /// defined C++ ops.
2436 /// This is used by both builders and parsers.
2437 /// This function creates the block in the region with arguments corresponding
2438 /// to the elemental types of `inputTypes` and `outputTypes`, which are asserted
2439 /// to be ShapedType.
2440 template <typename NamedStructuredOpType>
2441 static void fillStructuredOpRegion(
2442     OpBuilder &opBuilder, Region &region, TypeRange inputTypes,
2443     TypeRange outputTypes,
2444     llvm::function_ref<void(unsigned, unsigned)> errorHandler) {
2445   assert(llvm::all_of(outputTypes, [](Type t) { return t.isa<ShapedType>(); }));
2446 
2447   // TODO: atm all operands go through getElementTypeOrSelf,
2448   // reconsider when we have evidence we need to.
2449   SmallVector<Type, 8> argTypes;
2450   SmallVector<Location, 8> argLocs;
2451   for (auto containers : {inputTypes, outputTypes}) {
2452     for (auto t : containers) {
2453       argTypes.push_back(getElementTypeOrSelf(t));
2454 
2455       // TODO: Pass in a proper location here.
2456       argLocs.push_back(opBuilder.getUnknownLoc());
2457     }
2458   }
2459 
2460   // RAII.
2461   OpBuilder::InsertionGuard guard(opBuilder);
2462   Block *body =
2463       opBuilder.createBlock(&region, /*insertPt=*/{}, argTypes, argLocs);
2464   unsigned actual = body->getNumArguments();
2465   unsigned expected = NamedStructuredOpType::getNumRegionArgs();
2466   if (expected != actual) {
2467     if (errorHandler)
2468       errorHandler(expected, actual);
2469     return;
2470   }
2471 
2472   opBuilder.setInsertionPointToStart(body);
2473   ImplicitLocOpBuilder b(opBuilder.getUnknownLoc(), opBuilder);
2474   NamedStructuredOpType::regionBuilder(b, *body);
2475 
2476   // indexing_maps is an auto-generated method.
2477 
2478   // iterator_types is an auto-generated method.
2479 }
2480 
2481 /// Generic entry point to create both the region and the block of a LinalgOp.
2482 template <typename NamedStructuredOpType>
2483 void createAndFillStructuredOpRegion(OpBuilder &opBuilder,
2484                                      OperationState &result,
2485                                      TypeRange inputTypes,
2486                                      TypeRange outputTypes) {
2487   Region &region = *result.addRegion();
2488   fillStructuredOpRegion<NamedStructuredOpType>(
2489       opBuilder, region, inputTypes, outputTypes,
2490       [&](unsigned expected, unsigned actual) {
2491         assert(expected != actual && "incorrect number of arguments");
2492       });
2493 }
2494 
2495 /// Common parsing used for both named structured ops created by ods-gen and by
2496 /// manually defined C++ ops. Does not handle regions.
2497 static ParseResult
2498 parseCommonStructuredOpParts(OpAsmParser &parser, OperationState &result,
2499                              SmallVectorImpl<Type> &inputTypes,
2500                              SmallVectorImpl<Type> &outputTypes) {
2501   llvm::SMLoc inputsOperandsLoc, outputsOperandsLoc;
2502   SmallVector<OpAsmParser::OperandType, 4> inputsOperands, outputsOperands;
2503 
2504   parser.parseOptionalAttrDict(result.attributes);
2505 
2506   if (succeeded(parser.parseOptionalKeyword("ins"))) {
2507     if (parser.parseLParen())
2508       return failure();
2509 
2510     inputsOperandsLoc = parser.getCurrentLocation();
2511     if (parser.parseOperandList(inputsOperands) ||
2512         parser.parseColonTypeList(inputTypes) || parser.parseRParen())
2513       return failure();
2514   }
2515 
2516   if (succeeded(parser.parseOptionalKeyword("outs"))) {
2517     outputsOperandsLoc = parser.getCurrentLocation();
2518     if (parser.parseLParen() || parser.parseOperandList(outputsOperands) ||
2519         parser.parseColonTypeList(outputTypes) || parser.parseRParen())
2520       return failure();
2521   }
2522 
2523   if (parser.resolveOperands(inputsOperands, inputTypes, inputsOperandsLoc,
2524                              result.operands) ||
2525       parser.resolveOperands(outputsOperands, outputTypes, outputsOperandsLoc,
2526                              result.operands))
2527     return failure();
2528 
2529   result.addAttribute("operand_segment_sizes",
2530                       parser.getBuilder().getI32VectorAttr(
2531                           {static_cast<int32_t>(inputsOperands.size()),
2532                            static_cast<int32_t>(outputsOperands.size())}));
2533   return success();
2534 }
2535 
2536 template <typename NamedStructuredOpType>
2537 static void printCommonStructuredOpParts(OpAsmPrinter &p,
2538                                          NamedStructuredOpType op) {
2539   if (!op.inputs().empty())
2540     p << " ins(" << op.inputs() << " : " << op.inputs().getTypes() << ")";
2541   if (!op.outputs().empty())
2542     p << " outs(" << op.outputs() << " : " << op.outputs().getTypes() << ")";
2543 }
2544 
2545 //===----------------------------------------------------------------------===//
2546 // Specific parsing and printing for named structured ops created by ods-gen.
2547 //===----------------------------------------------------------------------===//
2548 
2549 template <typename NamedStructuredOpType>
2550 static ParseResult
2551 parseNamedStructuredOpRegion(OpAsmParser &parser, Region &region,
2552                              TypeRange inputTypes, TypeRange outputTypes) {
2553   ParseResult res = success();
2554   OpBuilder opBuilder(parser.getContext());
2555   // Resolve `captures` into `capturedValues` at parse time so we can build the
2556   // region with captures.
2557   SmallVector<Value> capturedValues;
2558   fillStructuredOpRegion<NamedStructuredOpType>(
2559       opBuilder, region, inputTypes, outputTypes,
2560       [&](unsigned expected, unsigned actual) {
2561         res = parser.emitError(
2562             parser.getCurrentLocation(),
2563             llvm::formatv("[parseNamedStructuredOpRegion] ods-gen generated "
2564                           "region expects {0} args, got {1}",
2565                           expected, actual));
2566         region.front().dump();
2567       });
2568   return res;
2569 }
2570 
2571 static ParseResult
2572 parseNamedStructuredOpResults(OpAsmParser &parser,
2573                               SmallVectorImpl<Type> &resultTypes) {
2574   if (parser.parseOptionalArrowTypeList(resultTypes))
2575     return failure();
2576   return success();
2577 }
2578 
2579 template <typename NamedStructuredOpType>
2580 static ParseResult parseNamedStructuredOp(OpAsmParser &parser,
2581                                           OperationState &result) {
2582   // TODO: Enable when ods-gen supports captures.
2583   SmallVector<Type, 1> inputTypes, outputTypes;
2584   if (parseCommonStructuredOpParts(parser, result, inputTypes, outputTypes))
2585     return failure();
2586 
2587   // TODO: consider merging results parsing into region parsing.
2588   // Need to wait for declarative assembly resolution to decide.
2589   SmallVector<Type, 1> outputTensorsTypes;
2590   if (parseNamedStructuredOpResults(parser, outputTensorsTypes))
2591     return failure();
2592   result.addTypes(outputTensorsTypes);
2593 
2594   std::unique_ptr<Region> region = std::make_unique<Region>();
2595   if (parseNamedStructuredOpRegion<NamedStructuredOpType>(
2596           parser, *region, inputTypes, outputTypes))
2597     return failure();
2598   result.addRegion(std::move(region));
2599 
2600   return success();
2601 }
2602 
2603 static void printNamedStructuredOpResults(OpAsmPrinter &p,
2604                                           TypeRange resultTypes) {
2605   if (resultTypes.empty())
2606     return;
2607   p.printOptionalArrowTypeList(resultTypes);
2608 }
2609 
2610 template <typename NamedStructuredOpType>
2611 static void printNamedStructuredOp(OpAsmPrinter &p, NamedStructuredOpType op) {
2612   p.printOptionalAttrDict(
2613       op->getAttrs(),
2614       /*elidedAttrs=*/{"operand_segment_sizes",
2615                        // See generated code in mlir-linalg-yaml-gen.cpp
2616                        "linalg.memoized_indexing_maps"});
2617 
2618   // Printing is shared with generic ops, except for the region and
2619   // attributes.
2620   printCommonStructuredOpParts(p, op);
2621 
2622   // Results printing.
2623   printNamedStructuredOpResults(p, op.result_tensors().getTypes());
2624 
2625   // Region is elided.
2626 }
2627 
2628 template <typename NamedStructuredOpType>
2629 static LogicalResult verifyNamedStructuredOp(NamedStructuredOpType op) {
2630   return verifyGenericOp<NamedStructuredOpType>(op);
2631 }
2632 
2633 //===----------------------------------------------------------------------===//
2634 // Canonicalizers and Folders.
2635 //===----------------------------------------------------------------------===//
2636 
2637 namespace {
2638 struct EraseDeadLinalgOp : public OpInterfaceRewritePattern<LinalgOp> {
2639   using OpInterfaceRewritePattern<LinalgOp>::OpInterfaceRewritePattern;
2640 
2641   LogicalResult matchAndRewrite(LinalgOp op,
2642                                 PatternRewriter &rewriter) const override {
2643     for (OpOperand *opOperand : op.getInputAndOutputOperands()) {
2644       // Linalg "inputs" may be either tensor or memref type.
2645       // tensor<0xelt_type> is a convention that may not always mean
2646       // "0 iterations". Only erase in cases we see memref<...x0x...>.
2647       auto mt = opOperand->get().getType().dyn_cast<MemRefType>();
2648       if (!mt)
2649         continue;
2650       if (llvm::is_contained(op.getShape(opOperand), 0)) {
2651         rewriter.eraseOp(op);
2652         return success();
2653       }
2654     }
2655     return failure();
2656   }
2657 };
2658 
2659 struct FoldTensorCastOp : public OpInterfaceRewritePattern<LinalgOp> {
2660   using OpInterfaceRewritePattern<LinalgOp>::OpInterfaceRewritePattern;
2661 
2662   LogicalResult matchAndRewrite(LinalgOp op,
2663                                 PatternRewriter &rewriter) const override {
2664     // If no operand comes from a tensor::CastOp and can be folded then fail.
2665     bool hasTensorCastOperand =
2666         llvm::any_of(op.getInputAndOutputOperands(), [&](OpOperand *opOperand) {
2667           if (opOperand->get().isa<BlockArgument>())
2668             return false;
2669           auto castOp = opOperand->get().getDefiningOp<tensor::CastOp>();
2670           return castOp && canFoldIntoConsumerOp(castOp);
2671         });
2672     if (!hasTensorCastOperand)
2673       return failure();
2674 
2675     SmallVector<Type, 4> newResultTypes;
2676     newResultTypes.reserve(op->getNumResults());
2677     SmallVector<Value, 4> newOperands;
2678     newOperands.reserve(op->getNumOperands());
2679     // Inputs may fold.
2680     for (OpOperand *opOperand : op.getInputOperands()) {
2681       auto tensorCastOp = opOperand->get().getDefiningOp<tensor::CastOp>();
2682       newOperands.push_back(canFoldIntoConsumerOp(tensorCastOp)
2683                                 ? tensorCastOp.source()
2684                                 : opOperand->get());
2685     }
2686     // Init tensors may fold, in which case the resultType must also change.
2687     for (OpOperand *opOperand : op.getOutputOperands()) {
2688       auto tensorCastOp = opOperand->get().getDefiningOp<tensor::CastOp>();
2689       bool fold = canFoldIntoConsumerOp(tensorCastOp);
2690       newOperands.push_back(fold ? tensorCastOp.getOperand()
2691                                  : opOperand->get());
2692       newResultTypes.push_back(newOperands.back().getType());
2693     }
2694     // Clone op.
2695     Operation *newOp =
2696         op.clone(rewriter, op->getLoc(), newResultTypes, newOperands);
2697     SmallVector<Value, 4> replacements;
2698     replacements.reserve(newOp->getNumResults());
2699     for (auto result : llvm::zip(op->getResults(), newOp->getResults())) {
2700       Value oldResult = std::get<0>(result);
2701       Value newResult = std::get<1>(result);
2702       if (newResult.getType() != oldResult.getType()) {
2703         replacements.push_back(rewriter.create<tensor::CastOp>(
2704             op->getLoc(), oldResult.getType(), newResult));
2705       } else {
2706         replacements.push_back(newResult);
2707       }
2708     }
2709     rewriter.replaceOp(op, replacements);
2710 
2711     return success();
2712   }
2713 };
2714 
2715 } // namespace
2716 
2717 #define LINALGOP_FOLDERS(XXX)                                                  \
2718   LogicalResult XXX::fold(ArrayRef<Attribute>,                                 \
2719                           SmallVectorImpl<OpFoldResult> &) {                   \
2720     return foldMemRefCast(*this);                                              \
2721   }
2722 
2723 LINALGOP_FOLDERS(CopyOp)
2724 LINALGOP_FOLDERS(FillOp)
2725 LINALGOP_FOLDERS(GenericOp)
2726 
2727 // All named ops canonicalizers and folders are auto-generated in the
2728 // .cpp.inc.
2729 
2730 //===----------------------------------------------------------------------===//
2731 // LinalgDialect
2732 //===----------------------------------------------------------------------===//
2733 
2734 void LinalgDialect::getCanonicalizationPatterns(
2735     RewritePatternSet &results) const {
2736   results.add<EraseDeadLinalgOp, FoldTensorCastOp>(getContext());
2737 }
2738 
2739 Operation *LinalgDialect::materializeConstant(OpBuilder &builder,
2740                                               Attribute value, Type type,
2741                                               Location loc) {
2742   return builder.create<arith::ConstantOp>(loc, type, value);
2743 }
2744