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/StandardOps/IR/Ops.h"
19 #include "mlir/IR/AffineExprVisitor.h"
20 #include "mlir/IR/Matchers.h"
21 #include "mlir/IR/OpImplementation.h"
22 #include "mlir/IR/PatternMatch.h"
23 #include "mlir/Parser.h"
24 
25 #include "llvm/ADT/DenseMap.h"
26 #include "llvm/ADT/SetVector.h"
27 #include "llvm/ADT/SmallSet.h"
28 #include "llvm/ADT/StringSet.h"
29 #include "llvm/Support/FormatVariadic.h"
30 #include "llvm/Support/MathExtras.h"
31 #include "llvm/Support/raw_ostream.h"
32 
33 using namespace mlir;
34 using namespace mlir::linalg;
35 
36 /// Forward declarations.
37 
38 /// Generic entry point to create the block for the region of a LinalgOp.
39 /// This is used by both named structured ops created by ods-gen and by manually
40 /// defined C++ ops.
41 /// This is used by both builders and parsers.
42 /// This function creates the block in the region with arguments corresponding
43 /// to the elemental types of `inputTypes` and `outputTypes`, which are asserted
44 /// to be ShapedType.
45 template <typename NamedStructuredOpType>
46 static void fillStructuredOpRegion(
47     OpBuilder &opBuilder, Region &region, TypeRange inputTypes,
48     TypeRange outputTypes, ValueRange captures = {},
49     std::function<void(unsigned, unsigned)> errorHandler = nullptr);
50 
51 /// Generic entry point to create both the region and the block of a LinalgOp.
52 template <typename NamedStructuredOpType>
53 static void
54 createAndFillStructuredOpRegion(OpBuilder &opBuilder, OperationState &result,
55                                 TypeRange inputTypes, TypeRange outputTypes,
56                                 ValueRange captures = {});
57 
58 /// Common parsing and printing used for both named structured ops created by
59 /// ods-gen and by manually defined C++ ops. Does not handle regions.
60 static ParseResult
61 parseCommonStructuredOpParts(OpAsmParser &parser, OperationState &result,
62                              SmallVectorImpl<Type> &inputTypes,
63                              SmallVectorImpl<Type> &outputTypes);
64 template <typename NamedStructuredOpType>
65 static void printCommonStructuredOpParts(OpAsmPrinter &p,
66                                          NamedStructuredOpType op);
67 
68 /// Specific parsing and printing for named structured ops created by ods-gen.
69 template <typename NamedStructuredOpType>
70 static ParseResult
71 parseNamedStructuredOpRegion(OpAsmParser &parser, Region &region,
72                              TypeRange inputTypes, TypeRange outputTypes,
73                              ArrayRef<OpAsmParser::OperandType> captures = {});
74 
75 static ParseResult
76 parseNamedStructuredOpResults(OpAsmParser &parser,
77                               SmallVectorImpl<Type> &resultTypes);
78 
79 template <typename NamedStructuredOpType>
80 static ParseResult
81 parseNamedStructuredOp(OpAsmParser &parser, OperationState &result,
82                        ArrayRef<OpAsmParser::OperandType> captures = {});
83 
84 static void printNamedStructuredOpResults(OpAsmPrinter &p,
85                                           TypeRange resultTypes);
86 
87 template <typename NamedStructuredOpType>
88 static void printNamedStructuredOp(OpAsmPrinter &p, NamedStructuredOpType op);
89 
90 /// Helper function to dispatch an OpFoldResult into either the `dynamicVec` if
91 /// it is a Value or into `staticVec` if it is an IntegerAttr.
92 /// In the case of a Value, a copy of the `sentinel` value is also pushed to
93 /// `staticVec`. This is useful to extract mixed static and dynamic entries that
94 /// come from an AttrSizedOperandSegments trait.
95 static void dispatchIndexOpFoldResult(OpFoldResult ofr,
96                                       SmallVectorImpl<Value> &dynamicVec,
97                                       SmallVectorImpl<int64_t> &staticVec,
98                                       int64_t sentinel) {
99   if (auto v = ofr.dyn_cast<Value>()) {
100     dynamicVec.push_back(v);
101     staticVec.push_back(sentinel);
102     return;
103   }
104   APInt apInt = ofr.dyn_cast<Attribute>().cast<IntegerAttr>().getValue();
105   staticVec.push_back(apInt.getSExtValue());
106 }
107 
108 /// This is a common class used for patterns of the form
109 /// ```
110 ///    someop(memrefcast) -> someop
111 /// ```
112 /// It folds the source of the memref_cast into the root operation directly.
113 static LogicalResult foldMemRefCast(Operation *op) {
114   bool folded = false;
115   for (OpOperand &operand : op->getOpOperands()) {
116     auto castOp = operand.get().getDefiningOp<MemRefCastOp>();
117     if (castOp && canFoldIntoConsumerOp(castOp)) {
118       operand.set(castOp.getOperand());
119       folded = true;
120     }
121   }
122   return success(folded);
123 }
124 
125 //===----------------------------------------------------------------------===//
126 // Region builder helper.
127 // TODO: Move this to a utility library.
128 // The public methods on this class are referenced directly from generated code
129 // and bind by name to math functions in the DSL as:
130 //   `applyfn__{fnName}`
131 // Examples:
132 //   `applyfn__add`
133 //   `applyfn__mul`
134 // The naming convention is intentional in order to match snake-cased DSL names.
135 // See mlir-linalg-ods-yaml-gen.cpp for the code that mates to this class.
136 //
137 // Implementations of the math functions must be polymorphic over numeric types,
138 // internally performing necessary casts. If the function application makes no
139 // sense, then the only recourse is to assert and return nullptr. This can be
140 // extended later if it becomes possible to fail construction of the region. The
141 // invariant should be enforced at a higher level.
142 //
143 // TODO: These helpers are currently type polymorphic over the class of integer
144 // and floating point types, but they will not internally cast within bit
145 // widths of a class (mixed precision such as i8->i32) or across classes
146 // (i.e. mixed float and integer). Many such combinations are ambiguous or need
147 // to be handled with care and work is being considered to extend the op
148 // language to make such cases explicit. In the mean-time, violating this will
149 // fail verification, which is deemed acceptable.
150 //===----------------------------------------------------------------------===//
151 
152 namespace {
153 
154 class RegionBuilderHelper {
155 public:
156   RegionBuilderHelper(Block &block) : block(block) {}
157 
158   // Generates operations to cast the given operand to a specified type.
159   // If the cast cannot be performed, a warning will be issued and the
160   // operand returned as-is (which will presumably yield a verification
161   // issue downstream).
162   Value cast(Type toType, Value operand) {
163     OpBuilder builder = getBuilder(operand);
164     auto loc = operand.getLoc();
165 
166     if (operand.getType() == toType)
167       return operand;
168     if (auto toIntType = toType.dyn_cast<IntegerType>()) {
169       // If operand is floating point, cast directly to the int type.
170       if (operand.getType().isa<FloatType>())
171         return builder.create<FPToSIOp>(loc, toType, operand);
172       if (auto fromIntType = operand.getType().dyn_cast<IntegerType>()) {
173         // Either sign extend or truncate.
174         if (toIntType.getWidth() > fromIntType.getWidth())
175           return builder.create<SignExtendIOp>(loc, toType, operand);
176         else if (toIntType.getWidth() < fromIntType.getWidth())
177           return builder.create<TruncateIOp>(loc, toType, operand);
178       }
179     } else if (auto toFloatType = toType.dyn_cast<FloatType>()) {
180       // If operand is integer, cast directly to the float type.
181       // Note that it is unclear how to cast from BF16<->FP16.
182       if (operand.getType().isa<IntegerType>())
183         return builder.create<SIToFPOp>(loc, toFloatType, operand);
184       if (auto fromFloatType = operand.getType().dyn_cast<FloatType>()) {
185         if (toFloatType.getWidth() > fromFloatType.getWidth())
186           return builder.create<FPExtOp>(loc, toFloatType, operand);
187         else if (toFloatType.getWidth() < fromFloatType.getWidth())
188           return builder.create<FPTruncOp>(loc, toFloatType, operand);
189       }
190     }
191 
192     emitWarning(operand.getLoc()) << "could not cast operand of type "
193                                   << operand.getType() << " to " << toType;
194     return operand;
195   }
196 
197   Value applyfn__add(Value lhs, Value rhs) {
198     OpBuilder builder = getBuilder(lhs);
199     if (isFloatingPoint(lhs))
200       return builder.create<AddFOp>(lhs.getLoc(), lhs, rhs);
201     else if (isInteger(lhs))
202       return builder.create<AddIOp>(lhs.getLoc(), lhs, rhs);
203     llvm_unreachable("unsupported non numeric type");
204   }
205 
206   Value applyfn__mul(Value lhs, Value rhs) {
207     OpBuilder builder = getBuilder(lhs);
208     if (isFloatingPoint(lhs))
209       return builder.create<MulFOp>(lhs.getLoc(), lhs, rhs);
210     else if (isInteger(lhs))
211       return builder.create<MulIOp>(lhs.getLoc(), lhs, rhs);
212     llvm_unreachable("unsupported non numeric type");
213   }
214 
215   void yieldOutputs(ValueRange values) {
216     assert(!values.empty() && "linalg ops must yield outputs");
217     if (values.empty())
218       return;
219     Value first = values.front();
220     OpBuilder builder = getBuilder(first);
221     builder.create<YieldOp>(first.getLoc(), values);
222   }
223 
224 private:
225   Block &block;
226 
227   bool isFloatingPoint(Value value) { return value.getType().isa<FloatType>(); }
228   bool isInteger(Value value) { return value.getType().isa<IntegerType>(); }
229 
230   OpBuilder getBuilder(Value value) {
231     OpBuilder builder(value.getContext());
232     builder.setInsertionPointToEnd(&block);
233     return builder;
234   }
235 };
236 
237 } // namespace
238 
239 //===----------------------------------------------------------------------===//
240 // CopyOp
241 //===----------------------------------------------------------------------===//
242 void CopyOp::regionBuilder(Block &block, ValueRange captures) {
243   using namespace edsc::intrinsics;
244   assert(block.getNumArguments() == 2 && "CopyOp regionBuilder expects 2 args");
245   (linalg_yield(block.getArgument(0)));
246 }
247 
248 void CopyOp::build(OpBuilder &builder, OperationState &result, Value input,
249                    Value output, AffineMap inputPermutation,
250                    AffineMap outputPermutation,
251                    ArrayRef<NamedAttribute> namedAttrs) {
252   result.addOperands({input, output});
253   result.addAttributes(namedAttrs);
254   if (inputPermutation)
255     result.addAttribute("inputPermutation",
256                         AffineMapAttr::get(inputPermutation));
257   if (outputPermutation)
258     result.addAttribute("outputPermutation",
259                         AffineMapAttr::get(outputPermutation));
260   result.addRegion();
261   fillStructuredOpRegion<CopyOp>(builder, *result.regions.front(),
262                                  TypeRange{input.getType()},
263                                  TypeRange{output.getType()});
264 }
265 
266 ParseResult parseCopyOpRegion(OpAsmParser &parser, Region &r, Type inputType,
267                               Type outputType) {
268   OpBuilder opBuilder(parser.getBuilder().getContext());
269   fillStructuredOpRegion<CopyOp>(opBuilder, r, TypeRange{inputType},
270                                  TypeRange{outputType});
271   return success();
272 }
273 
274 /// CopyOp region is elided when printing.
275 void printCopyOpRegion(OpAsmPrinter &, Operation *, Region &, Type, Type) {}
276 
277 static LogicalResult verify(CopyOp op) {
278   auto outputViewType = op.getOutputShapedType(0);
279   auto inputViewType = op.getInputShapedType(0);
280   if (inputViewType.getElementType() != outputViewType.getElementType())
281     return op.emitOpError("expects views of the same type");
282   if (inputViewType.getRank() != outputViewType.getRank())
283     return op.emitOpError("expects views of the same rank");
284   auto rank = op.getNumParallelLoops();
285   auto inputPermutationMap = op.inputPermutation();
286   if (inputPermutationMap) {
287     if (inputPermutationMap->getNumInputs() != rank)
288       return op.emitOpError("expects optional input_permutation map of rank ")
289              << rank;
290     if (!inputPermutationMap->isPermutation())
291       return op.emitOpError(
292           "expects optional input_permutation map to be a permutation");
293   }
294   auto outputPermutationMap = op.outputPermutation();
295   if (outputPermutationMap) {
296     if (outputPermutationMap->getNumInputs() != rank)
297       return op.emitOpError("expects optional output_permutation map of rank ")
298              << rank;
299     if (!outputPermutationMap->isPermutation())
300       return op.emitOpError(
301           "expects optional output_permutation map to be a permutation");
302   }
303   if (rank == 0 && inputPermutationMap)
304     return op.emitOpError("expected no input permutation when rank == 0");
305   if (rank == 0 && outputPermutationMap)
306     return op.emitOpError("expected no output permutation when rank == 0");
307   return success();
308 }
309 
310 void CopyOp::getEffects(
311     SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
312         &effects) {
313   effects.emplace_back(MemoryEffects::Read::get(), input(),
314                        SideEffects::DefaultResource::get());
315   effects.emplace_back(MemoryEffects::Write::get(), output(),
316                        SideEffects::DefaultResource::get());
317 }
318 
319 //===----------------------------------------------------------------------===//
320 // FillOp
321 //===----------------------------------------------------------------------===//
322 void FillOp::regionBuilder(Block &block, ValueRange captures) {
323   using namespace edsc::intrinsics;
324   assert(captures.size() == 1 && "FillOp regionBuilder expects 1 capture");
325   (linalg_yield(captures));
326 }
327 
328 void FillOp::build(OpBuilder &builder, OperationState &result, Value output,
329                    Value value) {
330   build(builder, result, output.getType().dyn_cast<RankedTensorType>(), output,
331         value);
332   fillStructuredOpRegion<FillOp>(builder, *result.regions.front(), TypeRange{},
333                                  TypeRange{output.getType()}, value);
334 }
335 
336 ParseResult parseFillOpRegion(OpAsmParser &parser, Region &r, Type outputType,
337                               OpAsmParser::OperandType valueRef) {
338   OpBuilder opBuilder(parser.getBuilder().getContext());
339   // Resolve `valueRef` into `value` at parse time so we can build the region
340   // with captures.
341   SmallVector<Value> value;
342   parser.resolveOperand(valueRef, getElementTypeOrSelf(outputType), value);
343   fillStructuredOpRegion<FillOp>(opBuilder, r, TypeRange{},
344                                  TypeRange{outputType}, value);
345   return success();
346 }
347 
348 /// FillOp region is elided when printing.
349 void printFillOpRegion(OpAsmPrinter &, Operation *, Region &, Type, Value) {}
350 
351 static LogicalResult verify(FillOp op) {
352   auto viewType = op.getOutputShapedType(0);
353   auto fillType = op.value().getType();
354   if (viewType.getElementType() != fillType)
355     return op.emitOpError("expects fill type to match view elemental type");
356   if (!op.getNumResults() && !viewType.isa<MemRefType>()) {
357     return op.emitOpError(
358         "expected fill op with no result value to use memref type");
359   }
360   return success();
361 }
362 
363 void FillOp::getEffects(
364     SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
365         &effects) {
366   if (output().getType().isa<MemRefType>())
367     effects.emplace_back(MemoryEffects::Write::get(), output(),
368                          SideEffects::DefaultResource::get());
369 }
370 
371 //===----------------------------------------------------------------------===//
372 // GenericOps
373 //===----------------------------------------------------------------------===//
374 void GenericOp::build(
375     OpBuilder &builder, OperationState &result, TypeRange resultTensorTypes,
376     ValueRange inputs, ValueRange outputs, ArrayRef<AffineMap> indexingMaps,
377     ArrayRef<StringRef> iteratorTypes, StringRef doc, StringRef libraryCall,
378     function_ref<void(OpBuilder &, Location, ValueRange)> bodyBuild) {
379   build(builder, result, resultTensorTypes, inputs, outputs,
380         builder.getAffineMapArrayAttr(indexingMaps),
381         builder.getStrArrayAttr(iteratorTypes),
382         doc.empty() ? StringAttr() : builder.getStringAttr(doc),
383         libraryCall.empty() ? StringAttr() : builder.getStringAttr(libraryCall),
384         ArrayAttr());
385   if (!bodyBuild)
386     return;
387 
388   SmallVector<Type, 4> blockArgTypes;
389   for (ValueRange container : {inputs, outputs})
390     for (Value v : container)
391       blockArgTypes.push_back(v.getType().cast<ShapedType>().getElementType());
392 
393   OpBuilder::InsertionGuard guard(builder);
394   auto &region = *result.regions.front();
395   Block *bodyBlock = builder.createBlock(&region, region.end(), blockArgTypes);
396   bodyBuild(builder, result.location, bodyBlock->getArguments());
397 }
398 
399 void GenericOp::build(
400     OpBuilder &builder, OperationState &result, ValueRange inputs,
401     ValueRange outputs, ArrayRef<AffineMap> indexingMaps,
402     ArrayRef<StringRef> iteratorTypes, StringRef doc, StringRef libraryCall,
403     function_ref<void(OpBuilder &, Location, ValueRange)> bodyBuild) {
404   build(builder, result, TypeRange{}, inputs, outputs, indexingMaps,
405         iteratorTypes, doc, libraryCall, bodyBuild);
406 }
407 
408 void GenericOp::build(
409     OpBuilder &builder, OperationState &result, ValueRange inputs,
410     ValueRange outputs, ArrayRef<AffineMap> indexingMaps,
411     ArrayRef<StringRef> iteratorTypes,
412     function_ref<void(OpBuilder &, Location, ValueRange)> bodyBuild) {
413   build(builder, result, inputs, outputs, indexingMaps, iteratorTypes,
414         /*doc=*/"",
415         /*libraryCall=*/"", bodyBuild);
416 }
417 
418 void GenericOp::build(
419     OpBuilder &builder, OperationState &result, TypeRange resultTensorTypes,
420     ValueRange inputs, ValueRange outputs, ArrayRef<AffineMap> indexingMaps,
421     ArrayRef<StringRef> iteratorTypes,
422     function_ref<void(OpBuilder &, Location, ValueRange)> bodyBuild) {
423   build(builder, result, resultTensorTypes, inputs, outputs, indexingMaps,
424         iteratorTypes,
425         /*doc=*/"",
426         /*libraryCall=*/"", bodyBuild);
427 }
428 void IndexedGenericOp::build(
429     OpBuilder &builder, OperationState &result, TypeRange resultTensorTypes,
430     ValueRange inputs, ValueRange outputs, ArrayRef<AffineMap> indexingMaps,
431     ArrayRef<StringRef> iteratorTypes, StringRef doc, StringRef libraryCall,
432     function_ref<void(OpBuilder &, Location, ValueRange, ValueRange)>
433         bodyBuild) {
434   build(builder, result, resultTensorTypes, inputs, outputs,
435         builder.getAffineMapArrayAttr(indexingMaps),
436         builder.getStrArrayAttr(iteratorTypes),
437         doc.empty() ? StringAttr() : builder.getStringAttr(doc),
438         libraryCall.empty() ? StringAttr() : builder.getStringAttr(libraryCall),
439         ArrayAttr());
440   if (!bodyBuild)
441     return;
442 
443   unsigned nLoops = iteratorTypes.size();
444   SmallVector<Type, 4> blockArgTypes(nLoops, builder.getIndexType());
445   for (ValueRange container : {inputs, outputs})
446     for (Value v : container)
447       blockArgTypes.push_back(v.getType().cast<ShapedType>().getElementType());
448 
449   OpBuilder::InsertionGuard guard(builder);
450   auto &region = *result.regions.front();
451   Block *bodyBlock = builder.createBlock(&region, region.end(), blockArgTypes);
452   bodyBuild(builder, result.location,
453             bodyBlock->getArguments().take_front(nLoops),
454             bodyBlock->getArguments().drop_front(nLoops));
455 }
456 
457 void IndexedGenericOp::build(
458     OpBuilder &builder, OperationState &result, ValueRange inputs,
459     ValueRange outputs, ArrayRef<AffineMap> indexingMaps,
460     ArrayRef<StringRef> iteratorTypes, StringRef doc, StringRef libraryCall,
461     function_ref<void(OpBuilder &, Location, ValueRange, ValueRange)>
462         bodyBuild) {
463   build(builder, result, TypeRange{}, inputs, outputs, indexingMaps,
464         iteratorTypes, doc, libraryCall, bodyBuild);
465 }
466 
467 void IndexedGenericOp::build(
468     OpBuilder &builder, OperationState &result, ValueRange inputs,
469     ValueRange outputs, ArrayRef<AffineMap> indexingMaps,
470     ArrayRef<StringRef> iteratorTypes,
471     function_ref<void(OpBuilder &, Location, ValueRange, ValueRange)>
472         bodyBuild) {
473   build(builder, result, inputs, outputs, indexingMaps, iteratorTypes,
474         /*doc=*/"", /*libraryCall=*/"", bodyBuild);
475 }
476 
477 void IndexedGenericOp::build(
478     OpBuilder &builder, OperationState &result, TypeRange resultTensorTypes,
479     ValueRange inputs, ValueRange outputs, ArrayRef<AffineMap> indexingMaps,
480     ArrayRef<StringRef> iteratorTypes,
481     function_ref<void(OpBuilder &, Location, ValueRange, ValueRange)>
482         bodyBuild) {
483   build(builder, result, resultTensorTypes, inputs, outputs, indexingMaps,
484         iteratorTypes,
485         /*doc=*/"",
486         /*libraryCall=*/"", bodyBuild);
487 }
488 
489 template <typename GenericOpType>
490 static void printGenericOp(OpAsmPrinter &p, GenericOpType op) {
491   p << op.getOperationName() << " ";
492 
493   // Print extra attributes.
494   auto genericAttrNames = op.linalgTraitAttrNames();
495 
496   llvm::StringSet<> genericAttrNamesSet;
497   genericAttrNamesSet.insert(genericAttrNames.begin(), genericAttrNames.end());
498   SmallVector<NamedAttribute, 8> genericAttrs;
499   for (auto attr : op->getAttrs())
500     if (genericAttrNamesSet.count(attr.first.strref()) > 0)
501       genericAttrs.push_back(attr);
502   if (!genericAttrs.empty()) {
503     auto genericDictAttr = DictionaryAttr::get(op.getContext(), genericAttrs);
504     p << genericDictAttr;
505   }
506 
507   // Printing is shared with named ops, except for the region and attributes
508   printCommonStructuredOpParts(p, op);
509 
510   genericAttrNames.push_back("operand_segment_sizes");
511   genericAttrNamesSet.insert(genericAttrNames.back());
512 
513   bool hasExtraAttrs = false;
514   for (NamedAttribute n : op->getAttrs()) {
515     if ((hasExtraAttrs = !genericAttrNamesSet.contains(n.first.strref())))
516       break;
517   }
518   if (hasExtraAttrs) {
519     p << " attrs = ";
520     p.printOptionalAttrDict(op->getAttrs(), /*elidedAttrs=*/genericAttrNames);
521   }
522 
523   // Print region.
524   if (!op.region().empty())
525     p.printRegion(op.region());
526 
527   // Print results.
528   printNamedStructuredOpResults(p, op.result_tensors().getTypes());
529 }
530 
531 static void print(OpAsmPrinter &p, GenericOp op) { printGenericOp(p, op); }
532 
533 static void print(OpAsmPrinter &p, IndexedGenericOp op) {
534   printGenericOp(p, op);
535 }
536 
537 static ParseResult parseGenericOp(OpAsmParser &parser, OperationState &result) {
538   DictionaryAttr dictAttr;
539   // Parse the core linalg traits that must check into a dictAttr.
540   // The name is unimportant as we will overwrite result.attributes.
541   // The core linalg traits must contain the information necessary to pass the
542   // verifier.
543   if (parser.parseAttribute(dictAttr, "_", result.attributes))
544     return failure();
545   result.attributes.assign(dictAttr.getValue().begin(),
546                            dictAttr.getValue().end());
547 
548   // Parsing is shared with named ops, except for the region.
549   SmallVector<Type, 1> inputTypes, outputTypes;
550   if (parseCommonStructuredOpParts(parser, result, inputTypes, outputTypes))
551     return failure();
552 
553   // Optional attributes may be added.
554   if (succeeded(parser.parseOptionalKeyword("attrs")))
555     if (failed(parser.parseEqual()) ||
556         failed(parser.parseOptionalAttrDict(result.attributes)))
557       return failure();
558 
559   SmallVector<OpAsmParser::OperandType, 8> regionOperands;
560   std::unique_ptr<Region> region = std::make_unique<Region>();
561   SmallVector<Type, 8> operandTypes, regionTypes;
562   if (parser.parseRegion(*region, regionOperands, regionTypes))
563     return failure();
564   result.addRegion(std::move(region));
565 
566   // Generic ops may specify that a subset of its outputs are tensors. Such
567   // outputs are specified in the result type.
568   // TODO: may need to move output parsing before region parsing.
569   // Need to wait for declarative assembly resolution to decide.
570   SmallVector<Type, 1> outputTensorsTypes;
571   if (parseNamedStructuredOpResults(parser, outputTensorsTypes))
572     return failure();
573   result.addTypes(outputTensorsTypes);
574 
575   return success();
576 }
577 
578 static void getGenericEffectsImpl(
579     SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
580         &effects,
581     ValueRange results, ValueRange inputBuffers, ValueRange outputs) {
582   for (Value value : results) {
583     effects.emplace_back(MemoryEffects::Allocate::get(), value,
584                          SideEffects::DefaultResource::get());
585   }
586   for (Value value : inputBuffers) {
587     effects.emplace_back(MemoryEffects::Read::get(), value,
588                          SideEffects::DefaultResource::get());
589   }
590   for (Value value : outputs) {
591     effects.emplace_back(MemoryEffects::Read::get(), value,
592                          SideEffects::DefaultResource::get());
593     effects.emplace_back(MemoryEffects::Write::get(), value,
594                          SideEffects::DefaultResource::get());
595   }
596 }
597 
598 void GenericOp::getEffects(
599     SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
600         &effects) {
601   getGenericEffectsImpl(effects, getOperation()->getResults(),
602                         getInputBuffers(), getOutputBuffers());
603 }
604 
605 void IndexedGenericOp::getEffects(
606     SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
607         &effects) {
608   getGenericEffectsImpl(effects, getOperation()->getResults(),
609                         getInputBuffers(), getOutputBuffers());
610 }
611 
612 namespace {
613 
614 template <typename GenericOpType>
615 struct AnnotationsVerifier {
616   static LogicalResult verify(GenericOpType op) { return success(); }
617 };
618 
619 template <>
620 LogicalResult AnnotationsVerifier<GenericOp>::verify(GenericOp op) {
621   ArrayAttr sparseAttr = op.sparseAttr();
622   if (!sparseAttr)
623     return success();
624   // Verify consistency of sparse annotations.
625   if (!op.hasTensorSemantics())
626     return op.emitOpError("expected sparse annotations on tensors only");
627   if (op.getNumOutputs() != 1)
628     return op.emitOpError("expected single output tensor");
629   unsigned numTensors = op.getNumShapedOperands();
630   if (sparseAttr.size() != numTensors)
631     return op.emitOpError("expected one sparse annotation for each tensor");
632   for (unsigned t = 0; t < numTensors; t++) {
633     auto dimAttr = sparseAttr[t].dyn_cast_or_null<ArrayAttr>();
634     if (!dimAttr)
635       return op.emitOpError("expected sparse annotation array for tensor ")
636              << t;
637     unsigned rank = op.getShapedType(t).getRank();
638     if (dimAttr.size() != rank)
639       return op.emitOpError("expected sparse annotation with rank ")
640              << rank << " for tensor " << t;
641     // Per-dimension annotations for each tensor consist of only "D" or "S".
642     for (unsigned d = 0; d < rank; d++) {
643       if (isDenseDim(dimAttr[d])) {
644         continue;
645       } else if (isSparseDim(dimAttr[d])) {
646         if (t == numTensors - 1)
647           return op.emitOpError("sparse output tensors not supported (yet)");
648         continue;
649       }
650       return op.emitOpError("expected sparse annotation at position ")
651              << d << " for tensor " << t;
652     }
653   }
654   return success();
655 }
656 
657 } // namespace
658 
659 template <typename GenericOpType>
660 static LogicalResult verifyGenericOp(GenericOpType op) {
661   if (failed(AnnotationsVerifier<GenericOpType>::verify(op)))
662     return failure();
663 
664   return success();
665 }
666 
667 static LogicalResult verify(GenericOp op) { return verifyGenericOp(op); }
668 
669 static LogicalResult verify(IndexedGenericOp op) { return verifyGenericOp(op); }
670 
671 //===----------------------------------------------------------------------===//
672 // InitTensorOp
673 //===----------------------------------------------------------------------===//
674 void InitTensorOp::build(OpBuilder &b, OperationState &result,
675                          ArrayRef<OpFoldResult> sizes, Type elementType,
676                          ArrayRef<NamedAttribute> attrs) {
677   unsigned rank = sizes.size();
678   SmallVector<Value, 4> dynamicSizes;
679   SmallVector<int64_t, 4> staticSizes;
680   for (unsigned i = 0; i < rank; ++i) {
681     // staticLow and staticHigh have full information of the padding config.
682     // This will grow staticLow and staticHigh with 1 value. If the config is
683     // dynamic (ie not a constant), dynamicLow and dynamicHigh will grow with 1
684     // value as well.
685     dispatchIndexOpFoldResult(sizes[i], dynamicSizes, staticSizes,
686                               ShapedType::kDynamicSize);
687   }
688   auto resultType = RankedTensorType ::get(staticSizes, elementType);
689   build(b, result, resultType, dynamicSizes, b.getI64ArrayAttr(staticSizes));
690   result.addAttributes(attrs);
691 }
692 
693 static LogicalResult verify(InitTensorOp op) {
694   RankedTensorType resultType = op.getType();
695   SmallVector<int64_t, 4> staticSizes = llvm::to_vector<4>(llvm::map_range(
696       op.static_sizes().cast<ArrayAttr>(),
697       [](Attribute a) -> int64_t { return a.cast<IntegerAttr>().getInt(); }));
698 
699   if (failed(verifyListOfOperandsOrIntegers(op, "sizes", resultType.getRank(),
700                                             op.static_sizes(), op.sizes(),
701                                             ShapedType::isDynamic)))
702     return failure();
703 
704   if (op.static_sizes().size() != static_cast<unsigned>(resultType.getRank()))
705     return op->emitError("expected ")
706            << resultType.getRank() << " sizes values";
707 
708   Type expectedType =
709       InitTensorOp::inferResultType(staticSizes, resultType.getElementType());
710   if (resultType != expectedType) {
711     return op.emitError("specified type ")
712            << resultType << " does not match the inferred type "
713            << expectedType;
714   }
715   return success();
716 }
717 
718 Type InitTensorOp::inferResultType(ArrayRef<int64_t> staticSizes,
719                                    Type elementType) {
720   return RankedTensorType::get(staticSizes, elementType);
721 }
722 
723 namespace {
724 /// Change the type of the result of a `linalg.init_tensor` by making the result
725 /// type statically sized along dimension that in the original operation where
726 /// defined as dynamic, but the size was defined using a `constant` op. For
727 /// example
728 ///
729 ///  %c5 = constant 5: index
730 ///  %0 = linalg.init_tensor [%arg0, %c5] : tensor<?x?xf32>
731 ///
732 ///  to
733 ///
734 ///  %0 = linalg.init_tensor [%arg0, 5] : tensor<?x5xf32>
735 struct ReplaceStaticShapeDims : OpRewritePattern<InitTensorOp> {
736   using OpRewritePattern<InitTensorOp>::OpRewritePattern;
737 
738   LogicalResult matchAndRewrite(InitTensorOp op,
739                                 PatternRewriter &rewriter) const override {
740     SmallVector<Value, 4> dynamicSizes;
741     SmallVector<int64_t, 4> staticSizes;
742     for (unsigned i = 0, e = op.getType().getRank(); i != e; ++i) {
743       // If the size is already static, nothing to do.
744       if (!op.isDynamicSize(i)) {
745         staticSizes.push_back(op.getStaticSize(i));
746         continue;
747       }
748 
749       // If the size is dynamic but defined using a `constant` op, get the
750       // constant value to find the static size to use.
751       unsigned operandNum = op.getIndexOfDynamicSize(i);
752       Value sizeOperand = op.getOperand(operandNum);
753       if (auto constantIndexOp = sizeOperand.getDefiningOp<ConstantIndexOp>()) {
754         staticSizes.push_back(constantIndexOp.getValue());
755         continue;
756       }
757 
758       // Fallback case. Keep the size dynamic.
759       dynamicSizes.push_back(sizeOperand);
760       staticSizes.push_back(ShapedType::kDynamicSize);
761     }
762     RankedTensorType newType =
763         RankedTensorType::get(staticSizes, op.getType().getElementType());
764     if (newType == op.getType())
765       return failure();
766     auto newOp =
767         rewriter.create<InitTensorOp>(op.getLoc(), newType, dynamicSizes,
768                                       rewriter.getI64ArrayAttr(staticSizes));
769     rewriter.replaceOpWithNewOp<tensor::CastOp>(op, op.getType(), newOp);
770     return success();
771   }
772 };
773 
774 /// Canonicalize a `linalg.init_tensor` -> `dim` pattern by replacing the `dim`
775 /// with
776 /// - A constant value if the size is static along the dimension.
777 /// - The dynamic value that defines the size of the result of
778 ///   `linalg.init_tensor` op.
779 struct ReplaceDimOfInitTensorOp : public OpRewritePattern<DimOp> {
780   using OpRewritePattern<DimOp>::OpRewritePattern;
781 
782   LogicalResult matchAndRewrite(DimOp dimOp,
783                                 PatternRewriter &rewriter) const override {
784     auto initTensorOp = dimOp.memrefOrTensor().getDefiningOp<InitTensorOp>();
785     if (!initTensorOp)
786       return failure();
787     auto dimIndex = dimOp.index().getDefiningOp<ConstantIndexOp>();
788     if (!dimIndex)
789       return failure();
790     int64_t index = dimIndex.getValue();
791     if (!initTensorOp.isDynamicSize(index)) {
792       rewriter.replaceOpWithNewOp<ConstantIndexOp>(
793           dimOp, initTensorOp.getStaticSize(index));
794     } else {
795       rewriter.replaceOp(dimOp, initTensorOp.getDynamicSize(index));
796     }
797     return success();
798   }
799 };
800 } // namespace
801 
802 namespace {
803 /// Since `init_tensor` operation creates a tensor needed only for its shape, a
804 /// subtensor of this is also needed only for its shape. The result can be
805 /// replaced by a new init_tensor operation of the same size as the subtensor
806 /// op.
807 struct FoldInitTensorWithSubTensorOp : public OpRewritePattern<SubTensorOp> {
808   using OpRewritePattern<SubTensorOp>::OpRewritePattern;
809 
810   LogicalResult matchAndRewrite(SubTensorOp subtensorOp,
811                                 PatternRewriter &rewriter) const override {
812     if (!subtensorOp.source().getDefiningOp<linalg::InitTensorOp>())
813       return failure();
814     rewriter.replaceOpWithNewOp<linalg::InitTensorOp>(
815         subtensorOp, subtensorOp.sizes(),
816         llvm::to_vector<4>(llvm::map_range(
817             subtensorOp.static_sizes(),
818             [](Attribute attr) { return attr.cast<IntegerAttr>().getInt(); })),
819         subtensorOp.getSourceType().getElementType());
820     return success();
821   }
822 };
823 
824 struct FoldInitTensorWithTensorReshapeOp
825     : public OpRewritePattern<TensorReshapeOp> {
826   using OpRewritePattern<TensorReshapeOp>::OpRewritePattern;
827 
828   LogicalResult matchAndRewrite(TensorReshapeOp reshapeOp,
829                                 PatternRewriter &rewriter) const override {
830     if (!reshapeOp.src().getDefiningOp<InitTensorOp>())
831       return failure();
832     Location loc = reshapeOp.getLoc();
833     SmallVector<Value, 4> resultShapeValues =
834         reshapeOp.getOutputShape(rewriter, loc);
835     Value initTensor = rewriter.create<InitTensorOp>(
836         loc, resultShapeValues, reshapeOp.getResultType().getElementType());
837     rewriter.replaceOpWithNewOp<tensor::CastOp>(
838         reshapeOp, reshapeOp.getResultType(), initTensor);
839     return success();
840   }
841 };
842 } // namespace
843 
844 void InitTensorOp::getCanonicalizationPatterns(
845     OwningRewritePatternList &results, MLIRContext *context) {
846   results
847       .insert<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<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<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<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(OwningRewritePatternList &results,
1549                                             MLIRContext *context) {
1550   results.insert<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<DimOp> {
1641   using OpRewritePattern<DimOp>::OpRewritePattern;
1642   LogicalResult matchAndRewrite(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(
1664     OwningRewritePatternList &results, MLIRContext *context) {
1665   results.insert<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, ArrayRef<StringRef> 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(),
1761                       builder.getStrArrayAttr(iteratorTypes));
1762   result.addTypes(outputs.getTypes());
1763 
1764   OpBuilder::InsertionGuard guard(builder);
1765   unsigned numIVs = steps.size();
1766   SmallVector<Type, 8> argTypes(numIVs, builder.getIndexType());
1767   Region *bodyRegion = result.addRegion();
1768   Block *bodyBlock = builder.createBlock(bodyRegion, {}, argTypes);
1769 
1770   if (bodyBuilderFn) {
1771     builder.setInsertionPointToStart(bodyBlock);
1772     bodyBuilderFn(builder, result.location, bodyBlock->getArguments());
1773   }
1774   TiledLoopOp::ensureTerminator(*bodyRegion, builder, result.location);
1775 }
1776 
1777 static void print(OpAsmPrinter &p, TiledLoopOp op) {
1778   p << op.getOperationName() << " (" << op.getBody()->getArguments() << ") = ("
1779     << op.lowerBound() << ") to (" << op.upperBound() << ") step (" << op.step()
1780     << ")";
1781 
1782   if (!op.inputs().empty())
1783     p << " ins (" << op.inputs() << ": " << TypeRange(op.inputs()) << ")";
1784   if (!op.outputs().empty())
1785     p << " outs (" << op.outputs() << ":" << TypeRange(op.outputs()) << ")";
1786 
1787   if (llvm::any_of(op.iterator_types(), [](Attribute attr) {
1788         return attr.cast<StringAttr>().getValue() !=
1789                getParallelIteratorTypeName();
1790       })) {
1791     p << " iterators" << op.iterator_types() << "";
1792   }
1793 
1794   p.printRegion(op.region(), /*printEntryBlockArgs=*/false);
1795   p.printOptionalAttrDict(
1796       op->getAttrs(), /*elidedAttrs=*/{TiledLoopOp::getOperandSegmentSizeAttr(),
1797                                        getIteratorTypesAttrName()});
1798 }
1799 
1800 static ParseResult parseTiledLoopOp(OpAsmParser &parser,
1801                                     OperationState &result) {
1802   auto &builder = parser.getBuilder();
1803   // Parse an opening `(` followed by induction variables followed by `)`
1804   SmallVector<OpAsmParser::OperandType, 4> ivs;
1805   if (parser.parseRegionArgumentList(ivs, /*requiredOperandCount=*/-1,
1806                                      OpAsmParser::Delimiter::Paren))
1807     return failure();
1808 
1809   // Parse loop bounds.
1810   SmallVector<OpAsmParser::OperandType, 4> lower;
1811   if (parser.parseEqual() ||
1812       parser.parseOperandList(lower, ivs.size(),
1813                               OpAsmParser::Delimiter::Paren) ||
1814       parser.resolveOperands(lower, builder.getIndexType(), result.operands))
1815     return failure();
1816 
1817   SmallVector<OpAsmParser::OperandType, 4> upper;
1818   if (parser.parseKeyword("to") ||
1819       parser.parseOperandList(upper, ivs.size(),
1820                               OpAsmParser::Delimiter::Paren) ||
1821       parser.resolveOperands(upper, builder.getIndexType(), result.operands))
1822     return failure();
1823 
1824   // Parse step values.
1825   SmallVector<OpAsmParser::OperandType, 4> steps;
1826   if (parser.parseKeyword("step") ||
1827       parser.parseOperandList(steps, ivs.size(),
1828                               OpAsmParser::Delimiter::Paren) ||
1829       parser.resolveOperands(steps, builder.getIndexType(), result.operands))
1830     return failure();
1831 
1832   // Parse input tensors.
1833   SmallVector<OpAsmParser::OperandType, 4> inputs;
1834   if (succeeded(parser.parseOptionalKeyword("ins"))) {
1835     SmallVector<Type, 4> inputTypes;
1836     llvm::SMLoc inputsOperandsLoc = parser.getCurrentLocation();
1837 
1838     if (parser.parseLParen() || parser.parseOperandList(inputs) ||
1839         parser.parseColonTypeList(inputTypes) || parser.parseRParen())
1840       return failure();
1841 
1842     if (parser.resolveOperands(inputs, inputTypes, inputsOperandsLoc,
1843                                result.operands))
1844       return failure();
1845   }
1846 
1847   // Parse output tensors.
1848   SmallVector<OpAsmParser::OperandType, 4> outputs;
1849   if (succeeded(parser.parseOptionalKeyword("outs"))) {
1850     SmallVector<Type, 4> outputTypes;
1851     llvm::SMLoc outputsOperandsLoc = parser.getCurrentLocation();
1852 
1853     if (parser.parseLParen() || parser.parseOperandList(outputs) ||
1854         parser.parseColonTypeList(outputTypes) || parser.parseRParen())
1855       return failure();
1856 
1857     if (parser.resolveOperands(outputs, outputTypes, outputsOperandsLoc,
1858                                result.operands))
1859       return failure();
1860     result.addTypes(outputTypes);
1861   }
1862 
1863   // Parse attributes.
1864   SmallVector<Attribute, 4> iterTypes;
1865   if (succeeded(parser.parseOptionalKeyword("iterators"))) {
1866     StringAttr iterType;
1867 
1868     if (parser.parseLSquare() || parser.parseAttribute(iterType))
1869       return failure();
1870     iterTypes.push_back(iterType);
1871     for (int i = 1, e = ivs.size(); i < e; ++i) {
1872       if (parser.parseComma() || parser.parseAttribute(iterType))
1873         return failure();
1874       iterTypes.push_back(iterType);
1875     }
1876     if (parser.parseRSquare())
1877       return failure();
1878   } else {
1879     auto parallelIter = builder.getStringAttr(getParallelIteratorTypeName());
1880     iterTypes = SmallVector<Attribute, 4>(ivs.size(), parallelIter);
1881   }
1882   result.addAttribute(getIteratorTypesAttrName(),
1883                       builder.getArrayAttr(iterTypes));
1884   result.addAttribute(
1885       TiledLoopOp::getOperandSegmentSizeAttr(),
1886       builder.getI32VectorAttr({static_cast<int32_t>(lower.size()),
1887                                 static_cast<int32_t>(upper.size()),
1888                                 static_cast<int32_t>(steps.size()),
1889                                 static_cast<int32_t>(inputs.size()),
1890                                 static_cast<int32_t>(outputs.size())}));
1891 
1892   // Parse the body.
1893   Region *body = result.addRegion();
1894   SmallVector<Type, 4> types(ivs.size(), builder.getIndexType());
1895   if (parser.parseRegion(*body, ivs, types))
1896     return failure();
1897 
1898   // Parse optional attributes.
1899   parser.parseOptionalAttrDict(result.attributes);
1900 
1901   return success();
1902 }
1903 
1904 Region &TiledLoopOp::getLoopBody() { return region(); }
1905 
1906 LogicalResult TiledLoopOp::moveOutOfLoop(ArrayRef<Operation *> ops) {
1907   for (auto *op : ops)
1908     op->moveBefore(*this);
1909   return success();
1910 }
1911 
1912 bool TiledLoopOp::isDefinedOutsideOfLoop(Value value) {
1913   return !region().isAncestor(value.getParentRegion());
1914 }
1915 
1916 static LogicalResult verify(TiledLoopOp op) { return success(); }
1917 
1918 /////// Operations corresponding to library calls defined with Tablegen ////////
1919 
1920 template <typename LinalgPoolingOp>
1921 static LogicalResult verifyStrideOrDilation(LinalgPoolingOp op,
1922                                             ArrayRef<Attribute> attrs,
1923                                             bool isStride) {
1924   auto strideOrDilation = isStride ? "stride" : "dilation";
1925   if (attrs.size() != op.getNumWindowLoops())
1926     return op.emitOpError("expects num ")
1927            << strideOrDilation
1928            << "s equal to number of window dimensions: " << attrs.size()
1929            << " vs " << op.getNumWindowLoops();
1930   return success();
1931 }
1932 
1933 void ConvOp::getEffects(
1934     SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
1935         &effects) {
1936   effects.emplace_back(MemoryEffects::Read::get(), input(),
1937                        SideEffects::DefaultResource::get());
1938   effects.emplace_back(MemoryEffects::Read::get(), filter(),
1939                        SideEffects::DefaultResource::get());
1940   effects.emplace_back(MemoryEffects::Write::get(), output(),
1941                        SideEffects::DefaultResource::get());
1942 }
1943 
1944 static LogicalResult verify(ConvOp op) {
1945   auto oType = op.output().getType().cast<MemRefType>();
1946   auto fType = op.filter().getType().cast<MemRefType>();
1947   auto iType = op.input().getType().cast<MemRefType>();
1948   if (oType.getElementType() != iType.getElementType() ||
1949       oType.getElementType() != fType.getElementType())
1950     return op.emitOpError("expects memref elemental types to match");
1951   if (oType.getRank() != iType.getRank() || oType.getRank() != fType.getRank())
1952     return op.emitOpError("expects memref ranks to match");
1953   if (auto strides = op.strides()) {
1954     if (failed(
1955             verifyStrideOrDilation(op, strides->getValue(), /*isStride=*/true)))
1956       return failure();
1957   }
1958   if (auto dilations = op.dilations()) {
1959     if (failed(verifyStrideOrDilation(op, dilations->getValue(),
1960                                       /*isStride=*/false)))
1961       return failure();
1962   }
1963   return success();
1964 }
1965 
1966 template <typename PoolingOp>
1967 static LogicalResult verifySingleInputPoolingOp(PoolingOp op) {
1968   auto inputType = op.input().getType().template cast<MemRefType>();
1969   auto outputType = op.output().getType().template cast<MemRefType>();
1970   if (outputType.getElementType() != inputType.getElementType())
1971     return op.emitOpError("expects memref elemental types to match");
1972 
1973   auto windowDimsType = op.windowDims().getType().template cast<MemRefType>();
1974   if (outputType.getRank() != inputType.getRank() ||
1975       outputType.getRank() != windowDimsType.getRank())
1976     return op.emitOpError("expects memref ranks to match");
1977 
1978   if (auto strides = op.strides()) {
1979     if (failed(
1980             verifyStrideOrDilation(op, strides->getValue(), /*isStride=*/true)))
1981       return failure();
1982   }
1983   if (auto dilations = op.dilations()) {
1984     if (failed(verifyStrideOrDilation(op, dilations->getValue(),
1985                                       /*isStride=*/false)))
1986       return failure();
1987   }
1988   return success();
1989 }
1990 
1991 #define DEFINE_POOLING_OP_GET_EFFECTS(OP_NAME)                                 \
1992   void OP_NAME::getEffects(                                                    \
1993       SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>      \
1994           &effects) {                                                          \
1995     effects.emplace_back(MemoryEffects::Read::get(), input(),                  \
1996                          SideEffects::DefaultResource::get());                 \
1997     effects.emplace_back(MemoryEffects::Write::get(), output(),                \
1998                          SideEffects::DefaultResource::get());                 \
1999   }
2000 
2001 static LogicalResult verify(PoolingMaxOp op) {
2002   return verifySingleInputPoolingOp(op);
2003 }
2004 static LogicalResult verify(PoolingMinOp op) {
2005   return verifySingleInputPoolingOp(op);
2006 }
2007 static LogicalResult verify(PoolingSumOp op) {
2008   return verifySingleInputPoolingOp(op);
2009 }
2010 
2011 DEFINE_POOLING_OP_GET_EFFECTS(PoolingMaxOp)
2012 DEFINE_POOLING_OP_GET_EFFECTS(PoolingMinOp)
2013 DEFINE_POOLING_OP_GET_EFFECTS(PoolingSumOp)
2014 
2015 namespace {
2016 struct EraseDeadLinalgOp;
2017 struct FoldTensorCastOp;
2018 } // namespace
2019 
2020 #include "mlir/Dialect/Linalg/IR/LinalgNamedStructuredOps.tcgen.cpp.inc"
2021 #include "mlir/Dialect/Linalg/IR/LinalgNamedStructuredOps.yamlgen.cpp.inc"
2022 
2023 #define GET_OP_CLASSES
2024 #include "mlir/Dialect/Linalg/IR/LinalgOps.cpp.inc"
2025 
2026 #define GET_OP_CLASSES
2027 #include "mlir/Dialect/Linalg/IR/LinalgStructuredOps.cpp.inc"
2028 
2029 #define GET_OP_CLASSES
2030 #include "mlir/Dialect/Linalg/IR/LinalgSparseOps.cpp.inc"
2031 
2032 /// Return the dims that are `iteratorTypeName` loops in the LinalgOp `op`.
2033 /// Assumes `op` is a LinalgOp.
2034 void mlir::linalg::getDimsOfType(Operation *op, StringRef iteratorTypeName,
2035                                  SmallVectorImpl<AffineExpr> &res) {
2036   if (!cast<LinalgOp>(op).iterator_types())
2037     return;
2038 
2039   unsigned dim = 0;
2040   MLIRContext *ctx = op->getContext();
2041   for (auto tn :
2042        cast<LinalgOp>(op).iterator_types().getAsValueRange<StringAttr>()) {
2043     if (tn == iteratorTypeName)
2044       res.push_back(getAffineDimExpr(dim, ctx));
2045     ++dim;
2046   }
2047 }
2048 
2049 AffineMap mlir::linalg::extractOrIdentityMap(Optional<AffineMap> maybeMap,
2050                                              unsigned rank,
2051                                              MLIRContext *context) {
2052   if (maybeMap)
2053     return maybeMap.getValue();
2054   if (rank == 0)
2055     return AffineMap::get(context);
2056   return AffineMap::getMultiDimIdentityMap(rank, context);
2057 }
2058 
2059 SmallVector<AffineExpr, 4>
2060 mlir::linalg::makeAffineDimExprs(unsigned num, unsigned &startIdx,
2061                                  MLIRContext *context) {
2062   SmallVector<AffineExpr, 4> res;
2063   res.reserve(num);
2064   for (unsigned i = 0; i < num; ++i)
2065     res.push_back(getAffineDimExpr(startIdx++, context));
2066   return res;
2067 }
2068 
2069 template <typename PoolingOp>
2070 SmallVector<AffineExpr, 4>
2071 mlir::linalg::weightedPoolingInputIndex(PoolingOp op,
2072                                         ArrayRef<AffineExpr> outputDims,
2073                                         ArrayRef<AffineExpr> windowDims) {
2074   assert(outputDims.size() == windowDims.size());
2075   SmallVector<AffineExpr, 4> res;
2076   res.reserve(outputDims.size());
2077   for (unsigned i = 0, e = outputDims.size(); i < e; ++i) {
2078     // TODO: add a level of indirection to linalg.generic.
2079     auto expr = op.getStride(i) * outputDims[i] +
2080                 op.getDilation(i) * windowDims[i] - op.getLowPad(i);
2081     res.push_back(expr);
2082   }
2083   return res;
2084 }
2085 
2086 #define INSTANTIATE_WEIGHTED_POOLING_INPUT_INDEX(OP_TYPE)                      \
2087   template SmallVector<AffineExpr, 4>                                          \
2088   mlir::linalg::weightedPoolingInputIndex<OP_TYPE>(                            \
2089       OP_TYPE op, ArrayRef<AffineExpr> outputDims,                             \
2090       ArrayRef<AffineExpr> windowDims);
2091 
2092 INSTANTIATE_WEIGHTED_POOLING_INPUT_INDEX(ConvOp)
2093 INSTANTIATE_WEIGHTED_POOLING_INPUT_INDEX(PoolingMaxOp)
2094 INSTANTIATE_WEIGHTED_POOLING_INPUT_INDEX(PoolingMinOp)
2095 INSTANTIATE_WEIGHTED_POOLING_INPUT_INDEX(PoolingSumOp)
2096 
2097 SmallVector<AffineExpr, 4> mlir::linalg::concat(ArrayRef<AffineExpr> a,
2098                                                 ArrayRef<AffineExpr> b) {
2099   auto rangeA = llvm::make_range(a.begin(), a.end());
2100   auto rangeB = llvm::make_range(b.begin(), b.end());
2101   auto concatRanges = llvm::concat<const AffineExpr>(rangeA, rangeB);
2102   return llvm::to_vector<4>(concatRanges);
2103 }
2104 
2105 static void appendMangledType(llvm::raw_string_ostream &ss, Type t) {
2106   if (auto memref = t.dyn_cast<MemRefType>()) {
2107     ss << "view";
2108     for (auto size : memref.getShape())
2109       if (size < 0)
2110         ss << "sx";
2111       else
2112         ss << size << "x";
2113     appendMangledType(ss, memref.getElementType());
2114   } else if (auto vec = t.dyn_cast<VectorType>()) {
2115     ss << "vector";
2116     llvm::interleave(
2117         vec.getShape(), [&](int64_t i) { ss << i; }, [&]() { ss << "x"; });
2118     appendMangledType(ss, vec.getElementType());
2119   } else if (t.isSignlessIntOrIndexOrFloat()) {
2120     ss << t;
2121   } else {
2122     llvm_unreachable("Invalid type for linalg library name mangling");
2123   }
2124 }
2125 
2126 std::string mlir::linalg::generateLibraryCallName(Operation *op) {
2127   assert(isa<LinalgOp>(op));
2128   std::string name(op->getName().getStringRef().str());
2129   name.reserve(128);
2130   std::replace(name.begin(), name.end(), '.', '_');
2131   llvm::raw_string_ostream ss(name);
2132   ss << "_";
2133   auto types = op->getOperandTypes();
2134   llvm::interleave(
2135       types.begin(), types.end(), [&](Type t) { appendMangledType(ss, t); },
2136       [&]() { ss << "_"; });
2137   return ss.str();
2138 }
2139 
2140 // TODO: Consider making all this boilerplate easy to autogenerate
2141 // with Tablegen. This seems a desirable property in the context of
2142 // OpInterfaces where a Linalg "named" op **isa** LinalgOp.
2143 OpFoldResult ReshapeOp::fold(ArrayRef<Attribute> operands) {
2144   if (succeeded(foldMemRefCast(*this)))
2145     return getResult();
2146   return foldReshapeOp(*this, operands);
2147 }
2148 OpFoldResult TensorReshapeOp::fold(ArrayRef<Attribute> operands) {
2149   return foldReshapeOp(*this, operands);
2150 }
2151 
2152 //===----------------------------------------------------------------------===//
2153 // Support for named Linalg ops defined in ods-gen.
2154 //===----------------------------------------------------------------------===//
2155 
2156 /// Generic entry point to create the block for the region of a LinalgOp.
2157 /// This is used by both named structured ops created by ods-gen and by manually
2158 /// defined C++ ops.
2159 /// This is used by both builders and parsers.
2160 /// This function creates the block in the region with arguments corresponding
2161 /// to the elemental types of `inputTypes` and `outputTypes`, which are asserted
2162 /// to be ShapedType.
2163 template <typename NamedStructuredOpType>
2164 static void
2165 fillStructuredOpRegion(OpBuilder &opBuilder, Region &region,
2166                        TypeRange inputTypes, TypeRange outputTypes,
2167                        ValueRange captures,
2168                        std::function<void(unsigned, unsigned)> errorHandler) {
2169   assert(llvm::all_of(inputTypes, [](Type t) { return t.isa<ShapedType>(); }));
2170   assert(llvm::all_of(outputTypes, [](Type t) { return t.isa<ShapedType>(); }));
2171 
2172   // TODO: atm all operands go through getElementTypeOrSelf,
2173   // reconsider when we have evidence we need to.
2174   SmallVector<Type, 8> argTypes;
2175   for (auto containers : {inputTypes, outputTypes})
2176     for (auto t : containers)
2177       argTypes.push_back(getElementTypeOrSelf(t));
2178 
2179   // RAII.
2180   OpBuilder::InsertionGuard guard(opBuilder);
2181   Block *body = opBuilder.createBlock(&region, /*insertPt=*/{}, argTypes);
2182   unsigned actual = body->getNumArguments();
2183   unsigned expected = NamedStructuredOpType::getNumRegionArgs();
2184   if (expected != actual) {
2185     if (errorHandler)
2186       errorHandler(expected, actual);
2187     return;
2188   }
2189 
2190   opBuilder.setInsertionPointToStart(body);
2191   mlir::edsc::ScopedContext scope(opBuilder, opBuilder.getUnknownLoc());
2192   NamedStructuredOpType::regionBuilder(*body, captures);
2193 
2194   // indexing_maps is an auto-generated method.
2195 
2196   // iterator_types is an auto-generated method.
2197 }
2198 
2199 /// Generic entry point to create both the region and the block of a LinalgOp.
2200 template <typename NamedStructuredOpType>
2201 void createAndFillStructuredOpRegion(OpBuilder &opBuilder,
2202                                      OperationState &result,
2203                                      TypeRange inputTypes,
2204                                      TypeRange outputTypes,
2205                                      ValueRange captures) {
2206   Region &region = *result.addRegion();
2207   fillStructuredOpRegion<NamedStructuredOpType>(
2208       opBuilder, region, inputTypes, outputTypes, captures,
2209       [&](unsigned expected, unsigned actual) {
2210         assert(expected != actual && "incorrect number of arguments");
2211       });
2212 }
2213 
2214 /// Common parsing used for both named structured ops created by ods-gen and by
2215 /// manually defined C++ ops. Does not handle regions.
2216 static ParseResult
2217 parseCommonStructuredOpParts(OpAsmParser &parser, OperationState &result,
2218                              SmallVectorImpl<Type> &inputTypes,
2219                              SmallVectorImpl<Type> &outputTypes) {
2220   llvm::SMLoc inputsOperandsLoc, outputsOperandsLoc;
2221   SmallVector<OpAsmParser::OperandType, 4> inputsOperands, outputsOperands;
2222 
2223   parser.parseOptionalAttrDict(result.attributes);
2224 
2225   if (succeeded(parser.parseOptionalKeyword("ins"))) {
2226     if (parser.parseLParen())
2227       return failure();
2228 
2229     inputsOperandsLoc = parser.getCurrentLocation();
2230     if (parser.parseOperandList(inputsOperands) ||
2231         parser.parseColonTypeList(inputTypes) || parser.parseRParen())
2232       return failure();
2233   }
2234 
2235   if (succeeded(parser.parseOptionalKeyword("outs"))) {
2236     outputsOperandsLoc = parser.getCurrentLocation();
2237     if (parser.parseLParen() || parser.parseOperandList(outputsOperands) ||
2238         parser.parseColonTypeList(outputTypes) || parser.parseRParen())
2239       return failure();
2240   }
2241 
2242   if (parser.resolveOperands(inputsOperands, inputTypes, inputsOperandsLoc,
2243                              result.operands) ||
2244       parser.resolveOperands(outputsOperands, outputTypes, outputsOperandsLoc,
2245                              result.operands))
2246     return failure();
2247 
2248   result.addAttribute("operand_segment_sizes",
2249                       parser.getBuilder().getI32VectorAttr(
2250                           {static_cast<int32_t>(inputsOperands.size()),
2251                            static_cast<int32_t>(outputsOperands.size())}));
2252   return success();
2253 }
2254 
2255 template <typename NamedStructuredOpType>
2256 static void printCommonStructuredOpParts(OpAsmPrinter &p,
2257                                          NamedStructuredOpType op) {
2258   if (!op.inputs().empty())
2259     p << " ins(" << op.inputs() << " : " << op.inputs().getTypes() << ")";
2260   if (!op.outputs().empty())
2261     p << " outs(" << op.outputs() << " : " << op.outputs().getTypes() << ")";
2262 }
2263 
2264 //===----------------------------------------------------------------------===//
2265 // Specific parsing and printing for named structured ops created by ods-gen.
2266 //===----------------------------------------------------------------------===//
2267 
2268 template <typename NamedStructuredOpType>
2269 static ParseResult
2270 parseNamedStructuredOpRegion(OpAsmParser &parser, Region &region,
2271                              TypeRange inputTypes, TypeRange outputTypes,
2272                              ArrayRef<OpAsmParser::OperandType> captures) {
2273   ParseResult res = success();
2274   OpBuilder opBuilder(parser.getBuilder().getContext());
2275   // Resolve `captures` into `capturedValues` at parse time so we can build the
2276   // region with captures.
2277   SmallVector<Value> capturedValues;
2278   fillStructuredOpRegion<NamedStructuredOpType>(
2279       opBuilder, region, inputTypes, outputTypes, capturedValues,
2280       [&](unsigned expected, unsigned actual) {
2281         res = parser.emitError(
2282             parser.getCurrentLocation(),
2283             llvm::formatv("[parseNamedStructuredOpRegion] ods-gen generated "
2284                           "region expects {0} args, got {1}",
2285                           expected, actual));
2286         region.front().dump();
2287       });
2288   return res;
2289 }
2290 
2291 static ParseResult
2292 parseNamedStructuredOpResults(OpAsmParser &parser,
2293                               SmallVectorImpl<Type> &resultTypes) {
2294   if (succeeded(parser.parseOptionalArrow()))
2295     if (parser.parseTypeList(resultTypes))
2296       return failure();
2297   return success();
2298 }
2299 
2300 template <typename NamedStructuredOpType>
2301 static ParseResult
2302 parseNamedStructuredOp(OpAsmParser &parser, OperationState &result,
2303                        ArrayRef<OpAsmParser::OperandType> captures) {
2304   // TODO: Enable when ods-gen supports captures.
2305   assert(captures.empty() && "unexpected captures for named structured ops");
2306   SmallVector<Type, 1> inputTypes, outputTypes;
2307   if (parseCommonStructuredOpParts(parser, result, inputTypes, outputTypes))
2308     return failure();
2309 
2310   // TODO: consider merging results parsing into region parsing.
2311   // Need to wait for declarative assembly resolution to decide.
2312   SmallVector<Type, 1> outputTensorsTypes;
2313   if (parseNamedStructuredOpResults(parser, outputTensorsTypes))
2314     return failure();
2315   result.addTypes(outputTensorsTypes);
2316 
2317   std::unique_ptr<Region> region = std::make_unique<Region>();
2318   if (parseNamedStructuredOpRegion<NamedStructuredOpType>(
2319           parser, *region, inputTypes, outputTypes, captures))
2320     return failure();
2321   result.addRegion(std::move(region));
2322 
2323   return success();
2324 }
2325 
2326 static void printNamedStructuredOpResults(OpAsmPrinter &p,
2327                                           TypeRange resultTypes) {
2328   if (resultTypes.empty())
2329     return;
2330   p.printOptionalArrowTypeList(resultTypes);
2331 }
2332 
2333 template <typename NamedStructuredOpType>
2334 static void printNamedStructuredOp(OpAsmPrinter &p, NamedStructuredOpType op) {
2335   p << op.getOperationName();
2336   p.printOptionalAttrDict(
2337       op->getAttrs(),
2338       /*elidedAttrs=*/{"operand_segment_sizes",
2339                        // See generated code in mlir-linalg-yaml-gen.cpp
2340                        "linalg.memoized_indexing_maps"});
2341 
2342   // Printing is shared with generic ops, except for the region and
2343   // attributes.
2344   printCommonStructuredOpParts(p, op);
2345 
2346   // Results printing.
2347   printNamedStructuredOpResults(p, op.result_tensors().getTypes());
2348 
2349   // Region is elided.
2350 }
2351 
2352 template <typename NamedStructuredOpType>
2353 static LogicalResult verifyNamedStructuredOp(NamedStructuredOpType op) {
2354   return verifyGenericOp<NamedStructuredOpType>(op);
2355 }
2356 
2357 //===----------------------------------------------------------------------===//
2358 // Canonicalizers and Folders.
2359 //===----------------------------------------------------------------------===//
2360 
2361 namespace {
2362 struct EraseDeadLinalgOp : public RewritePattern {
2363   EraseDeadLinalgOp(PatternBenefit benefit = 1)
2364       : RewritePattern(benefit, MatchAnyOpTypeTag()) {}
2365 
2366   LogicalResult matchAndRewrite(Operation *op,
2367                                 PatternRewriter &rewriter) const override {
2368     auto linalgOp = dyn_cast<LinalgOp>(op);
2369     if (!linalgOp)
2370       return failure();
2371     for (Value v : linalgOp.getShapedOperands()) {
2372       // Linalg "inputs" may be either tensor or memref type.
2373       // tensor<0xelt_type> is a convention that may not always mean
2374       // "0 iterations". Only erase in cases we see memref<...x0x...>.
2375       auto mt = v.getType().dyn_cast<MemRefType>();
2376       if (!mt)
2377         continue;
2378       if (llvm::is_contained(mt.getShape(), 0)) {
2379         rewriter.eraseOp(linalgOp);
2380         return success();
2381       }
2382     }
2383     return failure();
2384   }
2385 };
2386 
2387 struct FoldTensorCastOp : public RewritePattern {
2388   FoldTensorCastOp(PatternBenefit benefit = 1)
2389       : RewritePattern(benefit, MatchAnyOpTypeTag()) {}
2390 
2391   LogicalResult matchAndRewrite(Operation *op,
2392                                 PatternRewriter &rewriter) const override {
2393     auto linalgOp = dyn_cast<LinalgOp>(op);
2394     if (!linalgOp)
2395       return failure();
2396 
2397     // If no operand comes from a tensor::CastOp and can be folded then fail.
2398     bool hasTensorCastOperand =
2399         llvm::any_of(linalgOp.getShapedOperands(), [&](Value v) {
2400           if (v.isa<BlockArgument>())
2401             return false;
2402           auto castOp = v.getDefiningOp<tensor::CastOp>();
2403           return castOp && canFoldIntoConsumerOp(castOp);
2404         });
2405     if (!hasTensorCastOperand)
2406       return failure();
2407 
2408     SmallVector<Type, 4> newResultTypes;
2409     newResultTypes.reserve(op->getNumResults());
2410     SmallVector<Value, 4> newOperands;
2411     newOperands.reserve(op->getNumOperands());
2412     // Inputs may fold.
2413     for (Value v : linalgOp.getInputs()) {
2414       auto tensorCastOp = v.getDefiningOp<tensor::CastOp>();
2415       newOperands.push_back(
2416           canFoldIntoConsumerOp(tensorCastOp) ? tensorCastOp.source() : v);
2417     }
2418     // Init tensors may fold, in which case the resultType must also change.
2419     for (Value v : linalgOp.getOutputs()) {
2420       auto tensorCastOp = v.getDefiningOp<tensor::CastOp>();
2421       bool fold = canFoldIntoConsumerOp(tensorCastOp);
2422       newOperands.push_back(fold ? tensorCastOp.getOperand() : v);
2423       newResultTypes.push_back(newOperands.back().getType());
2424     }
2425     auto extraOperands = linalgOp.getAssumedNonShapedOperands();
2426     newOperands.append(extraOperands.begin(), extraOperands.end());
2427     // Clone op.
2428     Operation *newOp =
2429         linalgOp.clone(rewriter, op->getLoc(), newResultTypes, newOperands);
2430     SmallVector<Value, 4> replacements;
2431     replacements.reserve(newOp->getNumResults());
2432     for (auto result : llvm::zip(op->getResults(), newOp->getResults())) {
2433       Value oldResult = std::get<0>(result);
2434       Value newResult = std::get<1>(result);
2435       if (newResult.getType() != oldResult.getType()) {
2436         replacements.push_back(rewriter.create<tensor::CastOp>(
2437             op->getLoc(), oldResult.getType(), newResult));
2438       } else {
2439         replacements.push_back(newResult);
2440       }
2441     }
2442     rewriter.replaceOp(op, replacements);
2443 
2444     return success();
2445   }
2446 };
2447 
2448 /// Replaces std.dim operations that use the result of a LinalgOp (on tensors)
2449 /// with std.dim operations that use one of the arguments. For example,
2450 ///
2451 /// %0 = linalg.matmul ins(%arg0, %arg1, ...)
2452 /// %1 = dim %0, %c0
2453 ///
2454 /// with
2455 ///
2456 /// %1 = dim %arg0, %c0
2457 ///
2458 /// where possible. With this the result of the `linalg.matmul` is not used in
2459 /// dim operations. If the value produced is replaced with another value (say by
2460 /// tiling `linalg.matmul`) will make the `linalg.matmul` truly dead instead of
2461 /// used in a dim op that would prevent the DCE of this op.
2462 struct ReplaceDimOfLinalgOpResult : public OpRewritePattern<DimOp> {
2463   using OpRewritePattern<DimOp>::OpRewritePattern;
2464 
2465   LogicalResult matchAndRewrite(DimOp dimOp,
2466                                 PatternRewriter &rewriter) const override {
2467     Value dimValue = dimOp.memrefOrTensor();
2468     Optional<int64_t> dimIndex = dimOp.getConstantIndex();
2469     if (!dimIndex)
2470       return failure();
2471     auto linalgOp = dimValue.getDefiningOp<LinalgOp>();
2472     if (!linalgOp)
2473       return failure();
2474 
2475     unsigned resultIndex = dimValue.cast<OpResult>().getResultNumber();
2476     Optional<Value> operandDimValue = linalgOp.inferResultDimFromInputShapes(
2477         rewriter, dimOp.getLoc(), resultIndex,
2478         static_cast<unsigned>(*dimIndex));
2479     if (!operandDimValue) {
2480       // Its always possible to replace using the corresponding `outs`
2481       // parameter.
2482       operandDimValue = rewriter.create<DimOp>(
2483           dimOp.getLoc(), linalgOp.getOutput(resultIndex), *dimIndex);
2484     }
2485     rewriter.replaceOp(dimOp, *operandDimValue);
2486     return success();
2487   }
2488 };
2489 
2490 } // namespace
2491 
2492 namespace {
2493 // Deduplicate redundant args of a linalg op.
2494 // An arg is redundant if it has the same Value and indexing map as another.
2495 struct DeduplicateInputs : public RewritePattern {
2496   DeduplicateInputs(PatternBenefit benefit = 1)
2497       : RewritePattern(benefit, MatchAnyOpTypeTag()) {}
2498 
2499   LogicalResult matchAndRewrite(Operation *op,
2500                                 PatternRewriter &rewriter) const override {
2501     // This pattern reduces the number of arguments of an op, which breaks
2502     // the invariants of semantically charged named ops.
2503     if (!isa<GenericOp, IndexedGenericOp>(op))
2504       return failure();
2505     auto linalgOp = cast<LinalgOp>(op);
2506 
2507     // Associate each input to an equivalent "canonical" input that has the same
2508     // Value and indexing map.
2509     //
2510     // In the non-duplicate case, input `i` will have canonical input `i`. But
2511     // in the case of duplicated inputs, the canonical input could be some other
2512     // input `< i`. That is, a later input will have some earlier input as its
2513     // canonical input.
2514     llvm::SmallDenseMap<std::pair<Value, AffineMap>, int> canonicalInput;
2515     // For later remapping tasks like deduplicating payload block arguments,
2516     // having a simple "inputIndex -> canonicalInputIndex" integer mapping is
2517     // convenient.
2518     SmallVector<int, 6> canonicalInputIndices;
2519     for (int i = 0, e = linalgOp.getNumInputs(); i != e; i++) {
2520       Value input = linalgOp.getInput(i);
2521       AffineMap indexingMap = linalgOp.getInputIndexingMap(i);
2522       // STL-like maps have a convenient behavior for our use case here. In the
2523       // case of duplicate keys, the insertion is rejected, and the returned
2524       // iterator gives access to the value already in the map.
2525       auto pair = canonicalInput.insert({{input, indexingMap}, i});
2526       canonicalInputIndices.push_back(pair.first->second);
2527     }
2528 
2529     // If there are no duplicate args, then bail out.
2530     if (canonicalInput.size() == linalgOp.getNumInputs())
2531       return failure();
2532 
2533     // The operands for the newly canonicalized op.
2534     SmallVector<Value, 6> newOperands;
2535     for (auto v : llvm::enumerate(linalgOp.getInputs()))
2536       if (canonicalInputIndices[v.index()] == static_cast<int>(v.index()))
2537         newOperands.push_back(v.value());
2538     llvm::append_range(newOperands, linalgOp.getOutputs());
2539     llvm::append_range(newOperands, linalgOp.getAssumedNonShapedOperands());
2540 
2541     // Clone the old op with new operands.
2542     Operation *newOp = linalgOp.clone(rewriter, op->getLoc(),
2543                                       op->getResultTypes(), newOperands);
2544     auto newLinalgOp = cast<LinalgOp>(newOp);
2545 
2546     // Repair the indexing maps by filtering out the ones that have been
2547     // eliminated.
2548     SmallVector<AffineMap, 6> newIndexingMaps;
2549     for (int i = 0, e = newLinalgOp.getNumInputs(); i != e; i++)
2550       if (canonicalInputIndices[i] == i)
2551         newIndexingMaps.push_back(newLinalgOp.getIndexingMap(i));
2552     for (int i = 0, e = newLinalgOp.getNumOutputs(); i != e; i++)
2553       newIndexingMaps.push_back(newLinalgOp.getOutputIndexingMap(i));
2554     newOp->setAttr("indexing_maps",
2555                    rewriter.getAffineMapArrayAttr(newIndexingMaps));
2556 
2557     // Set the number of inputs to the new value. The `clone` call above kept
2558     // the value from the original op.
2559     newLinalgOp.setNumInputs(canonicalInput.size());
2560 
2561     // linalg.indexed_generic payloads have additional arguments prepended to
2562     // the block arg list.
2563     int bbArgBaseOffset = newLinalgOp.getNumPayloadInductionVariables();
2564 
2565     // Repair the payload entry block by RAUW'ing redundant arguments and
2566     // erasing them.
2567     Block &payload = newOp->getRegion(0).front();
2568     for (int i = 0, e = linalgOp.getNumInputs(); i < e; i++) {
2569       // Iterate in reverse, so that we erase later args first, preventing the
2570       // argument list from shifting unexpectedly and invalidating all our
2571       // indices.
2572       int reversed = e - i - 1;
2573       int canonicalIndex = canonicalInputIndices[reversed];
2574       if (canonicalInputIndices[reversed] == reversed)
2575         continue;
2576       payload.getArgument(bbArgBaseOffset + reversed)
2577           .replaceAllUsesWith(
2578               payload.getArgument(bbArgBaseOffset + canonicalIndex));
2579       payload.eraseArgument(bbArgBaseOffset + reversed);
2580     }
2581 
2582     rewriter.replaceOp(op, newOp->getResults());
2583     return success();
2584   }
2585 };
2586 
2587 /// Remove generic/indexed_generic operations (on tensors) that are just copying
2588 /// the values from inputs to the results. Requirements are
2589 /// 1) All iterator types are parallel
2590 /// 2) The body contains just a yield operation with the yielded values being
2591 ///    the arguments corresponding to the operands.
2592 struct RemoveIdentityLinalgOps : public RewritePattern {
2593   RemoveIdentityLinalgOps(PatternBenefit benefit = 1)
2594       : RewritePattern(benefit, MatchAnyOpTypeTag()) {}
2595 
2596   LogicalResult matchAndRewrite(Operation *op,
2597                                 PatternRewriter &rewriter) const override {
2598     if (auto copyOp = dyn_cast<CopyOp>(op)) {
2599       assert(copyOp.hasBufferSemantics());
2600       if (copyOp.input() == copyOp.output() &&
2601           copyOp.inputPermutation() == copyOp.outputPermutation()) {
2602         rewriter.eraseOp(op);
2603         return success();
2604       }
2605     }
2606 
2607     if (!isa<GenericOp, IndexedGenericOp>(op))
2608       return failure();
2609     LinalgOp genericOp = cast<LinalgOp>(op);
2610     if (!genericOp.hasTensorSemantics())
2611       return failure();
2612     // Check all indexing maps are identity.
2613     if (llvm::any_of(genericOp.getIndexingMaps(),
2614                      [](AffineMap map) { return !map.isIdentity(); }))
2615       return failure();
2616 
2617     // Check that the body of the linalg operation is just a linalg.yield
2618     // operation.
2619     Block &body = op->getRegion(0).front();
2620     if (!llvm::hasSingleElement(body))
2621       return failure();
2622     auto yieldOp = dyn_cast<linalg::YieldOp>(body.getTerminator());
2623     if (!yieldOp)
2624       return failure();
2625 
2626     // Get the argument number of the returned values. That is the operand
2627     // number to use for replacing uses of this operation.
2628     unsigned numIndexArgs = genericOp.getNumPayloadInductionVariables();
2629     SmallVector<Value, 4> returnedArgs;
2630     for (Value yieldVal : yieldOp.values()) {
2631       auto yieldArg = yieldVal.dyn_cast<BlockArgument>();
2632       if (!yieldArg || yieldArg.getOwner() != &body)
2633         return failure();
2634       unsigned argumentNumber = yieldArg.getArgNumber();
2635       if (argumentNumber < numIndexArgs)
2636         return failure();
2637       returnedArgs.push_back(op->getOperand(argumentNumber - numIndexArgs));
2638     }
2639     if (returnedArgs.size() != genericOp.getOperation()->getNumResults())
2640       return failure();
2641     rewriter.replaceOp(genericOp, returnedArgs);
2642     return success();
2643   }
2644 };
2645 } // namespace
2646 
2647 #define CANONICALIZERS_AND_FOLDERS(XXX)                                        \
2648   void XXX::getCanonicalizationPatterns(OwningRewritePatternList &results,     \
2649                                         MLIRContext *context) {                \
2650     results.insert<DeduplicateInputs, EraseDeadLinalgOp, FoldTensorCastOp,     \
2651                    RemoveIdentityLinalgOps>();                                 \
2652     results.insert<ReplaceDimOfLinalgOpResult>(context);                       \
2653   }                                                                            \
2654                                                                                \
2655   LogicalResult XXX::fold(ArrayRef<Attribute>,                                 \
2656                           SmallVectorImpl<OpFoldResult> &) {                   \
2657     return foldMemRefCast(*this);                                              \
2658   }
2659 
2660 CANONICALIZERS_AND_FOLDERS(ConvOp)
2661 CANONICALIZERS_AND_FOLDERS(PoolingMaxOp)
2662 CANONICALIZERS_AND_FOLDERS(PoolingMinOp)
2663 CANONICALIZERS_AND_FOLDERS(PoolingSumOp)
2664 CANONICALIZERS_AND_FOLDERS(CopyOp)
2665 CANONICALIZERS_AND_FOLDERS(FillOp)
2666 CANONICALIZERS_AND_FOLDERS(GenericOp)
2667 CANONICALIZERS_AND_FOLDERS(IndexedGenericOp)
2668 
2669 // All named ops canonicalizers and folders are auto-generated in the
2670 // .cpp.inc.
2671