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