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