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::UnresolvedOperand, 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           if (!tensor::CastOp::areCastCompatible(returnedArg.getType(),
841                                                  resultType))
842             return failure();
843           returnedArg = rewriter.create<tensor::CastOp>(
844               genericOp.getLoc(), resultType, returnedArg);
845         }
846       }
847       returnedArgs.push_back(returnedArg);
848     }
849 
850     if (returnedArgs.size() != genericOp->getNumResults())
851       return failure();
852     rewriter.replaceOp(genericOp, returnedArgs);
853     return success();
854   }
855 };
856 
857 /// Drop dead args of a linalg generic op.
858 /// An arg is dead if it has zero uses in the op region.
859 struct DeadArgsGenericOpInputs : public OpRewritePattern<GenericOp> {
860   using OpRewritePattern<GenericOp>::OpRewritePattern;
861   LogicalResult matchAndRewrite(GenericOp genericOp,
862                                 PatternRewriter &rewriter) const override {
863     SmallVector<AffineMap> oldIndexingMaps = genericOp.getIndexingMaps();
864     // Maps must be projected permutations.
865     if (llvm::any_of(genericOp.getIndexingMaps(), [](AffineMap map) {
866           return !map.isProjectedPermutation();
867         }))
868       return failure();
869     Block &payload = genericOp.region().front();
870     SmallVector<Value> newInputOperands;
871     SmallVector<AffineMap> newIndexingMaps;
872     bool deadArgFound = false;
873     int inputSize = genericOp.getInputOperands().size();
874     for (int i = inputSize - 1; i >= 0; i--) {
875       OpOperand *opOperand = genericOp.getInputOperand(i);
876       // Iterate in reverse, so that we erase later args first, preventing the
877       // argument list from shifting unexpectedly and invalidating all our
878       // indices.
879       if (payload.getArgument(i).use_empty() &&
880           !hasaUniqueDim(oldIndexingMaps, i)) {
881         payload.eraseArgument(i);
882         deadArgFound = true;
883         // remove this indexing map out of consideration for hasaUniqueDim check
884         oldIndexingMaps.erase(oldIndexingMaps.begin() + i);
885       } else {
886         newInputOperands.insert(newInputOperands.begin(), opOperand->get());
887         newIndexingMaps.insert(newIndexingMaps.begin(),
888                                genericOp.getTiedIndexingMap(opOperand));
889       }
890     }
891     // Bail out if there are no dead args.
892     if (!deadArgFound)
893       return failure();
894     for (OpOperand *opOperand : genericOp.getOutputOperands())
895       newIndexingMaps.push_back(genericOp.getTiedIndexingMap(opOperand));
896     SmallVector<Value> outputOperands = genericOp.getOutputOperands();
897 
898     auto newOp = rewriter.create<GenericOp>(
899         genericOp.getLoc(), genericOp->getResultTypes(), newInputOperands,
900         outputOperands, rewriter.getAffineMapArrayAttr(newIndexingMaps),
901         genericOp.iterator_types(), genericOp.docAttr(),
902         genericOp.library_callAttr());
903     // Copy over unknown attributes. They might be load bearing for some flow.
904     ArrayRef<StringRef> odsAttrs = genericOp.getAttributeNames();
905     for (NamedAttribute kv : genericOp->getAttrs()) {
906       if (!llvm::is_contained(odsAttrs, kv.getName().getValue())) {
907         newOp->setAttr(kv.getName(), kv.getValue());
908       }
909     }
910     rewriter.inlineRegionBefore(genericOp.region(), newOp.region(),
911                                 newOp.region().begin());
912     rewriter.replaceOp(genericOp, newOp->getResults());
913     return success();
914   }
915 };
916 } // namespace
917 
918 void GenericOp::getCanonicalizationPatterns(RewritePatternSet &results,
919                                             MLIRContext *context) {
920   results.add<DeduplicateGenericOpInputs, EraseIdentityGenericOp,
921               DeadArgsGenericOpInputs>(context);
922 }
923 
924 LogicalResult GenericOp::fold(ArrayRef<Attribute>,
925                               SmallVectorImpl<OpFoldResult> &) {
926   return foldMemRefCast(*this);
927 }
928 
929 //===----------------------------------------------------------------------===//
930 // InitTensorOp
931 //===----------------------------------------------------------------------===//
932 
933 void InitTensorOp::build(OpBuilder &b, OperationState &result,
934                          ArrayRef<OpFoldResult> sizes, Type elementType,
935                          ArrayRef<NamedAttribute> attrs) {
936   SmallVector<Value, 4> dynamicSizes;
937   SmallVector<int64_t, 4> staticSizes;
938   dispatchIndexOpFoldResults(sizes, dynamicSizes, staticSizes,
939                              ShapedType::kDynamicSize);
940   auto resultType = RankedTensorType ::get(staticSizes, elementType);
941   build(b, result, resultType, dynamicSizes, b.getI64ArrayAttr(staticSizes));
942   result.addAttributes(attrs);
943 }
944 
945 LogicalResult InitTensorOp::verify() {
946   RankedTensorType resultType = getType();
947   SmallVector<int64_t, 4> staticSizes = llvm::to_vector<4>(llvm::map_range(
948       static_sizes().cast<ArrayAttr>(),
949       [](Attribute a) -> int64_t { return a.cast<IntegerAttr>().getInt(); }));
950 
951   if (failed(verifyListOfOperandsOrIntegers(
952           *this, "sizes", resultType.getRank(), static_sizes(), sizes(),
953           ShapedType::isDynamic)))
954     return failure();
955 
956   if (static_sizes().size() != static_cast<unsigned>(resultType.getRank()))
957     return emitError("expected ") << resultType.getRank() << " sizes values";
958 
959   Type expectedType = InitTensorOp::inferResultType(
960       staticSizes, resultType.getElementType(), resultType.getEncoding());
961   if (resultType != expectedType) {
962     return emitError("specified type ")
963            << resultType << " does not match the inferred type "
964            << expectedType;
965   }
966   return success();
967 }
968 
969 Type InitTensorOp::inferResultType(ArrayRef<int64_t> staticSizes,
970                                    Type elementType, Attribute encoding) {
971   return RankedTensorType::get(staticSizes, elementType, encoding);
972 }
973 
974 SmallVector<OpFoldResult> InitTensorOp::getMixedSizes() {
975   SmallVector<OpFoldResult> mixedSizes;
976   mixedSizes.reserve(getType().getRank());
977   unsigned dynamicValIndex = 0;
978   for (Attribute attr : static_sizes()) {
979     auto intAttr = attr.cast<IntegerAttr>();
980     if (!ShapedType::isDynamic(intAttr.getInt())) {
981       mixedSizes.push_back(intAttr);
982       continue;
983     }
984     mixedSizes.push_back(sizes()[dynamicValIndex++]);
985   }
986   return mixedSizes;
987 }
988 
989 namespace {
990 /// Change the type of the result of a `linalg.init_tensor` by making the result
991 /// type statically sized along dimension that in the original operation where
992 /// defined as dynamic, but the size was defined using a `constant` op. For
993 /// example
994 ///
995 ///  %c5 = arith.constant 5: index
996 ///  %0 = linalg.init_tensor [%arg0, %c5] : tensor<?x?xf32>
997 ///
998 ///  to
999 ///
1000 ///  %0 = linalg.init_tensor [%arg0, 5] : tensor<?x5xf32>
1001 struct ReplaceStaticShapeDims : OpRewritePattern<InitTensorOp> {
1002   using OpRewritePattern<InitTensorOp>::OpRewritePattern;
1003 
1004   LogicalResult matchAndRewrite(InitTensorOp op,
1005                                 PatternRewriter &rewriter) const override {
1006     SmallVector<Value, 4> dynamicSizes;
1007     SmallVector<int64_t, 4> staticSizes;
1008     for (unsigned i = 0, e = op.getType().getRank(); i != e; ++i) {
1009       // If the size is already static, nothing to do.
1010       if (!op.isDynamicSize(i)) {
1011         staticSizes.push_back(op.getStaticSize(i));
1012         continue;
1013       }
1014 
1015       // If the size is dynamic but defined using a `constant` op, get the
1016       // constant value to find the static size to use.
1017       unsigned operandNum = op.getIndexOfDynamicSize(i);
1018       Value sizeOperand = op.getOperand(operandNum);
1019       if (auto constantIndexOp =
1020               sizeOperand.getDefiningOp<arith::ConstantIndexOp>()) {
1021         staticSizes.push_back(constantIndexOp.value());
1022         continue;
1023       }
1024 
1025       // Fallback case. Keep the size dynamic.
1026       dynamicSizes.push_back(sizeOperand);
1027       staticSizes.push_back(ShapedType::kDynamicSize);
1028     }
1029     RankedTensorType newType =
1030         RankedTensorType::get(staticSizes, op.getType().getElementType());
1031     if (newType == op.getType())
1032       return failure();
1033     auto newOp =
1034         rewriter.create<InitTensorOp>(op.getLoc(), newType, dynamicSizes,
1035                                       rewriter.getI64ArrayAttr(staticSizes));
1036     rewriter.replaceOpWithNewOp<tensor::CastOp>(op, op.getType(), newOp);
1037     return success();
1038   }
1039 };
1040 } // namespace
1041 
1042 namespace {
1043 /// Since `init_tensor` operation creates a tensor needed only for its shape, a
1044 /// slice of this is also needed only for its shape. The result can be
1045 /// replaced by a new init_tensor operation of the same size as the extract
1046 /// slice op.
1047 struct FoldInitTensorWithExtractSliceOp
1048     : public OpRewritePattern<tensor::ExtractSliceOp> {
1049   using OpRewritePattern<tensor::ExtractSliceOp>::OpRewritePattern;
1050 
1051   LogicalResult matchAndRewrite(tensor::ExtractSliceOp sliceOp,
1052                                 PatternRewriter &rewriter) const override {
1053     if (!sliceOp.source().getDefiningOp<linalg::InitTensorOp>())
1054       return failure();
1055     // ExtractSliceOp may be rank-reducing; its dynamic sizes must be preserved
1056     // as well as its result type.
1057     rewriter.replaceOpWithNewOp<linalg::InitTensorOp>(
1058         sliceOp, sliceOp.sizes(),
1059         sliceOp.result().getType().cast<RankedTensorType>().getShape(),
1060         sliceOp.getSourceType().getElementType());
1061     return success();
1062   }
1063 };
1064 
1065 template <typename TensorReshapeOp>
1066 struct FoldInitTensorWithTensorReshapeOp
1067     : public OpRewritePattern<TensorReshapeOp> {
1068   using OpRewritePattern<TensorReshapeOp>::OpRewritePattern;
1069 
1070   LogicalResult matchAndRewrite(TensorReshapeOp reshapeOp,
1071                                 PatternRewriter &rewriter) const override {
1072     if (!reshapeOp.src().template getDefiningOp<InitTensorOp>())
1073       return failure();
1074     Location loc = reshapeOp.getLoc();
1075     ReifiedRankedShapedTypeDims resultShapes;
1076     ReifyRankedShapedTypeOpInterface reifyShapedTypeInterface =
1077         cast<ReifyRankedShapedTypeOpInterface>(reshapeOp.getOperation());
1078     if (failed(reifyShapedTypeInterface.reifyResultShapes(rewriter,
1079                                                           resultShapes)) ||
1080         !llvm::hasSingleElement(resultShapes))
1081       return failure();
1082     Value initTensor = rewriter.create<InitTensorOp>(
1083         loc, getAsOpFoldResult(resultShapes[0]),
1084         reshapeOp.getResultType().getElementType());
1085     if (initTensor.getType() != reshapeOp.getResultType()) {
1086       rewriter.replaceOpWithNewOp<tensor::CastOp>(
1087           reshapeOp, reshapeOp.getResultType(), initTensor);
1088     } else {
1089       rewriter.replaceOp(reshapeOp, initTensor);
1090     }
1091     return success();
1092   }
1093 };
1094 
1095 struct FoldInitTensorWithDimOp : public OpRewritePattern<tensor::DimOp> {
1096   using OpRewritePattern<tensor::DimOp>::OpRewritePattern;
1097 
1098   LogicalResult matchAndRewrite(tensor::DimOp dimOp,
1099                                 PatternRewriter &rewriter) const override {
1100     Optional<int64_t> maybeConstantIndex = dimOp.getConstantIndex();
1101     auto initTensorOp = dimOp.source().getDefiningOp<linalg::InitTensorOp>();
1102     if (!initTensorOp || !maybeConstantIndex)
1103       return failure();
1104     if (!initTensorOp.isDynamicSize(*maybeConstantIndex))
1105       return failure();
1106     rewriter.replaceOp(dimOp, initTensorOp.getDynamicSize(*maybeConstantIndex));
1107     return success();
1108   }
1109 };
1110 
1111 /// Canonicalize
1112 ///
1113 /// ```mlir
1114 ///   %0 = linalg.init_tensor [%d0, %d1] : tensor<?x?xf32>
1115 ///   %1 = tensor.cast %0 : tensor<?x?xf32> to tensor<4x?xf32>
1116 /// ```
1117 ///
1118 /// into
1119 ///
1120 /// ```mlir
1121 ///   %0 = linalg.init_tensor [4, %d1] : tensor<4x?xf32>
1122 /// ```
1123 ///
1124 /// This assumes the input program is correct in terms of its shape. So it
1125 /// is safe to assume that `%d0` is in fact 4. If that was not the case, the
1126 /// input program is wrong to begin with, so its undefined behavior anyway (i.e.
1127 /// this optimization can still triggering without violating program semantics).
1128 struct FoldInitTensorWithTensorCastOp
1129     : public OpRewritePattern<tensor::CastOp> {
1130   using OpRewritePattern<tensor::CastOp>::OpRewritePattern;
1131 
1132   LogicalResult matchAndRewrite(tensor::CastOp castOp,
1133                                 PatternRewriter &rewriter) const override {
1134     if (!canFoldIntoProducerOp(castOp))
1135       return failure();
1136     auto producer = castOp.source().getDefiningOp<InitTensorOp>();
1137     if (!producer)
1138       return failure();
1139 
1140     auto resultType = castOp->getResult(0).getType().cast<RankedTensorType>();
1141     ArrayRef<int64_t> resultShape = resultType.getShape();
1142     SmallVector<OpFoldResult> currMixedSizes = producer.getMixedSizes();
1143     SmallVector<OpFoldResult> newMixedSizes;
1144     newMixedSizes.reserve(currMixedSizes.size());
1145     assert(resultShape.size() == currMixedSizes.size() &&
1146            "mismatch in result shape and sizes of init_tensor op");
1147     for (auto it : llvm::zip(resultShape, currMixedSizes)) {
1148       int64_t newDim = std::get<0>(it);
1149       OpFoldResult currDim = std::get<1>(it);
1150       // Case 1: The init tensor dim is static. Check that the tensor cast
1151       // result dim matches.
1152       if (auto attr = currDim.dyn_cast<Attribute>()) {
1153         if (ShapedType::isDynamic(newDim) ||
1154             newDim != attr.cast<IntegerAttr>().getInt()) {
1155           // Something is off, the cast result shape cannot be more dynamic than
1156           // the init tensor result shape (enforced by `canFoldIntoProducer`).
1157           // Abort for now.
1158           return rewriter.notifyMatchFailure(
1159               producer, "mismatch in static value of shape of init "
1160                         "tensor result and cast result");
1161         }
1162         newMixedSizes.push_back(attr);
1163         continue;
1164       }
1165 
1166       // Case 2 : The tensor cast shape is static, but init tensor result shape
1167       // is dynamic.
1168       if (!ShapedType::isDynamic(newDim)) {
1169         newMixedSizes.push_back(rewriter.getIndexAttr(newDim));
1170         continue;
1171       }
1172 
1173       // Case 3 : The tensor cast shape is dynamic and init tensor result shape
1174       // is dynamic. Use the dynamic value from the init tensor op.
1175       newMixedSizes.push_back(currDim);
1176     }
1177 
1178     rewriter.replaceOpWithNewOp<InitTensorOp>(castOp, newMixedSizes,
1179                                               resultType.getElementType());
1180     return success();
1181   }
1182 };
1183 
1184 } // namespace
1185 
1186 void InitTensorOp::getCanonicalizationPatterns(RewritePatternSet &results,
1187                                                MLIRContext *context) {
1188   results.add<FoldInitTensorWithTensorCastOp, FoldInitTensorWithDimOp,
1189               FoldInitTensorWithExtractSliceOp,
1190               FoldInitTensorWithTensorReshapeOp<tensor::ExpandShapeOp>,
1191               FoldInitTensorWithTensorReshapeOp<tensor::CollapseShapeOp>,
1192               ReplaceStaticShapeDims>(context);
1193 }
1194 
1195 LogicalResult InitTensorOp::reifyResultShapes(
1196     OpBuilder &builder, ReifiedRankedShapedTypeDims &reifiedReturnShapes) {
1197   auto shapes = llvm::to_vector<4>(llvm::map_range(
1198       llvm::seq<int64_t>(0, getType().getRank()), [&](int64_t dim) -> Value {
1199         if (isDynamicSize(dim))
1200           return getDynamicSize(dim);
1201         return builder.create<arith::ConstantIndexOp>(getLoc(),
1202                                                       getStaticSize(dim));
1203       }));
1204   reifiedReturnShapes.emplace_back(std::move(shapes));
1205   return success();
1206 }
1207 
1208 //===----------------------------------------------------------------------===//
1209 // YieldOp
1210 //===----------------------------------------------------------------------===//
1211 
1212 void linalg::YieldOp::print(OpAsmPrinter &p) {
1213   if (getNumOperands() > 0)
1214     p << ' ' << getOperands();
1215   p.printOptionalAttrDict((*this)->getAttrs());
1216   if (getNumOperands() > 0)
1217     p << " : " << getOperandTypes();
1218 }
1219 
1220 ParseResult YieldOp::parse(OpAsmParser &parser, OperationState &result) {
1221   SmallVector<OpAsmParser::UnresolvedOperand, 2> opInfo;
1222   SmallVector<Type, 2> types;
1223   SMLoc loc = parser.getCurrentLocation();
1224   return failure(parser.parseOperandList(opInfo) ||
1225                  parser.parseOptionalAttrDict(result.attributes) ||
1226                  (!opInfo.empty() && parser.parseColonTypeList(types)) ||
1227                  parser.resolveOperands(opInfo, types, loc, result.operands));
1228 }
1229 
1230 // Check the operand number and types must match the element types of the
1231 // LinalgOp interface's shaped operands.
1232 static LogicalResult verifyYield(linalg::YieldOp op, LinalgOp linalgOp) {
1233   if (op.getNumOperands() != linalgOp.getNumOutputs())
1234     return op.emitOpError("expected number of yield values (")
1235            << linalgOp.getNumOutputs()
1236            << ") to match the number of operands of the enclosing "
1237            << "LinalgOp (" << op.getNumOperands() << ")";
1238 
1239   for (OpOperand &opOperand : op->getOpOperands()) {
1240     OpOperand *outputOperand =
1241         linalgOp.getOutputOperand(opOperand.getOperandNumber());
1242     Type elementType = getElementTypeOrSelf(outputOperand->get().getType());
1243     if (opOperand.get().getType() != elementType)
1244       return op.emitOpError("type of yield operand ")
1245              << (opOperand.getOperandNumber() + 1) << " ("
1246              << opOperand.get().getType() << ") doesn't match "
1247              << "the element type of the enclosing linalg.generic op ("
1248              << elementType << ")";
1249   }
1250   return success();
1251 }
1252 
1253 LogicalResult linalg::YieldOp::verify() {
1254   auto *parentOp = (*this)->getParentOp();
1255   if (parentOp->getNumRegions() != 1 || parentOp->getRegion(0).empty())
1256     return emitOpError("expected single non-empty parent region");
1257 
1258   if (auto linalgOp = dyn_cast<LinalgOp>(parentOp))
1259     return verifyYield(*this, linalgOp);
1260 
1261   return emitOpError("expected parent op with LinalgOp interface");
1262 }
1263 
1264 //===----------------------------------------------------------------------===//
1265 // IndexOp
1266 //===----------------------------------------------------------------------===//
1267 
1268 LogicalResult IndexOp::verify() {
1269   auto linalgOp = dyn_cast<LinalgOp>((*this)->getParentOp());
1270   if (!linalgOp)
1271     return emitOpError("expected parent op with LinalgOp interface");
1272   if (linalgOp.getNumLoops() <= dim())
1273     return emitOpError("expected dim (")
1274            << dim() << ") to be lower than the number of loops ("
1275            << linalgOp.getNumLoops() << ") of the enclosing LinalgOp";
1276   return success();
1277 }
1278 
1279 /////// Operations corresponding to library calls defined with Tablegen ////////
1280 
1281 #include "mlir/Dialect/Linalg/IR/LinalgNamedStructuredOps.yamlgen.cpp.inc"
1282 
1283 #define GET_OP_CLASSES
1284 #include "mlir/Dialect/Linalg/IR/LinalgOps.cpp.inc"
1285 
1286 #define GET_OP_CLASSES
1287 #include "mlir/Dialect/Linalg/IR/LinalgStructuredOps.cpp.inc"
1288 
1289 /// Return the dims that are `iteratorTypeName` loops in the LinalgOp `op`.
1290 /// Assumes `op` is a LinalgOp.
1291 void mlir::linalg::getDimsOfType(Operation *op, StringRef iteratorTypeName,
1292                                  SmallVectorImpl<unsigned> &res) {
1293   if (!cast<LinalgOp>(op).iterator_types())
1294     return;
1295 
1296   unsigned dim = 0;
1297   for (auto tn :
1298        cast<LinalgOp>(op).iterator_types().getAsValueRange<StringAttr>()) {
1299     if (tn == iteratorTypeName)
1300       res.push_back(dim);
1301     ++dim;
1302   }
1303 }
1304 
1305 AffineMap mlir::linalg::extractOrIdentityMap(Optional<AffineMap> maybeMap,
1306                                              unsigned rank,
1307                                              MLIRContext *context) {
1308   if (maybeMap)
1309     return maybeMap.getValue();
1310   if (rank == 0)
1311     return AffineMap::get(context);
1312   return AffineMap::getMultiDimIdentityMap(rank, context);
1313 }
1314 
1315 SmallVector<AffineExpr, 4>
1316 mlir::linalg::makeAffineDimExprs(unsigned num, unsigned &startIdx,
1317                                  MLIRContext *context) {
1318   SmallVector<AffineExpr, 4> res;
1319   res.reserve(num);
1320   for (unsigned i = 0; i < num; ++i)
1321     res.push_back(getAffineDimExpr(startIdx++, context));
1322   return res;
1323 }
1324 
1325 SmallVector<AffineExpr, 4> mlir::linalg::concat(ArrayRef<AffineExpr> a,
1326                                                 ArrayRef<AffineExpr> b) {
1327   auto rangeA = llvm::make_range(a.begin(), a.end());
1328   auto rangeB = llvm::make_range(b.begin(), b.end());
1329   auto concatRanges = llvm::concat<const AffineExpr>(rangeA, rangeB);
1330   return llvm::to_vector<4>(concatRanges);
1331 }
1332 
1333 static void appendMangledType(llvm::raw_string_ostream &ss, Type t) {
1334   if (auto memref = t.dyn_cast<MemRefType>()) {
1335     ss << "view";
1336     for (auto size : memref.getShape())
1337       if (size < 0)
1338         ss << "sx";
1339       else
1340         ss << size << "x";
1341     appendMangledType(ss, memref.getElementType());
1342   } else if (auto vec = t.dyn_cast<VectorType>()) {
1343     ss << "vector";
1344     llvm::interleave(
1345         vec.getShape(), [&](int64_t i) { ss << i; }, [&]() { ss << "x"; });
1346     appendMangledType(ss, vec.getElementType());
1347   } else if (t.isSignlessIntOrIndexOrFloat()) {
1348     ss << t;
1349   } else {
1350     llvm_unreachable("Invalid type for linalg library name mangling");
1351   }
1352 }
1353 
1354 std::string mlir::linalg::generateLibraryCallName(Operation *op) {
1355   assert(isa<LinalgOp>(op));
1356   std::string name(op->getName().getStringRef().str());
1357   name.reserve(128);
1358   std::replace(name.begin(), name.end(), '.', '_');
1359   llvm::raw_string_ostream ss(name);
1360   ss << "_";
1361   auto types = op->getOperandTypes();
1362   llvm::interleave(
1363       types.begin(), types.end(), [&](Type t) { appendMangledType(ss, t); },
1364       [&]() { ss << "_"; });
1365   return ss.str();
1366 }
1367 
1368 //===----------------------------------------------------------------------===//
1369 // Support for named Linalg ops defined in ods-gen.
1370 //===----------------------------------------------------------------------===//
1371 
1372 /// Generic entry point to create the block for the region of a LinalgOp.
1373 /// This is used by both named structured ops created by ods-gen and by manually
1374 /// defined C++ ops.
1375 /// This is used by both builders and parsers.
1376 /// This function creates the block in the region with arguments corresponding
1377 /// to the elemental types of `inputTypes` and `outputTypes`, which are asserted
1378 /// to be ShapedType.
1379 template <typename NamedStructuredOpType>
1380 static void fillStructuredOpRegion(
1381     OpBuilder &opBuilder, Region &region, TypeRange inputTypes,
1382     TypeRange outputTypes, ArrayRef<NamedAttribute> attrs,
1383     llvm::function_ref<void(unsigned, unsigned)> errorHandler) {
1384   assert(llvm::all_of(outputTypes, [](Type t) { return t.isa<ShapedType>(); }));
1385 
1386   // TODO: atm all operands go through getElementTypeOrSelf,
1387   // reconsider when we have evidence we need to.
1388   SmallVector<Type, 8> argTypes;
1389   SmallVector<Location, 8> argLocs;
1390   for (auto containers : {inputTypes, outputTypes}) {
1391     for (auto t : containers) {
1392       argTypes.push_back(getElementTypeOrSelf(t));
1393 
1394       // TODO: Pass in a proper location here.
1395       argLocs.push_back(opBuilder.getUnknownLoc());
1396     }
1397   }
1398 
1399   // RAII.
1400   OpBuilder::InsertionGuard guard(opBuilder);
1401   Block *body =
1402       opBuilder.createBlock(&region, /*insertPt=*/{}, argTypes, argLocs);
1403   unsigned actual = body->getNumArguments();
1404   unsigned expected = NamedStructuredOpType::getNumRegionArgs();
1405   if (expected != actual) {
1406     if (errorHandler)
1407       errorHandler(expected, actual);
1408     return;
1409   }
1410 
1411   opBuilder.setInsertionPointToStart(body);
1412   ImplicitLocOpBuilder b(opBuilder.getUnknownLoc(), opBuilder);
1413   NamedStructuredOpType::regionBuilder(b, *body, attrs);
1414 
1415   // indexing_maps is an auto-generated method.
1416 
1417   // iterator_types is an auto-generated method.
1418 }
1419 
1420 /// Generic entry point to create both the region and the block of a LinalgOp.
1421 template <typename NamedStructuredOpType>
1422 void createAndFillStructuredOpRegion(OpBuilder &opBuilder,
1423                                      OperationState &result,
1424                                      TypeRange inputTypes,
1425                                      TypeRange outputTypes) {
1426   Region &region = *result.addRegion();
1427   fillStructuredOpRegion<NamedStructuredOpType>(
1428       opBuilder, region, inputTypes, outputTypes, result.attributes.getAttrs(),
1429       [&](unsigned expected, unsigned actual) {
1430         assert(expected != actual && "incorrect number of arguments");
1431       });
1432 }
1433 
1434 /// Common parsing used for both named structured ops created by ods-gen and by
1435 /// manually defined C++ ops. Does not handle regions.
1436 static ParseResult
1437 parseCommonStructuredOpParts(OpAsmParser &parser, OperationState &result,
1438                              SmallVectorImpl<Type> &inputTypes,
1439                              SmallVectorImpl<Type> &outputTypes) {
1440   SMLoc inputsOperandsLoc, outputsOperandsLoc;
1441   SmallVector<OpAsmParser::UnresolvedOperand, 4> inputsOperands,
1442       outputsOperands;
1443 
1444   parser.parseOptionalAttrDict(result.attributes);
1445 
1446   if (succeeded(parser.parseOptionalKeyword("ins"))) {
1447     if (parser.parseLParen())
1448       return failure();
1449 
1450     inputsOperandsLoc = parser.getCurrentLocation();
1451     if (parser.parseOperandList(inputsOperands) ||
1452         parser.parseColonTypeList(inputTypes) || parser.parseRParen())
1453       return failure();
1454   }
1455 
1456   if (succeeded(parser.parseOptionalKeyword("outs"))) {
1457     outputsOperandsLoc = parser.getCurrentLocation();
1458     if (parser.parseLParen() || parser.parseOperandList(outputsOperands) ||
1459         parser.parseColonTypeList(outputTypes) || parser.parseRParen())
1460       return failure();
1461   }
1462 
1463   if (parser.resolveOperands(inputsOperands, inputTypes, inputsOperandsLoc,
1464                              result.operands) ||
1465       parser.resolveOperands(outputsOperands, outputTypes, outputsOperandsLoc,
1466                              result.operands))
1467     return failure();
1468 
1469   result.addAttribute("operand_segment_sizes",
1470                       parser.getBuilder().getI32VectorAttr(
1471                           {static_cast<int32_t>(inputsOperands.size()),
1472                            static_cast<int32_t>(outputsOperands.size())}));
1473   return success();
1474 }
1475 
1476 template <typename NamedStructuredOpType>
1477 static void printCommonStructuredOpParts(OpAsmPrinter &p,
1478                                          NamedStructuredOpType op) {
1479   if (!op.inputs().empty())
1480     p << " ins(" << op.inputs() << " : " << op.inputs().getTypes() << ")";
1481   if (!op.outputs().empty())
1482     p << " outs(" << op.outputs() << " : " << op.outputs().getTypes() << ")";
1483 }
1484 
1485 //===----------------------------------------------------------------------===//
1486 // Specific parsing and printing for named structured ops created by ods-gen.
1487 //===----------------------------------------------------------------------===//
1488 
1489 template <typename NamedStructuredOpType>
1490 static ParseResult
1491 parseNamedStructuredOpRegion(OpAsmParser &parser, Region &region,
1492                              TypeRange inputTypes, TypeRange outputTypes,
1493                              ArrayRef<NamedAttribute> attrs) {
1494   ParseResult res = success();
1495   OpBuilder opBuilder(parser.getContext());
1496   // Resolve `captures` into `capturedValues` at parse time so we can build the
1497   // region with captures.
1498   SmallVector<Value> capturedValues;
1499   fillStructuredOpRegion<NamedStructuredOpType>(
1500       opBuilder, region, inputTypes, outputTypes, attrs,
1501       [&](unsigned expected, unsigned actual) {
1502         res = parser.emitError(
1503             parser.getCurrentLocation(),
1504             llvm::formatv("[parseNamedStructuredOpRegion] ods-gen generated "
1505                           "region expects {0} args, got {1}",
1506                           expected, actual));
1507         region.front().dump();
1508       });
1509   return res;
1510 }
1511 
1512 static ParseResult
1513 parseNamedStructuredOpResults(OpAsmParser &parser,
1514                               SmallVectorImpl<Type> &resultTypes) {
1515   if (parser.parseOptionalArrowTypeList(resultTypes))
1516     return failure();
1517   return success();
1518 }
1519 
1520 template <typename NamedStructuredOpType>
1521 static ParseResult parseNamedStructuredOp(OpAsmParser &parser,
1522                                           OperationState &result) {
1523   // TODO: Enable when ods-gen supports captures.
1524   SmallVector<Type, 1> inputTypes, outputTypes;
1525   if (parseCommonStructuredOpParts(parser, result, inputTypes, outputTypes))
1526     return failure();
1527 
1528   // TODO: consider merging results parsing into region parsing.
1529   // Need to wait for declarative assembly resolution to decide.
1530   SmallVector<Type, 1> outputTensorsTypes;
1531   if (parseNamedStructuredOpResults(parser, outputTensorsTypes))
1532     return failure();
1533   result.addTypes(outputTensorsTypes);
1534 
1535   std::unique_ptr<Region> region = std::make_unique<Region>();
1536   if (parseNamedStructuredOpRegion<NamedStructuredOpType>(
1537           parser, *region, inputTypes, outputTypes,
1538           result.attributes.getAttrs()))
1539     return failure();
1540   result.addRegion(std::move(region));
1541 
1542   return success();
1543 }
1544 
1545 static void printNamedStructuredOpResults(OpAsmPrinter &p,
1546                                           TypeRange resultTypes) {
1547   if (resultTypes.empty())
1548     return;
1549   p.printOptionalArrowTypeList(resultTypes);
1550 }
1551 
1552 template <typename NamedStructuredOpType>
1553 static void printNamedStructuredOp(OpAsmPrinter &p, NamedStructuredOpType op) {
1554   p.printOptionalAttrDict(
1555       op->getAttrs(),
1556       /*elidedAttrs=*/{"operand_segment_sizes",
1557                        // See generated code in mlir-linalg-yaml-gen.cpp
1558                        "linalg.memoized_indexing_maps"});
1559 
1560   // Printing is shared with generic ops, except for the region and
1561   // attributes.
1562   printCommonStructuredOpParts(p, op);
1563 
1564   // Results printing.
1565   printNamedStructuredOpResults(p, op.result_tensors().getTypes());
1566 
1567   // Region is elided.
1568 }
1569 
1570 template <typename NamedStructuredOpType>
1571 static LogicalResult verifyNamedStructuredOp(NamedStructuredOpType op) {
1572   return verifyGenericOp<NamedStructuredOpType>(op);
1573 }
1574 
1575 //===----------------------------------------------------------------------===//
1576 // Canonicalizers and Folders.
1577 //===----------------------------------------------------------------------===//
1578 
1579 namespace {
1580 struct EraseDeadLinalgOp : public OpInterfaceRewritePattern<LinalgOp> {
1581   using OpInterfaceRewritePattern<LinalgOp>::OpInterfaceRewritePattern;
1582 
1583   LogicalResult matchAndRewrite(LinalgOp op,
1584                                 PatternRewriter &rewriter) const override {
1585     for (OpOperand *opOperand : op.getInputAndOutputOperands()) {
1586       // Linalg "inputs" may be either tensor or memref type.
1587       // tensor<0xelt_type> is a convention that may not always mean
1588       // "0 iterations". Only erase in cases we see memref<...x0x...>.
1589       auto mt = opOperand->get().getType().dyn_cast<MemRefType>();
1590       if (!mt)
1591         continue;
1592       if (llvm::is_contained(op.getShape(opOperand), 0)) {
1593         rewriter.eraseOp(op);
1594         return success();
1595       }
1596     }
1597     return failure();
1598   }
1599 };
1600 
1601 struct FoldTensorCastProducerOp : public OpInterfaceRewritePattern<LinalgOp> {
1602   using OpInterfaceRewritePattern<LinalgOp>::OpInterfaceRewritePattern;
1603 
1604   LogicalResult matchAndRewrite(LinalgOp op,
1605                                 PatternRewriter &rewriter) const override {
1606     // If no operand comes from a tensor::CastOp and can be folded then fail.
1607     bool hasTensorCastOperand =
1608         llvm::any_of(op.getInputAndOutputOperands(), [&](OpOperand *opOperand) {
1609           if (opOperand->get().isa<BlockArgument>())
1610             return false;
1611           auto castOp = opOperand->get().getDefiningOp<tensor::CastOp>();
1612           return castOp && canFoldIntoConsumerOp(castOp);
1613         });
1614     if (!hasTensorCastOperand)
1615       return failure();
1616 
1617     SmallVector<Type, 4> newResultTypes;
1618     newResultTypes.reserve(op->getNumResults());
1619     SmallVector<Value, 4> newOperands;
1620     newOperands.reserve(op->getNumOperands());
1621     // Inputs may fold.
1622     for (OpOperand *opOperand : op.getInputOperands()) {
1623       auto tensorCastOp = opOperand->get().getDefiningOp<tensor::CastOp>();
1624       newOperands.push_back(canFoldIntoConsumerOp(tensorCastOp)
1625                                 ? tensorCastOp.source()
1626                                 : opOperand->get());
1627     }
1628     // Init tensors may fold, in which case the resultType must also change.
1629     for (OpOperand *opOperand : op.getOutputOperands()) {
1630       auto tensorCastOp = opOperand->get().getDefiningOp<tensor::CastOp>();
1631       bool fold = canFoldIntoConsumerOp(tensorCastOp);
1632       newOperands.push_back(fold ? tensorCastOp.getOperand()
1633                                  : opOperand->get());
1634       newResultTypes.push_back(newOperands.back().getType());
1635     }
1636     // Clone op.
1637     Operation *newOp =
1638         op.clone(rewriter, op->getLoc(), newResultTypes, newOperands);
1639     SmallVector<Value, 4> replacements;
1640     replacements.reserve(newOp->getNumResults());
1641     for (auto result : llvm::zip(op->getResults(), newOp->getResults())) {
1642       Value oldResult = std::get<0>(result);
1643       Value newResult = std::get<1>(result);
1644       if (newResult.getType() != oldResult.getType()) {
1645         replacements.push_back(rewriter.create<tensor::CastOp>(
1646             op->getLoc(), oldResult.getType(), newResult));
1647       } else {
1648         replacements.push_back(newResult);
1649       }
1650     }
1651     rewriter.replaceOp(op, replacements);
1652 
1653     return success();
1654   }
1655 };
1656 
1657 /// Fold LinalgOps with `tensor.cast` consumer if the `tensor.cast` has
1658 /// result that is more static than the linalg op.
1659 struct FoldTensorCastConsumerOp : public OpRewritePattern<tensor::CastOp> {
1660   using OpRewritePattern<tensor::CastOp>::OpRewritePattern;
1661 
1662   LogicalResult matchAndRewrite(tensor::CastOp castOp,
1663                                 PatternRewriter &rewriter) const override {
1664     if (!tensor::canFoldIntoProducerOp(castOp))
1665       return failure();
1666     auto linalgOp = castOp.source().getDefiningOp<LinalgOp>();
1667     if (!linalgOp)
1668       return failure();
1669 
1670     OpBuilder::InsertionGuard guard(rewriter);
1671     rewriter.setInsertionPoint(linalgOp);
1672 
1673     Location loc = linalgOp.getLoc();
1674     OpResult resultValue = castOp.source().cast<OpResult>();
1675     unsigned resultNumber = resultValue.getResultNumber();
1676     auto resultType = castOp->getResult(0).getType().cast<RankedTensorType>();
1677     // Replace the `outs` for the result with a `tensor.cast`. This cast is now
1678     // going from a more dynamic shape to a less dynamic shape. If the producer
1679     // for this cast, i.e. producer of the out operand, is also an operation
1680     // that folds with tensor.cast consumer (like this pattern), the cast will
1681     // continue to propagate as far up the stack as it can go.
1682     OpOperand *outOperand = linalgOp.getOutputOperand(resultNumber);
1683     Value newOperand =
1684         rewriter.create<tensor::CastOp>(loc, resultType, outOperand->get());
1685     SmallVector<Value> newOperands = linalgOp.getInputOperands();
1686     SmallVector<Value> outputOperands = linalgOp.getOutputOperands();
1687     outputOperands[resultNumber] = newOperand;
1688     newOperands.append(outputOperands.begin(), outputOperands.end());
1689 
1690     SmallVector<Type> resultTypes(linalgOp->result_type_begin(),
1691                                   linalgOp->result_type_end());
1692     resultTypes[resultNumber] = resultType;
1693     Operation *newOp = linalgOp.clone(rewriter, loc, resultTypes, newOperands);
1694 
1695     // Create a tensor.cast operation back to the original type.
1696     Value castBack = rewriter.create<tensor::CastOp>(
1697         loc, resultValue.getType(), newOp->getResult(resultNumber));
1698 
1699     SmallVector<Value> results(newOp->result_begin(), newOp->result_end());
1700     results[resultNumber] = castBack;
1701     rewriter.replaceOp(linalgOp, results);
1702     rewriter.replaceOp(castOp, newOp->getResult(resultNumber));
1703     return success();
1704   }
1705 };
1706 
1707 /// For each of the operand in `operands` this function maps the static sizes of
1708 /// dimensions to their affine dim expressions.
1709 static void populateMap(LinalgOp linalgOp, ArrayRef<OpOperand *> operands,
1710                         llvm::DenseMap<AffineExpr, int64_t> &affineExprToSize) {
1711   for (OpOperand *opOperand : operands) {
1712     if (linalgOp.isScalar(opOperand))
1713       continue;
1714     Value src = opOperand->get();
1715     auto sourceType = src.getType().cast<RankedTensorType>();
1716     auto sourceMap = linalgOp.getTiedIndexingMap(opOperand);
1717 
1718     // Get the `sourceShape` of the `sourceType`. If the operand is a result of
1719     // `tensor.cast` operation and source of the cast operation has a static
1720     // shape, then assign it to the `sourceShape`.
1721     auto parentOp = src.getDefiningOp();
1722     ArrayRef<int64_t> sourceShape = sourceType.getShape();
1723     if (parentOp) {
1724       if (auto castOp = dyn_cast<tensor::CastOp>(parentOp)) {
1725         Value castSource = castOp.source();
1726         auto castSourceType = castSource.getType().cast<RankedTensorType>();
1727         if (castSourceType.hasStaticShape())
1728           sourceShape = castSourceType.getShape();
1729       }
1730     }
1731 
1732     // If the source shape's dimension has a static shape, map the affine dim
1733     // expression to the known static size.
1734     for (unsigned i = 0; i < sourceShape.size(); i++) {
1735       if (sourceType.isDynamicDim(i))
1736         continue;
1737       if (auto affineDimExpr = sourceMap.getResult(i).dyn_cast<AffineDimExpr>())
1738         affineExprToSize.try_emplace(affineDimExpr, sourceShape[i]);
1739     }
1740   }
1741 }
1742 
1743 /// Creates new operand w.r.t 'opOperand' of `linalgOp` with static sizes
1744 /// mapped in `affineExprToSize`. New operands are created in `newOperands` and
1745 /// their result types is stored in `resultTypes`. If `opOperand` requires no
1746 /// change then `changeNeeded` is false and same operand is added in the
1747 /// `newOperands` list.
1748 static void createNewOperandWithStaticSizes(
1749     Location loc, PatternRewriter &rewriter, OpOperand *opOperand,
1750     llvm::DenseMap<AffineExpr, int64_t> &affineExprToSize, LinalgOp linalgOp,
1751     SmallVector<Value> &newOperands, SmallVector<Type> &resultTypes,
1752     bool &changeNeeded) {
1753   Value src = opOperand->get();
1754   newOperands.push_back(src);
1755   if (linalgOp.isScalar(opOperand))
1756     return;
1757   auto sourceType = src.getType().cast<RankedTensorType>();
1758   Type resultType = sourceType;
1759   if (sourceType.hasStaticShape() && linalgOp.isOutputTensor(opOperand)) {
1760     resultTypes.push_back(resultType);
1761     return;
1762   }
1763   ArrayRef<int64_t> sourceShape = sourceType.getShape();
1764   AffineMap sourceMap = linalgOp.getTiedIndexingMap(opOperand);
1765   SmallVector<int64_t> newShape;
1766   // If operand is updated with new shape, `newOperandNeeded` will be
1767   // true.
1768   bool newOperandNeeded = false;
1769   for (unsigned i = 0; i < sourceShape.size(); i++) {
1770     int64_t dimShape = sourceShape[i];
1771     AffineExpr dimExpr = sourceMap.getResult(i);
1772     if (affineExprToSize.find(dimExpr) == affineExprToSize.end() ||
1773         !sourceType.isDynamicDim(i)) {
1774       newShape.push_back(dimShape);
1775       continue;
1776     }
1777     // Dimension has a dynamic shape and corresponding affine dim
1778     // expression is present in the map. So assign the size for the
1779     // given affine dim expression to the dimension.
1780     newShape.push_back(affineExprToSize[dimExpr]);
1781     newOperandNeeded = true;
1782   }
1783   resultType = RankedTensorType::get(newShape, sourceType.getElementType());
1784   if (newOperandNeeded) {
1785     changeNeeded = true;
1786     // Get the new operand value given its size and element type by
1787     // casting it.
1788     Value newOperand = rewriter.create<tensor::CastOp>(loc, resultType, src);
1789     unsigned index = opOperand->getOperandNumber();
1790     newOperands[index] = newOperand;
1791   }
1792   if (linalgOp.isOutputTensor(opOperand))
1793     resultTypes.push_back(resultType);
1794 }
1795 
1796 /// Static shapes for the operands can be inferred if any one of the operands
1797 /// have a static shape. This can be done by referring to the affine dim
1798 /// expressions for the operand.
1799 struct InferStaticShapeOfOperands : public OpInterfaceRewritePattern<LinalgOp> {
1800   using OpInterfaceRewritePattern<LinalgOp>::OpInterfaceRewritePattern;
1801 
1802   LogicalResult matchAndRewrite(LinalgOp linalgOp,
1803                                 PatternRewriter &rewriter) const override {
1804     if (!linalgOp.hasTensorSemantics())
1805       return failure();
1806 
1807     // Maps must be projected permutations.
1808     if (llvm::any_of(linalgOp.getIndexingMaps(), [](AffineMap map) {
1809           return !map.isProjectedPermutation();
1810         }))
1811       return failure();
1812 
1813     // Maps affine dim expressions to the static size of that dimension.
1814     llvm::DenseMap<AffineExpr, int64_t> affineExprToSize;
1815     Location loc = linalgOp.getLoc();
1816 
1817     // For each of the affine dim expression, check if the size is known. If
1818     // known add that in the map.
1819     populateMap(linalgOp, linalgOp.getInputAndOutputOperands(),
1820                 affineExprToSize);
1821 
1822     SmallVector<Value> newOperands;
1823     SmallVector<Type> resultTypes;
1824 
1825     // `changeNeeded` is `false` if the operands of `linalgOp` require no
1826     // change in their types.
1827     bool changeNeeded = false;
1828     newOperands.reserve(linalgOp.getNumInputsAndOutputs());
1829     resultTypes.reserve(linalgOp.getNumOutputs());
1830 
1831     // Iterate over all the operands and update the static sizes.
1832     for (OpOperand *opOperand : linalgOp.getInputAndOutputOperands()) {
1833       createNewOperandWithStaticSizes(loc, rewriter, opOperand,
1834                                       affineExprToSize, linalgOp, newOperands,
1835                                       resultTypes, changeNeeded);
1836     }
1837 
1838     // If the generic op has all the required static information, no
1839     // canonicalization needed.
1840     if (!changeNeeded)
1841       return failure();
1842 
1843     // Clone op.
1844     Operation *newOp =
1845         linalgOp.clone(rewriter, linalgOp->getLoc(), resultTypes, newOperands);
1846     SmallVector<Value> replacements;
1847     replacements.reserve(newOp->getNumResults());
1848     for (auto it : llvm::zip(linalgOp->getResults(), newOp->getResults())) {
1849       Value newResult = std::get<1>(it);
1850       Value oldResult = std::get<0>(it);
1851       Type newType = newResult.getType();
1852       Type oldType = oldResult.getType();
1853       replacements.push_back(
1854           (newType != oldType)
1855               ? rewriter.create<tensor::CastOp>(loc, oldType, newResult)
1856               : newResult);
1857     }
1858     rewriter.replaceOp(linalgOp, replacements);
1859     return success();
1860   }
1861 };
1862 
1863 } // namespace
1864 
1865 // All named ops canonicalizers and folders are auto-generated in the
1866 // .cpp.inc.
1867 
1868 //===----------------------------------------------------------------------===//
1869 // LinalgDialect
1870 //===----------------------------------------------------------------------===//
1871 
1872 void LinalgDialect::getCanonicalizationPatterns(
1873     RewritePatternSet &results) const {
1874   results.add<EraseDeadLinalgOp, FoldTensorCastConsumerOp,
1875               FoldTensorCastProducerOp, InferStaticShapeOfOperands>(
1876       getContext());
1877 }
1878 
1879 Operation *LinalgDialect::materializeConstant(OpBuilder &builder,
1880                                               Attribute value, Type type,
1881                                               Location loc) {
1882   return builder.create<arith::ConstantOp>(loc, type, value);
1883 }
1884