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     bool allBool = allInteger && arg0.getType().getIntOrFloatBitWidth() == 1 &&
329                    arg1.getType().getIntOrFloatBitWidth() == 1;
330     if (!allComplex && !allFloatingPoint && !allInteger)
331       llvm_unreachable("unsupported non numeric type");
332     OpBuilder builder = getBuilder();
333     switch (binaryFn) {
334     case BinaryFn::add:
335       if (allComplex)
336         return builder.create<complex::AddOp>(arg0.getLoc(), arg0, arg1);
337       if (allFloatingPoint)
338         return builder.create<arith::AddFOp>(arg0.getLoc(), arg0, arg1);
339       if (allBool)
340         return builder.create<arith::OrIOp>(arg0.getLoc(), arg0, arg1);
341       return builder.create<arith::AddIOp>(arg0.getLoc(), arg0, arg1);
342     case BinaryFn::sub:
343       if (allComplex)
344         return builder.create<complex::SubOp>(arg0.getLoc(), arg0, arg1);
345       if (allFloatingPoint)
346         return builder.create<arith::SubFOp>(arg0.getLoc(), arg0, arg1);
347       if (allBool)
348         llvm_unreachable("unsupported operation: sub with bools");
349       return builder.create<arith::SubIOp>(arg0.getLoc(), arg0, arg1);
350     case BinaryFn::mul:
351       if (allComplex)
352         return builder.create<complex::MulOp>(arg0.getLoc(), arg0, arg1);
353       if (allFloatingPoint)
354         return builder.create<arith::MulFOp>(arg0.getLoc(), arg0, arg1);
355       if (allBool)
356         return builder.create<arith::AndIOp>(arg0.getLoc(), arg0, arg1);
357       return builder.create<arith::MulIOp>(arg0.getLoc(), arg0, arg1);
358     case BinaryFn::max_signed:
359       assert(!allComplex);
360       if (allFloatingPoint)
361         return builder.create<arith::MaxFOp>(arg0.getLoc(), arg0, arg1);
362       return builder.create<arith::MaxSIOp>(arg0.getLoc(), arg0, arg1);
363     case BinaryFn::min_signed:
364       assert(!allComplex);
365       if (allFloatingPoint)
366         return builder.create<arith::MinFOp>(arg0.getLoc(), arg0, arg1);
367       return builder.create<arith::MinSIOp>(arg0.getLoc(), arg0, arg1);
368     case BinaryFn::max_unsigned:
369       assert(!allComplex);
370       if (allFloatingPoint)
371         return builder.create<arith::MaxFOp>(arg0.getLoc(), arg0, arg1);
372       return builder.create<arith::MaxUIOp>(arg0.getLoc(), arg0, arg1);
373     case BinaryFn::min_unsigned:
374       assert(!allComplex);
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 isComplex(Value value) { return value.getType().isa<ComplexType>(); }
472   bool isFloatingPoint(Value value) { return value.getType().isa<FloatType>(); }
473   bool isInteger(Value value) { return value.getType().isa<IntegerType>(); }
474 
475   OpBuilder getBuilder() {
476     OpBuilder builder(context);
477     builder.setInsertionPointToEnd(&block);
478     return builder;
479   }
480 
481   MLIRContext *context;
482   Block &block;
483 };
484 
485 } // namespace
486 
487 //===----------------------------------------------------------------------===//
488 // FillOp
489 //===----------------------------------------------------------------------===//
490 
491 namespace {
492 
493 /// Fold linalg.fill -> tensor.expand/collapse_shape chain.
494 ///
495 /// For such op chains, we can create new linalg.fill ops with the result
496 /// type of the tensor.expand/collapse_shape op.
497 template <typename TensorReshapeOp>
498 struct FoldFillWithTensorReshape : OpRewritePattern<TensorReshapeOp> {
499   using OpRewritePattern<TensorReshapeOp>::OpRewritePattern;
500   LogicalResult matchAndRewrite(TensorReshapeOp reshapeOp,
501                                 PatternRewriter &rewriter) const override {
502     auto oldFill = reshapeOp.getSrc().template getDefiningOp<FillOp>();
503     if (!oldFill)
504       return failure();
505 
506     Location loc = oldFill.getLoc();
507     auto newInit = rewriter.create<TensorReshapeOp>(
508         loc, reshapeOp.getResultType(), oldFill.output(),
509         reshapeOp.getReassociation());
510     rewriter.replaceOpWithNewOp<FillOp>(reshapeOp, ValueRange{oldFill.value()},
511                                         ValueRange{newInit});
512 
513     return success();
514   }
515 };
516 
517 /// Fold tensor.pad(linalg.fill) into linalg.fill if the padding value and the
518 /// filling value are the same.
519 struct FoldFillWithPad final : public OpRewritePattern<tensor::PadOp> {
520   using OpRewritePattern::OpRewritePattern;
521 
522   LogicalResult matchAndRewrite(tensor::PadOp padOp,
523                                 PatternRewriter &rewriter) const override {
524     auto fillOp = padOp.getSource().getDefiningOp<linalg::FillOp>();
525     if (!fillOp)
526       return failure();
527 
528     // We can only fold if the padding value is the same as the original
529     // filling value.
530     Value padValue = padOp.getConstantPaddingValue();
531     if (!padValue || fillOp.value() != padValue)
532       return failure();
533 
534     ReifiedRankedShapedTypeDims reifiedShape;
535     ReifyRankedShapedTypeOpInterface interface =
536         cast<ReifyRankedShapedTypeOpInterface>(padOp.getOperation());
537     if (failed(interface.reifyResultShapes(rewriter, reifiedShape)))
538       return rewriter.notifyMatchFailure(
539           padOp, "failed to reify tensor.pad op result shape");
540 
541     auto oldResultType = padOp.getResultType();
542     SmallVector<int64_t, 4> staticShape(oldResultType.getRank(),
543                                         ShapedType::kDynamicSize);
544     auto newInitOp = rewriter.create<InitTensorOp>(
545         padOp.getLoc(), reifiedShape.front(), staticShape,
546         oldResultType.getElementType());
547     auto newFillOp = rewriter.create<FillOp>(
548         fillOp.getLoc(), ValueRange{padValue}, ValueRange{newInitOp});
549     rewriter.replaceOpWithNewOp<tensor::CastOp>(padOp, oldResultType,
550                                                 newFillOp.result());
551 
552     return success();
553   }
554 };
555 
556 /// Fold tensor.insert_slice(tensor.pad(<input>), linalg.fill) into
557 /// tensor.insert_slice(<input>, linalg.fill) if the padding value and the
558 /// filling value are the same.
559 struct FoldInsertPadIntoFill : public OpRewritePattern<tensor::InsertSliceOp> {
560   using OpRewritePattern::OpRewritePattern;
561 
562   LogicalResult matchAndRewrite(tensor::InsertSliceOp insertOp,
563                                 PatternRewriter &rewriter) const override {
564     auto srcPadOp = insertOp.getSource().getDefiningOp<tensor::PadOp>();
565     if (!srcPadOp)
566       return failure();
567 
568     if (insertOp.getType().getRank() != insertOp.getSourceType().getRank())
569       return failure();
570 
571     // Walk back the tensor.insert_slice chain and find the first destination
572     // value at the start of the chain.
573     Value firstDest = insertOp.getDest();
574     while (auto prevOp = firstDest.getDefiningOp<tensor::InsertSliceOp>()) {
575       if (prevOp.getType().getRank() != prevOp.getSourceType().getRank())
576         return failure();
577 
578       // Make sure the range of values accessed are disjoint. Without this, we
579       // cannot fold tensor.pad away.
580       bool disjoint = false;
581       for (int i = 0, e = prevOp.getType().getRank(); i < e; ++i) {
582         // If the dimension has dynamic offset/size, we cannot guarantee
583         // disjoint. So just skip it.
584         if (insertOp.isDynamicOffset(i) || insertOp.isDynamicSize(i) ||
585             insertOp.isDynamicStride(i) || prevOp.isDynamicOffset(i) ||
586             prevOp.isDynamicSize(i) || prevOp.isDynamicStride(i))
587           continue;
588 
589         // Get the range start and end, inclusively for both.
590         int64_t prevStart = prevOp.getStaticOffset(i);
591         int64_t prevEnd = prevStart + (prevOp.getStaticSize(i) - 1) *
592                                           prevOp.getStaticStride(i);
593         int64_t nextStart = insertOp.getStaticOffset(i);
594         int64_t nextEnd = nextStart + (insertOp.getStaticSize(i) - 1) *
595                                           insertOp.getStaticStride(i);
596         if (prevEnd < nextStart || nextEnd < prevStart) {
597           disjoint = true;
598           break;
599         }
600       }
601 
602       if (!disjoint)
603         break;
604       firstDest = prevOp.getDest();
605     }
606 
607     // Check whether the first destination is a fill op. For overlapped cases,
608     // this also cannot be true.
609     auto dstFillOp = firstDest.getDefiningOp<linalg::FillOp>();
610     if (!dstFillOp)
611       return failure();
612 
613     // We can only fold if the padding value is the same as the original
614     // filling value.
615     Value padValue = srcPadOp.getConstantPaddingValue();
616     if (!padValue || dstFillOp.value() != padValue)
617       return failure();
618 
619     SmallVector<OpFoldResult> lowPads = srcPadOp.getMixedLowPad();
620     SmallVector<OpFoldResult> oldOffsets = insertOp.getMixedOffsets();
621 
622     Location loc = insertOp.getLoc();
623     MLIRContext *context = getContext();
624 
625     AffineExpr sym0, sym1;
626     bindSymbols(context, sym0, sym1);
627     auto addMap = AffineMap::get(0, 2, {sym0 + sym1}, context);
628 
629     // Calculate the new offsets for the insert. It should be the old offsets
630     // plus low padding sizes.
631     SmallVector<OpFoldResult, 4> newOffsets;
632     for (const auto &p : llvm::zip(lowPads, oldOffsets)) {
633       Value padValue = getValueOrCreateConstantIndexOp(
634           rewriter, srcPadOp.getLoc(), std::get<0>(p));
635       Value offsetValue = getValueOrCreateConstantIndexOp(
636           rewriter, insertOp.getLoc(), std::get<1>(p));
637       newOffsets.push_back(
638           applyMapToValues(rewriter, loc, addMap, {offsetValue, padValue})[0]);
639     }
640 
641     SmallVector<OpFoldResult, 4> newSizes;
642     for (int i = 0, e = srcPadOp.getSourceType().getRank(); i < e; ++i) {
643       newSizes.push_back(
644           rewriter.create<tensor::DimOp>(loc, srcPadOp.getSource(), i)
645               .getResult());
646     }
647 
648     rewriter.replaceOpWithNewOp<tensor::InsertSliceOp>(
649         insertOp, srcPadOp.getSource(), insertOp.getDest(), newOffsets,
650         newSizes, insertOp.getMixedStrides());
651     return success();
652   }
653 };
654 
655 } // namespace
656 
657 void FillOp::getCanonicalizationPatterns(RewritePatternSet &results,
658                                          MLIRContext *context) {
659   results
660       .add<FoldFillWithPad, FoldFillWithTensorReshape<tensor::CollapseShapeOp>,
661            FoldFillWithTensorReshape<tensor::ExpandShapeOp>,
662            FoldInsertPadIntoFill>(context);
663 }
664 
665 //===----------------------------------------------------------------------===//
666 // GenericOps
667 //===----------------------------------------------------------------------===//
668 void GenericOp::build(
669     OpBuilder &builder, OperationState &result, TypeRange resultTensorTypes,
670     ValueRange inputs, ValueRange outputs, ArrayAttr indexingMaps,
671     ArrayAttr iteratorTypes, StringAttr doc, StringAttr libraryCall,
672     function_ref<void(OpBuilder &, Location, ValueRange)> bodyBuild,
673     ArrayRef<NamedAttribute> attributes) {
674   build(builder, result, resultTensorTypes, inputs, outputs, indexingMaps,
675         iteratorTypes, doc, libraryCall);
676   result.addAttributes(attributes);
677   if (!bodyBuild)
678     return;
679 
680   SmallVector<Type, 4> blockArgTypes;
681   SmallVector<Location, 4> blockArgLocs;
682   for (ValueRange container : {inputs, outputs}) {
683     for (Value v : container) {
684       blockArgTypes.push_back(getElementTypeOrSelf(v));
685       blockArgLocs.push_back(v.getLoc());
686     }
687   }
688 
689   OpBuilder::InsertionGuard guard(builder);
690   auto &region = *result.regions.front();
691   Block *bodyBlock =
692       builder.createBlock(&region, region.end(), blockArgTypes, blockArgLocs);
693   bodyBuild(builder, result.location, bodyBlock->getArguments());
694 }
695 
696 void GenericOp::build(
697     OpBuilder &builder, OperationState &result, TypeRange resultTensorTypes,
698     ValueRange inputs, ValueRange outputs, ArrayRef<AffineMap> indexingMaps,
699     ArrayRef<StringRef> iteratorTypes, StringRef doc, StringRef libraryCall,
700     function_ref<void(OpBuilder &, Location, ValueRange)> bodyBuild,
701     ArrayRef<NamedAttribute> attributes) {
702   build(builder, result, resultTensorTypes, inputs, outputs,
703         builder.getAffineMapArrayAttr(indexingMaps),
704         builder.getStrArrayAttr(iteratorTypes),
705         doc.empty() ? StringAttr() : builder.getStringAttr(doc),
706         libraryCall.empty() ? StringAttr() : builder.getStringAttr(libraryCall),
707         bodyBuild, attributes);
708 }
709 
710 void GenericOp::build(
711     OpBuilder &builder, OperationState &result, ValueRange inputs,
712     ValueRange outputs, ArrayRef<AffineMap> indexingMaps,
713     ArrayRef<StringRef> iteratorTypes, StringRef doc, StringRef libraryCall,
714     function_ref<void(OpBuilder &, Location, ValueRange)> bodyBuild,
715     ArrayRef<NamedAttribute> attributes) {
716   build(builder, result, TypeRange{}, inputs, outputs, indexingMaps,
717         iteratorTypes, doc, libraryCall, bodyBuild, attributes);
718 }
719 
720 void GenericOp::build(
721     OpBuilder &builder, OperationState &result, ValueRange inputs,
722     ValueRange outputs, ArrayRef<AffineMap> indexingMaps,
723     ArrayRef<StringRef> iteratorTypes,
724     function_ref<void(OpBuilder &, Location, ValueRange)> bodyBuild,
725     ArrayRef<NamedAttribute> attributes) {
726   build(builder, result, inputs, outputs, indexingMaps, iteratorTypes,
727         /*doc=*/"",
728         /*libraryCall=*/"", bodyBuild, attributes);
729 }
730 
731 void GenericOp::build(
732     OpBuilder &builder, OperationState &result, TypeRange resultTensorTypes,
733     ValueRange inputs, ValueRange outputs, ArrayRef<AffineMap> indexingMaps,
734     ArrayRef<StringRef> iteratorTypes,
735     function_ref<void(OpBuilder &, Location, ValueRange)> bodyBuild,
736     ArrayRef<NamedAttribute> attributes) {
737   build(builder, result, resultTensorTypes, inputs, outputs, indexingMaps,
738         iteratorTypes,
739         /*doc=*/"",
740         /*libraryCall=*/"", bodyBuild, attributes);
741 }
742 
743 void GenericOp::print(OpAsmPrinter &p) {
744   p << " ";
745 
746   // Print extra attributes.
747   auto genericAttrNames = linalgTraitAttrNames();
748 
749   llvm::StringSet<> genericAttrNamesSet;
750   genericAttrNamesSet.insert(genericAttrNames.begin(), genericAttrNames.end());
751   SmallVector<NamedAttribute, 8> genericAttrs;
752   for (auto attr : (*this)->getAttrs())
753     if (genericAttrNamesSet.count(attr.getName().strref()) > 0)
754       genericAttrs.push_back(attr);
755   if (!genericAttrs.empty()) {
756     auto genericDictAttr = DictionaryAttr::get(getContext(), genericAttrs);
757     p << genericDictAttr;
758   }
759 
760   // Printing is shared with named ops, except for the region and attributes
761   printCommonStructuredOpParts(p, inputs(), outputs());
762 
763   genericAttrNames.push_back("operand_segment_sizes");
764   genericAttrNamesSet.insert(genericAttrNames.back());
765 
766   bool hasExtraAttrs = false;
767   for (NamedAttribute n : (*this)->getAttrs()) {
768     if ((hasExtraAttrs = !genericAttrNamesSet.contains(n.getName().strref())))
769       break;
770   }
771   if (hasExtraAttrs) {
772     p << " attrs = ";
773     p.printOptionalAttrDict((*this)->getAttrs(),
774                             /*elidedAttrs=*/genericAttrNames);
775   }
776 
777   // Print region.
778   if (!region().empty()) {
779     p << ' ';
780     p.printRegion(region());
781   }
782 
783   // Print results.
784   printNamedStructuredOpResults(p, result_tensors().getTypes());
785 }
786 
787 ParseResult GenericOp::parse(OpAsmParser &parser, OperationState &result) {
788   DictionaryAttr dictAttr;
789   // Parse the core linalg traits that must check into a dictAttr.
790   // The name is unimportant as we will overwrite result.attributes.
791   // The core linalg traits must contain the information necessary to pass the
792   // verifier.
793   if (parser.parseAttribute(dictAttr, "_", result.attributes))
794     return failure();
795   result.attributes.assign(dictAttr.getValue().begin(),
796                            dictAttr.getValue().end());
797 
798   // Parsing is shared with named ops, except for the region.
799   SmallVector<Type, 1> inputTypes, outputTypes;
800   if (parseCommonStructuredOpParts(parser, result, inputTypes, outputTypes))
801     return failure();
802 
803   // Optional attributes may be added.
804   if (succeeded(parser.parseOptionalKeyword("attrs")))
805     if (failed(parser.parseEqual()) ||
806         failed(parser.parseOptionalAttrDict(result.attributes)))
807       return failure();
808 
809   std::unique_ptr<Region> region = std::make_unique<Region>();
810   if (parser.parseRegion(*region, {}))
811     return failure();
812   result.addRegion(std::move(region));
813 
814   // Generic ops may specify that a subset of its outputs are tensors. Such
815   // outputs are specified in the result type.
816   // TODO: may need to move output parsing before region parsing.
817   // Need to wait for declarative assembly resolution to decide.
818   SmallVector<Type, 1> outputTensorsTypes;
819   if (parseNamedStructuredOpResults(parser, outputTensorsTypes))
820     return failure();
821   result.addTypes(outputTensorsTypes);
822 
823   return success();
824 }
825 
826 static void getGenericEffectsImpl(
827     SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
828         &effects,
829     ValueRange results, ValueRange inputBuffers, ValueRange outputs) {
830   for (Value value : inputBuffers) {
831     effects.emplace_back(MemoryEffects::Read::get(), value,
832                          SideEffects::DefaultResource::get());
833   }
834   for (Value value : outputs) {
835     effects.emplace_back(MemoryEffects::Read::get(), value,
836                          SideEffects::DefaultResource::get());
837     effects.emplace_back(MemoryEffects::Write::get(), value,
838                          SideEffects::DefaultResource::get());
839   }
840 }
841 
842 void GenericOp::getEffects(
843     SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
844         &effects) {
845   SmallVector<Value> inputBuffers = getInputBufferOperands();
846   SmallVector<Value> outputBuffers = getOutputBufferOperands();
847   getGenericEffectsImpl(effects, getOperation()->getResults(), inputBuffers,
848                         outputBuffers);
849 }
850 
851 LogicalResult GenericOp::verify() { return success(); }
852 
853 namespace {
854 
855 struct DeduplicateAndRemoveDeadOperandsAndResults
856     : public OpRewritePattern<GenericOp> {
857   using OpRewritePattern<GenericOp>::OpRewritePattern;
858 
859   LogicalResult matchAndRewrite(GenericOp genericOp,
860                                 PatternRewriter &rewriter) const override {
861     // Create a map from argument position in the original op to the argument
862     // position in the new op. If the argument is dropped it wont have an entry.
863     llvm::SmallDenseMap<unsigned, unsigned> origToNewPos;
864     unsigned numNewArgs = 0;
865     SmallVector<OpOperand *> droppedOpOperands;
866     llvm::SmallDenseSet<unsigned> droppedOutputs;
867 
868     // Information needed to build the new op.
869     SmallVector<Value> newInputOperands, newOutputOperands;
870     SmallVector<AffineMap> newIndexingMaps;
871     SmallVector<Type> newResultTypes;
872 
873     // Input argument can be dropped if
874     // - it has no uses, or,
875     // - there is a duplicate operand which is accessed using the same
876     //   indexing map.
877     llvm::SmallDenseMap<std::pair<Value, AffineMap>, unsigned> dedupedInputs;
878     auto indexingMaps = genericOp.getIndexingMaps();
879     ArrayRef<AffineMap> unprocessedIndexingMaps(indexingMaps);
880     for (OpOperand *inputOpOperand : genericOp.getInputOperands()) {
881       BlockArgument arg = genericOp.getTiedBlockArgument(inputOpOperand);
882       unsigned argNum = arg.getArgNumber();
883       unprocessedIndexingMaps = unprocessedIndexingMaps.drop_front();
884 
885       // Check if operand is dead and if dropping the indexing map makes the
886       // loops to shape computation invalid.
887       if (!genericOp.payloadUsesValueFromOperand(inputOpOperand)) {
888         // Add the current operands to the list of potentially droppable
889         // operands. If it cannot be dropped, this needs to be popped back.
890         droppedOpOperands.push_back(inputOpOperand);
891         if (genericOp.canOpOperandsBeDropped(droppedOpOperands))
892           continue;
893         droppedOpOperands.pop_back();
894       }
895 
896       // Check if this operand is a duplicate.
897       AffineMap indexingMap = genericOp.getTiedIndexingMap(inputOpOperand);
898       auto it = dedupedInputs.find(
899           std::make_pair(inputOpOperand->get(), indexingMap));
900       if (it != dedupedInputs.end()) {
901         origToNewPos[argNum] = it->second;
902         droppedOpOperands.push_back(inputOpOperand);
903         continue;
904       }
905 
906       // This is a preserved argument.
907       origToNewPos[argNum] = numNewArgs;
908       dedupedInputs[{inputOpOperand->get(), indexingMap}] = numNewArgs;
909       newInputOperands.push_back(inputOpOperand->get());
910       newIndexingMaps.push_back(indexingMap);
911       numNewArgs++;
912     }
913 
914     // If the op doesnt have tensor semantics, keep all the outputs as
915     // preserved.
916     if (!genericOp.hasTensorSemantics()) {
917       for (OpOperand *outputOpOperand : genericOp.getOutputOperands()) {
918         unprocessedIndexingMaps = unprocessedIndexingMaps.drop_front();
919         BlockArgument arg = genericOp.getTiedBlockArgument(outputOpOperand);
920         origToNewPos[arg.getArgNumber()] = numNewArgs++;
921         newOutputOperands.push_back(outputOpOperand->get());
922         newIndexingMaps.push_back(
923             genericOp.getTiedIndexingMap(outputOpOperand));
924       }
925     } else {
926       // Output argument can be dropped if the result has
927       // - no users, and
928       // - it is not used in the payload, and
929       // - the corresponding indexing maps are not needed for loop bound
930       //   computation.
931       for (const auto &outputOpOperand :
932            llvm::enumerate(genericOp.getOutputOperands())) {
933         unprocessedIndexingMaps = unprocessedIndexingMaps.drop_front();
934         Value result = genericOp.getResult(outputOpOperand.index());
935         BlockArgument arg =
936             genericOp.getTiedBlockArgument(outputOpOperand.value());
937         if (result.use_empty() &&
938             !genericOp.payloadUsesValueFromOperand(outputOpOperand.value())) {
939           // Check if the opoperand can be dropped without affecting loop bound
940           // computation. Add the operand to the list of dropped op operand for
941           // checking. If it cannot be dropped, need to pop the value back.
942           droppedOpOperands.push_back(outputOpOperand.value());
943           if (genericOp.canOpOperandsBeDropped(droppedOpOperands)) {
944             droppedOutputs.insert(outputOpOperand.index());
945             continue;
946           }
947           droppedOpOperands.pop_back();
948         }
949 
950         origToNewPos[arg.getArgNumber()] = numNewArgs++;
951         newOutputOperands.push_back(outputOpOperand.value()->get());
952         newIndexingMaps.push_back(
953             genericOp.getTiedIndexingMap(outputOpOperand.value()));
954         newResultTypes.push_back(result.getType());
955       }
956     }
957 
958     // Check if there is any change to operands.
959     if (newInputOperands.size() + newOutputOperands.size() ==
960         static_cast<size_t>(genericOp.getNumInputsAndOutputs()))
961       return failure();
962 
963     // Create the new op with the body being empty.
964     Location loc = genericOp.getLoc();
965     auto newOp = rewriter.create<GenericOp>(
966         loc, newResultTypes, newInputOperands, newOutputOperands,
967         rewriter.getAffineMapArrayAttr(newIndexingMaps),
968         genericOp.iterator_types(), genericOp.docAttr(),
969         genericOp.library_callAttr(),
970         [](OpBuilder & /*builder*/, Location /*loc*/, ValueRange /*args*/) {
971           return;
972         });
973     // Copy over unknown attributes. They might be load bearing for some flow.
974     ArrayRef<StringRef> odsAttrs = genericOp.getAttributeNames();
975     for (NamedAttribute kv : genericOp->getAttrs())
976       if (!llvm::is_contained(odsAttrs, kv.getName().getValue()))
977         newOp->setAttr(kv.getName(), kv.getValue());
978 
979     // Merge the body of the original op with the new op.
980     Block *newOpBlock = &newOp.region().front();
981     Block *origOpBlock = &genericOp.region().front();
982     SmallVector<Value> replacements(origOpBlock->getNumArguments(), nullptr);
983     for (auto argNum : llvm::seq<unsigned>(0, origOpBlock->getNumArguments())) {
984       auto it = origToNewPos.find(argNum);
985       if (it != origToNewPos.end())
986         replacements[argNum] = newOpBlock->getArgument(it->second);
987     }
988     rewriter.mergeBlocks(origOpBlock, newOpBlock, replacements);
989 
990     // Drop the unused yield args.
991     Block *block = &newOp.region().front();
992     if (!droppedOutputs.empty()) {
993       OpBuilder::InsertionGuard g(rewriter);
994       SmallVector<Value> newYieldVals;
995       YieldOp origYieldOp = cast<YieldOp>(block->getTerminator());
996       rewriter.setInsertionPoint(origYieldOp);
997       for (const auto &yieldOpOperands :
998            llvm::enumerate(origYieldOp.values())) {
999         if (!droppedOutputs.count(yieldOpOperands.index())) {
1000           newYieldVals.push_back(yieldOpOperands.value());
1001           continue;
1002         }
1003       }
1004       rewriter.replaceOpWithNewOp<YieldOp>(origYieldOp, newYieldVals);
1005     }
1006 
1007     // Replace all live uses of the op.
1008     SmallVector<Value> replacementsVals(genericOp->getNumResults(), nullptr);
1009     unsigned newResultNum = 0;
1010     for (const auto &result : llvm::enumerate(genericOp.getResults()))
1011       if (!droppedOutputs.count(result.index()))
1012         replacementsVals[result.index()] = newOp.getResult(newResultNum++);
1013     rewriter.replaceOp(genericOp, replacementsVals);
1014     return success();
1015   }
1016 };
1017 
1018 /// Remove generic operations (on tensors) that are just copying
1019 /// the values from inputs to the results. Requirements are
1020 /// 1) All iterator types are parallel
1021 /// 2) The body contains just a yield operation with the yielded values being
1022 ///    the arguments corresponding to the operands.
1023 struct EraseIdentityGenericOp : public OpRewritePattern<GenericOp> {
1024   using OpRewritePattern<GenericOp>::OpRewritePattern;
1025 
1026   LogicalResult matchAndRewrite(GenericOp genericOp,
1027                                 PatternRewriter &rewriter) const override {
1028     // Check all indexing maps are identity.
1029     if (llvm::any_of(genericOp.getIndexingMaps(),
1030                      [](AffineMap map) { return !map.isIdentity(); }))
1031       return failure();
1032 
1033     // Check that the body of the linalg operation is just a linalg.yield
1034     // operation.
1035     Block &body = genericOp.region().front();
1036     if (!llvm::hasSingleElement(body))
1037       return failure();
1038     auto yieldOp = dyn_cast<linalg::YieldOp>(body.getTerminator());
1039     if (!yieldOp)
1040       return failure();
1041 
1042     // In the buffer case, we need to check exact buffer equality.
1043     if (genericOp.hasBufferSemantics()) {
1044       if (genericOp.getNumInputs() == 1 && genericOp.getNumOutputs() == 1 &&
1045           genericOp.getInputOperand(0)->get() ==
1046               genericOp.getOutputOperand(0)->get()) {
1047         rewriter.eraseOp(genericOp);
1048         return success();
1049       }
1050       return failure();
1051     }
1052 
1053     // Get the argument number of the returned values. That is the operand
1054     // number to use for replacing uses of this operation.
1055     SmallVector<Value> returnedArgs;
1056     for (const auto &yieldVal : llvm::enumerate(yieldOp.values())) {
1057       auto yieldArg = yieldVal.value().dyn_cast<BlockArgument>();
1058       if (!yieldArg || yieldArg.getOwner() != &body)
1059         return failure();
1060       unsigned argumentNumber = yieldArg.getArgNumber();
1061       Value returnedArg = genericOp->getOperand(argumentNumber);
1062       Type resultType = genericOp->getResult(yieldVal.index()).getType();
1063       // The input can have a different type than the result, e.g. a dynamic
1064       // input dimension can be turned into a static output dimension.
1065       Type returnType = returnedArg.getType();
1066       if (returnType != resultType) {
1067         // Distinguish between sparse conversion or dense tensor casting.
1068         // TODO: unify the two ops?
1069         if (sparse_tensor::getSparseTensorEncoding(returnType) ||
1070             sparse_tensor::getSparseTensorEncoding(resultType))
1071           returnedArg = rewriter.create<sparse_tensor::ConvertOp>(
1072               genericOp.getLoc(), resultType, returnedArg);
1073         else {
1074           if (!tensor::CastOp::areCastCompatible(returnedArg.getType(),
1075                                                  resultType))
1076             return failure();
1077           returnedArg = rewriter.create<tensor::CastOp>(
1078               genericOp.getLoc(), resultType, returnedArg);
1079         }
1080       }
1081       returnedArgs.push_back(returnedArg);
1082     }
1083 
1084     if (returnedArgs.size() != genericOp->getNumResults())
1085       return failure();
1086     rewriter.replaceOp(genericOp, returnedArgs);
1087     return success();
1088   }
1089 };
1090 } // namespace
1091 
1092 void GenericOp::getCanonicalizationPatterns(RewritePatternSet &results,
1093                                             MLIRContext *context) {
1094   results
1095       .add<DeduplicateAndRemoveDeadOperandsAndResults, EraseIdentityGenericOp>(
1096           context);
1097 }
1098 
1099 LogicalResult GenericOp::fold(ArrayRef<Attribute>,
1100                               SmallVectorImpl<OpFoldResult> &) {
1101   return foldMemRefCast(*this);
1102 }
1103 
1104 //===----------------------------------------------------------------------===//
1105 // InitTensorOp
1106 //===----------------------------------------------------------------------===//
1107 
1108 void InitTensorOp::build(OpBuilder &b, OperationState &result,
1109                          ArrayRef<OpFoldResult> sizes, Type elementType,
1110                          ArrayRef<NamedAttribute> attrs) {
1111   SmallVector<Value, 4> dynamicSizes;
1112   SmallVector<int64_t, 4> staticSizes;
1113   dispatchIndexOpFoldResults(sizes, dynamicSizes, staticSizes,
1114                              ShapedType::kDynamicSize);
1115   auto resultType = RankedTensorType ::get(staticSizes, elementType);
1116   build(b, result, resultType, dynamicSizes, b.getI64ArrayAttr(staticSizes));
1117   result.addAttributes(attrs);
1118 }
1119 
1120 LogicalResult InitTensorOp::verify() {
1121   RankedTensorType resultType = getType();
1122   SmallVector<int64_t, 4> staticSizes = llvm::to_vector<4>(llvm::map_range(
1123       static_sizes().cast<ArrayAttr>(),
1124       [](Attribute a) -> int64_t { return a.cast<IntegerAttr>().getInt(); }));
1125 
1126   if (failed(verifyListOfOperandsOrIntegers(
1127           *this, "sizes", resultType.getRank(), static_sizes(), sizes(),
1128           ShapedType::isDynamic)))
1129     return failure();
1130 
1131   if (static_sizes().size() != static_cast<unsigned>(resultType.getRank()))
1132     return emitError("expected ") << resultType.getRank() << " sizes values";
1133 
1134   Type expectedType = InitTensorOp::inferResultType(
1135       staticSizes, resultType.getElementType(), resultType.getEncoding());
1136   if (resultType != expectedType) {
1137     return emitError("specified type ")
1138            << resultType << " does not match the inferred type "
1139            << expectedType;
1140   }
1141   return success();
1142 }
1143 
1144 Type InitTensorOp::inferResultType(ArrayRef<int64_t> staticSizes,
1145                                    Type elementType, Attribute encoding) {
1146   return RankedTensorType::get(staticSizes, elementType, encoding);
1147 }
1148 
1149 SmallVector<OpFoldResult> InitTensorOp::getMixedSizes() {
1150   SmallVector<OpFoldResult> mixedSizes;
1151   mixedSizes.reserve(getType().getRank());
1152   unsigned dynamicValIndex = 0;
1153   for (Attribute attr : static_sizes()) {
1154     auto intAttr = attr.cast<IntegerAttr>();
1155     if (!ShapedType::isDynamic(intAttr.getInt())) {
1156       mixedSizes.push_back(intAttr);
1157       continue;
1158     }
1159     mixedSizes.push_back(sizes()[dynamicValIndex++]);
1160   }
1161   return mixedSizes;
1162 }
1163 
1164 namespace {
1165 /// Change the type of the result of a `linalg.init_tensor` by making the result
1166 /// type statically sized along dimension that in the original operation where
1167 /// defined as dynamic, but the size was defined using a `constant` op. For
1168 /// example
1169 ///
1170 ///  %c5 = arith.constant 5: index
1171 ///  %0 = linalg.init_tensor [%arg0, %c5] : tensor<?x?xf32>
1172 ///
1173 ///  to
1174 ///
1175 ///  %0 = linalg.init_tensor [%arg0, 5] : tensor<?x5xf32>
1176 struct ReplaceStaticShapeDims : OpRewritePattern<InitTensorOp> {
1177   using OpRewritePattern<InitTensorOp>::OpRewritePattern;
1178 
1179   LogicalResult matchAndRewrite(InitTensorOp op,
1180                                 PatternRewriter &rewriter) const override {
1181     SmallVector<Value, 4> dynamicSizes;
1182     SmallVector<int64_t, 4> staticSizes;
1183     for (unsigned i = 0, e = op.getType().getRank(); i != e; ++i) {
1184       // If the size is already static, nothing to do.
1185       if (!op.isDynamicSize(i)) {
1186         staticSizes.push_back(op.getStaticSize(i));
1187         continue;
1188       }
1189 
1190       // If the size is dynamic but defined using a `constant` op, get the
1191       // constant value to find the static size to use.
1192       unsigned operandNum = op.getIndexOfDynamicSize(i);
1193       Value sizeOperand = op.getOperand(operandNum);
1194       if (auto constantIndexOp =
1195               sizeOperand.getDefiningOp<arith::ConstantIndexOp>()) {
1196         staticSizes.push_back(constantIndexOp.value());
1197         continue;
1198       }
1199 
1200       // Fallback case. Keep the size dynamic.
1201       dynamicSizes.push_back(sizeOperand);
1202       staticSizes.push_back(ShapedType::kDynamicSize);
1203     }
1204     RankedTensorType newType =
1205         RankedTensorType::get(staticSizes, op.getType().getElementType());
1206     if (newType == op.getType())
1207       return failure();
1208     auto newOp =
1209         rewriter.create<InitTensorOp>(op.getLoc(), newType, dynamicSizes,
1210                                       rewriter.getI64ArrayAttr(staticSizes));
1211     rewriter.replaceOpWithNewOp<tensor::CastOp>(op, op.getType(), newOp);
1212     return success();
1213   }
1214 };
1215 } // namespace
1216 
1217 namespace {
1218 /// Since `init_tensor` operation creates a tensor needed only for its shape, a
1219 /// slice of this is also needed only for its shape. The result can be
1220 /// replaced by a new init_tensor operation of the same size as the extract
1221 /// slice op.
1222 struct FoldInitTensorWithExtractSliceOp
1223     : public OpRewritePattern<tensor::ExtractSliceOp> {
1224   using OpRewritePattern<tensor::ExtractSliceOp>::OpRewritePattern;
1225 
1226   LogicalResult matchAndRewrite(tensor::ExtractSliceOp sliceOp,
1227                                 PatternRewriter &rewriter) const override {
1228     if (!sliceOp.getSource().getDefiningOp<linalg::InitTensorOp>())
1229       return failure();
1230     // ExtractSliceOp may be rank-reducing; its dynamic sizes must be preserved
1231     // as well as its result type.
1232     rewriter.replaceOpWithNewOp<linalg::InitTensorOp>(
1233         sliceOp, sliceOp.getSizes(),
1234         sliceOp.getResult().getType().cast<RankedTensorType>().getShape(),
1235         sliceOp.getSourceType().getElementType());
1236     return success();
1237   }
1238 };
1239 
1240 template <typename TensorReshapeOp>
1241 struct FoldInitTensorWithTensorReshapeOp
1242     : public OpRewritePattern<TensorReshapeOp> {
1243   using OpRewritePattern<TensorReshapeOp>::OpRewritePattern;
1244 
1245   LogicalResult matchAndRewrite(TensorReshapeOp reshapeOp,
1246                                 PatternRewriter &rewriter) const override {
1247     if (!reshapeOp.getSrc().template getDefiningOp<InitTensorOp>())
1248       return failure();
1249     Location loc = reshapeOp.getLoc();
1250     ReifiedRankedShapedTypeDims resultShapes;
1251     ReifyRankedShapedTypeOpInterface reifyShapedTypeInterface =
1252         cast<ReifyRankedShapedTypeOpInterface>(reshapeOp.getOperation());
1253     if (failed(reifyShapedTypeInterface.reifyResultShapes(rewriter,
1254                                                           resultShapes)) ||
1255         !llvm::hasSingleElement(resultShapes))
1256       return failure();
1257     Value initTensor = rewriter.create<InitTensorOp>(
1258         loc, getAsOpFoldResult(resultShapes[0]),
1259         reshapeOp.getResultType().getElementType());
1260     if (initTensor.getType() != reshapeOp.getResultType()) {
1261       rewriter.replaceOpWithNewOp<tensor::CastOp>(
1262           reshapeOp, reshapeOp.getResultType(), initTensor);
1263     } else {
1264       rewriter.replaceOp(reshapeOp, initTensor);
1265     }
1266     return success();
1267   }
1268 };
1269 
1270 struct FoldInitTensorWithDimOp : public OpRewritePattern<tensor::DimOp> {
1271   using OpRewritePattern<tensor::DimOp>::OpRewritePattern;
1272 
1273   LogicalResult matchAndRewrite(tensor::DimOp dimOp,
1274                                 PatternRewriter &rewriter) const override {
1275     Optional<int64_t> maybeConstantIndex = dimOp.getConstantIndex();
1276     auto initTensorOp = dimOp.getSource().getDefiningOp<linalg::InitTensorOp>();
1277     if (!initTensorOp || !maybeConstantIndex)
1278       return failure();
1279     if (!initTensorOp.isDynamicSize(*maybeConstantIndex))
1280       return failure();
1281     rewriter.replaceOp(dimOp, initTensorOp.getDynamicSize(*maybeConstantIndex));
1282     return success();
1283   }
1284 };
1285 
1286 /// Canonicalize
1287 ///
1288 /// ```mlir
1289 ///   %0 = linalg.init_tensor [%d0, %d1] : tensor<?x?xf32>
1290 ///   %1 = tensor.cast %0 : tensor<?x?xf32> to tensor<4x?xf32>
1291 /// ```
1292 ///
1293 /// into
1294 ///
1295 /// ```mlir
1296 ///   %0 = linalg.init_tensor [4, %d1] : tensor<4x?xf32>
1297 /// ```
1298 ///
1299 /// This assumes the input program is correct in terms of its shape. So it
1300 /// is safe to assume that `%d0` is in fact 4. If that was not the case, the
1301 /// input program is wrong to begin with, so its undefined behavior anyway (i.e.
1302 /// this optimization can still triggering without violating program semantics).
1303 struct FoldInitTensorWithTensorCastOp
1304     : public OpRewritePattern<tensor::CastOp> {
1305   using OpRewritePattern<tensor::CastOp>::OpRewritePattern;
1306 
1307   LogicalResult matchAndRewrite(tensor::CastOp castOp,
1308                                 PatternRewriter &rewriter) const override {
1309     if (!canFoldIntoProducerOp(castOp))
1310       return failure();
1311     auto producer = castOp.getSource().getDefiningOp<InitTensorOp>();
1312     if (!producer)
1313       return failure();
1314 
1315     auto resultType = castOp->getResult(0).getType().cast<RankedTensorType>();
1316     ArrayRef<int64_t> resultShape = resultType.getShape();
1317     SmallVector<OpFoldResult> currMixedSizes = producer.getMixedSizes();
1318     SmallVector<OpFoldResult> newMixedSizes;
1319     newMixedSizes.reserve(currMixedSizes.size());
1320     assert(resultShape.size() == currMixedSizes.size() &&
1321            "mismatch in result shape and sizes of init_tensor op");
1322     for (auto it : llvm::zip(resultShape, currMixedSizes)) {
1323       int64_t newDim = std::get<0>(it);
1324       OpFoldResult currDim = std::get<1>(it);
1325       // Case 1: The init tensor dim is static. Check that the tensor cast
1326       // result dim matches.
1327       if (auto attr = currDim.dyn_cast<Attribute>()) {
1328         if (ShapedType::isDynamic(newDim) ||
1329             newDim != attr.cast<IntegerAttr>().getInt()) {
1330           // Something is off, the cast result shape cannot be more dynamic than
1331           // the init tensor result shape (enforced by `canFoldIntoProducer`).
1332           // Abort for now.
1333           return rewriter.notifyMatchFailure(
1334               producer, "mismatch in static value of shape of init "
1335                         "tensor result and cast result");
1336         }
1337         newMixedSizes.push_back(attr);
1338         continue;
1339       }
1340 
1341       // Case 2 : The tensor cast shape is static, but init tensor result shape
1342       // is dynamic.
1343       if (!ShapedType::isDynamic(newDim)) {
1344         newMixedSizes.push_back(rewriter.getIndexAttr(newDim));
1345         continue;
1346       }
1347 
1348       // Case 3 : The tensor cast shape is dynamic and init tensor result shape
1349       // is dynamic. Use the dynamic value from the init tensor op.
1350       newMixedSizes.push_back(currDim);
1351     }
1352 
1353     rewriter.replaceOpWithNewOp<InitTensorOp>(castOp, newMixedSizes,
1354                                               resultType.getElementType());
1355     return success();
1356   }
1357 };
1358 
1359 } // namespace
1360 
1361 void InitTensorOp::getCanonicalizationPatterns(RewritePatternSet &results,
1362                                                MLIRContext *context) {
1363   results.add<FoldInitTensorWithTensorCastOp, FoldInitTensorWithDimOp,
1364               FoldInitTensorWithExtractSliceOp,
1365               FoldInitTensorWithTensorReshapeOp<tensor::ExpandShapeOp>,
1366               FoldInitTensorWithTensorReshapeOp<tensor::CollapseShapeOp>,
1367               ReplaceStaticShapeDims>(context);
1368 }
1369 
1370 LogicalResult InitTensorOp::reifyResultShapes(
1371     OpBuilder &builder, ReifiedRankedShapedTypeDims &reifiedReturnShapes) {
1372   auto shapes = llvm::to_vector<4>(llvm::map_range(
1373       llvm::seq<int64_t>(0, getType().getRank()), [&](int64_t dim) -> Value {
1374         if (isDynamicSize(dim))
1375           return getDynamicSize(dim);
1376         return builder.create<arith::ConstantIndexOp>(getLoc(),
1377                                                       getStaticSize(dim));
1378       }));
1379   reifiedReturnShapes.emplace_back(std::move(shapes));
1380   return success();
1381 }
1382 
1383 //===----------------------------------------------------------------------===//
1384 // YieldOp
1385 //===----------------------------------------------------------------------===//
1386 
1387 void linalg::YieldOp::print(OpAsmPrinter &p) {
1388   if (getNumOperands() > 0)
1389     p << ' ' << getOperands();
1390   p.printOptionalAttrDict((*this)->getAttrs());
1391   if (getNumOperands() > 0)
1392     p << " : " << getOperandTypes();
1393 }
1394 
1395 ParseResult YieldOp::parse(OpAsmParser &parser, OperationState &result) {
1396   SmallVector<OpAsmParser::UnresolvedOperand, 2> opInfo;
1397   SmallVector<Type, 2> types;
1398   SMLoc loc = parser.getCurrentLocation();
1399   return failure(parser.parseOperandList(opInfo) ||
1400                  parser.parseOptionalAttrDict(result.attributes) ||
1401                  (!opInfo.empty() && parser.parseColonTypeList(types)) ||
1402                  parser.resolveOperands(opInfo, types, loc, result.operands));
1403 }
1404 
1405 // Check the operand number and types must match the element types of the
1406 // LinalgOp interface's shaped operands.
1407 static LogicalResult verifyYield(linalg::YieldOp op, LinalgOp linalgOp) {
1408   if (op.getNumOperands() != linalgOp.getNumOutputs())
1409     return op.emitOpError("expected number of yield values (")
1410            << linalgOp.getNumOutputs()
1411            << ") to match the number of operands of the enclosing "
1412            << "LinalgOp (" << op.getNumOperands() << ")";
1413 
1414   for (OpOperand &opOperand : op->getOpOperands()) {
1415     OpOperand *outputOperand =
1416         linalgOp.getOutputOperand(opOperand.getOperandNumber());
1417     Type elementType = getElementTypeOrSelf(outputOperand->get().getType());
1418     if (opOperand.get().getType() != elementType)
1419       return op.emitOpError("type of yield operand ")
1420              << (opOperand.getOperandNumber() + 1) << " ("
1421              << opOperand.get().getType() << ") doesn't match "
1422              << "the element type of the enclosing linalg.generic op ("
1423              << elementType << ")";
1424   }
1425   return success();
1426 }
1427 
1428 LogicalResult linalg::YieldOp::verify() {
1429   auto *parentOp = (*this)->getParentOp();
1430   if (parentOp->getNumRegions() != 1 || parentOp->getRegion(0).empty())
1431     return emitOpError("expected single non-empty parent region");
1432 
1433   if (auto linalgOp = dyn_cast<LinalgOp>(parentOp))
1434     return verifyYield(*this, linalgOp);
1435 
1436   return emitOpError("expected parent op with LinalgOp interface");
1437 }
1438 
1439 //===----------------------------------------------------------------------===//
1440 // IndexOp
1441 //===----------------------------------------------------------------------===//
1442 
1443 LogicalResult IndexOp::verify() {
1444   auto linalgOp = dyn_cast<LinalgOp>((*this)->getParentOp());
1445   if (!linalgOp)
1446     return emitOpError("expected parent op with LinalgOp interface");
1447   if (linalgOp.getNumLoops() <= dim())
1448     return emitOpError("expected dim (")
1449            << dim() << ") to be lower than the number of loops ("
1450            << linalgOp.getNumLoops() << ") of the enclosing LinalgOp";
1451   return success();
1452 }
1453 
1454 /////// Operations corresponding to library calls defined with Tablegen ////////
1455 
1456 #include "mlir/Dialect/Linalg/IR/LinalgNamedStructuredOps.yamlgen.cpp.inc"
1457 
1458 #define GET_OP_CLASSES
1459 #include "mlir/Dialect/Linalg/IR/LinalgOps.cpp.inc"
1460 
1461 #define GET_OP_CLASSES
1462 #include "mlir/Dialect/Linalg/IR/LinalgStructuredOps.cpp.inc"
1463 
1464 /// Return the dims that are `iteratorTypeName` loops in the LinalgOp `op`.
1465 /// Assumes `op` is a LinalgOp.
1466 void mlir::linalg::getDimsOfType(Operation *op, StringRef iteratorTypeName,
1467                                  SmallVectorImpl<unsigned> &res) {
1468   if (!cast<LinalgOp>(op).iterator_types())
1469     return;
1470 
1471   unsigned dim = 0;
1472   for (auto tn :
1473        cast<LinalgOp>(op).iterator_types().getAsValueRange<StringAttr>()) {
1474     if (tn == iteratorTypeName)
1475       res.push_back(dim);
1476     ++dim;
1477   }
1478 }
1479 
1480 AffineMap mlir::linalg::extractOrIdentityMap(Optional<AffineMap> maybeMap,
1481                                              unsigned rank,
1482                                              MLIRContext *context) {
1483   if (maybeMap)
1484     return *maybeMap;
1485   if (rank == 0)
1486     return AffineMap::get(context);
1487   return AffineMap::getMultiDimIdentityMap(rank, context);
1488 }
1489 
1490 SmallVector<AffineExpr, 4>
1491 mlir::linalg::makeAffineDimExprs(unsigned num, unsigned &startIdx,
1492                                  MLIRContext *context) {
1493   SmallVector<AffineExpr, 4> res;
1494   res.reserve(num);
1495   for (unsigned i = 0; i < num; ++i)
1496     res.push_back(getAffineDimExpr(startIdx++, context));
1497   return res;
1498 }
1499 
1500 SmallVector<AffineExpr, 4> mlir::linalg::concat(ArrayRef<AffineExpr> a,
1501                                                 ArrayRef<AffineExpr> b) {
1502   auto rangeA = llvm::make_range(a.begin(), a.end());
1503   auto rangeB = llvm::make_range(b.begin(), b.end());
1504   auto concatRanges = llvm::concat<const AffineExpr>(rangeA, rangeB);
1505   return llvm::to_vector<4>(concatRanges);
1506 }
1507 
1508 static void appendMangledType(llvm::raw_string_ostream &ss, Type t) {
1509   if (auto memref = t.dyn_cast<MemRefType>()) {
1510     ss << "view";
1511     for (auto size : memref.getShape())
1512       if (size < 0)
1513         ss << "sx";
1514       else
1515         ss << size << "x";
1516     appendMangledType(ss, memref.getElementType());
1517   } else if (auto vec = t.dyn_cast<VectorType>()) {
1518     ss << "vector";
1519     llvm::interleave(
1520         vec.getShape(), [&](int64_t i) { ss << i; }, [&]() { ss << "x"; });
1521     appendMangledType(ss, vec.getElementType());
1522   } else if (t.isSignlessIntOrIndexOrFloat()) {
1523     ss << t;
1524   } else {
1525     llvm_unreachable("Invalid type for linalg library name mangling");
1526   }
1527 }
1528 
1529 std::string mlir::linalg::generateLibraryCallName(Operation *op) {
1530   assert(isa<LinalgOp>(op));
1531   std::string name(op->getName().getStringRef().str());
1532   name.reserve(128);
1533   std::replace(name.begin(), name.end(), '.', '_');
1534   llvm::raw_string_ostream ss(name);
1535   ss << "_";
1536   auto types = op->getOperandTypes();
1537   llvm::interleave(
1538       types.begin(), types.end(), [&](Type t) { appendMangledType(ss, t); },
1539       [&]() { ss << "_"; });
1540   return ss.str();
1541 }
1542 
1543 //===----------------------------------------------------------------------===//
1544 // Canonicalizers and Folders.
1545 //===----------------------------------------------------------------------===//
1546 
1547 namespace {
1548 struct EraseDeadLinalgOp : public OpInterfaceRewritePattern<LinalgOp> {
1549   using OpInterfaceRewritePattern<LinalgOp>::OpInterfaceRewritePattern;
1550 
1551   LogicalResult matchAndRewrite(LinalgOp op,
1552                                 PatternRewriter &rewriter) const override {
1553     for (OpOperand *opOperand : op.getInputAndOutputOperands()) {
1554       // Linalg "inputs" may be either tensor or memref type.
1555       // tensor<0xelt_type> is a convention that may not always mean
1556       // "0 iterations". Only erase in cases we see memref<...x0x...>.
1557       auto mt = opOperand->get().getType().dyn_cast<MemRefType>();
1558       if (!mt)
1559         continue;
1560       if (llvm::is_contained(op.getShape(opOperand), 0)) {
1561         rewriter.eraseOp(op);
1562         return success();
1563       }
1564     }
1565     return failure();
1566   }
1567 };
1568 
1569 struct FoldTensorCastProducerOp : public OpInterfaceRewritePattern<LinalgOp> {
1570   using OpInterfaceRewritePattern<LinalgOp>::OpInterfaceRewritePattern;
1571 
1572   LogicalResult matchAndRewrite(LinalgOp op,
1573                                 PatternRewriter &rewriter) const override {
1574     // If no operand comes from a tensor::CastOp and can be folded then fail.
1575     bool hasTensorCastOperand =
1576         llvm::any_of(op.getInputAndOutputOperands(), [&](OpOperand *opOperand) {
1577           if (opOperand->get().isa<BlockArgument>())
1578             return false;
1579           auto castOp = opOperand->get().getDefiningOp<tensor::CastOp>();
1580           return castOp && canFoldIntoConsumerOp(castOp);
1581         });
1582     if (!hasTensorCastOperand)
1583       return failure();
1584 
1585     SmallVector<Type, 4> newResultTypes;
1586     newResultTypes.reserve(op->getNumResults());
1587     SmallVector<Value, 4> newOperands;
1588     newOperands.reserve(op->getNumOperands());
1589     // Inputs may fold.
1590     for (OpOperand *opOperand : op.getInputOperands()) {
1591       auto tensorCastOp = opOperand->get().getDefiningOp<tensor::CastOp>();
1592       newOperands.push_back(canFoldIntoConsumerOp(tensorCastOp)
1593                                 ? tensorCastOp.getSource()
1594                                 : opOperand->get());
1595     }
1596     // Init tensors may fold, in which case the resultType must also change.
1597     for (OpOperand *opOperand : op.getOutputOperands()) {
1598       auto tensorCastOp = opOperand->get().getDefiningOp<tensor::CastOp>();
1599       bool fold = canFoldIntoConsumerOp(tensorCastOp);
1600       newOperands.push_back(fold ? tensorCastOp.getOperand()
1601                                  : opOperand->get());
1602       newResultTypes.push_back(newOperands.back().getType());
1603     }
1604     // Clone op.
1605     Operation *newOp =
1606         op.clone(rewriter, op->getLoc(), newResultTypes, newOperands);
1607     SmallVector<Value, 4> replacements;
1608     replacements.reserve(newOp->getNumResults());
1609     for (auto result : llvm::zip(op->getResults(), newOp->getResults())) {
1610       Value oldResult = std::get<0>(result);
1611       Value newResult = std::get<1>(result);
1612       if (newResult.getType() != oldResult.getType()) {
1613         replacements.push_back(rewriter.create<tensor::CastOp>(
1614             op->getLoc(), oldResult.getType(), newResult));
1615       } else {
1616         replacements.push_back(newResult);
1617       }
1618     }
1619     rewriter.replaceOp(op, replacements);
1620 
1621     return success();
1622   }
1623 };
1624 
1625 /// Fold LinalgOps with `tensor.cast` consumer if the `tensor.cast` has
1626 /// result that is more static than the linalg op.
1627 struct FoldTensorCastConsumerOp : public OpRewritePattern<tensor::CastOp> {
1628   using OpRewritePattern<tensor::CastOp>::OpRewritePattern;
1629 
1630   LogicalResult matchAndRewrite(tensor::CastOp castOp,
1631                                 PatternRewriter &rewriter) const override {
1632     if (!tensor::canFoldIntoProducerOp(castOp))
1633       return failure();
1634     auto linalgOp = castOp.getSource().getDefiningOp<LinalgOp>();
1635     if (!linalgOp)
1636       return failure();
1637 
1638     OpBuilder::InsertionGuard guard(rewriter);
1639     rewriter.setInsertionPoint(linalgOp);
1640 
1641     Location loc = linalgOp.getLoc();
1642     OpResult resultValue = castOp.getSource().cast<OpResult>();
1643     unsigned resultNumber = resultValue.getResultNumber();
1644     auto resultType = castOp->getResult(0).getType().cast<RankedTensorType>();
1645     // Replace the `outs` for the result with a `tensor.cast`. This cast is now
1646     // going from a more dynamic shape to a less dynamic shape. If the producer
1647     // for this cast, i.e. producer of the out operand, is also an operation
1648     // that folds with tensor.cast consumer (like this pattern), the cast will
1649     // continue to propagate as far up the stack as it can go.
1650     OpOperand *outOperand = linalgOp.getOutputOperand(resultNumber);
1651     Value newOperand =
1652         rewriter.create<tensor::CastOp>(loc, resultType, outOperand->get());
1653     SmallVector<Value> newOperands = linalgOp.getInputOperands();
1654     SmallVector<Value> outputOperands = linalgOp.getOutputOperands();
1655     outputOperands[resultNumber] = newOperand;
1656     newOperands.append(outputOperands.begin(), outputOperands.end());
1657 
1658     SmallVector<Type> resultTypes(linalgOp->result_type_begin(),
1659                                   linalgOp->result_type_end());
1660     resultTypes[resultNumber] = resultType;
1661     Operation *newOp = linalgOp.clone(rewriter, loc, resultTypes, newOperands);
1662 
1663     // Create a tensor.cast operation back to the original type.
1664     Value castBack = rewriter.create<tensor::CastOp>(
1665         loc, resultValue.getType(), newOp->getResult(resultNumber));
1666 
1667     SmallVector<Value> results(newOp->result_begin(), newOp->result_end());
1668     results[resultNumber] = castBack;
1669     rewriter.replaceOp(linalgOp, results);
1670     rewriter.replaceOp(castOp, newOp->getResult(resultNumber));
1671     return success();
1672   }
1673 };
1674 
1675 /// For each of the operand in `operands` this function maps the static sizes of
1676 /// dimensions to their affine dim expressions.
1677 static void populateMap(LinalgOp linalgOp, ArrayRef<OpOperand *> operands,
1678                         llvm::DenseMap<AffineExpr, int64_t> &affineExprToSize) {
1679   for (OpOperand *opOperand : operands) {
1680     if (linalgOp.isScalar(opOperand))
1681       continue;
1682     Value src = opOperand->get();
1683     auto sourceType = src.getType().cast<RankedTensorType>();
1684     auto sourceMap = linalgOp.getTiedIndexingMap(opOperand);
1685 
1686     // Get the `sourceShape` of the `sourceType`. If the operand is a result of
1687     // `tensor.cast` operation and source of the cast operation has a static
1688     // shape, then assign it to the `sourceShape`.
1689     auto *parentOp = src.getDefiningOp();
1690     ArrayRef<int64_t> sourceShape = sourceType.getShape();
1691     if (parentOp) {
1692       if (auto castOp = dyn_cast<tensor::CastOp>(parentOp)) {
1693         Value castSource = castOp.getSource();
1694         auto castSourceType = castSource.getType().cast<RankedTensorType>();
1695         if (castSourceType.hasStaticShape())
1696           sourceShape = castSourceType.getShape();
1697       }
1698     }
1699 
1700     // If the source shape's dimension has a static shape, map the affine dim
1701     // expression to the known static size.
1702     for (unsigned i = 0; i < sourceShape.size(); i++) {
1703       if (sourceType.isDynamicDim(i))
1704         continue;
1705       if (auto affineDimExpr = sourceMap.getResult(i).dyn_cast<AffineDimExpr>())
1706         affineExprToSize.try_emplace(affineDimExpr, sourceShape[i]);
1707     }
1708   }
1709 }
1710 
1711 /// Creates new operand w.r.t 'opOperand' of `linalgOp` with static sizes
1712 /// mapped in `affineExprToSize`. New operands are created in `newOperands` and
1713 /// their result types is stored in `resultTypes`. If `opOperand` requires no
1714 /// change then `changeNeeded` is false and same operand is added in the
1715 /// `newOperands` list.
1716 static void createNewOperandWithStaticSizes(
1717     Location loc, PatternRewriter &rewriter, OpOperand *opOperand,
1718     llvm::DenseMap<AffineExpr, int64_t> &affineExprToSize, LinalgOp linalgOp,
1719     SmallVector<Value> &newOperands, SmallVector<Type> &resultTypes,
1720     bool &changeNeeded) {
1721   Value src = opOperand->get();
1722   newOperands.push_back(src);
1723   if (linalgOp.isScalar(opOperand))
1724     return;
1725   auto sourceType = src.getType().cast<RankedTensorType>();
1726   Type resultType = sourceType;
1727   if (sourceType.hasStaticShape() && linalgOp.isOutputTensor(opOperand)) {
1728     resultTypes.push_back(resultType);
1729     return;
1730   }
1731   ArrayRef<int64_t> sourceShape = sourceType.getShape();
1732   AffineMap sourceMap = linalgOp.getTiedIndexingMap(opOperand);
1733   SmallVector<int64_t> newShape;
1734   // If operand is updated with new shape, `newOperandNeeded` will be
1735   // true.
1736   bool newOperandNeeded = false;
1737   for (unsigned i = 0; i < sourceShape.size(); i++) {
1738     int64_t dimShape = sourceShape[i];
1739     AffineExpr dimExpr = sourceMap.getResult(i);
1740     if (affineExprToSize.find(dimExpr) == affineExprToSize.end() ||
1741         !sourceType.isDynamicDim(i)) {
1742       newShape.push_back(dimShape);
1743       continue;
1744     }
1745     // Dimension has a dynamic shape and corresponding affine dim
1746     // expression is present in the map. So assign the size for the
1747     // given affine dim expression to the dimension.
1748     newShape.push_back(affineExprToSize[dimExpr]);
1749     newOperandNeeded = true;
1750   }
1751   resultType = RankedTensorType::get(newShape, sourceType.getElementType());
1752   if (newOperandNeeded) {
1753     changeNeeded = true;
1754     // Get the new operand value given its size and element type by
1755     // casting it.
1756     Value newOperand = rewriter.create<tensor::CastOp>(loc, resultType, src);
1757     unsigned index = opOperand->getOperandNumber();
1758     newOperands[index] = newOperand;
1759   }
1760   if (linalgOp.isOutputTensor(opOperand))
1761     resultTypes.push_back(resultType);
1762 }
1763 
1764 /// Static shapes for the operands can be inferred if any one of the operands
1765 /// have a static shape. This can be done by referring to the affine dim
1766 /// expressions for the operand.
1767 struct InferStaticShapeOfOperands : public OpInterfaceRewritePattern<LinalgOp> {
1768   using OpInterfaceRewritePattern<LinalgOp>::OpInterfaceRewritePattern;
1769 
1770   LogicalResult matchAndRewrite(LinalgOp linalgOp,
1771                                 PatternRewriter &rewriter) const override {
1772     if (!linalgOp.hasTensorSemantics())
1773       return failure();
1774 
1775     // Maps must be projected permutations.
1776     if (llvm::any_of(linalgOp.getIndexingMaps(), [](AffineMap map) {
1777           return !map.isProjectedPermutation();
1778         }))
1779       return failure();
1780 
1781     // Maps affine dim expressions to the static size of that dimension.
1782     llvm::DenseMap<AffineExpr, int64_t> affineExprToSize;
1783     Location loc = linalgOp.getLoc();
1784 
1785     // For each of the affine dim expression, check if the size is known. If
1786     // known add that in the map.
1787     populateMap(linalgOp, linalgOp.getInputAndOutputOperands(),
1788                 affineExprToSize);
1789 
1790     SmallVector<Value> newOperands;
1791     SmallVector<Type> resultTypes;
1792 
1793     // `changeNeeded` is `false` if the operands of `linalgOp` require no
1794     // change in their types.
1795     bool changeNeeded = false;
1796     newOperands.reserve(linalgOp.getNumInputsAndOutputs());
1797     resultTypes.reserve(linalgOp.getNumOutputs());
1798 
1799     // Iterate over all the operands and update the static sizes.
1800     for (OpOperand *opOperand : linalgOp.getInputAndOutputOperands()) {
1801       createNewOperandWithStaticSizes(loc, rewriter, opOperand,
1802                                       affineExprToSize, linalgOp, newOperands,
1803                                       resultTypes, changeNeeded);
1804     }
1805 
1806     // If the generic op has all the required static information, no
1807     // canonicalization needed.
1808     if (!changeNeeded)
1809       return failure();
1810 
1811     // Clone op.
1812     Operation *newOp =
1813         linalgOp.clone(rewriter, linalgOp->getLoc(), resultTypes, newOperands);
1814     SmallVector<Value> replacements;
1815     replacements.reserve(newOp->getNumResults());
1816     for (auto it : llvm::zip(linalgOp->getResults(), newOp->getResults())) {
1817       Value newResult = std::get<1>(it);
1818       Value oldResult = std::get<0>(it);
1819       Type newType = newResult.getType();
1820       Type oldType = oldResult.getType();
1821       replacements.push_back(
1822           (newType != oldType)
1823               ? rewriter.create<tensor::CastOp>(loc, oldType, newResult)
1824               : newResult);
1825     }
1826     rewriter.replaceOp(linalgOp, replacements);
1827     return success();
1828   }
1829 };
1830 
1831 } // namespace
1832 
1833 // All named ops canonicalizers and folders are auto-generated in the
1834 // .cpp.inc.
1835 
1836 //===----------------------------------------------------------------------===//
1837 // LinalgDialect
1838 //===----------------------------------------------------------------------===//
1839 
1840 void LinalgDialect::getCanonicalizationPatterns(
1841     RewritePatternSet &results) const {
1842   results.add<EraseDeadLinalgOp, FoldTensorCastConsumerOp,
1843               FoldTensorCastProducerOp, InferStaticShapeOfOperands>(
1844       getContext());
1845 }
1846 
1847 Operation *LinalgDialect::materializeConstant(OpBuilder &builder,
1848                                               Attribute value, Type type,
1849                                               Location loc) {
1850   return builder.create<arith::ConstantOp>(loc, type, value);
1851 }
1852