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