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/Linalg.h"
14 
15 #include "mlir/Dialect/Arithmetic/Utils/Utils.h"
16 #include "mlir/Dialect/SCF/SCF.h"
17 #include "mlir/Dialect/SparseTensor/IR/SparseTensor.h"
18 #include "mlir/Dialect/Utils/ReshapeOpsUtils.h"
19 #include "mlir/Dialect/Utils/StaticValueUtils.h"
20 #include "mlir/IR/AffineExprVisitor.h"
21 #include "mlir/IR/Matchers.h"
22 #include "mlir/IR/OpImplementation.h"
23 #include "mlir/IR/PatternMatch.h"
24 #include "mlir/Interfaces/InferTypeOpInterface.h"
25 #include "mlir/Parser/Parser.h"
26 
27 #include "llvm/ADT/DenseMap.h"
28 #include "llvm/ADT/SetVector.h"
29 #include "llvm/ADT/SmallSet.h"
30 #include "llvm/ADT/StringSet.h"
31 #include "llvm/ADT/TypeSwitch.h"
32 #include "llvm/Support/FormatVariadic.h"
33 #include "llvm/Support/MathExtras.h"
34 #include "llvm/Support/raw_ostream.h"
35 
36 using namespace mlir;
37 using namespace mlir::linalg;
38 
39 /// Forward declarations.
40 
41 /// Generic entry point to create the block for the region of a LinalgOp.
42 /// This is used by both named structured ops created by ods-gen and by manually
43 /// defined C++ ops.
44 /// This is used by both builders and parsers.
45 /// This function creates the block in the region with arguments corresponding
46 /// to the elemental types of `inputTypes` and `outputTypes`. The latter are
47 /// asserted to be of ShapedType.
48 template <typename NamedStructuredOpType>
49 static void fillStructuredOpRegion(
50     OpBuilder &opBuilder, Region &region, TypeRange inputTypes,
51     TypeRange outputTypes, ArrayRef<NamedAttribute> attrs,
52     llvm::function_ref<void(unsigned, unsigned)> errorHandler = nullptr);
53 
54 /// Generic entry point to create both the region and the block of a LinalgOp.
55 template <typename NamedStructuredOpType>
56 static void
57 createAndFillStructuredOpRegion(OpBuilder &opBuilder, OperationState &result,
58                                 TypeRange inputTypes, TypeRange outputTypes);
59 
60 /// Common parsing and printing used for both named structured ops created by
61 /// ods-gen and by manually defined C++ ops. Does not handle regions.
62 static ParseResult
63 parseCommonStructuredOpParts(OpAsmParser &parser, OperationState &result,
64                              SmallVectorImpl<Type> &inputTypes,
65                              SmallVectorImpl<Type> &outputTypes);
66 template <typename NamedStructuredOpType>
67 static void printCommonStructuredOpParts(OpAsmPrinter &p,
68                                          NamedStructuredOpType op);
69 
70 /// Specific parsing and printing for named structured ops created by ods-gen.
71 template <typename NamedStructuredOpType>
72 static ParseResult
73 parseNamedStructuredOpRegion(OpAsmParser &parser, Region &region,
74                              TypeRange inputTypes, TypeRange outputTypes,
75                              ArrayRef<NamedAttribute> attrs);
76 
77 static ParseResult
78 parseNamedStructuredOpResults(OpAsmParser &parser,
79                               SmallVectorImpl<Type> &resultTypes);
80 
81 template <typename NamedStructuredOpType>
82 static ParseResult parseNamedStructuredOp(OpAsmParser &parser,
83                                           OperationState &result);
84 
85 static void printNamedStructuredOpResults(OpAsmPrinter &p,
86                                           TypeRange resultTypes);
87 
88 template <typename NamedStructuredOpType>
89 static void printNamedStructuredOp(OpAsmPrinter &p, NamedStructuredOpType op);
90 
91 /// This is a common class used for patterns of the form
92 /// ```
93 ///    someop(memrefcast(%src)) -> someop(%src)
94 /// ```
95 /// It folds the source of the memref.cast into the root operation directly.
96 static LogicalResult foldMemRefCast(Operation *op) {
97   bool folded = false;
98   for (OpOperand &operand : op->getOpOperands()) {
99     auto castOp = operand.get().getDefiningOp<memref::CastOp>();
100     if (castOp && memref::CastOp::canFoldIntoConsumerOp(castOp)) {
101       operand.set(castOp.getOperand());
102       folded = true;
103     }
104   }
105   return success(folded);
106 }
107 
108 //===----------------------------------------------------------------------===//
109 // Region builder helper.
110 // TODO: Move this to a utility library.
111 // The public methods on this class are referenced directly from generated code.
112 // Helper build the unary, binary, and type conversion functions defined by the
113 // DSL. See mlir-linalg-ods-yaml-gen.cpp for the code that uses this class.
114 //
115 // Implementations of the math functions must be polymorphic over numeric types,
116 // internally performing necessary casts. If the function application makes no
117 // sense, then the only recourse is to assert and return nullptr. This can be
118 // extended later if it becomes possible to fail construction of the region. The
119 // invariant should be enforced at a higher level.
120 //
121 // TODO: These helpers are currently type polymorphic over the class of integer
122 // and floating point types, but they will not internally cast within bit
123 // widths of a class (mixed precision such as i8->i32) or across classes
124 // (i.e. mixed float and integer). Many such combinations are ambiguous or need
125 // to be handled with care and work is being considered to extend the op
126 // language to make such cases explicit. In the mean-time, violating this will
127 // fail verification, which is deemed acceptable.
128 //===----------------------------------------------------------------------===//
129 
130 namespace {
131 
132 class RegionBuilderHelper {
133 public:
134   RegionBuilderHelper(MLIRContext *context, Block &block)
135       : context(context), block(block) {}
136 
137   // Build the unary functions defined by OpDSL.
138   Value buildUnaryFn(UnaryFn unaryFn, Value arg) {
139     if (!isFloatingPoint(arg))
140       llvm_unreachable("unsupported non numeric type");
141     OpBuilder builder = getBuilder();
142     switch (unaryFn) {
143     case UnaryFn::exp:
144       return builder.create<math::ExpOp>(arg.getLoc(), arg);
145     case UnaryFn::log:
146       return builder.create<math::LogOp>(arg.getLoc(), arg);
147     }
148     llvm_unreachable("unsupported unary function");
149   }
150 
151   // Build the binary functions defined by OpDSL.
152   Value buildBinaryFn(BinaryFn binaryFn, Value arg0, Value arg1) {
153     bool allFloatingPoint = isFloatingPoint(arg0) && isFloatingPoint(arg1);
154     bool allInteger = isInteger(arg0) && isInteger(arg1);
155     if (!allFloatingPoint && !allInteger)
156       llvm_unreachable("unsupported non numeric type");
157     OpBuilder builder = getBuilder();
158     switch (binaryFn) {
159     case BinaryFn::add:
160       if (allFloatingPoint)
161         return builder.create<arith::AddFOp>(arg0.getLoc(), arg0, arg1);
162       return builder.create<arith::AddIOp>(arg0.getLoc(), arg0, arg1);
163     case BinaryFn::sub:
164       if (allFloatingPoint)
165         return builder.create<arith::SubFOp>(arg0.getLoc(), arg0, arg1);
166       return builder.create<arith::SubIOp>(arg0.getLoc(), arg0, arg1);
167     case BinaryFn::mul:
168       if (allFloatingPoint)
169         return builder.create<arith::MulFOp>(arg0.getLoc(), arg0, arg1);
170       return builder.create<arith::MulIOp>(arg0.getLoc(), arg0, arg1);
171     case BinaryFn::max_signed:
172       if (allFloatingPoint)
173         return builder.create<arith::MaxFOp>(arg0.getLoc(), arg0, arg1);
174       return builder.create<arith::MaxSIOp>(arg0.getLoc(), arg0, arg1);
175     case BinaryFn::min_signed:
176       if (allFloatingPoint)
177         return builder.create<arith::MinFOp>(arg0.getLoc(), arg0, arg1);
178       return builder.create<arith::MinSIOp>(arg0.getLoc(), arg0, arg1);
179     case BinaryFn::max_unsigned:
180       if (allFloatingPoint)
181         return builder.create<arith::MaxFOp>(arg0.getLoc(), arg0, arg1);
182       return builder.create<arith::MaxUIOp>(arg0.getLoc(), arg0, arg1);
183     case BinaryFn::min_unsigned:
184       if (allFloatingPoint)
185         return builder.create<arith::MinFOp>(arg0.getLoc(), arg0, arg1);
186       return builder.create<arith::MinUIOp>(arg0.getLoc(), arg0, arg1);
187     }
188     llvm_unreachable("unsupported binary function");
189   }
190 
191   // Build the type functions defined by OpDSL.
192   Value buildTypeFn(TypeFn typeFn, Type toType, Value operand) {
193     switch (typeFn) {
194     case TypeFn::cast_signed:
195       return cast(toType, operand, false);
196     case TypeFn::cast_unsigned:
197       return cast(toType, operand, true);
198     }
199     llvm_unreachable("unsupported type conversion function");
200   }
201 
202   void yieldOutputs(ValueRange values) {
203     OpBuilder builder = getBuilder();
204     Location loc = builder.getUnknownLoc();
205     builder.create<YieldOp>(loc, values);
206   }
207 
208   Value constant(const std::string &value) {
209     OpBuilder builder = getBuilder();
210     Location loc = builder.getUnknownLoc();
211     Attribute valueAttr = parseAttribute(value, builder.getContext());
212     return builder.create<arith::ConstantOp>(loc, valueAttr.getType(),
213                                              valueAttr);
214   }
215 
216   Value index(int64_t dim) {
217     OpBuilder builder = getBuilder();
218     return builder.create<IndexOp>(builder.getUnknownLoc(), dim);
219   }
220 
221   Type getIntegerType(unsigned width) {
222     return IntegerType::get(context, width);
223   }
224 
225   Type getFloat32Type() { return Float32Type::get(context); }
226   Type getFloat64Type() { return Float64Type::get(context); }
227 
228 private:
229   // Generates operations to cast the given operand to a specified type.
230   // If the cast cannot be performed, a warning will be issued and the
231   // operand returned as-is (which will presumably yield a verification
232   // issue downstream).
233   Value cast(Type toType, Value operand, bool isUnsignedCast) {
234     OpBuilder builder = getBuilder();
235     auto loc = operand.getLoc();
236 
237     if (operand.getType() == toType)
238       return operand;
239     if (auto toIntType = toType.dyn_cast<IntegerType>()) {
240       // If operand is floating point, cast directly to the int type.
241       if (operand.getType().isa<FloatType>()) {
242         if (isUnsignedCast)
243           return builder.create<arith::FPToUIOp>(loc, toType, operand);
244         return builder.create<arith::FPToSIOp>(loc, toType, operand);
245       }
246       // Cast index operands directly to the int type.
247       if (operand.getType().isIndex())
248         return builder.create<arith::IndexCastOp>(loc, toType, operand);
249       if (auto fromIntType = operand.getType().dyn_cast<IntegerType>()) {
250         // Either extend or truncate.
251         if (toIntType.getWidth() > fromIntType.getWidth()) {
252           if (isUnsignedCast)
253             return builder.create<arith::ExtUIOp>(loc, toType, operand);
254           return builder.create<arith::ExtSIOp>(loc, toType, operand);
255         }
256         if (toIntType.getWidth() < fromIntType.getWidth())
257           return builder.create<arith::TruncIOp>(loc, toType, operand);
258       }
259     } else if (auto toFloatType = toType.dyn_cast<FloatType>()) {
260       // If operand is integer, cast directly to the float type.
261       // Note that it is unclear how to cast from BF16<->FP16.
262       if (operand.getType().isa<IntegerType>()) {
263         if (isUnsignedCast)
264           return builder.create<arith::UIToFPOp>(loc, toFloatType, operand);
265         return builder.create<arith::SIToFPOp>(loc, toFloatType, operand);
266       }
267       if (auto fromFloatType = operand.getType().dyn_cast<FloatType>()) {
268         if (toFloatType.getWidth() > fromFloatType.getWidth())
269           return builder.create<arith::ExtFOp>(loc, toFloatType, operand);
270         if (toFloatType.getWidth() < fromFloatType.getWidth())
271           return builder.create<arith::TruncFOp>(loc, toFloatType, operand);
272       }
273     }
274 
275     emitWarning(operand.getLoc()) << "could not cast operand of type "
276                                   << operand.getType() << " to " << toType;
277     return operand;
278   }
279 
280   bool isFloatingPoint(Value value) { return value.getType().isa<FloatType>(); }
281   bool isInteger(Value value) { return value.getType().isa<IntegerType>(); }
282 
283   OpBuilder getBuilder() {
284     OpBuilder builder(context);
285     builder.setInsertionPointToEnd(&block);
286     return builder;
287   }
288 
289   MLIRContext *context;
290   Block &block;
291 };
292 
293 } // namespace
294 
295 //===----------------------------------------------------------------------===//
296 // FillOp
297 //===----------------------------------------------------------------------===//
298 void FillOp::regionBuilder(ImplicitLocOpBuilder &b, Block &block,
299                            ArrayRef<NamedAttribute> attrs) {
300   assert(block.getNumArguments() == 2 && "FillOp regionBuilder expects 2 args");
301   b.create<linalg::YieldOp>(block.getArgument(0));
302 }
303 
304 void FillOp::build(OpBuilder &builder, OperationState &result, Value value,
305                    Value output) {
306   build(builder, result, output.getType().dyn_cast<RankedTensorType>(), value,
307         output);
308   fillStructuredOpRegion<FillOp>(
309       builder, *result.regions.front(), TypeRange{value.getType()},
310       TypeRange{output.getType()}, result.attributes.getAttrs(), {});
311 }
312 
313 ParseResult parseFillOpRegion(OpAsmParser &parser, Region &r, Type valueType,
314                               Type outputType) {
315   OpBuilder opBuilder(parser.getContext());
316   fillStructuredOpRegion<FillOp>(opBuilder, r, TypeRange{valueType},
317                                  TypeRange{outputType}, {});
318   return success();
319 }
320 
321 /// FillOp region is elided when printing.
322 void printFillOpRegion(OpAsmPrinter &, Operation *, Region &, Type, Type) {}
323 
324 LogicalResult FillOp::verify() {
325   OpOperand *output = getOutputOperand(0);
326   Type fillType = value().getType();
327   if (getElementTypeOrSelf(output->get()) != fillType)
328     return emitOpError("expects fill type to match view elemental type");
329   return success();
330 }
331 
332 void FillOp::getEffects(
333     SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
334         &effects) {
335   if (output().getType().isa<MemRefType>())
336     effects.emplace_back(MemoryEffects::Write::get(), output(),
337                          SideEffects::DefaultResource::get());
338 }
339 
340 namespace {
341 
342 /// Fold linalg.fill -> tensor.expand/collapse_shape chain.
343 ///
344 /// For such op chains, we can create new linalg.fill ops with the result
345 /// type of the tensor.expand/collapse_shape op.
346 template <typename TensorReshapeOp>
347 struct FoldFillWithTensorReshape : OpRewritePattern<TensorReshapeOp> {
348   using OpRewritePattern<TensorReshapeOp>::OpRewritePattern;
349   LogicalResult matchAndRewrite(TensorReshapeOp reshapeOp,
350                                 PatternRewriter &rewriter) const override {
351     auto oldFill = reshapeOp.src().template getDefiningOp<FillOp>();
352     if (!oldFill)
353       return failure();
354 
355     Location loc = oldFill.getLoc();
356     auto newInit = rewriter.create<TensorReshapeOp>(
357         loc, reshapeOp.getResultType(), oldFill.output(),
358         reshapeOp.reassociation());
359     rewriter.replaceOpWithNewOp<FillOp>(reshapeOp, oldFill.value(), newInit);
360 
361     return success();
362   }
363 };
364 
365 /// Fold tensor.pad(linalg.fill) into linalg.fill if the padding value and the
366 /// filling value are the same.
367 struct FoldFillWithPad final : public OpRewritePattern<tensor::PadOp> {
368   using OpRewritePattern::OpRewritePattern;
369 
370   LogicalResult matchAndRewrite(tensor::PadOp padOp,
371                                 PatternRewriter &rewriter) const override {
372     auto fillOp = padOp.source().getDefiningOp<linalg::FillOp>();
373     if (!fillOp)
374       return failure();
375 
376     // We can only fold if the padding value is the same as the original
377     // filling value.
378     Value padValue = padOp.getConstantPaddingValue();
379     if (!padValue || fillOp.value() != padValue)
380       return failure();
381 
382     ReifiedRankedShapedTypeDims reifiedShape;
383     ReifyRankedShapedTypeOpInterface interface =
384         cast<ReifyRankedShapedTypeOpInterface>(padOp.getOperation());
385     if (failed(interface.reifyResultShapes(rewriter, reifiedShape)))
386       return rewriter.notifyMatchFailure(
387           padOp, "failed to reify tensor.pad op result shape");
388 
389     auto oldResultType = padOp.getResultType();
390     SmallVector<int64_t, 4> staticShape(oldResultType.getRank(),
391                                         ShapedType::kDynamicSize);
392     auto newInitOp = rewriter.create<InitTensorOp>(
393         padOp.getLoc(), reifiedShape.front(), staticShape,
394         oldResultType.getElementType());
395     auto newFillOp =
396         rewriter.create<FillOp>(fillOp.getLoc(), padValue, newInitOp);
397     rewriter.replaceOpWithNewOp<tensor::CastOp>(padOp, oldResultType,
398                                                 newFillOp.result());
399 
400     return success();
401   }
402 };
403 
404 /// Fold tensor.insert_slice(tensor.pad(<input>), linalg.fill) into
405 /// tensor.insert_slice(<input>, linalg.fill) if the padding value and the
406 /// filling value are the same.
407 struct FoldInsertPadIntoFill : public OpRewritePattern<tensor::InsertSliceOp> {
408   using OpRewritePattern::OpRewritePattern;
409 
410   LogicalResult matchAndRewrite(tensor::InsertSliceOp insertOp,
411                                 PatternRewriter &rewriter) const override {
412     auto srcPadOp = insertOp.source().getDefiningOp<tensor::PadOp>();
413     if (!srcPadOp)
414       return failure();
415 
416     if (insertOp.getType().getRank() != insertOp.getSourceType().getRank())
417       return failure();
418 
419     // Walk back the tensor.insert_slice chain and find the first destination
420     // value at the start of the chain.
421     Value firstDest = insertOp.dest();
422     while (auto prevOp = firstDest.getDefiningOp<tensor::InsertSliceOp>()) {
423       if (prevOp.getType().getRank() != prevOp.getSourceType().getRank())
424         return failure();
425 
426       // Make sure the range of values accessed are disjoint. Without this, we
427       // cannot fold tensor.pad away.
428       bool disjoint = false;
429       for (int i = 0, e = prevOp.getType().getRank(); i < e; ++i) {
430         // If the dimension has dynamic offset/size, we cannot guarantee
431         // disjoint. So just skip it.
432         if (insertOp.isDynamicOffset(i) || insertOp.isDynamicSize(i) ||
433             insertOp.isDynamicStride(i) || prevOp.isDynamicOffset(i) ||
434             prevOp.isDynamicSize(i) || prevOp.isDynamicStride(i))
435           continue;
436 
437         // Get the range start and end, inclusively for both.
438         int64_t prevStart = prevOp.getStaticOffset(i);
439         int64_t prevEnd = prevStart + (prevOp.getStaticSize(i) - 1) *
440                                           prevOp.getStaticStride(i);
441         int64_t nextStart = insertOp.getStaticOffset(i);
442         int64_t nextEnd = nextStart + (insertOp.getStaticSize(i) - 1) *
443                                           insertOp.getStaticStride(i);
444         if (prevEnd < nextStart || nextEnd < prevStart) {
445           disjoint = true;
446           break;
447         }
448       }
449 
450       if (!disjoint)
451         break;
452       firstDest = prevOp.dest();
453     }
454 
455     // Check whether the first destination is a fill op. For overlapped cases,
456     // this also cannot be true.
457     auto dstFillOp = firstDest.getDefiningOp<linalg::FillOp>();
458     if (!dstFillOp)
459       return failure();
460 
461     // We can only fold if the padding value is the same as the original
462     // filling value.
463     Value padValue = srcPadOp.getConstantPaddingValue();
464     if (!padValue || dstFillOp.value() != padValue)
465       return failure();
466 
467     SmallVector<OpFoldResult> lowPads = srcPadOp.getMixedLowPad();
468     SmallVector<OpFoldResult> oldOffsets = insertOp.getMixedOffsets();
469 
470     Location loc = insertOp.getLoc();
471     MLIRContext *context = getContext();
472 
473     AffineExpr sym0, sym1;
474     bindSymbols(context, sym0, sym1);
475     auto addMap = AffineMap::get(0, 2, {sym0 + sym1}, context);
476 
477     // Calculate the new offsets for the insert. It should be the old offsets
478     // plus low padding sizes.
479     SmallVector<OpFoldResult, 4> newOffsets;
480     for (const auto &p : llvm::zip(lowPads, oldOffsets)) {
481       Value padValue = getValueOrCreateConstantIndexOp(
482           rewriter, srcPadOp.getLoc(), std::get<0>(p));
483       Value offsetValue = getValueOrCreateConstantIndexOp(
484           rewriter, insertOp.getLoc(), std::get<1>(p));
485       newOffsets.push_back(
486           applyMapToValues(rewriter, loc, addMap, {offsetValue, padValue})[0]);
487     }
488 
489     SmallVector<OpFoldResult, 4> newSizes;
490     for (int i = 0, e = srcPadOp.getSourceType().getRank(); i < e; ++i) {
491       newSizes.push_back(
492           rewriter.create<tensor::DimOp>(loc, srcPadOp.source(), i).result());
493     }
494 
495     rewriter.replaceOpWithNewOp<tensor::InsertSliceOp>(
496         insertOp, srcPadOp.source(), insertOp.dest(), newOffsets, newSizes,
497         insertOp.getMixedStrides());
498     return success();
499   }
500 };
501 
502 } // namespace
503 
504 void FillOp::getCanonicalizationPatterns(RewritePatternSet &results,
505                                          MLIRContext *context) {
506   results
507       .add<FoldFillWithPad, FoldFillWithTensorReshape<tensor::CollapseShapeOp>,
508            FoldFillWithTensorReshape<tensor::ExpandShapeOp>,
509            FoldInsertPadIntoFill>(context);
510 }
511 
512 //===----------------------------------------------------------------------===//
513 // GenericOps
514 //===----------------------------------------------------------------------===//
515 void GenericOp::build(
516     OpBuilder &builder, OperationState &result, TypeRange resultTensorTypes,
517     ValueRange inputs, ValueRange outputs, ArrayRef<AffineMap> indexingMaps,
518     ArrayRef<StringRef> iteratorTypes, StringRef doc, StringRef libraryCall,
519     function_ref<void(OpBuilder &, Location, ValueRange)> bodyBuild,
520     ArrayRef<NamedAttribute> attributes) {
521   build(builder, result, resultTensorTypes, inputs, outputs,
522         builder.getAffineMapArrayAttr(indexingMaps),
523         builder.getStrArrayAttr(iteratorTypes),
524         doc.empty() ? StringAttr() : builder.getStringAttr(doc),
525         libraryCall.empty() ? StringAttr()
526                             : builder.getStringAttr(libraryCall));
527   result.addAttributes(attributes);
528   if (!bodyBuild)
529     return;
530 
531   SmallVector<Type, 4> blockArgTypes;
532   SmallVector<Location, 4> blockArgLocs;
533   for (ValueRange container : {inputs, outputs}) {
534     for (Value v : container) {
535       blockArgTypes.push_back(getElementTypeOrSelf(v));
536       blockArgLocs.push_back(v.getLoc());
537     }
538   }
539 
540   OpBuilder::InsertionGuard guard(builder);
541   auto &region = *result.regions.front();
542   Block *bodyBlock =
543       builder.createBlock(&region, region.end(), blockArgTypes, blockArgLocs);
544   bodyBuild(builder, result.location, bodyBlock->getArguments());
545 }
546 
547 void GenericOp::build(
548     OpBuilder &builder, OperationState &result, ValueRange inputs,
549     ValueRange outputs, ArrayRef<AffineMap> indexingMaps,
550     ArrayRef<StringRef> iteratorTypes, StringRef doc, StringRef libraryCall,
551     function_ref<void(OpBuilder &, Location, ValueRange)> bodyBuild,
552     ArrayRef<NamedAttribute> attributes) {
553   build(builder, result, TypeRange{}, inputs, outputs, indexingMaps,
554         iteratorTypes, doc, libraryCall, bodyBuild, attributes);
555 }
556 
557 void GenericOp::build(
558     OpBuilder &builder, OperationState &result, ValueRange inputs,
559     ValueRange outputs, ArrayRef<AffineMap> indexingMaps,
560     ArrayRef<StringRef> iteratorTypes,
561     function_ref<void(OpBuilder &, Location, ValueRange)> bodyBuild,
562     ArrayRef<NamedAttribute> attributes) {
563   build(builder, result, inputs, outputs, indexingMaps, iteratorTypes,
564         /*doc=*/"",
565         /*libraryCall=*/"", bodyBuild, attributes);
566 }
567 
568 void GenericOp::build(
569     OpBuilder &builder, OperationState &result, TypeRange resultTensorTypes,
570     ValueRange inputs, ValueRange outputs, ArrayRef<AffineMap> indexingMaps,
571     ArrayRef<StringRef> iteratorTypes,
572     function_ref<void(OpBuilder &, Location, ValueRange)> bodyBuild,
573     ArrayRef<NamedAttribute> attributes) {
574   build(builder, result, resultTensorTypes, inputs, outputs, indexingMaps,
575         iteratorTypes,
576         /*doc=*/"",
577         /*libraryCall=*/"", bodyBuild, attributes);
578 }
579 
580 void GenericOp::print(OpAsmPrinter &p) {
581   p << " ";
582 
583   // Print extra attributes.
584   auto genericAttrNames = linalgTraitAttrNames();
585 
586   llvm::StringSet<> genericAttrNamesSet;
587   genericAttrNamesSet.insert(genericAttrNames.begin(), genericAttrNames.end());
588   SmallVector<NamedAttribute, 8> genericAttrs;
589   for (auto attr : (*this)->getAttrs())
590     if (genericAttrNamesSet.count(attr.getName().strref()) > 0)
591       genericAttrs.push_back(attr);
592   if (!genericAttrs.empty()) {
593     auto genericDictAttr = DictionaryAttr::get(getContext(), genericAttrs);
594     p << genericDictAttr;
595   }
596 
597   // Printing is shared with named ops, except for the region and attributes
598   printCommonStructuredOpParts(p, *this);
599 
600   genericAttrNames.push_back("operand_segment_sizes");
601   genericAttrNamesSet.insert(genericAttrNames.back());
602 
603   bool hasExtraAttrs = false;
604   for (NamedAttribute n : (*this)->getAttrs()) {
605     if ((hasExtraAttrs = !genericAttrNamesSet.contains(n.getName().strref())))
606       break;
607   }
608   if (hasExtraAttrs) {
609     p << " attrs = ";
610     p.printOptionalAttrDict((*this)->getAttrs(),
611                             /*elidedAttrs=*/genericAttrNames);
612   }
613 
614   // Print region.
615   if (!region().empty()) {
616     p << ' ';
617     p.printRegion(region());
618   }
619 
620   // Print results.
621   printNamedStructuredOpResults(p, result_tensors().getTypes());
622 }
623 
624 ParseResult GenericOp::parse(OpAsmParser &parser, OperationState &result) {
625   DictionaryAttr dictAttr;
626   // Parse the core linalg traits that must check into a dictAttr.
627   // The name is unimportant as we will overwrite result.attributes.
628   // The core linalg traits must contain the information necessary to pass the
629   // verifier.
630   if (parser.parseAttribute(dictAttr, "_", result.attributes))
631     return failure();
632   result.attributes.assign(dictAttr.getValue().begin(),
633                            dictAttr.getValue().end());
634 
635   // Parsing is shared with named ops, except for the region.
636   SmallVector<Type, 1> inputTypes, outputTypes;
637   if (parseCommonStructuredOpParts(parser, result, inputTypes, outputTypes))
638     return failure();
639 
640   // Optional attributes may be added.
641   if (succeeded(parser.parseOptionalKeyword("attrs")))
642     if (failed(parser.parseEqual()) ||
643         failed(parser.parseOptionalAttrDict(result.attributes)))
644       return failure();
645 
646   SmallVector<OpAsmParser::OperandType, 8> regionOperands;
647   std::unique_ptr<Region> region = std::make_unique<Region>();
648   SmallVector<Type, 8> operandTypes, regionTypes;
649   if (parser.parseRegion(*region, regionOperands, regionTypes))
650     return failure();
651   result.addRegion(std::move(region));
652 
653   // Generic ops may specify that a subset of its outputs are tensors. Such
654   // outputs are specified in the result type.
655   // TODO: may need to move output parsing before region parsing.
656   // Need to wait for declarative assembly resolution to decide.
657   SmallVector<Type, 1> outputTensorsTypes;
658   if (parseNamedStructuredOpResults(parser, outputTensorsTypes))
659     return failure();
660   result.addTypes(outputTensorsTypes);
661 
662   return success();
663 }
664 
665 static void getGenericEffectsImpl(
666     SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
667         &effects,
668     ValueRange results, ValueRange inputBuffers, ValueRange outputs) {
669   for (Value value : results) {
670     effects.emplace_back(MemoryEffects::Allocate::get(), value,
671                          SideEffects::DefaultResource::get());
672   }
673   for (Value value : inputBuffers) {
674     effects.emplace_back(MemoryEffects::Read::get(), value,
675                          SideEffects::DefaultResource::get());
676   }
677   for (Value value : outputs) {
678     effects.emplace_back(MemoryEffects::Read::get(), value,
679                          SideEffects::DefaultResource::get());
680     effects.emplace_back(MemoryEffects::Write::get(), value,
681                          SideEffects::DefaultResource::get());
682   }
683 }
684 
685 void GenericOp::getEffects(
686     SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
687         &effects) {
688   SmallVector<Value> inputBuffers = getInputBufferOperands();
689   SmallVector<Value> outputBuffers = getOutputBufferOperands();
690   getGenericEffectsImpl(effects, getOperation()->getResults(), inputBuffers,
691                         outputBuffers);
692 }
693 
694 template <typename GenericOpType>
695 static LogicalResult verifyGenericOp(GenericOpType op) {
696   return success();
697 }
698 
699 LogicalResult GenericOp::verify() { return verifyGenericOp(*this); }
700 
701 namespace {
702 // Deduplicate redundant args of a linalg generic op.
703 // An arg is redundant if it has the same Value and indexing map as another.
704 struct DeduplicateGenericOpInputs : public OpRewritePattern<GenericOp> {
705   using OpRewritePattern<GenericOp>::OpRewritePattern;
706 
707   LogicalResult matchAndRewrite(GenericOp genericOp,
708                                 PatternRewriter &rewriter) const override {
709     // Associate each input to an equivalent "canonical" input that has the same
710     // Value and indexing map.
711     //
712     // In the non-duplicate case, input `i` will have canonical input `i`. But
713     // in the case of duplicated inputs, the canonical input could be some other
714     // input `< i`. That is, a later input will have some earlier input as its
715     // canonical input.
716     llvm::SmallDenseMap<std::pair<Value, AffineMap>, unsigned> canonicalInput;
717     // For later remapping tasks like deduplicating payload block arguments,
718     // having a simple "inputIndex -> canonicalInputIndex" integer mapping is
719     // convenient.
720     SmallVector<unsigned> canonicalInputIndices;
721     for (OpOperand *opOperand : genericOp.getInputOperands()) {
722       AffineMap indexingMap = genericOp.getTiedIndexingMap(opOperand);
723       // STL-like maps have a convenient behavior for our use case here. In the
724       // case of duplicate keys, the insertion is rejected, and the returned
725       // iterator gives access to the value already in the map.
726       auto pair = canonicalInput.insert(
727           {{opOperand->get(), indexingMap}, opOperand->getOperandNumber()});
728       canonicalInputIndices.push_back(pair.first->second);
729     }
730 
731     // If there are no duplicate args, then bail out.
732     if (canonicalInput.size() == genericOp.getNumInputs())
733       return failure();
734 
735     // The operands for the newly canonicalized op.
736     SmallVector<Value> newInputOperands;
737     for (OpOperand *opOperand : genericOp.getInputOperands())
738       if (canonicalInputIndices[opOperand->getOperandNumber()] ==
739           opOperand->getOperandNumber())
740         newInputOperands.push_back(opOperand->get());
741 
742     // Repair the indexing maps by filtering out the ones that have been
743     // eliminated.
744     SmallVector<AffineMap> newIndexingMaps;
745     for (OpOperand *opOperand : genericOp.getInputOperands())
746       if (canonicalInputIndices[opOperand->getOperandNumber()] ==
747           opOperand->getOperandNumber())
748         newIndexingMaps.push_back(genericOp.getTiedIndexingMap(opOperand));
749     for (OpOperand *opOperand : genericOp.getOutputOperands())
750       newIndexingMaps.push_back(genericOp.getTiedIndexingMap(opOperand));
751 
752     // Clone the old op with new operands.
753     SmallVector<Value> outputOperands = genericOp.getOutputOperands();
754     auto newOp = rewriter.create<GenericOp>(
755         genericOp.getLoc(), genericOp->getResultTypes(), newInputOperands,
756         outputOperands, rewriter.getAffineMapArrayAttr(newIndexingMaps),
757         genericOp.iterator_types(), genericOp.docAttr(),
758         genericOp.library_callAttr());
759 
760     // Copy over unknown attributes. They might be load bearing for some flow.
761     ArrayRef<StringRef> odsAttrs = genericOp.getAttributeNames();
762     for (NamedAttribute kv : genericOp->getAttrs()) {
763       if (!llvm::is_contained(odsAttrs, kv.getName().getValue())) {
764         newOp->setAttr(kv.getName(), kv.getValue());
765       }
766     }
767 
768     rewriter.inlineRegionBefore(genericOp.region(), newOp.region(),
769                                 newOp.region().begin());
770 
771     // Repair the payload entry block by RAUW'ing redundant arguments and
772     // erasing them.
773     Block &payload = newOp.region().front();
774     SmallVector<OpOperand *> inputOperands = genericOp.getInputOperands();
775     for (OpOperand *opOperand : llvm::reverse(inputOperands)) {
776       // Iterate in reverse, so that we erase later args first, preventing the
777       // argument list from shifting unexpectedly and invalidating all our
778       // indices.
779       unsigned operandNumber = opOperand->getOperandNumber();
780       if (canonicalInputIndices[operandNumber] == operandNumber)
781         continue;
782       payload.getArgument(operandNumber)
783           .replaceAllUsesWith(
784               payload.getArgument(canonicalInputIndices[operandNumber]));
785       payload.eraseArgument(operandNumber);
786     }
787 
788     rewriter.replaceOp(genericOp, newOp->getResults());
789     return success();
790   }
791 };
792 
793 /// Remove generic operations (on tensors) that are just copying
794 /// the values from inputs to the results. Requirements are
795 /// 1) All iterator types are parallel
796 /// 2) The body contains just a yield operation with the yielded values being
797 ///    the arguments corresponding to the operands.
798 struct EraseIdentityGenericOp : public OpRewritePattern<GenericOp> {
799   using OpRewritePattern<GenericOp>::OpRewritePattern;
800 
801   LogicalResult matchAndRewrite(GenericOp genericOp,
802                                 PatternRewriter &rewriter) const override {
803     // Check all indexing maps are identity.
804     if (llvm::any_of(genericOp.getIndexingMaps(),
805                      [](AffineMap map) { return !map.isIdentity(); }))
806       return failure();
807 
808     // Check that the body of the linalg operation is just a linalg.yield
809     // operation.
810     Block &body = genericOp.region().front();
811     if (!llvm::hasSingleElement(body))
812       return failure();
813     auto yieldOp = dyn_cast<linalg::YieldOp>(body.getTerminator());
814     if (!yieldOp)
815       return failure();
816 
817     // In the buffer case, we need to check exact buffer equality.
818     if (genericOp.hasBufferSemantics()) {
819       if (genericOp.getNumInputs() == 1 && genericOp.getNumOutputs() == 1 &&
820           genericOp.getInputOperand(0)->get() ==
821               genericOp.getOutputOperand(0)->get()) {
822         rewriter.eraseOp(genericOp);
823         return success();
824       }
825       return failure();
826     }
827 
828     // Get the argument number of the returned values. That is the operand
829     // number to use for replacing uses of this operation.
830     SmallVector<Value> returnedArgs;
831     for (const auto &yieldVal : llvm::enumerate(yieldOp.values())) {
832       auto yieldArg = yieldVal.value().dyn_cast<BlockArgument>();
833       if (!yieldArg || yieldArg.getOwner() != &body)
834         return failure();
835       unsigned argumentNumber = yieldArg.getArgNumber();
836       Value returnedArg = genericOp->getOperand(argumentNumber);
837       Type resultType = genericOp->getResult(yieldVal.index()).getType();
838       // The input can have a different type than the result, e.g. a dynamic
839       // input dimension can be turned into a static output dimension.
840       Type returnType = returnedArg.getType();
841       if (returnType != resultType) {
842         // Distinguish between sparse conversion or dense tensor casting.
843         // TODO: unify the two ops?
844         if (sparse_tensor::getSparseTensorEncoding(returnType) ||
845             sparse_tensor::getSparseTensorEncoding(resultType))
846           returnedArg = rewriter.create<sparse_tensor::ConvertOp>(
847               genericOp.getLoc(), resultType, returnedArg);
848         else
849           returnedArg = rewriter.create<tensor::CastOp>(
850               genericOp.getLoc(), resultType, returnedArg);
851       }
852       returnedArgs.push_back(returnedArg);
853     }
854 
855     if (returnedArgs.size() != genericOp->getNumResults())
856       return failure();
857     rewriter.replaceOp(genericOp, returnedArgs);
858     return success();
859   }
860 };
861 
862 /// For each of the operand in `operands` this function maps the static sizes of
863 /// dimensions to their affine dim expressions.
864 static void populateMap(GenericOp genericOp, ArrayRef<OpOperand *> operands,
865                         llvm::DenseMap<AffineExpr, int64_t> &affineExprToSize) {
866   for (OpOperand *opOperand : operands) {
867     if (genericOp.isScalar(opOperand))
868       continue;
869     Value src = opOperand->get();
870     auto sourceType = src.getType().cast<RankedTensorType>();
871     auto sourceMap = genericOp.getTiedIndexingMap(opOperand);
872 
873     // Get the `sourceShape` of the `sourceType`. If the operand is a result of
874     // `tensor.cast` operation and source of the cast operation has a static
875     // shape, then assign it to the `sourceShape`.
876     auto *parentOp = src.getDefiningOp();
877     ArrayRef<int64_t> sourceShape = sourceType.getShape();
878     if (parentOp) {
879       if (auto castOp = dyn_cast<tensor::CastOp>(parentOp)) {
880         Value castSource = castOp.source();
881         auto castSourceType = castSource.getType().cast<RankedTensorType>();
882         if (castSourceType.hasStaticShape())
883           sourceShape = castSourceType.getShape();
884       }
885     }
886 
887     // If the source shape's dimension has a static shape, map the affine dim
888     // expression to the known static size.
889     for (unsigned i = 0; i < sourceShape.size(); i++) {
890       if (sourceType.isDynamicDim(i))
891         continue;
892       if (auto affineDimExpr = sourceMap.getResult(i).dyn_cast<AffineDimExpr>())
893         affineExprToSize.try_emplace(affineDimExpr, sourceShape[i]);
894     }
895   }
896 }
897 
898 /// Creates new operand w.r.t 'opOperand' of `genericOp` with static sizes
899 /// mapped in `affineExprToSize`. New operands are created in `newOperands` and
900 /// their result types is stored in `resultTypes`. If `opOperand` requires no
901 /// change then `changeNeeded` is false and same operand is added in the
902 /// `newOperands` list.
903 static void createNewOperandWithStaticSizes(
904     Location loc, PatternRewriter &rewriter, OpOperand *opOperand,
905     llvm::DenseMap<AffineExpr, int64_t> &affineExprToSize, GenericOp genericOp,
906     SmallVector<Value> &newOperands, SmallVector<Type> &resultTypes,
907     bool &changeNeeded) {
908   Value src = opOperand->get();
909   newOperands.push_back(src);
910   if (genericOp.isScalar(opOperand))
911     return;
912   auto sourceType = src.getType().cast<RankedTensorType>();
913   Type resultType = sourceType;
914   if (sourceType.hasStaticShape() && genericOp.isOutputTensor(opOperand)) {
915     resultTypes.push_back(resultType);
916     return;
917   }
918   ArrayRef<int64_t> sourceShape = sourceType.getShape();
919   AffineMap sourceMap = genericOp.getTiedIndexingMap(opOperand);
920   SmallVector<int64_t> newShape;
921   // If operand is updated with new shape, `newOperandNeeded` will be
922   // true.
923   bool newOperandNeeded = false;
924   for (unsigned i = 0; i < sourceShape.size(); i++) {
925     int64_t dimShape = sourceShape[i];
926     AffineExpr dimExpr = sourceMap.getResult(i);
927     if (affineExprToSize.find(dimExpr) == affineExprToSize.end() ||
928         !sourceType.isDynamicDim(i)) {
929       newShape.push_back(dimShape);
930       continue;
931     }
932     // Dimension has a dynamic shape and corresponding affine dim
933     // expression is present in the map. So assign the size for the
934     // given affine dim expression to the dimension.
935     newShape.push_back(affineExprToSize[dimExpr]);
936     newOperandNeeded = true;
937   }
938   resultType = RankedTensorType::get(newShape, sourceType.getElementType());
939   if (newOperandNeeded) {
940     changeNeeded = true;
941     // Get the new operand value given its size and element type by
942     // casting it.
943     Value newOperand = rewriter.create<tensor::CastOp>(loc, resultType, src);
944     unsigned index = opOperand->getOperandNumber();
945     newOperands[index] = newOperand;
946   }
947   if (genericOp.isOutputTensor(opOperand))
948     resultTypes.push_back(resultType);
949 }
950 
951 /// Static shapes for the operands can be inferred if any one of the operands
952 /// have a static shape. This can be done by referring to the affine dim
953 /// expressions for the operand.
954 struct InferStaticShapeOfOperands : public OpRewritePattern<GenericOp> {
955   using OpRewritePattern<GenericOp>::OpRewritePattern;
956 
957   LogicalResult matchAndRewrite(GenericOp genericOp,
958                                 PatternRewriter &rewriter) const override {
959     if (!genericOp.hasTensorSemantics())
960       return failure();
961 
962     // Maps must be projected permutations.
963     if (llvm::any_of(genericOp.getIndexingMaps(), [](AffineMap map) {
964           return !map.isProjectedPermutation();
965         }))
966       return failure();
967 
968     // Maps affine dim expressions to the static size of that dimension.
969     llvm::DenseMap<AffineExpr, int64_t> affineExprToSize;
970     Location loc = genericOp.getLoc();
971 
972     // For each of the affine dim expression, check if the size is known. If
973     // known add that in the map.
974     populateMap(genericOp, genericOp.getInputAndOutputOperands(),
975                 affineExprToSize);
976 
977     SmallVector<Value> newOperands;
978     SmallVector<Type> resultTypes;
979 
980     // `changeNeeded` is `false` if the operands of `genericOp` require no
981     // change in their types.
982     bool changeNeeded = false;
983     newOperands.reserve(genericOp.getNumInputsAndOutputs());
984     resultTypes.reserve(genericOp.getNumOutputs());
985 
986     // Iterate over all the operands and update the static sizes.
987     for (OpOperand *opOperand : genericOp.getInputAndOutputOperands()) {
988       createNewOperandWithStaticSizes(loc, rewriter, opOperand,
989                                       affineExprToSize, genericOp, newOperands,
990                                       resultTypes, changeNeeded);
991     }
992 
993     // If the generic op has all the required static information, no
994     // canonicalization needed.
995     if (!changeNeeded)
996       return failure();
997 
998     // Clone op.
999     Operation *newOp =
1000         cast<linalg::LinalgOp>(genericOp.getOperation())
1001             .clone(rewriter, genericOp->getLoc(), resultTypes, newOperands);
1002     SmallVector<Value> replacements;
1003     replacements.reserve(newOp->getNumResults());
1004     for (auto it : llvm::zip(genericOp->getResults(), newOp->getResults())) {
1005       Value newResult = std::get<1>(it);
1006       Value oldResult = std::get<0>(it);
1007       Type newType = newResult.getType();
1008       Type oldType = oldResult.getType();
1009       replacements.push_back(
1010           (newType != oldType)
1011               ? rewriter.create<tensor::CastOp>(loc, oldType, newResult)
1012               : newResult);
1013     }
1014     rewriter.replaceOp(genericOp, replacements);
1015     return success();
1016   }
1017 };
1018 } // namespace
1019 
1020 void GenericOp::getCanonicalizationPatterns(RewritePatternSet &results,
1021                                             MLIRContext *context) {
1022   results.add<DeduplicateGenericOpInputs, EraseIdentityGenericOp,
1023               InferStaticShapeOfOperands>(context);
1024 }
1025 
1026 //===----------------------------------------------------------------------===//
1027 // InitTensorOp
1028 //===----------------------------------------------------------------------===//
1029 
1030 void InitTensorOp::build(OpBuilder &b, OperationState &result,
1031                          ArrayRef<OpFoldResult> sizes, Type elementType,
1032                          ArrayRef<NamedAttribute> attrs) {
1033   SmallVector<Value, 4> dynamicSizes;
1034   SmallVector<int64_t, 4> staticSizes;
1035   dispatchIndexOpFoldResults(sizes, dynamicSizes, staticSizes,
1036                              ShapedType::kDynamicSize);
1037   auto resultType = RankedTensorType ::get(staticSizes, elementType);
1038   build(b, result, resultType, dynamicSizes, b.getI64ArrayAttr(staticSizes));
1039   result.addAttributes(attrs);
1040 }
1041 
1042 LogicalResult InitTensorOp::verify() {
1043   RankedTensorType resultType = getType();
1044   SmallVector<int64_t, 4> staticSizes = llvm::to_vector<4>(llvm::map_range(
1045       static_sizes().cast<ArrayAttr>(),
1046       [](Attribute a) -> int64_t { return a.cast<IntegerAttr>().getInt(); }));
1047 
1048   if (failed(verifyListOfOperandsOrIntegers(
1049           *this, "sizes", resultType.getRank(), static_sizes(), sizes(),
1050           ShapedType::isDynamic)))
1051     return failure();
1052 
1053   if (static_sizes().size() != static_cast<unsigned>(resultType.getRank()))
1054     return emitError("expected ") << resultType.getRank() << " sizes values";
1055 
1056   Type expectedType = InitTensorOp::inferResultType(
1057       staticSizes, resultType.getElementType(), resultType.getEncoding());
1058   if (resultType != expectedType) {
1059     return emitError("specified type ")
1060            << resultType << " does not match the inferred type "
1061            << expectedType;
1062   }
1063   return success();
1064 }
1065 
1066 Type InitTensorOp::inferResultType(ArrayRef<int64_t> staticSizes,
1067                                    Type elementType, Attribute encoding) {
1068   return RankedTensorType::get(staticSizes, elementType, encoding);
1069 }
1070 
1071 namespace {
1072 /// Change the type of the result of a `linalg.init_tensor` by making the result
1073 /// type statically sized along dimension that in the original operation where
1074 /// defined as dynamic, but the size was defined using a `constant` op. For
1075 /// example
1076 ///
1077 ///  %c5 = arith.constant 5: index
1078 ///  %0 = linalg.init_tensor [%arg0, %c5] : tensor<?x?xf32>
1079 ///
1080 ///  to
1081 ///
1082 ///  %0 = linalg.init_tensor [%arg0, 5] : tensor<?x5xf32>
1083 struct ReplaceStaticShapeDims : OpRewritePattern<InitTensorOp> {
1084   using OpRewritePattern<InitTensorOp>::OpRewritePattern;
1085 
1086   LogicalResult matchAndRewrite(InitTensorOp op,
1087                                 PatternRewriter &rewriter) const override {
1088     SmallVector<Value, 4> dynamicSizes;
1089     SmallVector<int64_t, 4> staticSizes;
1090     for (unsigned i = 0, e = op.getType().getRank(); i != e; ++i) {
1091       // If the size is already static, nothing to do.
1092       if (!op.isDynamicSize(i)) {
1093         staticSizes.push_back(op.getStaticSize(i));
1094         continue;
1095       }
1096 
1097       // If the size is dynamic but defined using a `constant` op, get the
1098       // constant value to find the static size to use.
1099       unsigned operandNum = op.getIndexOfDynamicSize(i);
1100       Value sizeOperand = op.getOperand(operandNum);
1101       if (auto constantIndexOp =
1102               sizeOperand.getDefiningOp<arith::ConstantIndexOp>()) {
1103         staticSizes.push_back(constantIndexOp.value());
1104         continue;
1105       }
1106 
1107       // Fallback case. Keep the size dynamic.
1108       dynamicSizes.push_back(sizeOperand);
1109       staticSizes.push_back(ShapedType::kDynamicSize);
1110     }
1111     RankedTensorType newType =
1112         RankedTensorType::get(staticSizes, op.getType().getElementType());
1113     if (newType == op.getType())
1114       return failure();
1115     auto newOp =
1116         rewriter.create<InitTensorOp>(op.getLoc(), newType, dynamicSizes,
1117                                       rewriter.getI64ArrayAttr(staticSizes));
1118     rewriter.replaceOpWithNewOp<tensor::CastOp>(op, op.getType(), newOp);
1119     return success();
1120   }
1121 };
1122 } // namespace
1123 
1124 namespace {
1125 /// Since `init_tensor` operation creates a tensor needed only for its shape, a
1126 /// slice of this is also needed only for its shape. The result can be
1127 /// replaced by a new init_tensor operation of the same size as the extract
1128 /// slice op.
1129 struct FoldInitTensorWithExtractSliceOp
1130     : public OpRewritePattern<tensor::ExtractSliceOp> {
1131   using OpRewritePattern<tensor::ExtractSliceOp>::OpRewritePattern;
1132 
1133   LogicalResult matchAndRewrite(tensor::ExtractSliceOp sliceOp,
1134                                 PatternRewriter &rewriter) const override {
1135     if (!sliceOp.source().getDefiningOp<linalg::InitTensorOp>())
1136       return failure();
1137     // ExtractSliceOp may be rank-reducing; its dynamic sizes must be preserved
1138     // as well as its result type.
1139     rewriter.replaceOpWithNewOp<linalg::InitTensorOp>(
1140         sliceOp, sliceOp.sizes(),
1141         sliceOp.result().getType().cast<RankedTensorType>().getShape(),
1142         sliceOp.getSourceType().getElementType());
1143     return success();
1144   }
1145 };
1146 
1147 template <typename TensorReshapeOp>
1148 struct FoldInitTensorWithTensorReshapeOp
1149     : public OpRewritePattern<TensorReshapeOp> {
1150   using OpRewritePattern<TensorReshapeOp>::OpRewritePattern;
1151 
1152   LogicalResult matchAndRewrite(TensorReshapeOp reshapeOp,
1153                                 PatternRewriter &rewriter) const override {
1154     if (!reshapeOp.src().template getDefiningOp<InitTensorOp>())
1155       return failure();
1156     Location loc = reshapeOp.getLoc();
1157     ReifiedRankedShapedTypeDims resultShapes;
1158     ReifyRankedShapedTypeOpInterface reifyShapedTypeInterface =
1159         cast<ReifyRankedShapedTypeOpInterface>(reshapeOp.getOperation());
1160     if (failed(reifyShapedTypeInterface.reifyResultShapes(rewriter,
1161                                                           resultShapes)) ||
1162         !llvm::hasSingleElement(resultShapes))
1163       return failure();
1164     Value initTensor = rewriter.create<InitTensorOp>(
1165         loc, getAsOpFoldResult(resultShapes[0]),
1166         reshapeOp.getResultType().getElementType());
1167     if (initTensor.getType() != reshapeOp.getResultType()) {
1168       rewriter.replaceOpWithNewOp<tensor::CastOp>(
1169           reshapeOp, reshapeOp.getResultType(), initTensor);
1170     } else {
1171       rewriter.replaceOp(reshapeOp, initTensor);
1172     }
1173     return success();
1174   }
1175 };
1176 
1177 struct FoldInitTensorWithDimOp : public OpRewritePattern<tensor::DimOp> {
1178   using OpRewritePattern<tensor::DimOp>::OpRewritePattern;
1179 
1180   LogicalResult matchAndRewrite(tensor::DimOp dimOp,
1181                                 PatternRewriter &rewriter) const override {
1182     Optional<int64_t> maybeConstantIndex = dimOp.getConstantIndex();
1183     auto initTensorOp = dimOp.source().getDefiningOp<linalg::InitTensorOp>();
1184     if (!initTensorOp || !maybeConstantIndex)
1185       return failure();
1186     if (!initTensorOp.isDynamicSize(*maybeConstantIndex))
1187       return failure();
1188     rewriter.replaceOp(dimOp, initTensorOp.getDynamicSize(*maybeConstantIndex));
1189     return success();
1190   }
1191 };
1192 } // namespace
1193 
1194 void InitTensorOp::getCanonicalizationPatterns(RewritePatternSet &results,
1195                                                MLIRContext *context) {
1196   results.add<FoldInitTensorWithDimOp, FoldInitTensorWithExtractSliceOp,
1197               FoldInitTensorWithTensorReshapeOp<tensor::ExpandShapeOp>,
1198               FoldInitTensorWithTensorReshapeOp<tensor::CollapseShapeOp>,
1199               ReplaceStaticShapeDims>(context);
1200 }
1201 
1202 LogicalResult InitTensorOp::reifyResultShapes(
1203     OpBuilder &builder, ReifiedRankedShapedTypeDims &reifiedReturnShapes) {
1204   auto shapes = llvm::to_vector<4>(llvm::map_range(
1205       llvm::seq<int64_t>(0, getType().getRank()), [&](int64_t dim) -> Value {
1206         if (isDynamicSize(dim))
1207           return getDynamicSize(dim);
1208         return builder.create<arith::ConstantIndexOp>(getLoc(),
1209                                                       getStaticSize(dim));
1210       }));
1211   reifiedReturnShapes.emplace_back(std::move(shapes));
1212   return success();
1213 }
1214 
1215 //===----------------------------------------------------------------------===//
1216 // YieldOp
1217 //===----------------------------------------------------------------------===//
1218 
1219 void linalg::YieldOp::print(OpAsmPrinter &p) {
1220   if (getNumOperands() > 0)
1221     p << ' ' << getOperands();
1222   p.printOptionalAttrDict((*this)->getAttrs());
1223   if (getNumOperands() > 0)
1224     p << " : " << getOperandTypes();
1225 }
1226 
1227 ParseResult YieldOp::parse(OpAsmParser &parser, OperationState &result) {
1228   SmallVector<OpAsmParser::OperandType, 2> opInfo;
1229   SmallVector<Type, 2> types;
1230   SMLoc loc = parser.getCurrentLocation();
1231   return failure(parser.parseOperandList(opInfo) ||
1232                  parser.parseOptionalAttrDict(result.attributes) ||
1233                  (!opInfo.empty() && parser.parseColonTypeList(types)) ||
1234                  parser.resolveOperands(opInfo, types, loc, result.operands));
1235 }
1236 
1237 // Check the operand number and types must match the element types of the
1238 // LinalgOp interface's shaped operands.
1239 static LogicalResult verifyYield(linalg::YieldOp op, LinalgOp linalgOp) {
1240   if (op.getNumOperands() != linalgOp.getNumOutputs())
1241     return op.emitOpError("expected number of yield values (")
1242            << linalgOp.getNumOutputs()
1243            << ") to match the number of operands of the enclosing "
1244            << "LinalgOp (" << op.getNumOperands() << ")";
1245 
1246   for (OpOperand &opOperand : op->getOpOperands()) {
1247     OpOperand *outputOperand =
1248         linalgOp.getOutputOperand(opOperand.getOperandNumber());
1249     Type elementType = getElementTypeOrSelf(outputOperand->get().getType());
1250     if (opOperand.get().getType() != elementType)
1251       return op.emitOpError("type of yield operand ")
1252              << (opOperand.getOperandNumber() + 1) << " ("
1253              << opOperand.get().getType() << ") doesn't match "
1254              << "the element type of the enclosing linalg.generic op ("
1255              << elementType << ")";
1256   }
1257   return success();
1258 }
1259 
1260 LogicalResult linalg::YieldOp::verify() {
1261   auto *parentOp = (*this)->getParentOp();
1262   if (parentOp->getNumRegions() != 1 || parentOp->getRegion(0).empty())
1263     return emitOpError("expected single non-empty parent region");
1264 
1265   if (auto linalgOp = dyn_cast<LinalgOp>(parentOp))
1266     return verifyYield(*this, cast<LinalgOp>(parentOp));
1267 
1268   return emitOpError("expected parent op with LinalgOp interface");
1269 }
1270 
1271 //===----------------------------------------------------------------------===//
1272 // IndexOp
1273 //===----------------------------------------------------------------------===//
1274 
1275 LogicalResult IndexOp::verify() {
1276   auto linalgOp = dyn_cast<LinalgOp>((*this)->getParentOp());
1277   if (!linalgOp)
1278     return emitOpError("expected parent op with LinalgOp interface");
1279   if (linalgOp.getNumLoops() <= dim())
1280     return emitOpError("expected dim (")
1281            << dim() << ") to be lower than the number of loops ("
1282            << linalgOp.getNumLoops() << ") of the enclosing LinalgOp";
1283   return success();
1284 }
1285 
1286 /////// Operations corresponding to library calls defined with Tablegen ////////
1287 
1288 #include "mlir/Dialect/Linalg/IR/LinalgNamedStructuredOps.yamlgen.cpp.inc"
1289 
1290 #define GET_OP_CLASSES
1291 #include "mlir/Dialect/Linalg/IR/LinalgOps.cpp.inc"
1292 
1293 #define GET_OP_CLASSES
1294 #include "mlir/Dialect/Linalg/IR/LinalgStructuredOps.cpp.inc"
1295 
1296 /// Return the dims that are `iteratorTypeName` loops in the LinalgOp `op`.
1297 /// Assumes `op` is a LinalgOp.
1298 void mlir::linalg::getDimsOfType(Operation *op, StringRef iteratorTypeName,
1299                                  SmallVectorImpl<unsigned> &res) {
1300   if (!cast<LinalgOp>(op).iterator_types())
1301     return;
1302 
1303   unsigned dim = 0;
1304   for (auto tn :
1305        cast<LinalgOp>(op).iterator_types().getAsValueRange<StringAttr>()) {
1306     if (tn == iteratorTypeName)
1307       res.push_back(dim);
1308     ++dim;
1309   }
1310 }
1311 
1312 AffineMap mlir::linalg::extractOrIdentityMap(Optional<AffineMap> maybeMap,
1313                                              unsigned rank,
1314                                              MLIRContext *context) {
1315   if (maybeMap)
1316     return maybeMap.getValue();
1317   if (rank == 0)
1318     return AffineMap::get(context);
1319   return AffineMap::getMultiDimIdentityMap(rank, context);
1320 }
1321 
1322 SmallVector<AffineExpr, 4>
1323 mlir::linalg::makeAffineDimExprs(unsigned num, unsigned &startIdx,
1324                                  MLIRContext *context) {
1325   SmallVector<AffineExpr, 4> res;
1326   res.reserve(num);
1327   for (unsigned i = 0; i < num; ++i)
1328     res.push_back(getAffineDimExpr(startIdx++, context));
1329   return res;
1330 }
1331 
1332 SmallVector<AffineExpr, 4> mlir::linalg::concat(ArrayRef<AffineExpr> a,
1333                                                 ArrayRef<AffineExpr> b) {
1334   auto rangeA = llvm::make_range(a.begin(), a.end());
1335   auto rangeB = llvm::make_range(b.begin(), b.end());
1336   auto concatRanges = llvm::concat<const AffineExpr>(rangeA, rangeB);
1337   return llvm::to_vector<4>(concatRanges);
1338 }
1339 
1340 static void appendMangledType(llvm::raw_string_ostream &ss, Type t) {
1341   if (auto memref = t.dyn_cast<MemRefType>()) {
1342     ss << "view";
1343     for (auto size : memref.getShape())
1344       if (size < 0)
1345         ss << "sx";
1346       else
1347         ss << size << "x";
1348     appendMangledType(ss, memref.getElementType());
1349   } else if (auto vec = t.dyn_cast<VectorType>()) {
1350     ss << "vector";
1351     llvm::interleave(
1352         vec.getShape(), [&](int64_t i) { ss << i; }, [&]() { ss << "x"; });
1353     appendMangledType(ss, vec.getElementType());
1354   } else if (t.isSignlessIntOrIndexOrFloat()) {
1355     ss << t;
1356   } else {
1357     llvm_unreachable("Invalid type for linalg library name mangling");
1358   }
1359 }
1360 
1361 std::string mlir::linalg::generateLibraryCallName(Operation *op) {
1362   assert(isa<LinalgOp>(op));
1363   std::string name(op->getName().getStringRef().str());
1364   name.reserve(128);
1365   std::replace(name.begin(), name.end(), '.', '_');
1366   llvm::raw_string_ostream ss(name);
1367   ss << "_";
1368   auto types = op->getOperandTypes();
1369   llvm::interleave(
1370       types.begin(), types.end(), [&](Type t) { appendMangledType(ss, t); },
1371       [&]() { ss << "_"; });
1372   return ss.str();
1373 }
1374 
1375 //===----------------------------------------------------------------------===//
1376 // Support for named Linalg ops defined in ods-gen.
1377 //===----------------------------------------------------------------------===//
1378 
1379 /// Generic entry point to create the block for the region of a LinalgOp.
1380 /// This is used by both named structured ops created by ods-gen and by manually
1381 /// defined C++ ops.
1382 /// This is used by both builders and parsers.
1383 /// This function creates the block in the region with arguments corresponding
1384 /// to the elemental types of `inputTypes` and `outputTypes`, which are asserted
1385 /// to be ShapedType.
1386 template <typename NamedStructuredOpType>
1387 static void fillStructuredOpRegion(
1388     OpBuilder &opBuilder, Region &region, TypeRange inputTypes,
1389     TypeRange outputTypes, ArrayRef<NamedAttribute> attrs,
1390     llvm::function_ref<void(unsigned, unsigned)> errorHandler) {
1391   assert(llvm::all_of(outputTypes, [](Type t) { return t.isa<ShapedType>(); }));
1392 
1393   // TODO: atm all operands go through getElementTypeOrSelf,
1394   // reconsider when we have evidence we need to.
1395   SmallVector<Type, 8> argTypes;
1396   SmallVector<Location, 8> argLocs;
1397   for (auto containers : {inputTypes, outputTypes}) {
1398     for (auto t : containers) {
1399       argTypes.push_back(getElementTypeOrSelf(t));
1400 
1401       // TODO: Pass in a proper location here.
1402       argLocs.push_back(opBuilder.getUnknownLoc());
1403     }
1404   }
1405 
1406   // RAII.
1407   OpBuilder::InsertionGuard guard(opBuilder);
1408   Block *body =
1409       opBuilder.createBlock(&region, /*insertPt=*/{}, argTypes, argLocs);
1410   unsigned actual = body->getNumArguments();
1411   unsigned expected = NamedStructuredOpType::getNumRegionArgs();
1412   if (expected != actual) {
1413     if (errorHandler)
1414       errorHandler(expected, actual);
1415     return;
1416   }
1417 
1418   opBuilder.setInsertionPointToStart(body);
1419   ImplicitLocOpBuilder b(opBuilder.getUnknownLoc(), opBuilder);
1420   NamedStructuredOpType::regionBuilder(b, *body, attrs);
1421 
1422   // indexing_maps is an auto-generated method.
1423 
1424   // iterator_types is an auto-generated method.
1425 }
1426 
1427 /// Generic entry point to create both the region and the block of a LinalgOp.
1428 template <typename NamedStructuredOpType>
1429 void createAndFillStructuredOpRegion(OpBuilder &opBuilder,
1430                                      OperationState &result,
1431                                      TypeRange inputTypes,
1432                                      TypeRange outputTypes) {
1433   Region &region = *result.addRegion();
1434   fillStructuredOpRegion<NamedStructuredOpType>(
1435       opBuilder, region, inputTypes, outputTypes, result.attributes.getAttrs(),
1436       [&](unsigned expected, unsigned actual) {
1437         assert(expected != actual && "incorrect number of arguments");
1438       });
1439 }
1440 
1441 /// Common parsing used for both named structured ops created by ods-gen and by
1442 /// manually defined C++ ops. Does not handle regions.
1443 static ParseResult
1444 parseCommonStructuredOpParts(OpAsmParser &parser, OperationState &result,
1445                              SmallVectorImpl<Type> &inputTypes,
1446                              SmallVectorImpl<Type> &outputTypes) {
1447   SMLoc inputsOperandsLoc, outputsOperandsLoc;
1448   SmallVector<OpAsmParser::OperandType, 4> inputsOperands, outputsOperands;
1449 
1450   parser.parseOptionalAttrDict(result.attributes);
1451 
1452   if (succeeded(parser.parseOptionalKeyword("ins"))) {
1453     if (parser.parseLParen())
1454       return failure();
1455 
1456     inputsOperandsLoc = parser.getCurrentLocation();
1457     if (parser.parseOperandList(inputsOperands) ||
1458         parser.parseColonTypeList(inputTypes) || parser.parseRParen())
1459       return failure();
1460   }
1461 
1462   if (succeeded(parser.parseOptionalKeyword("outs"))) {
1463     outputsOperandsLoc = parser.getCurrentLocation();
1464     if (parser.parseLParen() || parser.parseOperandList(outputsOperands) ||
1465         parser.parseColonTypeList(outputTypes) || parser.parseRParen())
1466       return failure();
1467   }
1468 
1469   if (parser.resolveOperands(inputsOperands, inputTypes, inputsOperandsLoc,
1470                              result.operands) ||
1471       parser.resolveOperands(outputsOperands, outputTypes, outputsOperandsLoc,
1472                              result.operands))
1473     return failure();
1474 
1475   result.addAttribute("operand_segment_sizes",
1476                       parser.getBuilder().getI32VectorAttr(
1477                           {static_cast<int32_t>(inputsOperands.size()),
1478                            static_cast<int32_t>(outputsOperands.size())}));
1479   return success();
1480 }
1481 
1482 template <typename NamedStructuredOpType>
1483 static void printCommonStructuredOpParts(OpAsmPrinter &p,
1484                                          NamedStructuredOpType op) {
1485   if (!op.inputs().empty())
1486     p << " ins(" << op.inputs() << " : " << op.inputs().getTypes() << ")";
1487   if (!op.outputs().empty())
1488     p << " outs(" << op.outputs() << " : " << op.outputs().getTypes() << ")";
1489 }
1490 
1491 //===----------------------------------------------------------------------===//
1492 // Specific parsing and printing for named structured ops created by ods-gen.
1493 //===----------------------------------------------------------------------===//
1494 
1495 template <typename NamedStructuredOpType>
1496 static ParseResult
1497 parseNamedStructuredOpRegion(OpAsmParser &parser, Region &region,
1498                              TypeRange inputTypes, TypeRange outputTypes,
1499                              ArrayRef<NamedAttribute> attrs) {
1500   ParseResult res = success();
1501   OpBuilder opBuilder(parser.getContext());
1502   // Resolve `captures` into `capturedValues` at parse time so we can build the
1503   // region with captures.
1504   SmallVector<Value> capturedValues;
1505   fillStructuredOpRegion<NamedStructuredOpType>(
1506       opBuilder, region, inputTypes, outputTypes, attrs,
1507       [&](unsigned expected, unsigned actual) {
1508         res = parser.emitError(
1509             parser.getCurrentLocation(),
1510             llvm::formatv("[parseNamedStructuredOpRegion] ods-gen generated "
1511                           "region expects {0} args, got {1}",
1512                           expected, actual));
1513         region.front().dump();
1514       });
1515   return res;
1516 }
1517 
1518 static ParseResult
1519 parseNamedStructuredOpResults(OpAsmParser &parser,
1520                               SmallVectorImpl<Type> &resultTypes) {
1521   if (parser.parseOptionalArrowTypeList(resultTypes))
1522     return failure();
1523   return success();
1524 }
1525 
1526 template <typename NamedStructuredOpType>
1527 static ParseResult parseNamedStructuredOp(OpAsmParser &parser,
1528                                           OperationState &result) {
1529   // TODO: Enable when ods-gen supports captures.
1530   SmallVector<Type, 1> inputTypes, outputTypes;
1531   if (parseCommonStructuredOpParts(parser, result, inputTypes, outputTypes))
1532     return failure();
1533 
1534   // TODO: consider merging results parsing into region parsing.
1535   // Need to wait for declarative assembly resolution to decide.
1536   SmallVector<Type, 1> outputTensorsTypes;
1537   if (parseNamedStructuredOpResults(parser, outputTensorsTypes))
1538     return failure();
1539   result.addTypes(outputTensorsTypes);
1540 
1541   std::unique_ptr<Region> region = std::make_unique<Region>();
1542   if (parseNamedStructuredOpRegion<NamedStructuredOpType>(
1543           parser, *region, inputTypes, outputTypes,
1544           result.attributes.getAttrs()))
1545     return failure();
1546   result.addRegion(std::move(region));
1547 
1548   return success();
1549 }
1550 
1551 static void printNamedStructuredOpResults(OpAsmPrinter &p,
1552                                           TypeRange resultTypes) {
1553   if (resultTypes.empty())
1554     return;
1555   p.printOptionalArrowTypeList(resultTypes);
1556 }
1557 
1558 template <typename NamedStructuredOpType>
1559 static void printNamedStructuredOp(OpAsmPrinter &p, NamedStructuredOpType op) {
1560   p.printOptionalAttrDict(
1561       op->getAttrs(),
1562       /*elidedAttrs=*/{"operand_segment_sizes",
1563                        // See generated code in mlir-linalg-yaml-gen.cpp
1564                        "linalg.memoized_indexing_maps"});
1565 
1566   // Printing is shared with generic ops, except for the region and
1567   // attributes.
1568   printCommonStructuredOpParts(p, op);
1569 
1570   // Results printing.
1571   printNamedStructuredOpResults(p, op.result_tensors().getTypes());
1572 
1573   // Region is elided.
1574 }
1575 
1576 template <typename NamedStructuredOpType>
1577 static LogicalResult verifyNamedStructuredOp(NamedStructuredOpType op) {
1578   return verifyGenericOp<NamedStructuredOpType>(op);
1579 }
1580 
1581 //===----------------------------------------------------------------------===//
1582 // Canonicalizers and Folders.
1583 //===----------------------------------------------------------------------===//
1584 
1585 namespace {
1586 struct EraseDeadLinalgOp : public OpInterfaceRewritePattern<LinalgOp> {
1587   using OpInterfaceRewritePattern<LinalgOp>::OpInterfaceRewritePattern;
1588 
1589   LogicalResult matchAndRewrite(LinalgOp op,
1590                                 PatternRewriter &rewriter) const override {
1591     for (OpOperand *opOperand : op.getInputAndOutputOperands()) {
1592       // Linalg "inputs" may be either tensor or memref type.
1593       // tensor<0xelt_type> is a convention that may not always mean
1594       // "0 iterations". Only erase in cases we see memref<...x0x...>.
1595       auto mt = opOperand->get().getType().dyn_cast<MemRefType>();
1596       if (!mt)
1597         continue;
1598       if (llvm::is_contained(op.getShape(opOperand), 0)) {
1599         rewriter.eraseOp(op);
1600         return success();
1601       }
1602     }
1603     return failure();
1604   }
1605 };
1606 
1607 struct FoldTensorCastOp : public OpInterfaceRewritePattern<LinalgOp> {
1608   using OpInterfaceRewritePattern<LinalgOp>::OpInterfaceRewritePattern;
1609 
1610   LogicalResult matchAndRewrite(LinalgOp op,
1611                                 PatternRewriter &rewriter) const override {
1612     // If no operand comes from a tensor::CastOp and can be folded then fail.
1613     bool hasTensorCastOperand =
1614         llvm::any_of(op.getInputAndOutputOperands(), [&](OpOperand *opOperand) {
1615           if (opOperand->get().isa<BlockArgument>())
1616             return false;
1617           auto castOp = opOperand->get().getDefiningOp<tensor::CastOp>();
1618           return castOp && canFoldIntoConsumerOp(castOp);
1619         });
1620     if (!hasTensorCastOperand)
1621       return failure();
1622 
1623     SmallVector<Type, 4> newResultTypes;
1624     newResultTypes.reserve(op->getNumResults());
1625     SmallVector<Value, 4> newOperands;
1626     newOperands.reserve(op->getNumOperands());
1627     // Inputs may fold.
1628     for (OpOperand *opOperand : op.getInputOperands()) {
1629       auto tensorCastOp = opOperand->get().getDefiningOp<tensor::CastOp>();
1630       newOperands.push_back(canFoldIntoConsumerOp(tensorCastOp)
1631                                 ? tensorCastOp.source()
1632                                 : opOperand->get());
1633     }
1634     // Init tensors may fold, in which case the resultType must also change.
1635     for (OpOperand *opOperand : op.getOutputOperands()) {
1636       auto tensorCastOp = opOperand->get().getDefiningOp<tensor::CastOp>();
1637       bool fold = canFoldIntoConsumerOp(tensorCastOp);
1638       newOperands.push_back(fold ? tensorCastOp.getOperand()
1639                                  : opOperand->get());
1640       newResultTypes.push_back(newOperands.back().getType());
1641     }
1642     // Clone op.
1643     Operation *newOp =
1644         op.clone(rewriter, op->getLoc(), newResultTypes, newOperands);
1645     SmallVector<Value, 4> replacements;
1646     replacements.reserve(newOp->getNumResults());
1647     for (auto result : llvm::zip(op->getResults(), newOp->getResults())) {
1648       Value oldResult = std::get<0>(result);
1649       Value newResult = std::get<1>(result);
1650       if (newResult.getType() != oldResult.getType()) {
1651         replacements.push_back(rewriter.create<tensor::CastOp>(
1652             op->getLoc(), oldResult.getType(), newResult));
1653       } else {
1654         replacements.push_back(newResult);
1655       }
1656     }
1657     rewriter.replaceOp(op, replacements);
1658 
1659     return success();
1660   }
1661 };
1662 
1663 } // namespace
1664 
1665 #define LINALGOP_FOLDERS(XXX)                                                  \
1666   LogicalResult XXX::fold(ArrayRef<Attribute>,                                 \
1667                           SmallVectorImpl<OpFoldResult> &) {                   \
1668     return foldMemRefCast(*this);                                              \
1669   }
1670 
1671 LINALGOP_FOLDERS(FillOp)
1672 LINALGOP_FOLDERS(GenericOp)
1673 
1674 // All named ops canonicalizers and folders are auto-generated in the
1675 // .cpp.inc.
1676 
1677 //===----------------------------------------------------------------------===//
1678 // LinalgDialect
1679 //===----------------------------------------------------------------------===//
1680 
1681 void LinalgDialect::getCanonicalizationPatterns(
1682     RewritePatternSet &results) const {
1683   results.add<EraseDeadLinalgOp, FoldTensorCastOp>(getContext());
1684 }
1685 
1686 Operation *LinalgDialect::materializeConstant(OpBuilder &builder,
1687                                               Attribute value, Type type,
1688                                               Location loc) {
1689   return builder.create<arith::ConstantOp>(loc, type, value);
1690 }
1691