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