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