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