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()
451                             : builder.getStringAttr(libraryCall));
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()
506                             : builder.getStringAttr(libraryCall));
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 template <typename GenericOpType>
680 static LogicalResult verifyGenericOp(GenericOpType op) {
681   return success();
682 }
683 
684 static LogicalResult verify(GenericOp op) { return verifyGenericOp(op); }
685 
686 static LogicalResult verify(IndexedGenericOp op) { return verifyGenericOp(op); }
687 
688 namespace {
689 
690 /// Replace indexed_generic ops by generic ops that access the iteration indices
691 /// using index operation calls.
692 struct ConvertIndexedToGenericOp : OpRewritePattern<IndexedGenericOp> {
693   using OpRewritePattern<IndexedGenericOp>::OpRewritePattern;
694   LogicalResult matchAndRewrite(IndexedGenericOp indexedOp,
695                                 PatternRewriter &rewriter) const override {
696     // Replace all uses of the index block arguments.
697     BlockAndValueMapping bvm;
698     if (Block *body = indexedOp.getBody()) {
699       rewriter.setInsertionPointToStart(body);
700       for (const auto &en : llvm::enumerate(
701                body->getArguments().take_front(indexedOp.getNumLoops()))) {
702         Value index = rewriter.create<IndexOp>(indexedOp.getLoc(), en.index());
703         bvm.map(en.value(), index);
704       }
705     }
706 
707     // Create a generic replacement operation and clone the body.
708     rewriter.setInsertionPointAfter(indexedOp);
709     SmallVector<StringRef> iterators = llvm::to_vector<4>(
710         indexedOp.iterator_types().getAsValueRange<StringAttr>());
711     GenericOp genericOp = rewriter.create<GenericOp>(
712         indexedOp.getLoc(), indexedOp->getResultTypes(), indexedOp.getInputs(),
713         indexedOp.getOutputs(), indexedOp.getIndexingMaps(), iterators);
714     Region &genericRegion = genericOp.region();
715     Region &indexedRegion = indexedOp.region();
716     rewriter.cloneRegionBefore(indexedRegion, genericRegion,
717                                genericRegion.begin(), bvm);
718 
719     rewriter.replaceOp(indexedOp, genericOp->getResults());
720     return success();
721   }
722 };
723 } // namespace
724 
725 void IndexedGenericOp::getCanonicalizationPatterns(RewritePatternSet &results,
726                                                    MLIRContext *context) {
727   results.add<ConvertIndexedToGenericOp>(context);
728 }
729 
730 //===----------------------------------------------------------------------===//
731 // InitTensorOp
732 //===----------------------------------------------------------------------===//
733 void InitTensorOp::build(OpBuilder &b, OperationState &result,
734                          ArrayRef<OpFoldResult> sizes, Type elementType,
735                          ArrayRef<NamedAttribute> attrs) {
736   unsigned rank = sizes.size();
737   SmallVector<Value, 4> dynamicSizes;
738   SmallVector<int64_t, 4> staticSizes;
739   for (unsigned i = 0; i < rank; ++i) {
740     dispatchIndexOpFoldResult(sizes[i], dynamicSizes, staticSizes,
741                               ShapedType::kDynamicSize);
742   }
743   auto resultType = RankedTensorType ::get(staticSizes, elementType);
744   build(b, result, resultType, dynamicSizes, b.getI64ArrayAttr(staticSizes));
745   result.addAttributes(attrs);
746 }
747 
748 static LogicalResult verify(InitTensorOp op) {
749   RankedTensorType resultType = op.getType();
750   SmallVector<int64_t, 4> staticSizes = llvm::to_vector<4>(llvm::map_range(
751       op.static_sizes().cast<ArrayAttr>(),
752       [](Attribute a) -> int64_t { return a.cast<IntegerAttr>().getInt(); }));
753 
754   if (failed(verifyListOfOperandsOrIntegers(op, "sizes", resultType.getRank(),
755                                             op.static_sizes(), op.sizes(),
756                                             ShapedType::isDynamic)))
757     return failure();
758 
759   if (op.static_sizes().size() != static_cast<unsigned>(resultType.getRank()))
760     return op->emitError("expected ")
761            << resultType.getRank() << " sizes values";
762 
763   Type expectedType =
764       InitTensorOp::inferResultType(staticSizes, resultType.getElementType());
765   if (resultType != expectedType) {
766     return op.emitError("specified type ")
767            << resultType << " does not match the inferred type "
768            << expectedType;
769   }
770   return success();
771 }
772 
773 Type InitTensorOp::inferResultType(ArrayRef<int64_t> staticSizes,
774                                    Type elementType) {
775   return RankedTensorType::get(staticSizes, elementType);
776 }
777 
778 namespace {
779 /// Change the type of the result of a `linalg.init_tensor` by making the result
780 /// type statically sized along dimension that in the original operation where
781 /// defined as dynamic, but the size was defined using a `constant` op. For
782 /// example
783 ///
784 ///  %c5 = constant 5: index
785 ///  %0 = linalg.init_tensor [%arg0, %c5] : tensor<?x?xf32>
786 ///
787 ///  to
788 ///
789 ///  %0 = linalg.init_tensor [%arg0, 5] : tensor<?x5xf32>
790 struct ReplaceStaticShapeDims : OpRewritePattern<InitTensorOp> {
791   using OpRewritePattern<InitTensorOp>::OpRewritePattern;
792 
793   LogicalResult matchAndRewrite(InitTensorOp op,
794                                 PatternRewriter &rewriter) const override {
795     SmallVector<Value, 4> dynamicSizes;
796     SmallVector<int64_t, 4> staticSizes;
797     for (unsigned i = 0, e = op.getType().getRank(); i != e; ++i) {
798       // If the size is already static, nothing to do.
799       if (!op.isDynamicSize(i)) {
800         staticSizes.push_back(op.getStaticSize(i));
801         continue;
802       }
803 
804       // If the size is dynamic but defined using a `constant` op, get the
805       // constant value to find the static size to use.
806       unsigned operandNum = op.getIndexOfDynamicSize(i);
807       Value sizeOperand = op.getOperand(operandNum);
808       if (auto constantIndexOp = sizeOperand.getDefiningOp<ConstantIndexOp>()) {
809         staticSizes.push_back(constantIndexOp.getValue());
810         continue;
811       }
812 
813       // Fallback case. Keep the size dynamic.
814       dynamicSizes.push_back(sizeOperand);
815       staticSizes.push_back(ShapedType::kDynamicSize);
816     }
817     RankedTensorType newType =
818         RankedTensorType::get(staticSizes, op.getType().getElementType());
819     if (newType == op.getType())
820       return failure();
821     auto newOp =
822         rewriter.create<InitTensorOp>(op.getLoc(), newType, dynamicSizes,
823                                       rewriter.getI64ArrayAttr(staticSizes));
824     rewriter.replaceOpWithNewOp<tensor::CastOp>(op, op.getType(), newOp);
825     return success();
826   }
827 };
828 } // namespace
829 
830 namespace {
831 /// Since `init_tensor` operation creates a tensor needed only for its shape, a
832 /// subtensor of this is also needed only for its shape. The result can be
833 /// replaced by a new init_tensor operation of the same size as the subtensor
834 /// op.
835 struct FoldInitTensorWithSubTensorOp : public OpRewritePattern<SubTensorOp> {
836   using OpRewritePattern<SubTensorOp>::OpRewritePattern;
837 
838   LogicalResult matchAndRewrite(SubTensorOp subtensorOp,
839                                 PatternRewriter &rewriter) const override {
840     if (!subtensorOp.source().getDefiningOp<linalg::InitTensorOp>())
841       return failure();
842     rewriter.replaceOpWithNewOp<linalg::InitTensorOp>(
843         subtensorOp, subtensorOp.sizes(),
844         llvm::to_vector<4>(llvm::map_range(
845             subtensorOp.static_sizes(),
846             [](Attribute attr) { return attr.cast<IntegerAttr>().getInt(); })),
847         subtensorOp.getSourceType().getElementType());
848     return success();
849   }
850 };
851 
852 struct FoldInitTensorWithTensorReshapeOp
853     : public OpRewritePattern<TensorReshapeOp> {
854   using OpRewritePattern<TensorReshapeOp>::OpRewritePattern;
855 
856   LogicalResult matchAndRewrite(TensorReshapeOp reshapeOp,
857                                 PatternRewriter &rewriter) const override {
858     if (!reshapeOp.src().getDefiningOp<InitTensorOp>())
859       return failure();
860     Location loc = reshapeOp.getLoc();
861     SmallVector<SmallVector<Value>, 4> resultShapes;
862     if (failed(reshapeOp.reifyReturnTypeShapesPerResultDim(rewriter,
863                                                            resultShapes)) ||
864         !llvm::hasSingleElement(resultShapes))
865       return failure();
866     Value initTensor = rewriter.create<InitTensorOp>(
867         loc, getAsOpFoldResult(resultShapes[0]),
868         reshapeOp.getResultType().getElementType());
869     if (initTensor.getType() != reshapeOp.getResultType()) {
870       rewriter.replaceOpWithNewOp<tensor::CastOp>(
871           reshapeOp, reshapeOp.getResultType(), initTensor);
872     } else {
873       rewriter.replaceOp(reshapeOp, initTensor);
874     }
875     return success();
876   }
877 };
878 } // namespace
879 
880 void InitTensorOp::getCanonicalizationPatterns(RewritePatternSet &results,
881                                                MLIRContext *context) {
882   results.add<FoldInitTensorWithSubTensorOp, FoldInitTensorWithTensorReshapeOp,
883               ReplaceStaticShapeDims>(context);
884 }
885 
886 LogicalResult InitTensorOp::reifyReturnTypeShapesPerResultDim(
887     OpBuilder &builder,
888     SmallVectorImpl<SmallVector<Value>> &reifiedReturnShapes) {
889   auto shapes = llvm::to_vector<4>(llvm::map_range(
890       llvm::seq<int64_t>(0, getType().getRank()), [&](int64_t dim) -> Value {
891         if (isDynamicSize(dim))
892           return getDynamicSize(dim);
893         return builder.create<ConstantIndexOp>(getLoc(), getStaticSize(dim));
894       }));
895   reifiedReturnShapes.emplace_back(std::move(shapes));
896   return success();
897 }
898 
899 //===----------------------------------------------------------------------===//
900 // PadTensorOp
901 //===----------------------------------------------------------------------===//
902 
903 /// Extract int64_t values from the assumed ArrayAttr of IntegerAttr.
904 static SmallVector<int64_t, 4> extractFromI64ArrayAttr(Attribute attr) {
905   return llvm::to_vector<4>(
906       llvm::map_range(attr.cast<ArrayAttr>(), [](Attribute a) -> int64_t {
907         return a.cast<IntegerAttr>().getInt();
908       }));
909 }
910 
911 static LogicalResult verify(PadTensorOp op) {
912   auto sourceType = op.source().getType().cast<RankedTensorType>();
913   auto resultType = op.result().getType().cast<RankedTensorType>();
914   auto expectedType = PadTensorOp::inferResultType(
915       sourceType, extractFromI64ArrayAttr(op.static_low()),
916       extractFromI64ArrayAttr(op.static_high()));
917   for (int i = 0, e = sourceType.getRank(); i < e; ++i) {
918     if (resultType.getDimSize(i) == expectedType.getDimSize(i))
919       continue;
920     if (expectedType.isDynamicDim(i))
921       continue;
922     return op.emitError("specified type ")
923            << resultType << " does not match the inferred type "
924            << expectedType;
925   }
926 
927   auto &region = op.region();
928   unsigned rank = resultType.getRank();
929   Block &block = region.front();
930   if (block.getNumArguments() != rank)
931     return op.emitError("expected the block to have ") << rank << " arguments";
932 
933   // Note: the number and type of yield values are checked in the YieldOp.
934   for (auto en : llvm::enumerate(block.getArgumentTypes())) {
935     if (!en.value().isIndex())
936       return op.emitOpError("expected block argument ")
937              << (en.index() + 1) << " to be an index";
938   }
939 
940   return success();
941 }
942 
943 RankedTensorType PadTensorOp::inferResultType(RankedTensorType sourceType,
944                                               ArrayRef<int64_t> staticLow,
945                                               ArrayRef<int64_t> staticHigh) {
946   unsigned rank = sourceType.getRank();
947   assert(staticLow.size() == rank && "unexpected staticLow size mismatch");
948   assert(staticHigh.size() == rank && "unexpected staticHigh size mismatch");
949 
950   SmallVector<int64_t, 4> resultShape;
951   for (auto i : llvm::seq<unsigned>(0, rank)) {
952     if (sourceType.isDynamicDim(i) ||
953         staticLow[i] == ShapedType::kDynamicSize ||
954         staticHigh[i] == ShapedType::kDynamicSize) {
955       resultShape.push_back(ShapedType::kDynamicSize);
956     } else {
957       int64_t size = sourceType.getDimSize(i) + staticLow[i] + staticHigh[i];
958       resultShape.push_back(size);
959     }
960   }
961 
962   return RankedTensorType::get(resultShape, sourceType.getElementType());
963 }
964 
965 void PadTensorOp::build(OpBuilder &b, OperationState &result, Value source,
966                         ArrayRef<int64_t> staticLow,
967                         ArrayRef<int64_t> staticHigh, ValueRange low,
968                         ValueRange high, ArrayRef<NamedAttribute> attrs) {
969   auto sourceType = source.getType().cast<RankedTensorType>();
970   auto resultType = inferResultType(sourceType, staticLow, staticHigh);
971   build(b, result, resultType, source, low, high, b.getI64ArrayAttr(staticLow),
972         b.getI64ArrayAttr(staticHigh));
973   result.addAttributes(attrs);
974 }
975 
976 void PadTensorOp::build(OpBuilder &b, OperationState &result, Value source,
977                         ValueRange low, ValueRange high,
978                         ArrayRef<NamedAttribute> attrs) {
979   auto sourceType = source.getType().cast<RankedTensorType>();
980   unsigned rank = sourceType.getRank();
981   SmallVector<int64_t, 4> staticVector(ShapedType::kDynamicSize, rank);
982   build(b, result, source, staticVector, staticVector, low, high, attrs);
983 }
984 
985 void PadTensorOp::build(OpBuilder &b, OperationState &result, Type resultType,
986                         Value source, ArrayRef<OpFoldResult> low,
987                         ArrayRef<OpFoldResult> high,
988                         ArrayRef<NamedAttribute> attrs) {
989   assert(resultType.isa<RankedTensorType>());
990   auto sourceType = source.getType().cast<RankedTensorType>();
991   unsigned rank = sourceType.getRank();
992   SmallVector<Value, 4> dynamicLow, dynamicHigh;
993   SmallVector<int64_t, 4> staticLow, staticHigh;
994   for (unsigned i = 0; i < rank; ++i) {
995     // staticLow and staticHigh have full information of the padding config.
996     // This will grow staticLow and staticHigh with 1 value. If the config is
997     // dynamic (ie not a constant), dynamicLow and dynamicHigh will grow with 1
998     // value as well.
999     dispatchIndexOpFoldResult(low[i], dynamicLow, staticLow,
1000                               ShapedType::kDynamicSize);
1001     dispatchIndexOpFoldResult(high[i], dynamicHigh, staticHigh,
1002                               ShapedType::kDynamicSize);
1003   }
1004   if (!resultType) {
1005     resultType =
1006         PadTensorOp::inferResultType(sourceType, staticLow, staticHigh);
1007   }
1008   build(b, result, resultType, source, dynamicLow, dynamicHigh,
1009         b.getI64ArrayAttr(staticLow), b.getI64ArrayAttr(staticHigh));
1010 }
1011 
1012 PadTensorOp PadTensorOp::createPadScalarOp(Type type, Value source, Value pad,
1013                                            ArrayRef<OpFoldResult> low,
1014                                            ArrayRef<OpFoldResult> high,
1015                                            Location loc, OpBuilder &builder) {
1016   auto padTensorOp =
1017       builder.create<linalg::PadTensorOp>(loc, type, source, low, high);
1018   int rank = padTensorOp.getResultType().getRank();
1019   SmallVector<Type, 4> blockArgTypes;
1020   blockArgTypes.assign(rank, builder.getIndexType());
1021   auto &region = padTensorOp.region();
1022   // `builder.createBlock` changes the insertion point within the block. Create
1023   // a guard to reset the insertion point of the builder after it is destroyed.
1024   OpBuilder::InsertionGuard guard(builder);
1025   builder.createBlock(&region, region.end(), blockArgTypes);
1026   builder.create<linalg::YieldOp>(loc, pad);
1027   return padTensorOp;
1028 }
1029 
1030 PadTensorOp PadTensorOp::createPadHighOp(Type type, Value source, Value pad,
1031                                          Location loc, OpBuilder &builder) {
1032   SmallVector<OpFoldResult, 4> low, high;
1033   auto rankedTensorType = type.cast<RankedTensorType>();
1034   assert(rankedTensorType.hasStaticShape());
1035   int rank = rankedTensorType.getRank();
1036   for (int i = 0; i < rank; ++i) {
1037     auto dimOp = builder.createOrFold<memref::DimOp>(loc, source, i);
1038     auto resultDimSize = builder.createOrFold<ConstantIndexOp>(
1039         loc, rankedTensorType.getDimSize(i));
1040     auto highValue = builder.createOrFold<SubIOp>(loc, resultDimSize, dimOp);
1041     high.push_back(highValue);
1042     low.push_back(builder.createOrFold<ConstantIndexOp>(loc, 0));
1043   }
1044   return PadTensorOp::createPadScalarOp(type, source, pad, low, high, loc,
1045                                         builder);
1046 }
1047 
1048 LogicalResult PadTensorOp::reifyReturnTypeShapesPerResultDim(
1049     OpBuilder &b, SmallVectorImpl<SmallVector<Value>> &reifiedReturnShapes) {
1050   Location loc = getLoc();
1051   auto lowPad = getMixedLowPad();
1052   auto highPad = getMixedHighPad();
1053   SmallVector<Value> shapes;
1054   for (auto dim : llvm::seq<int64_t>(0, getSourceType().getRank())) {
1055     // Shape along each dimension is source dim + low pad + high pad.
1056     SmallVector<Value> mapOperands;
1057     mapOperands.push_back(b.createOrFold<memref::DimOp>(loc, source(), dim));
1058     AffineExpr expr = b.getAffineDimExpr(0);
1059     unsigned numSymbols = 0;
1060     auto addOpFoldResult = [&](OpFoldResult valueOrAttr) {
1061       if (Value v = valueOrAttr.dyn_cast<Value>()) {
1062         expr = expr + b.getAffineSymbolExpr(numSymbols++);
1063         mapOperands.push_back(v);
1064         return;
1065       }
1066       int64_t staticValue =
1067           valueOrAttr.get<Attribute>().cast<IntegerAttr>().getInt();
1068       expr = expr + staticValue;
1069     };
1070     addOpFoldResult(lowPad[dim]);
1071     addOpFoldResult(highPad[dim]);
1072     shapes.push_back(applyMapToValues(
1073         b, loc, AffineMap::get(1, numSymbols, expr), mapOperands)[0]);
1074   }
1075   reifiedReturnShapes.emplace_back(std::move(shapes));
1076   return success();
1077 }
1078 
1079 //===----------------------------------------------------------------------===//
1080 // ReshapeOp
1081 //===----------------------------------------------------------------------===//
1082 
1083 Optional<SmallVector<ReassociationIndices>>
1084 mlir::linalg::getReassociationIndicesForReshape(ShapedType sourceType,
1085                                                 ShapedType targetType) {
1086   // Make the sourceType greater rank than the targetType. If they are same
1087   // rank, then its an unsupported reshape op.
1088   if (sourceType.getRank() == targetType.getRank())
1089     return llvm::None;
1090   if (sourceType.getRank() < targetType.getRank())
1091     std::swap(sourceType, targetType);
1092 
1093   ArrayRef<int64_t> sourceShape = sourceType.getShape();
1094   ArrayRef<int64_t> targetShape = targetType.getShape();
1095   unsigned sourceDim = 0;
1096   SmallVector<ReassociationIndices> reassociationMap;
1097   reassociationMap.reserve(targetType.getRank());
1098 
1099   ReassociationIndices currIndices;
1100   int64_t prodOfCollapsedDims = 1;
1101   while (sourceDim < sourceShape.size()) {
1102     unsigned targetDim = reassociationMap.size();
1103 
1104     // If all the dimensions of the targetShape are exhausted, then the
1105     // remaining dims in the source shape must be all 1s. So for such cases, set
1106     // 1 as the target shape. The actual reassociation indices will be handled
1107     // later.
1108     int64_t currTargetShape =
1109         (targetDim < targetType.getRank() ? targetShape[targetDim] : 1);
1110     while (sourceShape[sourceDim] != ShapedType::kDynamicSize &&
1111            prodOfCollapsedDims * sourceShape[sourceDim] < currTargetShape &&
1112            sourceDim < sourceShape.size()) {
1113       prodOfCollapsedDims *= sourceShape[sourceDim];
1114       currIndices.push_back(sourceDim++);
1115     }
1116 
1117     // If the current expanded dimension is dynamic, then the collapsed
1118     // dimensions should also be dynamic and product of all previous unprocessed
1119     // dimensions of the expanded shape should be 1.
1120     if (sourceShape[sourceDim] == ShapedType::kDynamicSize &&
1121         (currTargetShape != ShapedType::kDynamicSize ||
1122          prodOfCollapsedDims != 1))
1123       return llvm::None;
1124 
1125     // If the collapsed dim is dynamic, the current expanded dim should also
1126     // be dynamic.
1127     if (currTargetShape == ShapedType::kDynamicSize &&
1128         sourceShape[sourceDim] != ShapedType::kDynamicSize)
1129       return llvm::None;
1130 
1131     // For static shapes, if the product of dimensions of the expanded shape
1132     // should match the collapsed dimension shape.
1133     if (prodOfCollapsedDims * sourceShape[sourceDim] != currTargetShape)
1134       return llvm::None;
1135 
1136     currIndices.push_back(sourceDim++);
1137     // If the reassociation is empty but the currIndices is not, this by
1138     // definition is folding unit-dimensions with the result being scalar type.
1139     // So only append the `currIndices` if reassociation map is not empty.
1140     if (targetDim == targetShape.size()) {
1141       if (!reassociationMap.empty() && !currIndices.empty())
1142         reassociationMap.back().append(currIndices.begin(), currIndices.end());
1143       // Break out of the loops. We should be done here.
1144       break;
1145     }
1146     reassociationMap.emplace_back(ReassociationIndices{});
1147     std::swap(reassociationMap.back(), currIndices);
1148     prodOfCollapsedDims = 1;
1149   }
1150   // All the dimensions in the two shapes must have been processed.
1151   if (reassociationMap.size() != targetShape.size() ||
1152       sourceDim != sourceShape.size())
1153     return llvm::None;
1154   return reassociationMap;
1155 }
1156 
1157 template <typename ReshapeLikeOp>
1158 static void print(OpAsmPrinter &p, ReshapeLikeOp op) {
1159   p << op.getOperationName() << ' ' << op.src() << " [";
1160 
1161   llvm::interleaveComma(op.reassociation(), p, [&](const Attribute &attr) {
1162     p << '[';
1163     auto arrayAttr = attr.template cast<ArrayAttr>();
1164     llvm::interleaveComma(arrayAttr, p, [&](const Attribute &attr) {
1165       p << attr.cast<IntegerAttr>().getInt();
1166     });
1167     p << ']';
1168   });
1169 
1170   p << "] ";
1171   p.printOptionalAttrDict(op->getAttrs(),
1172                           /*elidedAttrs=*/{op.getReassociationAttrName()});
1173   p << ": " << op.src().getType() << " into " << op.getType();
1174 }
1175 
1176 static void print(OpAsmPrinter &p, linalg::ReshapeOp op) {
1177   print<linalg::ReshapeOp>(p, op);
1178 }
1179 
1180 static void print(OpAsmPrinter &p, linalg::TensorReshapeOp op) {
1181   print<linalg::TensorReshapeOp>(p, op);
1182 }
1183 
1184 static ParseResult parseReshapeLikeOp(OpAsmParser &parser,
1185                                       OperationState &result) {
1186   // Parse the operand.
1187   OpAsmParser::OperandType src;
1188   if (parser.parseOperand(src))
1189     return failure();
1190 
1191   // Parse reassociation indices.
1192   Builder &b = parser.getBuilder();
1193   SmallVector<Attribute, 4> reassociation;
1194   if (parser.parseLSquare())
1195     return failure();
1196 
1197   while (true) {
1198     if (succeeded(parser.parseOptionalRSquare()))
1199       break;
1200     if (parser.parseLSquare())
1201       return failure();
1202     SmallVector<int64_t> indices;
1203     while (true) {
1204       int64_t index;
1205       if (parser.parseInteger(index))
1206         return failure();
1207       indices.push_back(index);
1208 
1209       if (succeeded(parser.parseOptionalComma()))
1210         continue;
1211       if (failed(parser.parseRSquare()))
1212         return failure();
1213       break;
1214     }
1215     reassociation.push_back(b.getI64ArrayAttr(indices));
1216     if (succeeded(parser.parseOptionalComma()))
1217       continue;
1218     if (failed(parser.parseRSquare()))
1219       return failure();
1220     break;
1221   }
1222 
1223   result.addAttribute(ReshapeOp::getReassociationAttrName(),
1224                       b.getArrayAttr(reassociation));
1225 
1226   // Parse optional attributes.
1227   parser.parseOptionalAttrDict(result.attributes);
1228 
1229   // Parse types.
1230   Type srcType;
1231   Type resultType;
1232   if (parser.parseColon() || parser.parseType(srcType) ||
1233       parser.resolveOperand(src, srcType, result.operands) ||
1234       parser.parseKeyword("into") || parser.parseType(resultType))
1235     return failure();
1236   result.addTypes(resultType);
1237   return success();
1238 }
1239 
1240 /// Collapse reassociation maps that are used in pair of reshape ops where one
1241 /// is a producer and other is the consumer. Only valid to use this method when
1242 /// both the producer and consumer are collapsing dimensions or both are
1243 /// expanding dimensions.
1244 ///
1245 /// For example,
1246 ///   mapsProducer = [affine_map<(d0, d1, d2, d3, d4) -> (d0, d1)>,
1247 ///                   affine_map<(d0, d1, d2, d3, d4) -> (d2)>,
1248 ///                   affine_map<(d0, d1, d2, d3, d4) -> (d3, d4)>]
1249 ///   mapsConsumer = [affine_map<(d0, d1, d2) -> (d0, d1)>,
1250 ///                   affine_map<(d0, d1, d2) -> (d2)>]
1251 ///
1252 /// is folded into
1253 ///
1254 ///   result = [affine_map<(d0, d1, d2, d3, d4) -> (d0, d1, d2)>,
1255 ///             affine_map<(d0, d1, d2, d3, d4) -> (d3, d4)>]
1256 static Optional<SmallVector<ReassociationIndices>>
1257 collapseReassociationIndices(ArrayRef<AffineMap> mapsProducer,
1258                              ArrayRef<AffineMap> mapsConsumer,
1259                              MLIRContext *context) {
1260   // Make the producer the larger sized vector. If they are of same size, the
1261   // resulting reshape is not a supported reshape op.
1262   if (mapsProducer.size() == mapsConsumer.size())
1263     return llvm::None;
1264   if (mapsProducer.size() < mapsConsumer.size())
1265     std::swap(mapsProducer, mapsConsumer);
1266 
1267   // Handle the corner case of the result being a rank 0 shaped type. Return an
1268   // empty reassociation.
1269   if (mapsConsumer.empty())
1270     return SmallVector<ReassociationIndices>{};
1271   if (mapsProducer.size() != mapsConsumer[0].getNumDims())
1272     return llvm::None;
1273 
1274   unsigned currDim = 0;
1275   SmallVector<ReassociationIndices> reassociationMaps;
1276   for (AffineMap rhs : mapsConsumer) {
1277     ReassociationIndices reassociations;
1278     for (AffineExpr rhsExpr : rhs.getResults()) {
1279       AffineDimExpr dimExpr = rhsExpr.cast<AffineDimExpr>();
1280       for (int i = 0, e = mapsProducer[dimExpr.getPosition()].getNumResults();
1281            i < e; ++i)
1282         reassociations.push_back(currDim++);
1283     }
1284     reassociationMaps.push_back(std::move(reassociations));
1285   }
1286   return reassociationMaps;
1287 }
1288 
1289 namespace {
1290 /// Pattern to collapse producer/consumer reshape ops that are both collapsing
1291 /// dimensions or are both expanding dimensions.
1292 template <typename ReshapeOpTy>
1293 struct CollapseReshapeOps : public OpRewritePattern<ReshapeOpTy> {
1294   using OpRewritePattern<ReshapeOpTy>::OpRewritePattern;
1295   LogicalResult matchAndRewrite(ReshapeOpTy reshapeOp,
1296                                 PatternRewriter &rewriter) const override {
1297     auto srcReshapeOp = reshapeOp.src().template getDefiningOp<ReshapeOpTy>();
1298     if (!srcReshapeOp)
1299       return failure();
1300 
1301     ShapedType srcReshapeSrcType = srcReshapeOp.getSrcType();
1302     ShapedType intermediateType = reshapeOp.getSrcType();
1303     ShapedType resultType = reshapeOp.getResultType();
1304 
1305     auto areReshapeOpsFoldable = [](ShapedType largerType,
1306                                     ShapedType intermediateType,
1307                                     ShapedType smallerType) -> bool {
1308       return largerType.getRank() > intermediateType.getRank() &&
1309              intermediateType.getRank() > smallerType.getRank();
1310     };
1311     Optional<SmallVector<ReassociationIndices>> reassociationIndices =
1312         llvm::None;
1313     // Check if producer and consumer are both expanding dims or both collapsing
1314     // dims. In this case, try to compose the affine maps. This works for
1315     // dynamic shapes too.
1316     if (areReshapeOpsFoldable(resultType, intermediateType,
1317                               srcReshapeSrcType) ||
1318         areReshapeOpsFoldable(srcReshapeSrcType, intermediateType,
1319                               resultType)) {
1320       reassociationIndices = collapseReassociationIndices(
1321           srcReshapeOp.getReassociationMaps(), reshapeOp.getReassociationMaps(),
1322           rewriter.getContext());
1323     }
1324     if (!reassociationIndices) {
1325       // If the source reshape can be collapsed/expanded into the target reshape
1326       // they can still be folded. This can only be reasoned about statically
1327       // for cases where
1328       // - either all shapes are static, or
1329       // - The number of dynamic dimensions matches in the source of source and
1330       //   result with all other dimensions being 1.
1331       reassociationIndices =
1332           getReassociationIndicesForReshape(srcReshapeSrcType, resultType);
1333     }
1334     if (!reassociationIndices)
1335       return failure();
1336     rewriter.replaceOpWithNewOp<ReshapeOpTy>(
1337         reshapeOp, resultType, srcReshapeOp.src(), *reassociationIndices);
1338     return success();
1339   }
1340 };
1341 } // namespace
1342 
1343 template <typename ReshapeOpTy>
1344 static OpFoldResult foldReshapeOp(ReshapeOpTy reshapeOp,
1345                                   ArrayRef<Attribute> operands) {
1346   // Fold producer-consumer reshape ops that where the operand type of the
1347   // producer is same as the return type of the consumer.
1348   ReshapeOpTy reshapeSrcOp =
1349       reshapeOp.src().template getDefiningOp<ReshapeOpTy>();
1350   if (reshapeSrcOp && reshapeSrcOp.getSrcType() == reshapeOp.getResultType())
1351     return reshapeSrcOp.src();
1352   // Reshape of a constant can be replaced with a new constant.
1353   if (auto elements = operands.front().dyn_cast_or_null<DenseElementsAttr>()) {
1354     return elements.reshape(
1355         reshapeOp.getResult().getType().template cast<ShapedType>());
1356   }
1357   return nullptr;
1358 }
1359 
1360 /// Return true if the reassociation specification is valid, false otherwise.
1361 /// When false, the `invalidIndex` integer pointer is optionally filled with the
1362 /// index of the offending reassociation map.
1363 static bool isReassociationValid(ArrayRef<AffineMap> reassociation,
1364                                  int *invalidIndex = nullptr) {
1365   if (reassociation.empty())
1366     return true;
1367   unsigned nDims = reassociation[0].getNumDims();
1368   unsigned nextExpectedDim = 0;
1369   for (auto it : llvm::enumerate(reassociation)) {
1370     auto m = it.value();
1371     if (m.getNumDims() != nDims || m.getNumSymbols() != 0) {
1372       if (invalidIndex)
1373         *invalidIndex = it.index();
1374       return false;
1375     }
1376     for (auto e : m.getResults()) {
1377       auto d = e.dyn_cast<AffineDimExpr>();
1378       if (!d || d.getPosition() != nextExpectedDim++) {
1379         if (invalidIndex)
1380           *invalidIndex = it.index();
1381         return false;
1382       }
1383     }
1384   }
1385   if (nextExpectedDim != nDims) {
1386     if (invalidIndex)
1387       *invalidIndex = reassociation.size() - 1;
1388     return false;
1389   }
1390   return true;
1391 }
1392 
1393 /// Detect whether memref dims [dim, dim + extent) can be reshaped without
1394 /// copies.
1395 static bool isReshapableDimBand(unsigned dim, unsigned extent,
1396                                 ArrayRef<int64_t> sizes,
1397                                 ArrayRef<AffineExpr> strides) {
1398   assert(sizes.size() == strides.size() && "mismatched ranks");
1399   // off by 1 indexing to avoid out of bounds
1400   //                       V
1401   for (auto idx = dim, e = dim + extent; idx + 1 < e; ++idx) {
1402     // Only bands of static shapes are reshapable. This is due to the fact that
1403     // there is no relation between dynamic sizes and dynamic strides: we do not
1404     // have enough information to know whether a "-1" size corresponds to the
1405     // proper symbol in the AffineExpr of a stride.
1406     if (ShapedType::isDynamic(sizes[dim + 1]))
1407       return false;
1408     // TODO: Refine this by passing the proper nDims and nSymbols so we can
1409     // simplify on the fly and catch more reshapable cases.
1410     if (strides[idx] != strides[idx + 1] * sizes[idx + 1])
1411       return false;
1412   }
1413   return true;
1414 }
1415 
1416 /// Compute the MemRefType obtained by applying the `reassociation` (which is
1417 /// expected to be valid) to `type`.
1418 /// If `type` is Contiguous MemRefType, this always produce a contiguous
1419 /// MemRefType.
1420 static MemRefType
1421 computeReshapeCollapsedType(MemRefType type,
1422                             ArrayRef<AffineMap> reassociation) {
1423   auto sizes = type.getShape();
1424   AffineExpr offset;
1425   SmallVector<AffineExpr, 4> strides;
1426   auto status = getStridesAndOffset(type, strides, offset);
1427   (void)status;
1428   assert(succeeded(status) && "expected strided memref");
1429 
1430   SmallVector<int64_t, 4> newSizes;
1431   newSizes.reserve(reassociation.size());
1432   SmallVector<AffineExpr, 4> newStrides;
1433   newStrides.reserve(reassociation.size());
1434 
1435   // Use the fact that reassociation is valid to simplify the logic: only use
1436   // each map's rank.
1437   assert(isReassociationValid(reassociation) && "invalid reassociation");
1438   unsigned currentDim = 0;
1439   for (AffineMap m : reassociation) {
1440     unsigned dim = m.getNumResults();
1441     int64_t size = 1;
1442     AffineExpr stride = strides[currentDim + dim - 1];
1443     if (!isReshapableDimBand(currentDim, dim, sizes, strides)) {
1444       size = ShapedType::kDynamicSize;
1445       stride = AffineExpr();
1446     } else {
1447       for (unsigned d = 0; d < dim; ++d)
1448         size *= sizes[currentDim + d];
1449     }
1450     newSizes.push_back(size);
1451     newStrides.push_back(stride);
1452     currentDim += dim;
1453   }
1454 
1455   // Early-exit: if `type` is contiguous, the result must be contiguous.
1456   if (canonicalizeStridedLayout(type).getAffineMaps().empty())
1457     return MemRefType::Builder(type).setShape(newSizes).setAffineMaps({});
1458 
1459   // Convert back to int64_t because we don't have enough information to create
1460   // new strided layouts from AffineExpr only. This corresponds to a case where
1461   // copies may be necessary.
1462   int64_t intOffset = ShapedType::kDynamicStrideOrOffset;
1463   if (auto o = offset.dyn_cast<AffineConstantExpr>())
1464     intOffset = o.getValue();
1465   SmallVector<int64_t, 4> intStrides;
1466   intStrides.reserve(strides.size());
1467   for (auto stride : newStrides) {
1468     if (auto cst = stride.dyn_cast_or_null<AffineConstantExpr>())
1469       intStrides.push_back(cst.getValue());
1470     else
1471       intStrides.push_back(ShapedType::kDynamicStrideOrOffset);
1472   }
1473   auto layout =
1474       makeStridedLinearLayoutMap(intStrides, intOffset, type.getContext());
1475   return canonicalizeStridedLayout(
1476       MemRefType::Builder(type).setShape(newSizes).setAffineMaps({layout}));
1477 }
1478 
1479 
1480 template <typename AffineExprTy>
1481 unsigned getMaxPosOfType(ArrayRef<ReassociationExprs> exprArrays) {
1482   unsigned pos = 0;
1483   for (const auto &exprs : exprArrays) {
1484     for (auto expr : exprs) {
1485       expr.walk([&pos](AffineExpr e) {
1486         if (auto d = e.dyn_cast<AffineExprTy>())
1487           pos = std::max(pos, d.getPosition());
1488       });
1489     }
1490   }
1491   return pos;
1492 }
1493 
1494 static SmallVector<AffineMap, 4>
1495 getSymbolLessAffineMaps(ArrayRef<ReassociationExprs> reassociation) {
1496   unsigned maxDim = getMaxPosOfType<AffineDimExpr>(reassociation);
1497   assert(getMaxPosOfType<AffineSymbolExpr>(reassociation) == 0 &&
1498          "Expected symbol-less expressions");
1499   SmallVector<AffineMap, 4> maps;
1500   maps.reserve(reassociation.size());
1501   for (const auto &exprs : reassociation) {
1502     assert(!exprs.empty());
1503     maps.push_back(AffineMap::get(maxDim + 1, 0, exprs, exprs[0].getContext()));
1504   }
1505   return maps;
1506 }
1507 
1508 static SmallVector<ReassociationIndices, 2> convertReassociationMapsToIndices(
1509     OpBuilder &b, ArrayRef<ReassociationExprs> reassociationExprs) {
1510   SmallVector<ReassociationIndices, 2> reassociationIndices;
1511   for (const auto &exprs : reassociationExprs) {
1512     ReassociationIndices indices;
1513     indices.reserve(exprs.size());
1514     for (const auto &expr : exprs)
1515       indices.push_back(expr.cast<AffineDimExpr>().getPosition());
1516     reassociationIndices.push_back(indices);
1517   }
1518   return reassociationIndices;
1519 }
1520 
1521 static SmallVector<SmallVector<AffineExpr, 2>, 2>
1522 convertReassociationIndicesToExprs(
1523     OpBuilder &b, ArrayRef<ReassociationIndices> reassociationIndices) {
1524   SmallVector<SmallVector<AffineExpr, 2>, 2> reassociationMaps;
1525   for (const auto &indices : reassociationIndices) {
1526     SmallVector<AffineExpr, 2> reassociationMap;
1527     reassociationMap.reserve(indices.size());
1528     for (int64_t index : indices)
1529       reassociationMap.push_back(b.getAffineDimExpr(index));
1530     reassociationMaps.push_back(std::move(reassociationMap));
1531   }
1532   return reassociationMaps;
1533 }
1534 
1535 SmallVector<AffineMap, 4> ReshapeOp::getReassociationMaps() {
1536   return getSymbolLessAffineMaps(getReassociationExprs());
1537 }
1538 SmallVector<ReassociationExprs, 4> ReshapeOp::getReassociationExprs() {
1539   OpBuilder b(this->getContext());
1540   return convertReassociationIndicesToExprs(b, getReassociationIndices());
1541 }
1542 SmallVector<AffineMap, 4> TensorReshapeOp::getReassociationMaps() {
1543   return getSymbolLessAffineMaps(getReassociationExprs());
1544 }
1545 SmallVector<ReassociationExprs, 4> TensorReshapeOp::getReassociationExprs() {
1546   OpBuilder b(this->getContext());
1547   return convertReassociationIndicesToExprs(b, getReassociationIndices());
1548 }
1549 /// For reshape op compute the shape at dimension `dimIndex` of the output in
1550 /// terms of shape of the `src`, when the reshape op is a collapsing
1551 /// operation. It is the product of the shape of the collapsed dimensions of the
1552 /// `src`.
1553 static OpFoldResult
1554 getCollapsedOutputDimFromInputShape(OpBuilder &builder, Location loc,
1555                                     int64_t dimIndex, Value src,
1556                                     ArrayRef<AffineMap> reassociationMap) {
1557   AffineMap map = reassociationMap[dimIndex];
1558   unsigned startPos =
1559       map.getResults().front().cast<AffineDimExpr>().getPosition();
1560   unsigned endPos = map.getResults().back().cast<AffineDimExpr>().getPosition();
1561   AffineExpr expr;
1562   SmallVector<Value, 2> dynamicDims;
1563   for (auto dim : llvm::seq(startPos, endPos + 1)) {
1564     dynamicDims.push_back(builder.createOrFold<memref::DimOp>(loc, src, dim));
1565     AffineExpr currExpr = builder.getAffineSymbolExpr(dim - startPos);
1566     expr = (expr ? expr * currExpr : currExpr);
1567   }
1568   return applyMapToValues(builder, loc,
1569                           AffineMap::get(0, endPos - startPos + 1, expr),
1570                           dynamicDims)[0];
1571 }
1572 
1573 /// Given the `src` of a collapsing reshape op and its reassociation maps,
1574 /// compute the shape of the result of the reshape.
1575 static SmallVector<OpFoldResult, 4> getCollapsedOutputShapeFromInputShape(
1576     OpBuilder &builder, Location loc, Value src,
1577     ArrayRef<int64_t> dstStaticShape, ArrayRef<AffineMap> reassociation) {
1578   return llvm::to_vector<4>(llvm::map_range(
1579       llvm::seq<int64_t>(0, dstStaticShape.size()), [&](int64_t dim) {
1580         return getCollapsedOutputDimFromInputShape(builder, loc, dim, src,
1581                                                    reassociation);
1582       }));
1583 }
1584 
1585 /// Compute a map that for a given dimension of the expanded type gives the
1586 /// dimension in the collapsed type it maps to. Essentially its the inverse of
1587 /// the `reassocation` maps.
1588 static llvm::DenseMap<int64_t, int64_t>
1589 getExpandedDimToCollapsedDimMap(ArrayRef<AffineMap> reassociation) {
1590   llvm::DenseMap<int64_t, int64_t> expandedDimToCollapsedDim;
1591   for (auto map : enumerate(reassociation)) {
1592     unsigned startPos =
1593         map.value().getResults().front().cast<AffineDimExpr>().getPosition();
1594     unsigned endPos =
1595         map.value().getResults().back().cast<AffineDimExpr>().getPosition();
1596     for (auto dim : llvm::seq(startPos, endPos + 1)) {
1597       expandedDimToCollapsedDim[dim] = map.index();
1598     }
1599   }
1600   return expandedDimToCollapsedDim;
1601 }
1602 
1603 /// For an expanding reshape op, compute the value for a dimension of the output
1604 /// from the shape of the input.
1605 static OpFoldResult getExpandedOutputDimFromInputShape(
1606     OpBuilder &builder, Location loc, int64_t dimIndex, Value src,
1607     ArrayRef<int64_t> dstStaticShape, ArrayRef<AffineMap> reassociation,
1608     llvm::DenseMap<int64_t, int64_t> &expandedDimToCollapsedDim) {
1609   if (!ShapedType::isDynamic(dstStaticShape[dimIndex])) {
1610     return builder.getI64IntegerAttr(dstStaticShape[dimIndex]);
1611   }
1612   unsigned sourceDimPos = expandedDimToCollapsedDim[dimIndex];
1613   unsigned startPos = reassociation[sourceDimPos]
1614                           .getResults()
1615                           .front()
1616                           .cast<AffineDimExpr>()
1617                           .getPosition();
1618   unsigned endPos = reassociation[sourceDimPos]
1619                         .getResults()
1620                         .back()
1621                         .cast<AffineDimExpr>()
1622                         .getPosition();
1623   int64_t linearizedStaticDim = 1;
1624   for (auto d :
1625        llvm::enumerate(dstStaticShape.slice(startPos, endPos - startPos + 1))) {
1626     if (d.index() + startPos == static_cast<unsigned>(dimIndex))
1627       continue;
1628     assert(!ShapedType::isDynamic(d.value()) &&
1629            "single dimension cannot be expanded into multiple dynamic "
1630            "dimensions");
1631     linearizedStaticDim *= d.value();
1632   }
1633   Value sourceDim = builder.create<memref::DimOp>(loc, src, sourceDimPos);
1634   return applyMapToValues(
1635       builder, loc,
1636       AffineMap::get(
1637           0, 1, builder.getAffineSymbolExpr(0).floorDiv(linearizedStaticDim)),
1638       sourceDim)[0];
1639 }
1640 
1641 /// Given the `src` of an expanding reshape op, the reassociation maps and the
1642 /// result type, compute the shape of the result of the reshape.
1643 static SmallVector<OpFoldResult, 4> getExpandedOutputShapeFromInputShape(
1644     OpBuilder &builder, Location loc, Value src,
1645     ArrayRef<int64_t> dstStaticShape, ArrayRef<AffineMap> reassociation) {
1646   llvm::DenseMap<int64_t, int64_t> expandedDimToCollapsedDim =
1647       getExpandedDimToCollapsedDimMap(reassociation);
1648   return llvm::to_vector<4>(llvm::map_range(
1649       llvm::seq<int64_t>(0, dstStaticShape.size()), [&](int64_t dim) {
1650         return getExpandedOutputDimFromInputShape(builder, loc, dim, src,
1651                                                   dstStaticShape, reassociation,
1652                                                   expandedDimToCollapsedDim);
1653       }));
1654 }
1655 
1656 static SmallVector<OpFoldResult, 4>
1657 getReshapeOutputShapeFromInputShape(OpBuilder &builder, Location loc, Value src,
1658                                     ArrayRef<int64_t> dstStaticShape,
1659                                     ArrayRef<AffineMap> reassocation) {
1660   return dstStaticShape.size() >
1661                  static_cast<size_t>(src.getType().cast<ShapedType>().getRank())
1662              ? getExpandedOutputShapeFromInputShape(
1663                    builder, loc, src, dstStaticShape, reassocation)
1664              : getCollapsedOutputShapeFromInputShape(
1665                    builder, loc, src, dstStaticShape, reassocation);
1666 }
1667 
1668 static ArrayAttr
1669 getReassociationIndicesAttribute(OpBuilder &b,
1670                                  ArrayRef<ReassociationIndices> reassociation) {
1671   SmallVector<Attribute, 4> reassociationAttr =
1672       llvm::to_vector<4>(llvm::map_range(
1673           reassociation, [&](ReassociationIndices indices) -> Attribute {
1674             return b.getI64ArrayAttr(indices).cast<Attribute>();
1675           }));
1676   return b.getArrayAttr(reassociationAttr);
1677 }
1678 
1679 void mlir::linalg::ReshapeOp::build(
1680     OpBuilder &b, OperationState &result, Value src,
1681     ArrayRef<ReassociationIndices> reassociation,
1682     ArrayRef<NamedAttribute> attrs) {
1683   auto memRefType = src.getType().cast<MemRefType>();
1684   auto resultType = computeReshapeCollapsedType(
1685       memRefType, getSymbolLessAffineMaps(
1686                       convertReassociationIndicesToExprs(b, reassociation)));
1687   build(b, result, resultType, src, attrs);
1688   result.addAttribute(ReshapeOp::getReassociationAttrName(),
1689                       getReassociationIndicesAttribute(b, reassociation));
1690 }
1691 
1692 void mlir::linalg::ReshapeOp::build(
1693     OpBuilder &b, OperationState &result, Type resultType, Value src,
1694     ArrayRef<ReassociationIndices> reassociation,
1695     ArrayRef<NamedAttribute> attrs) {
1696   build(b, result, resultType, src, attrs);
1697   result.addAttribute(ReshapeOp::getReassociationAttrName(),
1698                       getReassociationIndicesAttribute(b, reassociation));
1699 }
1700 
1701 Value mlir::linalg::ReshapeOp::getViewSource() { return src(); }
1702 
1703 /// Verify that shapes of the reshaped types using following rules
1704 /// 1) if a dimension in the collapsed type is static, then the corresponding
1705 ///    dimensions in the expanded shape should be
1706 ///    a) static
1707 ///    b) the product should be same as the collaped shape.
1708 /// 2) if a dimension in the collaped type is dynamic, one and only one of the
1709 ///    corresponding dimensions in the expanded type should be dynamic. This
1710 ///    rule is only needed with reshape operations that are expanding.
1711 template <typename OpTy>
1712 static LogicalResult verifyReshapeLikeShapes(OpTy op, ShapedType collapsedType,
1713                                              ShapedType expandedType,
1714                                              bool isExpandingReshape) {
1715   ArrayRef<int64_t> collapsedShape = collapsedType.getShape();
1716   ArrayRef<int64_t> expandedShape = expandedType.getShape();
1717   unsigned expandedDimStart = 0;
1718   for (auto map : llvm::enumerate(op.getReassociationMaps())) {
1719     Optional<int64_t> dynamicShape;
1720     int64_t linearizedStaticShape = 1;
1721     for (auto dim : llvm::enumerate(expandedShape.slice(
1722              expandedDimStart, map.value().getNumResults()))) {
1723       if (ShapedType::isDynamic(dim.value())) {
1724         if (isExpandingReshape && dynamicShape) {
1725           return op->emitOpError("invalid to have a single dimension (")
1726                  << map.index() << ") expanded into multiple dynamic dims ("
1727                  << expandedDimStart + dynamicShape.getValue() << ","
1728                  << expandedDimStart + dim.index() << ")";
1729         }
1730         dynamicShape = dim.index();
1731       } else {
1732         linearizedStaticShape *= dim.value();
1733       }
1734     }
1735     if (dynamicShape) {
1736       if (!ShapedType::isDynamic(collapsedShape[map.index()])) {
1737         return op->emitOpError("expected dimension ")
1738                << map.index()
1739                << " of collapsed type to be dynamic since one or more of the "
1740                   "corresponding dimensions in the expanded type is dynamic";
1741       }
1742     } else {
1743       if (collapsedShape[map.index()] != linearizedStaticShape) {
1744         return op->emitOpError("expected dimension ")
1745                << map.index() << " of collapsed type to be static value of "
1746                << linearizedStaticShape << " ";
1747       }
1748     }
1749     expandedDimStart += map.value().getNumResults();
1750   }
1751   return success();
1752 }
1753 
1754 // Common verifier for reshape-like types. Fills `expandedType` and
1755 // `collapsedType` with the proper `src` or `result` type.
1756 template <typename Op, typename T>
1757 static LogicalResult verifyReshapeLikeTypes(Op op, T &expandedType,
1758                                             T &collapsedType) {
1759   expandedType = op.getSrcType();
1760   collapsedType = op.getResultType();
1761   unsigned expandedRank = expandedType.getRank();
1762   unsigned collapsedRank = collapsedType.getRank();
1763   bool isCollapse = expandedRank > collapsedRank;
1764   if (!isCollapse) {
1765     std::swap(expandedRank, collapsedRank);
1766     std::swap(expandedType, collapsedType);
1767   }
1768   if (expandedRank == 0)
1769     return op.emitOpError("expected non-zero memref ranks");
1770   if (expandedRank == collapsedRank)
1771     return op.emitOpError("expected to collapse or expand dims");
1772 
1773   if (collapsedRank == 0) {
1774     // If collapsed rank is 0, then expanded type must be static shaped and of
1775     // sizes 1.
1776     if (llvm::any_of(expandedType.getShape(),
1777                      [](int64_t dim) -> bool { return dim != 1; }))
1778       return op.emitOpError("invalid to reshape tensor/memref with non-unit "
1779                             "extent dimensions to zero-rank tensor/memref");
1780     return success();
1781   }
1782   if (collapsedRank != op.reassociation().size())
1783     return op.emitOpError("expected rank of the collapsed type(")
1784            << collapsedRank << ") to be the number of reassociation maps("
1785            << op.reassociation().size() << ")";
1786   auto maps = op.getReassociationMaps();
1787   for (auto it : llvm::enumerate(maps))
1788     if (it.value().getNumDims() != expandedRank)
1789       return op.emitOpError("expected reassociation map #")
1790              << it.index() << " of same rank as expanded memref("
1791              << expandedRank << "), but got " << it.value().getNumDims();
1792   int invalidIdx = 0;
1793   if (!isReassociationValid(maps, &invalidIdx))
1794     return op.emitOpError("expected reassociation map #")
1795            << invalidIdx << " to be valid and contiguous";
1796   return verifyReshapeLikeShapes(op, collapsedType, expandedType, !isCollapse);
1797 }
1798 
1799 static LogicalResult verify(ReshapeOp op) {
1800   MemRefType expandedType, collapsedType;
1801   if (failed(verifyReshapeLikeTypes(op, expandedType, collapsedType)))
1802     return failure();
1803   auto maps = op.getReassociationMaps();
1804   MemRefType expectedType = computeReshapeCollapsedType(expandedType, maps);
1805   if (collapsedType != expectedType)
1806     return op.emitOpError("expected collapsed type to be ")
1807            << expectedType << ", but got " << collapsedType;
1808   return success();
1809 }
1810 
1811 void ReshapeOp::getCanonicalizationPatterns(RewritePatternSet &results,
1812                                             MLIRContext *context) {
1813   results.add<CollapseReshapeOps<ReshapeOp>>(context);
1814 }
1815 
1816 //===----------------------------------------------------------------------===//
1817 // TensorReshapeOp
1818 //===----------------------------------------------------------------------===//
1819 
1820 /// Compute the RankedTensorType obtained by applying `reassociation` to `type`.
1821 static RankedTensorType
1822 computeTensorReshapeCollapsedType(RankedTensorType type,
1823                                   ArrayRef<AffineMap> reassociation) {
1824   auto shape = type.getShape();
1825   SmallVector<int64_t, 4> newShape;
1826   newShape.reserve(reassociation.size());
1827 
1828   // Use the fact that reassociation is valid to simplify the logic: only use
1829   // each map's rank.
1830   assert(isReassociationValid(reassociation) && "invalid reassociation");
1831   unsigned currentDim = 0;
1832   for (AffineMap m : reassociation) {
1833     unsigned dim = m.getNumResults();
1834     auto band = shape.slice(currentDim, dim);
1835     int64_t size = 1;
1836     if (llvm::is_contained(band, ShapedType::kDynamicSize))
1837       size = ShapedType::kDynamicSize;
1838     else
1839       for (unsigned d = 0; d < dim; ++d)
1840         size *= shape[currentDim + d];
1841     newShape.push_back(size);
1842     currentDim += dim;
1843   }
1844 
1845   return RankedTensorType::get(newShape, type.getElementType());
1846 }
1847 
1848 void mlir::linalg::TensorReshapeOp::build(
1849     OpBuilder &b, OperationState &result, Value src,
1850     ArrayRef<ReassociationIndices> reassociation,
1851     ArrayRef<NamedAttribute> attrs) {
1852   auto resultType = computeTensorReshapeCollapsedType(
1853       src.getType().cast<RankedTensorType>(),
1854       getSymbolLessAffineMaps(
1855           convertReassociationIndicesToExprs(b, reassociation)));
1856   build(b, result, resultType, src, attrs);
1857   result.addAttribute(ReshapeOp::getReassociationAttrName(),
1858                       getReassociationIndicesAttribute(b, reassociation));
1859 }
1860 
1861 void mlir::linalg::TensorReshapeOp::build(
1862     OpBuilder &b, OperationState &result, Type resultType, Value src,
1863     ArrayRef<ReassociationIndices> reassociation,
1864     ArrayRef<NamedAttribute> attrs) {
1865   build(b, result, resultType, src, attrs);
1866   result.addAttribute(ReshapeOp::getReassociationAttrName(),
1867                       getReassociationIndicesAttribute(b, reassociation));
1868 }
1869 
1870 static LogicalResult verify(TensorReshapeOp op) {
1871   RankedTensorType expandedType, collapsedType;
1872   if (failed(verifyReshapeLikeTypes(op, expandedType, collapsedType)))
1873     return failure();
1874 
1875   auto maps = op.getReassociationMaps();
1876   RankedTensorType expectedType =
1877       computeTensorReshapeCollapsedType(expandedType, maps);
1878   if (collapsedType != expectedType)
1879     return op.emitOpError("expected collapsed type to be ")
1880            << expectedType << ", but got " << collapsedType;
1881   return success();
1882 }
1883 
1884 namespace {
1885 /// Reshape of a splat constant can be replaced with a constant of the result
1886 /// type.
1887 struct FoldReshapeWithConstant : OpRewritePattern<TensorReshapeOp> {
1888   using OpRewritePattern<TensorReshapeOp>::OpRewritePattern;
1889   LogicalResult matchAndRewrite(TensorReshapeOp reshapeOp,
1890                                 PatternRewriter &rewriter) const override {
1891     DenseElementsAttr attr;
1892     if (!matchPattern(reshapeOp.src(), m_Constant(&attr)))
1893       return failure();
1894     if (!attr || !attr.isSplat())
1895       return failure();
1896     DenseElementsAttr newAttr = DenseElementsAttr::getFromRawBuffer(
1897         reshapeOp.getResultType(), attr.getRawData(), true);
1898     rewriter.replaceOpWithNewOp<ConstantOp>(reshapeOp, newAttr);
1899     return success();
1900   }
1901 };
1902 
1903 /// Fold linalg.fill -> linalg.tensor_reshape chain.
1904 ///
1905 /// For such op chains, we can create new linalg.fill ops with the result
1906 /// type of the linalg.tensor_reshape op.
1907 struct FoldFillWithTensorReshape : OpRewritePattern<TensorReshapeOp> {
1908   using OpRewritePattern<TensorReshapeOp>::OpRewritePattern;
1909   LogicalResult matchAndRewrite(TensorReshapeOp reshapeOp,
1910                                 PatternRewriter &rewriter) const override {
1911     auto oldFill = reshapeOp.src().getDefiningOp<FillOp>();
1912     if (!oldFill)
1913       return failure();
1914 
1915     Location loc = oldFill.getLoc();
1916     auto newInit = rewriter.create<TensorReshapeOp>(
1917         loc, reshapeOp.getResultType(), oldFill.output(),
1918         reshapeOp.reassociation());
1919     rewriter.replaceOpWithNewOp<FillOp>(reshapeOp, newInit, oldFill.value());
1920 
1921     return success();
1922   }
1923 };
1924 } // namespace
1925 
1926 void TensorReshapeOp::getCanonicalizationPatterns(RewritePatternSet &results,
1927                                                   MLIRContext *context) {
1928   results.add<CollapseReshapeOps<TensorReshapeOp>, FoldFillWithTensorReshape,
1929               FoldInitTensorWithTensorReshapeOp, FoldReshapeWithConstant>(
1930       context);
1931 }
1932 
1933 LogicalResult TensorReshapeOp::reifyReturnTypeShapesPerResultDim(
1934     OpBuilder &b, SmallVectorImpl<SmallVector<Value>> &reifiedReturnShapes) {
1935   auto resultShape =
1936       getAsValues(b, getLoc(),
1937                   getReshapeOutputShapeFromInputShape(
1938                       b, getLoc(), src(), getResultType().getShape(),
1939                       getReassociationMaps()));
1940   reifiedReturnShapes.emplace_back(std::move(resultShape));
1941   return success();
1942 }
1943 
1944 //===----------------------------------------------------------------------===//
1945 // YieldOp
1946 //===----------------------------------------------------------------------===//
1947 
1948 static void print(OpAsmPrinter &p, linalg::YieldOp op) {
1949   p << op.getOperationName();
1950   if (op.getNumOperands() > 0)
1951     p << ' ' << op.getOperands();
1952   p.printOptionalAttrDict(op->getAttrs());
1953   if (op.getNumOperands() > 0)
1954     p << " : " << op.getOperandTypes();
1955 }
1956 
1957 static ParseResult parseYieldOp(OpAsmParser &parser, OperationState &result) {
1958   SmallVector<OpAsmParser::OperandType, 2> opInfo;
1959   SmallVector<Type, 2> types;
1960   llvm::SMLoc loc = parser.getCurrentLocation();
1961   return failure(parser.parseOperandList(opInfo) ||
1962                  parser.parseOptionalAttrDict(result.attributes) ||
1963                  (!opInfo.empty() && parser.parseColonTypeList(types)) ||
1964                  parser.resolveOperands(opInfo, types, loc, result.operands));
1965 }
1966 
1967 // Check the operand number and types must match the element types of the
1968 // LinalgOp interface's shaped operands.
1969 static LogicalResult verifyYield(linalg::YieldOp op,
1970                                  LinalgOp linalgOpInterface) {
1971   auto nOutputs = linalgOpInterface.getNumOutputs();
1972   if (op.getNumOperands() != nOutputs)
1973     return op.emitOpError("expected number of yield values (")
1974            << nOutputs << ") to match the number of operands of the enclosing "
1975            << "LinalgOp (" << op.getNumOperands() << ")";
1976 
1977   for (unsigned i = 0; i != nOutputs; ++i) {
1978     auto elementType =
1979         linalgOpInterface.getOutputShapedType(i).getElementType();
1980     if (op.getOperand(i).getType() != elementType)
1981       return op.emitOpError("type of yield operand ")
1982              << (i + 1) << " (" << op.getOperand(i).getType()
1983              << ") doesn't match "
1984              << "the element type of the enclosing linalg.generic op ("
1985              << elementType << ")";
1986   }
1987   return success();
1988 }
1989 
1990 static LogicalResult verify(linalg::YieldOp op) {
1991   auto *parentOp = op->getParentOp();
1992   if (parentOp->getNumRegions() != 1 || parentOp->getRegion(0).empty())
1993     return op.emitOpError("expected single non-empty parent region");
1994 
1995   if (auto linalgOp = dyn_cast<LinalgOp>(parentOp))
1996     return verifyYield(op, cast<LinalgOp>(parentOp));
1997 
1998   if (auto padTensorOp = dyn_cast<linalg::PadTensorOp>(parentOp)) {
1999     if (op.getNumOperands() != 1)
2000       return op.emitOpError("expected single yield operand (got ")
2001              << op->getNumOperands() << ")";
2002     if (op.getOperand(0).getType() !=
2003         padTensorOp.getType().cast<ShapedType>().getElementType())
2004       return op.emitOpError("expected yield type to match shape element type");
2005     return success();
2006   }
2007 
2008   if (auto tiledLoopOp = dyn_cast<linalg::TiledLoopOp>(parentOp)) {
2009     // Check if output args with tensor types match results types.
2010     SmallVector<Value, 2> tensorOuts;
2011     llvm::copy_if(
2012         tiledLoopOp.outputs(), std::back_inserter(tensorOuts),
2013         [&](Value out) { return out.getType().isa<RankedTensorType>(); });
2014     if (tensorOuts.size() != op.values().size())
2015       return op.emitOpError("expected number of tensor output args = ")
2016              << tensorOuts.size() << " to match the number of yield operands = "
2017              << op.values().size();
2018 
2019     TypeRange tensorTypes(llvm::makeArrayRef(tensorOuts));
2020     for (auto &item :
2021          llvm::enumerate(llvm::zip(tensorTypes, op.getOperandTypes()))) {
2022       Type outType, resultType;
2023       unsigned index = item.index();
2024       std::tie(outType, resultType) = item.value();
2025       if (outType != resultType)
2026         return op.emitOpError("expected yield operand ")
2027                << index << " with type = " << resultType
2028                << " to match output arg type = " << outType;
2029     }
2030     return success();
2031   }
2032   return op.emitOpError("expected parent op with LinalgOp interface");
2033 }
2034 
2035 //===----------------------------------------------------------------------===//
2036 // TiledLoopOp
2037 //===----------------------------------------------------------------------===//
2038 
2039 void TiledLoopOp::build(OpBuilder &builder, OperationState &result,
2040                         ValueRange lowerBounds, ValueRange upperBounds,
2041                         ValueRange steps, ValueRange inputs, ValueRange outputs,
2042                         ArrayAttr iteratorTypes,
2043                         function_ref<void(OpBuilder &, Location, ValueRange,
2044                                           ValueRange, ValueRange)>
2045                             bodyBuilderFn) {
2046   result.addOperands(lowerBounds);
2047   result.addOperands(upperBounds);
2048   result.addOperands(steps);
2049   result.addOperands(inputs);
2050   result.addOperands(outputs);
2051   result.addAttribute(
2052       TiledLoopOp::getOperandSegmentSizeAttr(),
2053       builder.getI32VectorAttr({static_cast<int32_t>(lowerBounds.size()),
2054                                 static_cast<int32_t>(upperBounds.size()),
2055                                 static_cast<int32_t>(steps.size()),
2056                                 static_cast<int32_t>(inputs.size()),
2057                                 static_cast<int32_t>(outputs.size())}));
2058   result.addAttribute(getIteratorTypesAttrName(), iteratorTypes);
2059 
2060   // Add output types for `RankedTensorType` output arguments.
2061   for (Value output : outputs) {
2062     Type outputType = output.getType();
2063     if (outputType.isa<RankedTensorType>())
2064       result.addTypes(outputType);
2065   }
2066 
2067   OpBuilder::InsertionGuard guard(builder);
2068   unsigned numIVs = steps.size();
2069   SmallVector<Type, 8> argTypes(numIVs, builder.getIndexType());
2070   for (Type type : TypeRange(inputs))
2071     argTypes.push_back(type);
2072   for (Type type : TypeRange(outputs))
2073     argTypes.push_back(type);
2074   Region *bodyRegion = result.addRegion();
2075   Block *bodyBlock = builder.createBlock(bodyRegion, {}, argTypes);
2076 
2077   if (bodyBuilderFn) {
2078     builder.setInsertionPointToStart(bodyBlock);
2079     bodyBuilderFn(builder, result.location,
2080                   bodyBlock->getArguments().take_front(numIVs),
2081                   bodyBlock->getArguments().slice(numIVs, inputs.size()),
2082                   bodyBlock->getArguments().take_back(outputs.size()));
2083     TiledLoopOp::ensureTerminator(*bodyRegion, builder, result.location);
2084   }
2085 }
2086 
2087 static void print(OpAsmPrinter &p, TiledLoopOp op) {
2088   p << op.getOperationName() << " (" << op.getInductionVars() << ") = ("
2089     << op.lowerBound() << ") to (" << op.upperBound() << ") step (" << op.step()
2090     << ")";
2091 
2092   if (!op.inputs().empty()) {
2093     p << " ins (";
2094     llvm::interleaveComma(llvm::zip(op.getRegionInputArgs(), op.inputs()), p,
2095                           [&](auto it) {
2096                             p << std::get<0>(it) << " = " << std::get<1>(it)
2097                               << ": " << std::get<1>(it).getType();
2098                           });
2099     p << ")";
2100   }
2101   if (!op.outputs().empty()) {
2102     p << " outs (";
2103     llvm::interleaveComma(llvm::zip(op.getRegionOutputArgs(), op.outputs()), p,
2104                           [&](auto it) {
2105                             p << std::get<0>(it) << " = " << std::get<1>(it)
2106                               << ": " << std::get<1>(it).getType();
2107                           });
2108     p << ")";
2109   }
2110 
2111   if (llvm::any_of(op.iterator_types(), [](Attribute attr) {
2112         return attr.cast<StringAttr>().getValue() !=
2113                getParallelIteratorTypeName();
2114       })) {
2115     p << " iterators" << op.iterator_types() << "";
2116   }
2117 
2118   p.printRegion(op.region(), /*printEntryBlockArgs=*/false);
2119   p.printOptionalAttrDict(
2120       op->getAttrs(), /*elidedAttrs=*/{TiledLoopOp::getOperandSegmentSizeAttr(),
2121                                        getIteratorTypesAttrName()});
2122 }
2123 
2124 static ParseResult parseTiledLoopOp(OpAsmParser &parser,
2125                                     OperationState &result) {
2126   auto &builder = parser.getBuilder();
2127   // Parse an opening `(` followed by induction variables followed by `)`
2128   SmallVector<OpAsmParser::OperandType, 4> ivs;
2129   if (parser.parseRegionArgumentList(ivs, /*requiredOperandCount=*/-1,
2130                                      OpAsmParser::Delimiter::Paren))
2131     return failure();
2132 
2133   // Parse loop bounds.
2134   SmallVector<OpAsmParser::OperandType, 4> lower;
2135   if (parser.parseEqual() ||
2136       parser.parseOperandList(lower, ivs.size(),
2137                               OpAsmParser::Delimiter::Paren) ||
2138       parser.resolveOperands(lower, builder.getIndexType(), result.operands))
2139     return failure();
2140 
2141   SmallVector<OpAsmParser::OperandType, 4> upper;
2142   if (parser.parseKeyword("to") ||
2143       parser.parseOperandList(upper, ivs.size(),
2144                               OpAsmParser::Delimiter::Paren) ||
2145       parser.resolveOperands(upper, builder.getIndexType(), result.operands))
2146     return failure();
2147 
2148   // Parse step values.
2149   SmallVector<OpAsmParser::OperandType, 4> steps;
2150   if (parser.parseKeyword("step") ||
2151       parser.parseOperandList(steps, ivs.size(),
2152                               OpAsmParser::Delimiter::Paren) ||
2153       parser.resolveOperands(steps, builder.getIndexType(), result.operands))
2154     return failure();
2155 
2156   // Parse input tensors.
2157   SmallVector<OpAsmParser::OperandType, 4> inputs, input_region_args;
2158   SmallVector<Type, 4> inputTypes;
2159   if (succeeded(parser.parseOptionalKeyword("ins"))) {
2160     llvm::SMLoc inputsOperandsLoc = parser.getCurrentLocation();
2161 
2162     if (parser.parseAssignmentListWithTypes(input_region_args, inputs,
2163                                             inputTypes))
2164       return failure();
2165 
2166     if (parser.resolveOperands(inputs, inputTypes, inputsOperandsLoc,
2167                                result.operands))
2168       return failure();
2169   }
2170 
2171   // Parse output tensors.
2172   SmallVector<OpAsmParser::OperandType, 4> outputs, output_region_args;
2173   SmallVector<Type, 4> outputTypes;
2174   if (succeeded(parser.parseOptionalKeyword("outs"))) {
2175     llvm::SMLoc outputsOperandsLoc = parser.getCurrentLocation();
2176 
2177     if (parser.parseAssignmentListWithTypes(output_region_args, outputs,
2178                                             outputTypes))
2179       return failure();
2180 
2181     if (parser.resolveOperands(outputs, outputTypes, outputsOperandsLoc,
2182                                result.operands))
2183       return failure();
2184     for (Type outputType : outputTypes)
2185       if (outputType.isa<RankedTensorType>())
2186         result.addTypes(outputType);
2187   }
2188 
2189   // Parse attributes.
2190   SmallVector<Attribute, 4> iterTypes;
2191   if (succeeded(parser.parseOptionalKeyword("iterators"))) {
2192     StringAttr iterType;
2193 
2194     if (parser.parseLSquare() || parser.parseAttribute(iterType))
2195       return failure();
2196     iterTypes.push_back(iterType);
2197     for (int i = 1, e = ivs.size(); i < e; ++i) {
2198       if (parser.parseComma() || parser.parseAttribute(iterType))
2199         return failure();
2200       iterTypes.push_back(iterType);
2201     }
2202     if (parser.parseRSquare())
2203       return failure();
2204   } else {
2205     auto parallelIter = builder.getStringAttr(getParallelIteratorTypeName());
2206     iterTypes = SmallVector<Attribute, 4>(ivs.size(), parallelIter);
2207   }
2208   result.addAttribute(getIteratorTypesAttrName(),
2209                       builder.getArrayAttr(iterTypes));
2210   result.addAttribute(
2211       TiledLoopOp::getOperandSegmentSizeAttr(),
2212       builder.getI32VectorAttr({static_cast<int32_t>(lower.size()),
2213                                 static_cast<int32_t>(upper.size()),
2214                                 static_cast<int32_t>(steps.size()),
2215                                 static_cast<int32_t>(inputs.size()),
2216                                 static_cast<int32_t>(outputs.size())}));
2217 
2218   // Parse the body.
2219   Region *body = result.addRegion();
2220 
2221   SmallVector<Type, 4> region_types(ivs.size(), builder.getIndexType());
2222   region_types.append(inputTypes);
2223   region_types.append(outputTypes);
2224 
2225   SmallVector<OpAsmParser::OperandType, 4> region_args(ivs);
2226   region_args.append(input_region_args);
2227   region_args.append(output_region_args);
2228 
2229   if (parser.parseRegion(*body, region_args, region_types))
2230     return failure();
2231 
2232   // Parse optional attributes.
2233   parser.parseOptionalAttrDict(result.attributes);
2234 
2235   return success();
2236 }
2237 
2238 Region &TiledLoopOp::getLoopBody() { return region(); }
2239 
2240 LogicalResult TiledLoopOp::moveOutOfLoop(ArrayRef<Operation *> ops) {
2241   for (auto *op : ops)
2242     op->moveBefore(*this);
2243   return success();
2244 }
2245 
2246 bool TiledLoopOp::isDefinedOutsideOfLoop(Value value) {
2247   return !region().isAncestor(value.getParentRegion());
2248 }
2249 
2250 static LogicalResult verify(TiledLoopOp op) {
2251   // Check if iterator types are provided for every loop dimension.
2252   if (op.iterator_types().size() != op.getNumLoops())
2253     return op.emitOpError("expected iterator types array attribute size = ")
2254            << op.iterator_types().size()
2255            << " to match the number of loops = " << op.getNumLoops();
2256 
2257   // Check if types of input arguments match region args types.
2258   for (auto &item :
2259        llvm::enumerate(llvm::zip(op.inputs(), op.getRegionInputArgs()))) {
2260     Value input, inputRegionArg;
2261     unsigned index = item.index();
2262     std::tie(input, inputRegionArg) = item.value();
2263     if (input.getType() != inputRegionArg.getType())
2264       return op.emitOpError("expected input arg ")
2265              << index << " with type = " << input.getType()
2266              << " to match region arg " << index + op.getNumLoops()
2267              << " type = " << inputRegionArg.getType();
2268   }
2269 
2270   // Check if types of input arguments match region args types.
2271   for (auto &item :
2272        llvm::enumerate(llvm::zip(op.outputs(), op.getRegionOutputArgs()))) {
2273     Value output, outputRegionArg;
2274     unsigned index = item.index();
2275     std::tie(output, outputRegionArg) = item.value();
2276     if (output.getType() != outputRegionArg.getType())
2277       return op.emitOpError("expected output arg ")
2278              << index << " with type = " << output.getType()
2279              << " to match region arg "
2280              << index + op.getNumLoops() + op.inputs().size()
2281              << " type = " << outputRegionArg.getType();
2282   }
2283   return success();
2284 }
2285 
2286 namespace {
2287 
2288 static constexpr int64_t kNoMatch = -1;
2289 
2290 // Folds away TiledLoopOp inputs if they have no uses within the body.
2291 //
2292 // Example:
2293 //
2294 // %0 = linalg.tiled_loop ...  ins (%in_ = %in: tensor<...>,
2295 //                                  %in_buf_ = %in_buf: memref<...>) {...}
2296 // Becomes
2297 //
2298 // linalg.tiled_loop ...  ins (%in_buf_ = %in_buf: memref<...>) {...}
2299 struct TiledLoopInputsFolder : public OpRewritePattern<linalg::TiledLoopOp> {
2300   using OpRewritePattern<linalg::TiledLoopOp>::OpRewritePattern;
2301 
2302   LogicalResult matchAndRewrite(linalg::TiledLoopOp tiledLoop,
2303                                 PatternRewriter &rewriter) const final {
2304     SmallVector<Value, 2> newInputs, regionInputTensorArgs;
2305     // Store ids of the corresponding old and new input operands.
2306     SmallVector<int64_t, 2> oldInputIdToNew(tiledLoop.inputs().size(),
2307                                             kNoMatch);
2308     for (auto en : llvm::enumerate(
2309              llvm::zip(tiledLoop.inputs(), tiledLoop.getRegionInputArgs()))) {
2310       Value in, bbArg;
2311       size_t index = en.index();
2312       std::tie(in, bbArg) = en.value();
2313       if (!bbArg.use_empty()) {
2314         oldInputIdToNew[index] = newInputs.size();
2315         newInputs.push_back(in);
2316       }
2317     }
2318     if (newInputs.size() == tiledLoop.inputs().size())
2319       return failure();
2320     Location loc = tiledLoop.getLoc();
2321     auto newTiledLoop = rewriter.create<TiledLoopOp>(
2322         loc, tiledLoop.lowerBound(), tiledLoop.upperBound(), tiledLoop.step(),
2323         newInputs, tiledLoop.outputs(), tiledLoop.iterator_types());
2324 
2325     // Clone the region.
2326     BlockAndValueMapping bvm;
2327     bvm.map(tiledLoop.getInductionVars(), newTiledLoop.getInductionVars());
2328     bvm.map(tiledLoop.getRegionOutputArgs(),
2329             newTiledLoop.getRegionOutputArgs());
2330     for (const auto &en : llvm::enumerate(oldInputIdToNew))
2331       if (en.value() != kNoMatch)
2332         bvm.map(tiledLoop.getRegionInputArgs()[en.index()],
2333                 newTiledLoop.getRegionInputArgs()[en.value()]);
2334     OpBuilder innerBuilder =
2335         OpBuilder::atBlockEnd(newTiledLoop.getBody(), rewriter.getListener());
2336     for (auto &op : *tiledLoop.getBody())
2337       innerBuilder.clone(op, bvm);
2338     rewriter.replaceOp(tiledLoop, newTiledLoop.getResults());
2339 
2340     return success();
2341   }
2342 };
2343 
2344 // Folds away TiledLoopOp output tensors when the following conditions are met:
2345 // * result of `linalg.tiled_loop` has no uses
2346 // * output tensor is the argument of `linalg.yield`
2347 //
2348 // Example:
2349 //
2350 // %0 = linalg.tiled_loop ...  outs (%o_ = %out: tensor<...>,
2351 //                                   %obuf_ = %out_buf: memref<...>) {
2352 //   ...
2353 //   linalg.yield %o_ : tensor ...
2354 // }
2355 //
2356 // Becomes
2357 //
2358 // linalg.tiled_loop ...  outs (%obuf_ = %out_buf: memref<...>) {
2359 //   ...
2360 //   linalg.yield
2361 // }
2362 struct TiledLoopResultsFolder : public OpRewritePattern<linalg::TiledLoopOp> {
2363   using OpRewritePattern<linalg::TiledLoopOp>::OpRewritePattern;
2364 
2365   LogicalResult matchAndRewrite(linalg::TiledLoopOp tiledLoop,
2366                                 PatternRewriter &rewriter) const final {
2367     if (tiledLoop.getNumResults() == 0)
2368       return failure();
2369 
2370     Block *block = tiledLoop.getBody();
2371     auto yieldOp = cast<linalg::YieldOp>(block->getTerminator());
2372 
2373     // Match the pattern and collect output buffers that will replace the output
2374     // tensors and also the ops that will be ignored when cloning the body.
2375     SmallVector<Value, 2> newOutputOperands, newYieldArgs;
2376     int resultId = 0;
2377     // Store ids of the corresponding old and new output operands.
2378     SmallVector<int64_t, 2> oldOutputIdToNew(tiledLoop.outputs().size(),
2379                                              kNoMatch);
2380     // Store ids of the corresponding old and new results.
2381     SmallVector<int64_t, 2> oldResultIdToNew(tiledLoop.getNumResults(),
2382                                              kNoMatch);
2383     SmallVector<Value, 2> resultReplacement(tiledLoop.getNumResults());
2384     for (auto en : llvm::enumerate(
2385              llvm::zip(tiledLoop.outputs(), tiledLoop.getRegionOutputArgs()))) {
2386       size_t index = en.index();
2387       Value out = std::get<0>(en.value());
2388       Value outRegionArg = std::get<1>(en.value());
2389 
2390       if (!out.getType().isa<RankedTensorType>()) {
2391         oldOutputIdToNew[index] = newOutputOperands.size();
2392         newOutputOperands.push_back(out);
2393         continue;
2394       }
2395       Value result = tiledLoop.getResult(resultId);
2396       Value yieldArg = yieldOp.getOperand(resultId);
2397       if (yieldArg != outRegionArg || !result.use_empty()) {
2398         oldOutputIdToNew[index] = newOutputOperands.size();
2399         oldResultIdToNew[resultId] = newYieldArgs.size();
2400         resultReplacement[resultId] = out;
2401         newOutputOperands.push_back(out);
2402         newYieldArgs.push_back(yieldArg);
2403       }
2404       ++resultId;
2405     }
2406     if (newOutputOperands.size() == tiledLoop.outputs().size())
2407       return failure();
2408 
2409     Location loc = tiledLoop.getLoc();
2410     auto newTiledLoop = rewriter.create<TiledLoopOp>(
2411         loc, tiledLoop.lowerBound(), tiledLoop.upperBound(), tiledLoop.step(),
2412         tiledLoop.inputs(), newOutputOperands, tiledLoop.iterator_types());
2413 
2414     // Clone the region.
2415     BlockAndValueMapping bvm;
2416     bvm.map(tiledLoop.getInductionVars(), newTiledLoop.getInductionVars());
2417     bvm.map(tiledLoop.getRegionInputArgs(), newTiledLoop.getRegionInputArgs());
2418     for (const auto &en : llvm::enumerate(oldOutputIdToNew)) {
2419       if (en.value() != kNoMatch)
2420         bvm.map(tiledLoop.getRegionOutputArgs()[en.index()],
2421                 newTiledLoop.getRegionOutputArgs()[en.value()]);
2422       else
2423         bvm.map(tiledLoop.getRegionOutputArgs()[en.index()],
2424                 tiledLoop.outputs()[en.index()]);
2425     }
2426     OpBuilder innerBuilder =
2427         OpBuilder::atBlockEnd(newTiledLoop.getBody(), rewriter.getListener());
2428     for (auto &op : tiledLoop.getBody()->without_terminator())
2429       innerBuilder.clone(op, bvm);
2430     innerBuilder.create<linalg::YieldOp>(
2431         loc, llvm::to_vector<2>(llvm::map_range(
2432                  newYieldArgs, [&](Value arg) { return bvm.lookup(arg); })));
2433 
2434     for (const auto &en : llvm::enumerate(oldResultIdToNew))
2435       if (en.value() != kNoMatch)
2436         resultReplacement[en.index()] = newTiledLoop.getResult(en.value());
2437     rewriter.replaceOp(tiledLoop, resultReplacement);
2438 
2439     return success();
2440   }
2441 };
2442 } // namespace
2443 
2444 void TiledLoopOp::getCanonicalizationPatterns(OwningRewritePatternList &results,
2445                                               MLIRContext *context) {
2446   results.insert<TiledLoopInputsFolder, TiledLoopResultsFolder>(context);
2447 }
2448 
2449 LogicalResult TiledLoopOp::fold(ArrayRef<Attribute>,
2450                                 SmallVectorImpl<OpFoldResult> &) {
2451   return foldMemRefCastInTiledLoopOp(*this);
2452 }
2453 
2454 //===----------------------------------------------------------------------===//
2455 // IndexOp
2456 //===----------------------------------------------------------------------===//
2457 
2458 static LogicalResult verify(IndexOp op) {
2459   auto linalgOp = dyn_cast<LinalgOp>(op->getParentOp());
2460   if (!linalgOp)
2461     return op.emitOpError("expected parent op with LinalgOp interface");
2462   if (linalgOp.getNumLoops() <= op.dim())
2463     return op.emitOpError("expected dim (")
2464            << op.dim() << ") to be lower than the number of loops ("
2465            << linalgOp.getNumLoops() << ") of the enclosing LinalgOp";
2466   return success();
2467 }
2468 
2469 /////// Operations corresponding to library calls defined with Tablegen ////////
2470 
2471 template <typename LinalgPoolingOp>
2472 static LogicalResult verifyStrideOrDilation(LinalgPoolingOp op,
2473                                             ArrayRef<Attribute> attrs,
2474                                             bool isStride) {
2475   auto strideOrDilation = isStride ? "stride" : "dilation";
2476   if (attrs.size() != op.getNumWindowLoops())
2477     return op.emitOpError("expects num ")
2478            << strideOrDilation
2479            << "s equal to number of window dimensions: " << attrs.size()
2480            << " vs " << op.getNumWindowLoops();
2481   return success();
2482 }
2483 
2484 void ConvOp::getEffects(
2485     SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
2486         &effects) {
2487   effects.emplace_back(MemoryEffects::Read::get(), input(),
2488                        SideEffects::DefaultResource::get());
2489   effects.emplace_back(MemoryEffects::Read::get(), filter(),
2490                        SideEffects::DefaultResource::get());
2491   effects.emplace_back(MemoryEffects::Write::get(), output(),
2492                        SideEffects::DefaultResource::get());
2493 }
2494 
2495 static LogicalResult verify(ConvOp op) {
2496   auto oType = op.output().getType().cast<MemRefType>();
2497   auto fType = op.filter().getType().cast<MemRefType>();
2498   auto iType = op.input().getType().cast<MemRefType>();
2499   if (oType.getElementType() != iType.getElementType() ||
2500       oType.getElementType() != fType.getElementType())
2501     return op.emitOpError("expects memref elemental types to match");
2502   if (oType.getRank() != iType.getRank() || oType.getRank() != fType.getRank())
2503     return op.emitOpError("expects memref ranks to match");
2504   if (auto strides = op.strides()) {
2505     if (failed(verifyStrideOrDilation(op, strides->getValue(),
2506                                       /*isStride=*/true)))
2507       return failure();
2508   }
2509   if (auto dilations = op.dilations()) {
2510     if (failed(verifyStrideOrDilation(op, dilations->getValue(),
2511                                       /*isStride=*/false)))
2512       return failure();
2513   }
2514   return success();
2515 }
2516 
2517 template <typename PoolingOp>
2518 static LogicalResult verifySingleInputPoolingOp(PoolingOp op) {
2519   auto inputType = op.input().getType().template cast<MemRefType>();
2520   auto outputType = op.output().getType().template cast<MemRefType>();
2521   if (outputType.getElementType() != inputType.getElementType())
2522     return op.emitOpError("expects memref elemental types to match");
2523 
2524   auto windowDimsType = op.windowDims().getType().template cast<MemRefType>();
2525   if (outputType.getRank() != inputType.getRank() ||
2526       outputType.getRank() != windowDimsType.getRank())
2527     return op.emitOpError("expects memref ranks to match");
2528 
2529   if (auto strides = op.strides()) {
2530     if (failed(verifyStrideOrDilation(op, strides->getValue(),
2531                                       /*isStride=*/true)))
2532       return failure();
2533   }
2534   if (auto dilations = op.dilations()) {
2535     if (failed(verifyStrideOrDilation(op, dilations->getValue(),
2536                                       /*isStride=*/false)))
2537       return failure();
2538   }
2539   return success();
2540 }
2541 
2542 #define DEFINE_POOLING_OP_GET_EFFECTS(OP_NAME)                                 \
2543   void OP_NAME::getEffects(                                                    \
2544       SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>      \
2545           &effects) {                                                          \
2546     effects.emplace_back(MemoryEffects::Read::get(), input(),                  \
2547                          SideEffects::DefaultResource::get());                 \
2548     effects.emplace_back(MemoryEffects::Write::get(), output(),                \
2549                          SideEffects::DefaultResource::get());                 \
2550   }
2551 
2552 static LogicalResult verify(PoolingMaxOp op) {
2553   return verifySingleInputPoolingOp(op);
2554 }
2555 static LogicalResult verify(PoolingMinOp op) {
2556   return verifySingleInputPoolingOp(op);
2557 }
2558 static LogicalResult verify(PoolingSumOp op) {
2559   return verifySingleInputPoolingOp(op);
2560 }
2561 
2562 DEFINE_POOLING_OP_GET_EFFECTS(PoolingMaxOp)
2563 DEFINE_POOLING_OP_GET_EFFECTS(PoolingMinOp)
2564 DEFINE_POOLING_OP_GET_EFFECTS(PoolingSumOp)
2565 
2566 namespace {
2567 struct EraseDeadLinalgOp;
2568 struct FoldTensorCastOp;
2569 } // namespace
2570 
2571 #include "mlir/Dialect/Linalg/IR/LinalgNamedStructuredOps.tcgen.cpp.inc"
2572 #include "mlir/Dialect/Linalg/IR/LinalgNamedStructuredOps.yamlgen.cpp.inc"
2573 
2574 #define GET_OP_CLASSES
2575 #include "mlir/Dialect/Linalg/IR/LinalgOps.cpp.inc"
2576 
2577 #define GET_OP_CLASSES
2578 #include "mlir/Dialect/Linalg/IR/LinalgStructuredOps.cpp.inc"
2579 
2580 /// Return the dims that are `iteratorTypeName` loops in the LinalgOp `op`.
2581 /// Assumes `op` is a LinalgOp.
2582 void mlir::linalg::getDimsOfType(Operation *op, StringRef iteratorTypeName,
2583                                  SmallVectorImpl<AffineExpr> &res) {
2584   if (!cast<LinalgOp>(op).iterator_types())
2585     return;
2586 
2587   unsigned dim = 0;
2588   MLIRContext *ctx = op->getContext();
2589   for (auto tn :
2590        cast<LinalgOp>(op).iterator_types().getAsValueRange<StringAttr>()) {
2591     if (tn == iteratorTypeName)
2592       res.push_back(getAffineDimExpr(dim, ctx));
2593     ++dim;
2594   }
2595 }
2596 
2597 AffineMap mlir::linalg::extractOrIdentityMap(Optional<AffineMap> maybeMap,
2598                                              unsigned rank,
2599                                              MLIRContext *context) {
2600   if (maybeMap)
2601     return maybeMap.getValue();
2602   if (rank == 0)
2603     return AffineMap::get(context);
2604   return AffineMap::getMultiDimIdentityMap(rank, context);
2605 }
2606 
2607 SmallVector<AffineExpr, 4>
2608 mlir::linalg::makeAffineDimExprs(unsigned num, unsigned &startIdx,
2609                                  MLIRContext *context) {
2610   SmallVector<AffineExpr, 4> res;
2611   res.reserve(num);
2612   for (unsigned i = 0; i < num; ++i)
2613     res.push_back(getAffineDimExpr(startIdx++, context));
2614   return res;
2615 }
2616 
2617 template <typename PoolingOp>
2618 SmallVector<AffineExpr, 4>
2619 mlir::linalg::weightedPoolingInputIndex(PoolingOp op,
2620                                         ArrayRef<AffineExpr> outputDims,
2621                                         ArrayRef<AffineExpr> windowDims) {
2622   assert(outputDims.size() == windowDims.size());
2623   SmallVector<AffineExpr, 4> res;
2624   res.reserve(outputDims.size());
2625   for (unsigned i = 0, e = outputDims.size(); i < e; ++i) {
2626     // TODO: add a level of indirection to linalg.generic.
2627     auto expr = op.getStride(i) * outputDims[i] +
2628                 op.getDilation(i) * windowDims[i] - op.getLowPad(i);
2629     res.push_back(expr);
2630   }
2631   return res;
2632 }
2633 
2634 #define INSTANTIATE_WEIGHTED_POOLING_INPUT_INDEX(OP_TYPE)                      \
2635   template SmallVector<AffineExpr, 4>                                          \
2636   mlir::linalg::weightedPoolingInputIndex<OP_TYPE>(                            \
2637       OP_TYPE op, ArrayRef<AffineExpr> outputDims,                             \
2638       ArrayRef<AffineExpr> windowDims);
2639 
2640 INSTANTIATE_WEIGHTED_POOLING_INPUT_INDEX(ConvOp)
2641 INSTANTIATE_WEIGHTED_POOLING_INPUT_INDEX(PoolingMaxOp)
2642 INSTANTIATE_WEIGHTED_POOLING_INPUT_INDEX(PoolingMinOp)
2643 INSTANTIATE_WEIGHTED_POOLING_INPUT_INDEX(PoolingSumOp)
2644 
2645 SmallVector<AffineExpr, 4> mlir::linalg::concat(ArrayRef<AffineExpr> a,
2646                                                 ArrayRef<AffineExpr> b) {
2647   auto rangeA = llvm::make_range(a.begin(), a.end());
2648   auto rangeB = llvm::make_range(b.begin(), b.end());
2649   auto concatRanges = llvm::concat<const AffineExpr>(rangeA, rangeB);
2650   return llvm::to_vector<4>(concatRanges);
2651 }
2652 
2653 static void appendMangledType(llvm::raw_string_ostream &ss, Type t) {
2654   if (auto memref = t.dyn_cast<MemRefType>()) {
2655     ss << "view";
2656     for (auto size : memref.getShape())
2657       if (size < 0)
2658         ss << "sx";
2659       else
2660         ss << size << "x";
2661     appendMangledType(ss, memref.getElementType());
2662   } else if (auto vec = t.dyn_cast<VectorType>()) {
2663     ss << "vector";
2664     llvm::interleave(
2665         vec.getShape(), [&](int64_t i) { ss << i; }, [&]() { ss << "x"; });
2666     appendMangledType(ss, vec.getElementType());
2667   } else if (t.isSignlessIntOrIndexOrFloat()) {
2668     ss << t;
2669   } else {
2670     llvm_unreachable("Invalid type for linalg library name mangling");
2671   }
2672 }
2673 
2674 std::string mlir::linalg::generateLibraryCallName(Operation *op) {
2675   assert(isa<LinalgOp>(op));
2676   std::string name(op->getName().getStringRef().str());
2677   name.reserve(128);
2678   std::replace(name.begin(), name.end(), '.', '_');
2679   llvm::raw_string_ostream ss(name);
2680   ss << "_";
2681   auto types = op->getOperandTypes();
2682   llvm::interleave(
2683       types.begin(), types.end(), [&](Type t) { appendMangledType(ss, t); },
2684       [&]() { ss << "_"; });
2685   return ss.str();
2686 }
2687 
2688 // TODO: Consider making all this boilerplate easy to autogenerate
2689 // with Tablegen. This seems a desirable property in the context of
2690 // OpInterfaces where a Linalg "named" op **isa** LinalgOp.
2691 OpFoldResult ReshapeOp::fold(ArrayRef<Attribute> operands) {
2692   if (succeeded(foldMemRefCast(*this)))
2693     return getResult();
2694   return foldReshapeOp(*this, operands);
2695 }
2696 OpFoldResult TensorReshapeOp::fold(ArrayRef<Attribute> operands) {
2697   return foldReshapeOp(*this, operands);
2698 }
2699 
2700 //===----------------------------------------------------------------------===//
2701 // Support for named Linalg ops defined in ods-gen.
2702 //===----------------------------------------------------------------------===//
2703 
2704 /// Generic entry point to create the block for the region of a LinalgOp.
2705 /// This is used by both named structured ops created by ods-gen and by manually
2706 /// defined C++ ops.
2707 /// This is used by both builders and parsers.
2708 /// This function creates the block in the region with arguments corresponding
2709 /// to the elemental types of `inputTypes` and `outputTypes`, which are asserted
2710 /// to be ShapedType.
2711 template <typename NamedStructuredOpType>
2712 static void
2713 fillStructuredOpRegion(OpBuilder &opBuilder, Region &region,
2714                        TypeRange inputTypes, TypeRange outputTypes,
2715                        ValueRange captures,
2716                        std::function<void(unsigned, unsigned)> errorHandler) {
2717   assert(llvm::all_of(inputTypes, [](Type t) { return t.isa<ShapedType>(); }));
2718   assert(llvm::all_of(outputTypes, [](Type t) { return t.isa<ShapedType>(); }));
2719 
2720   // TODO: atm all operands go through getElementTypeOrSelf,
2721   // reconsider when we have evidence we need to.
2722   SmallVector<Type, 8> argTypes;
2723   for (auto containers : {inputTypes, outputTypes})
2724     for (auto t : containers)
2725       argTypes.push_back(getElementTypeOrSelf(t));
2726 
2727   // RAII.
2728   OpBuilder::InsertionGuard guard(opBuilder);
2729   Block *body = opBuilder.createBlock(&region, /*insertPt=*/{}, argTypes);
2730   unsigned actual = body->getNumArguments();
2731   unsigned expected = NamedStructuredOpType::getNumRegionArgs();
2732   if (expected != actual) {
2733     if (errorHandler)
2734       errorHandler(expected, actual);
2735     return;
2736   }
2737 
2738   opBuilder.setInsertionPointToStart(body);
2739   mlir::edsc::ScopedContext scope(opBuilder, opBuilder.getUnknownLoc());
2740   NamedStructuredOpType::regionBuilder(*body, captures);
2741 
2742   // indexing_maps is an auto-generated method.
2743 
2744   // iterator_types is an auto-generated method.
2745 }
2746 
2747 /// Generic entry point to create both the region and the block of a LinalgOp.
2748 template <typename NamedStructuredOpType>
2749 void createAndFillStructuredOpRegion(OpBuilder &opBuilder,
2750                                      OperationState &result,
2751                                      TypeRange inputTypes,
2752                                      TypeRange outputTypes,
2753                                      ValueRange captures) {
2754   Region &region = *result.addRegion();
2755   fillStructuredOpRegion<NamedStructuredOpType>(
2756       opBuilder, region, inputTypes, outputTypes, captures,
2757       [&](unsigned expected, unsigned actual) {
2758         assert(expected != actual && "incorrect number of arguments");
2759       });
2760 }
2761 
2762 /// Common parsing used for both named structured ops created by ods-gen and by
2763 /// manually defined C++ ops. Does not handle regions.
2764 static ParseResult
2765 parseCommonStructuredOpParts(OpAsmParser &parser, OperationState &result,
2766                              SmallVectorImpl<Type> &inputTypes,
2767                              SmallVectorImpl<Type> &outputTypes) {
2768   llvm::SMLoc inputsOperandsLoc, outputsOperandsLoc;
2769   SmallVector<OpAsmParser::OperandType, 4> inputsOperands, outputsOperands;
2770 
2771   parser.parseOptionalAttrDict(result.attributes);
2772 
2773   if (succeeded(parser.parseOptionalKeyword("ins"))) {
2774     if (parser.parseLParen())
2775       return failure();
2776 
2777     inputsOperandsLoc = parser.getCurrentLocation();
2778     if (parser.parseOperandList(inputsOperands) ||
2779         parser.parseColonTypeList(inputTypes) || parser.parseRParen())
2780       return failure();
2781   }
2782 
2783   if (succeeded(parser.parseOptionalKeyword("outs"))) {
2784     outputsOperandsLoc = parser.getCurrentLocation();
2785     if (parser.parseLParen() || parser.parseOperandList(outputsOperands) ||
2786         parser.parseColonTypeList(outputTypes) || parser.parseRParen())
2787       return failure();
2788   }
2789 
2790   if (parser.resolveOperands(inputsOperands, inputTypes, inputsOperandsLoc,
2791                              result.operands) ||
2792       parser.resolveOperands(outputsOperands, outputTypes, outputsOperandsLoc,
2793                              result.operands))
2794     return failure();
2795 
2796   result.addAttribute("operand_segment_sizes",
2797                       parser.getBuilder().getI32VectorAttr(
2798                           {static_cast<int32_t>(inputsOperands.size()),
2799                            static_cast<int32_t>(outputsOperands.size())}));
2800   return success();
2801 }
2802 
2803 template <typename NamedStructuredOpType>
2804 static void printCommonStructuredOpParts(OpAsmPrinter &p,
2805                                          NamedStructuredOpType op) {
2806   if (!op.inputs().empty())
2807     p << " ins(" << op.inputs() << " : " << op.inputs().getTypes() << ")";
2808   if (!op.outputs().empty())
2809     p << " outs(" << op.outputs() << " : " << op.outputs().getTypes() << ")";
2810 }
2811 
2812 //===----------------------------------------------------------------------===//
2813 // Specific parsing and printing for named structured ops created by ods-gen.
2814 //===----------------------------------------------------------------------===//
2815 
2816 template <typename NamedStructuredOpType>
2817 static ParseResult
2818 parseNamedStructuredOpRegion(OpAsmParser &parser, Region &region,
2819                              TypeRange inputTypes, TypeRange outputTypes,
2820                              ArrayRef<OpAsmParser::OperandType> captures) {
2821   ParseResult res = success();
2822   OpBuilder opBuilder(parser.getBuilder().getContext());
2823   // Resolve `captures` into `capturedValues` at parse time so we can build the
2824   // region with captures.
2825   SmallVector<Value> capturedValues;
2826   fillStructuredOpRegion<NamedStructuredOpType>(
2827       opBuilder, region, inputTypes, outputTypes, capturedValues,
2828       [&](unsigned expected, unsigned actual) {
2829         res = parser.emitError(
2830             parser.getCurrentLocation(),
2831             llvm::formatv("[parseNamedStructuredOpRegion] ods-gen generated "
2832                           "region expects {0} args, got {1}",
2833                           expected, actual));
2834         region.front().dump();
2835       });
2836   return res;
2837 }
2838 
2839 static ParseResult
2840 parseNamedStructuredOpResults(OpAsmParser &parser,
2841                               SmallVectorImpl<Type> &resultTypes) {
2842   if (succeeded(parser.parseOptionalArrow()))
2843     if (parser.parseTypeList(resultTypes))
2844       return failure();
2845   return success();
2846 }
2847 
2848 template <typename NamedStructuredOpType>
2849 static ParseResult
2850 parseNamedStructuredOp(OpAsmParser &parser, OperationState &result,
2851                        ArrayRef<OpAsmParser::OperandType> captures) {
2852   // TODO: Enable when ods-gen supports captures.
2853   assert(captures.empty() && "unexpected captures for named structured ops");
2854   SmallVector<Type, 1> inputTypes, outputTypes;
2855   if (parseCommonStructuredOpParts(parser, result, inputTypes, outputTypes))
2856     return failure();
2857 
2858   // TODO: consider merging results parsing into region parsing.
2859   // Need to wait for declarative assembly resolution to decide.
2860   SmallVector<Type, 1> outputTensorsTypes;
2861   if (parseNamedStructuredOpResults(parser, outputTensorsTypes))
2862     return failure();
2863   result.addTypes(outputTensorsTypes);
2864 
2865   std::unique_ptr<Region> region = std::make_unique<Region>();
2866   if (parseNamedStructuredOpRegion<NamedStructuredOpType>(
2867           parser, *region, inputTypes, outputTypes, captures))
2868     return failure();
2869   result.addRegion(std::move(region));
2870 
2871   return success();
2872 }
2873 
2874 static void printNamedStructuredOpResults(OpAsmPrinter &p,
2875                                           TypeRange resultTypes) {
2876   if (resultTypes.empty())
2877     return;
2878   p.printOptionalArrowTypeList(resultTypes);
2879 }
2880 
2881 template <typename NamedStructuredOpType>
2882 static void printNamedStructuredOp(OpAsmPrinter &p, NamedStructuredOpType op) {
2883   p << op.getOperationName();
2884   p.printOptionalAttrDict(
2885       op->getAttrs(),
2886       /*elidedAttrs=*/{"operand_segment_sizes",
2887                        // See generated code in mlir-linalg-yaml-gen.cpp
2888                        "linalg.memoized_indexing_maps"});
2889 
2890   // Printing is shared with generic ops, except for the region and
2891   // attributes.
2892   printCommonStructuredOpParts(p, op);
2893 
2894   // Results printing.
2895   printNamedStructuredOpResults(p, op.result_tensors().getTypes());
2896 
2897   // Region is elided.
2898 }
2899 
2900 template <typename NamedStructuredOpType>
2901 static LogicalResult verifyNamedStructuredOp(NamedStructuredOpType op) {
2902   return verifyGenericOp<NamedStructuredOpType>(op);
2903 }
2904 
2905 //===----------------------------------------------------------------------===//
2906 // Canonicalizers and Folders.
2907 //===----------------------------------------------------------------------===//
2908 
2909 namespace {
2910 struct EraseDeadLinalgOp : public OpInterfaceRewritePattern<LinalgOp> {
2911   using OpInterfaceRewritePattern<LinalgOp>::OpInterfaceRewritePattern;
2912 
2913   LogicalResult matchAndRewrite(LinalgOp op,
2914                                 PatternRewriter &rewriter) const override {
2915     for (Value v : op.getShapedOperands()) {
2916       // Linalg "inputs" may be either tensor or memref type.
2917       // tensor<0xelt_type> is a convention that may not always mean
2918       // "0 iterations". Only erase in cases we see memref<...x0x...>.
2919       auto mt = v.getType().dyn_cast<MemRefType>();
2920       if (!mt)
2921         continue;
2922       if (llvm::is_contained(mt.getShape(), 0)) {
2923         rewriter.eraseOp(op);
2924         return success();
2925       }
2926     }
2927     return failure();
2928   }
2929 };
2930 
2931 struct FoldTensorCastOp : public OpInterfaceRewritePattern<LinalgOp> {
2932   using OpInterfaceRewritePattern<LinalgOp>::OpInterfaceRewritePattern;
2933 
2934   LogicalResult matchAndRewrite(LinalgOp op,
2935                                 PatternRewriter &rewriter) const override {
2936     // If no operand comes from a tensor::CastOp and can be folded then fail.
2937     bool hasTensorCastOperand =
2938         llvm::any_of(op.getShapedOperands(), [&](Value v) {
2939           if (v.isa<BlockArgument>())
2940             return false;
2941           auto castOp = v.getDefiningOp<tensor::CastOp>();
2942           return castOp && canFoldIntoConsumerOp(castOp);
2943         });
2944     if (!hasTensorCastOperand)
2945       return failure();
2946 
2947     SmallVector<Type, 4> newResultTypes;
2948     newResultTypes.reserve(op->getNumResults());
2949     SmallVector<Value, 4> newOperands;
2950     newOperands.reserve(op->getNumOperands());
2951     // Inputs may fold.
2952     for (Value v : op.getInputs()) {
2953       auto tensorCastOp = v.getDefiningOp<tensor::CastOp>();
2954       newOperands.push_back(
2955           canFoldIntoConsumerOp(tensorCastOp) ? tensorCastOp.source() : v);
2956     }
2957     // Init tensors may fold, in which case the resultType must also change.
2958     for (Value v : op.getOutputs()) {
2959       auto tensorCastOp = v.getDefiningOp<tensor::CastOp>();
2960       bool fold = canFoldIntoConsumerOp(tensorCastOp);
2961       newOperands.push_back(fold ? tensorCastOp.getOperand() : v);
2962       newResultTypes.push_back(newOperands.back().getType());
2963     }
2964     auto extraOperands = op.getAssumedNonShapedOperands();
2965     newOperands.append(extraOperands.begin(), extraOperands.end());
2966     // Clone op.
2967     Operation *newOp =
2968         op.clone(rewriter, op->getLoc(), newResultTypes, newOperands);
2969     SmallVector<Value, 4> replacements;
2970     replacements.reserve(newOp->getNumResults());
2971     for (auto result : llvm::zip(op->getResults(), newOp->getResults())) {
2972       Value oldResult = std::get<0>(result);
2973       Value newResult = std::get<1>(result);
2974       if (newResult.getType() != oldResult.getType()) {
2975         replacements.push_back(rewriter.create<tensor::CastOp>(
2976             op->getLoc(), oldResult.getType(), newResult));
2977       } else {
2978         replacements.push_back(newResult);
2979       }
2980     }
2981     rewriter.replaceOp(op, replacements);
2982 
2983     return success();
2984   }
2985 };
2986 } // namespace
2987 
2988 namespace {
2989 // Deduplicate redundant args of a linalg op.
2990 // An arg is redundant if it has the same Value and indexing map as another.
2991 struct DeduplicateInputs : public OpInterfaceRewritePattern<LinalgOp> {
2992   using OpInterfaceRewritePattern<LinalgOp>::OpInterfaceRewritePattern;
2993 
2994   LogicalResult matchAndRewrite(LinalgOp op,
2995                                 PatternRewriter &rewriter) const override {
2996     // This pattern reduces the number of arguments of an op, which breaks
2997     // the invariants of semantically charged named ops.
2998     if (!isa<GenericOp, IndexedGenericOp>(op))
2999       return failure();
3000 
3001     // Associate each input to an equivalent "canonical" input that has the same
3002     // Value and indexing map.
3003     //
3004     // In the non-duplicate case, input `i` will have canonical input `i`. But
3005     // in the case of duplicated inputs, the canonical input could be some other
3006     // input `< i`. That is, a later input will have some earlier input as its
3007     // canonical input.
3008     llvm::SmallDenseMap<std::pair<Value, AffineMap>, int> canonicalInput;
3009     // For later remapping tasks like deduplicating payload block arguments,
3010     // having a simple "inputIndex -> canonicalInputIndex" integer mapping is
3011     // convenient.
3012     SmallVector<int, 6> canonicalInputIndices;
3013     for (int i = 0, e = op.getNumInputs(); i != e; i++) {
3014       Value input = op.getInput(i);
3015       AffineMap indexingMap = op.getInputIndexingMap(i);
3016       // STL-like maps have a convenient behavior for our use case here. In the
3017       // case of duplicate keys, the insertion is rejected, and the returned
3018       // iterator gives access to the value already in the map.
3019       auto pair = canonicalInput.insert({{input, indexingMap}, i});
3020       canonicalInputIndices.push_back(pair.first->second);
3021     }
3022 
3023     // If there are no duplicate args, then bail out.
3024     if (canonicalInput.size() == op.getNumInputs())
3025       return failure();
3026 
3027     // The operands for the newly canonicalized op.
3028     SmallVector<Value, 6> newOperands;
3029     for (auto v : llvm::enumerate(op.getInputs()))
3030       if (canonicalInputIndices[v.index()] == static_cast<int>(v.index()))
3031         newOperands.push_back(v.value());
3032     llvm::append_range(newOperands, op.getOutputs());
3033     llvm::append_range(newOperands, op.getAssumedNonShapedOperands());
3034 
3035     // Clone the old op with new operands.
3036     Operation *newOp =
3037         op.clone(rewriter, op->getLoc(), op->getResultTypes(), newOperands);
3038     auto newLinalgOp = cast<LinalgOp>(newOp);
3039 
3040     // Repair the indexing maps by filtering out the ones that have been
3041     // eliminated.
3042     SmallVector<AffineMap, 6> newIndexingMaps;
3043     for (int i = 0, e = newLinalgOp.getNumInputs(); i != e; i++)
3044       if (canonicalInputIndices[i] == i)
3045         newIndexingMaps.push_back(newLinalgOp.getIndexingMap(i));
3046     for (int i = 0, e = newLinalgOp.getNumOutputs(); i != e; i++)
3047       newIndexingMaps.push_back(newLinalgOp.getOutputIndexingMap(i));
3048     newOp->setAttr("indexing_maps",
3049                    rewriter.getAffineMapArrayAttr(newIndexingMaps));
3050 
3051     // Set the number of inputs to the new value. The `clone` call above kept
3052     // the value from the original op.
3053     newLinalgOp.setNumInputs(canonicalInput.size());
3054 
3055     // linalg.indexed_generic payloads have additional arguments prepended to
3056     // the block arg list.
3057     int bbArgBaseOffset = newLinalgOp.getNumPayloadInductionVariables();
3058 
3059     // Repair the payload entry block by RAUW'ing redundant arguments and
3060     // erasing them.
3061     Block &payload = newOp->getRegion(0).front();
3062     for (int i = 0, e = op.getNumInputs(); i < e; i++) {
3063       // Iterate in reverse, so that we erase later args first, preventing the
3064       // argument list from shifting unexpectedly and invalidating all our
3065       // indices.
3066       int reversed = e - i - 1;
3067       int canonicalIndex = canonicalInputIndices[reversed];
3068       if (canonicalInputIndices[reversed] == reversed)
3069         continue;
3070       payload.getArgument(bbArgBaseOffset + reversed)
3071           .replaceAllUsesWith(
3072               payload.getArgument(bbArgBaseOffset + canonicalIndex));
3073       payload.eraseArgument(bbArgBaseOffset + reversed);
3074     }
3075 
3076     rewriter.replaceOp(op, newOp->getResults());
3077     return success();
3078   }
3079 };
3080 
3081 /// Remove generic/indexed_generic operations (on tensors) that are just copying
3082 /// the values from inputs to the results. Requirements are
3083 /// 1) All iterator types are parallel
3084 /// 2) The body contains just a yield operation with the yielded values being
3085 ///    the arguments corresponding to the operands.
3086 struct RemoveIdentityLinalgOps : public OpInterfaceRewritePattern<LinalgOp> {
3087   using OpInterfaceRewritePattern<LinalgOp>::OpInterfaceRewritePattern;
3088 
3089   LogicalResult matchAndRewrite(LinalgOp op,
3090                                 PatternRewriter &rewriter) const override {
3091     if (auto copyOp = dyn_cast<CopyOp>(*op)) {
3092       assert(copyOp.hasBufferSemantics());
3093       if (copyOp.input() == copyOp.output() &&
3094           copyOp.inputPermutation() == copyOp.outputPermutation()) {
3095         rewriter.eraseOp(op);
3096         return success();
3097       }
3098     }
3099 
3100     if (!isa<GenericOp, IndexedGenericOp>(op))
3101       return failure();
3102     if (!op.hasTensorSemantics())
3103       return failure();
3104     // Check all indexing maps are identity.
3105     if (llvm::any_of(op.getIndexingMaps(),
3106                      [](AffineMap map) { return !map.isIdentity(); }))
3107       return failure();
3108 
3109     // Check that the body of the linalg operation is just a linalg.yield
3110     // operation.
3111     Block &body = op->getRegion(0).front();
3112     if (!llvm::hasSingleElement(body))
3113       return failure();
3114     auto yieldOp = dyn_cast<linalg::YieldOp>(body.getTerminator());
3115     if (!yieldOp)
3116       return failure();
3117 
3118     // Get the argument number of the returned values. That is the operand
3119     // number to use for replacing uses of this operation.
3120     unsigned numIndexArgs = op.getNumPayloadInductionVariables();
3121     SmallVector<Value, 4> returnedArgs;
3122     for (Value yieldVal : yieldOp.values()) {
3123       auto yieldArg = yieldVal.dyn_cast<BlockArgument>();
3124       if (!yieldArg || yieldArg.getOwner() != &body)
3125         return failure();
3126       unsigned argumentNumber = yieldArg.getArgNumber();
3127       if (argumentNumber < numIndexArgs)
3128         return failure();
3129       returnedArgs.push_back(op->getOperand(argumentNumber - numIndexArgs));
3130     }
3131     if (returnedArgs.size() != op.getOperation()->getNumResults())
3132       return failure();
3133     rewriter.replaceOp(op, returnedArgs);
3134     return success();
3135   }
3136 };
3137 } // namespace
3138 
3139 #define CANONICALIZERS_AND_FOLDERS(XXX)                                        \
3140   void XXX::getCanonicalizationPatterns(RewritePatternSet &results,            \
3141                                         MLIRContext *context) {                \
3142     results.add<DeduplicateInputs, EraseDeadLinalgOp, FoldTensorCastOp,        \
3143                 RemoveIdentityLinalgOps>(context);                             \
3144   }                                                                            \
3145                                                                                \
3146   LogicalResult XXX::fold(ArrayRef<Attribute>,                                 \
3147                           SmallVectorImpl<OpFoldResult> &) {                   \
3148     return foldMemRefCast(*this);                                              \
3149   }
3150 
3151 CANONICALIZERS_AND_FOLDERS(ConvOp)
3152 CANONICALIZERS_AND_FOLDERS(PoolingMaxOp)
3153 CANONICALIZERS_AND_FOLDERS(PoolingMinOp)
3154 CANONICALIZERS_AND_FOLDERS(PoolingSumOp)
3155 CANONICALIZERS_AND_FOLDERS(CopyOp)
3156 CANONICALIZERS_AND_FOLDERS(FillOp)
3157 CANONICALIZERS_AND_FOLDERS(GenericOp)
3158 
3159 // All named ops canonicalizers and folders are auto-generated in the
3160 // .cpp.inc.
3161