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