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