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