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/LinalgOps.h"
14 #include "mlir/Dialect/Linalg/EDSC/Intrinsics.h"
15 #include "mlir/Dialect/Linalg/IR/LinalgTypes.h"
16 #include "mlir/Dialect/StandardOps/IR/Ops.h"
17 #include "mlir/IR/AffineExpr.h"
18 #include "mlir/IR/AffineMap.h"
19 #include "mlir/IR/Builders.h"
20 #include "mlir/IR/Function.h"
21 #include "mlir/IR/Matchers.h"
22 #include "mlir/IR/Module.h"
23 #include "mlir/IR/OpImplementation.h"
24 #include "mlir/IR/PatternMatch.h"
25 #include "mlir/IR/StandardTypes.h"
26 #include "mlir/Support/LLVM.h"
27 
28 #include "llvm/ADT/SetVector.h"
29 #include "llvm/ADT/StringSet.h"
30 #include "llvm/Support/FormatVariadic.h"
31 #include "llvm/Support/MathExtras.h"
32 #include "llvm/Support/raw_ostream.h"
33 
34 using namespace mlir;
35 using namespace mlir::linalg;
36 
37 /// Forward declarations.
38 template <typename NamedStructuredOpType>
39 static void buildNamedStructuredOpRegionAndAttributes(
40     OpBuilder &opBuilder, OperationState &result, TypeRange inputTypes,
41     TypeRange outputBufferTypes, TypeRange initTensorTypes,
42     TypeRange resultTypes);
43 
44 static ParseResult
45 parseCommonStructuredOpParts(OpAsmParser &parser, OperationState &result,
46                              SmallVectorImpl<Type> &inputTypes,
47                              SmallVectorImpl<Type> &outputBufferTypes,
48                              SmallVectorImpl<Type> &initTensorTypes);
49 
50 template <typename NamedStructuredOpType>
51 static ParseResult
52 parseNamedStructuredOpRegion(OpAsmParser &parser, Region &region,
53                              TypeRange inputTypes, TypeRange outputBufferTypes,
54                              TypeRange initTensorTypes, TypeRange resultTypes);
55 static ParseResult
56 parseNamedStructuredOpResults(OpAsmParser &parser,
57                               SmallVectorImpl<Type> &resultTypes);
58 
59 template <typename NamedStructuredOpType>
60 static ParseResult parseNamedStructuredOp(OpAsmParser &parser,
61                                           OperationState &result);
62 
63 template <typename NamedStructuredOpType>
64 static void printCommonStructuredOpParts(OpAsmPrinter &p,
65                                          NamedStructuredOpType op);
66 
67 static void printNamedStructuredOpResults(OpAsmPrinter &p,
68                                           TypeRange resultTypes);
69 
70 template <typename NamedStructuredOpType>
71 static void printNamedStructuredOp(OpAsmPrinter &p, NamedStructuredOpType op);
72 
73 template <typename NamedStructuredOpType>
74 static LogicalResult verifyNamedStructuredOp(NamedStructuredOpType op);
75 
76 /// This is a common class used for patterns of the form
77 /// ```
78 ///    someop(memrefcast) -> someop
79 /// ```
80 /// It folds the source of the memref_cast into the root operation directly.
81 static LogicalResult foldMemRefCast(Operation *op) {
82   bool folded = false;
83   for (OpOperand &operand : op->getOpOperands()) {
84     auto castOp = operand.get().getDefiningOp<MemRefCastOp>();
85     if (castOp && canFoldIntoConsumerOp(castOp)) {
86       operand.set(castOp.getOperand());
87       folded = true;
88     }
89   }
90   return success(folded);
91 }
92 
93 ///////////////////// Operations defined with Tablegen /////////////////////////
94 // For such operations that do not correspond to library calls (i.e. defined in
95 // LinalgOps.td), we define an overloaded `print` function and a
96 // parse`className` function.
97 
98 //===----------------------------------------------------------------------===//
99 // GenericOps
100 //===----------------------------------------------------------------------===//
101 void GenericOp::build(
102     OpBuilder &builder, OperationState &result, TypeRange resultTensorTypes,
103     ValueRange inputs, ValueRange outputBuffers, ValueRange initTensors,
104     ArrayRef<AffineMap> indexingMaps, ArrayRef<StringRef> iteratorTypes,
105     StringRef doc, StringRef libraryCall, IntegerAttr symbolSource,
106     function_ref<void(OpBuilder &, Location, ValueRange)> bodyBuild) {
107   build(builder, result, resultTensorTypes, inputs, outputBuffers, initTensors,
108         builder.getAffineMapArrayAttr(indexingMaps),
109         builder.getStrArrayAttr(iteratorTypes),
110         doc.empty() ? StringAttr() : builder.getStringAttr(doc),
111         libraryCall.empty() ? StringAttr() : builder.getStringAttr(libraryCall),
112         symbolSource);
113   if (!bodyBuild)
114     return;
115 
116   SmallVector<Type, 4> blockArgTypes;
117   for (ValueRange container : {inputs, outputBuffers, initTensors})
118     for (Value v : container)
119       blockArgTypes.push_back(v.getType().cast<ShapedType>().getElementType());
120 
121   OpBuilder::InsertionGuard guard(builder);
122   auto &region = *result.regions.front();
123   Block *bodyBlock = builder.createBlock(&region, region.end(), blockArgTypes);
124   bodyBuild(builder, result.location, bodyBlock->getArguments());
125 }
126 
127 void GenericOp::build(
128     OpBuilder &builder, OperationState &result, ValueRange inputs,
129     ValueRange outputBuffers, ArrayRef<AffineMap> indexingMaps,
130     ArrayRef<StringRef> iteratorTypes, StringRef doc, StringRef libraryCall,
131     IntegerAttr symbolSource,
132     function_ref<void(OpBuilder &, Location, ValueRange)> bodyBuild) {
133   build(builder, result, TypeRange{}, inputs, outputBuffers, ValueRange{},
134         indexingMaps, iteratorTypes, doc, libraryCall, symbolSource, bodyBuild);
135 }
136 
137 void GenericOp::build(
138     OpBuilder &builder, OperationState &result, ValueRange inputs,
139     ValueRange outputBuffers, ArrayRef<AffineMap> indexingMaps,
140     ArrayRef<StringRef> iteratorTypes,
141     function_ref<void(OpBuilder &, Location, ValueRange)> bodyBuild) {
142   build(builder, result, inputs, outputBuffers, indexingMaps, iteratorTypes,
143         /*doc=*/"",
144         /*libraryCall=*/"",
145         /*symbolSource=*/IntegerAttr(), bodyBuild);
146 }
147 
148 void GenericOp::build(
149     OpBuilder &builder, OperationState &result, TypeRange resultTensorTypes,
150     ValueRange inputs, ValueRange outputBuffers, ValueRange initTensors,
151     ArrayRef<AffineMap> indexingMaps, ArrayRef<StringRef> iteratorTypes,
152     function_ref<void(OpBuilder &, Location, ValueRange)> bodyBuild) {
153   build(builder, result, resultTensorTypes, inputs, outputBuffers, initTensors,
154         indexingMaps, iteratorTypes,
155         /*doc=*/"",
156         /*libraryCall=*/"",
157         /*symbolSource=*/IntegerAttr(), bodyBuild);
158 }
159 
160 void IndexedGenericOp::build(
161     OpBuilder &builder, OperationState &result, TypeRange resultTensorTypes,
162     ValueRange inputs, ValueRange outputBuffers, ValueRange initTensors,
163     ArrayRef<AffineMap> indexingMaps, ArrayRef<StringRef> iteratorTypes,
164     StringRef doc, StringRef libraryCall, IntegerAttr symbolSource,
165     function_ref<void(OpBuilder &, Location, ValueRange, ValueRange)>
166         bodyBuild) {
167   build(builder, result, resultTensorTypes, inputs, outputBuffers, initTensors,
168         builder.getAffineMapArrayAttr(indexingMaps),
169         builder.getStrArrayAttr(iteratorTypes),
170         doc.empty() ? StringAttr() : builder.getStringAttr(doc),
171         libraryCall.empty() ? StringAttr() : builder.getStringAttr(libraryCall),
172         symbolSource);
173   if (!bodyBuild)
174     return;
175 
176   unsigned nLoops = iteratorTypes.size();
177   SmallVector<Type, 4> blockArgTypes(nLoops, builder.getIndexType());
178   for (ValueRange container : {inputs, outputBuffers, initTensors})
179     for (Value v : container)
180       blockArgTypes.push_back(v.getType().cast<ShapedType>().getElementType());
181 
182   OpBuilder::InsertionGuard guard(builder);
183   auto &region = *result.regions.front();
184   Block *bodyBlock = builder.createBlock(&region, region.end(), blockArgTypes);
185   bodyBuild(builder, result.location,
186             bodyBlock->getArguments().take_front(nLoops),
187             bodyBlock->getArguments().drop_front(nLoops));
188 }
189 
190 void IndexedGenericOp::build(
191     OpBuilder &builder, OperationState &result, ValueRange inputs,
192     ValueRange outputBuffers, ArrayRef<AffineMap> indexingMaps,
193     ArrayRef<StringRef> iteratorTypes, StringRef doc, StringRef libraryCall,
194     IntegerAttr symbolSource,
195     function_ref<void(OpBuilder &, Location, ValueRange, ValueRange)>
196         bodyBuild) {
197   build(builder, result, TypeRange{}, inputs, outputBuffers, ValueRange{},
198         indexingMaps, iteratorTypes, doc, libraryCall, symbolSource, bodyBuild);
199 }
200 
201 void IndexedGenericOp::build(
202     OpBuilder &builder, OperationState &result, ValueRange inputs,
203     ValueRange outputBuffers, ArrayRef<AffineMap> indexingMaps,
204     ArrayRef<StringRef> iteratorTypes,
205     function_ref<void(OpBuilder &, Location, ValueRange, ValueRange)>
206         bodyBuild) {
207   build(builder, result, inputs, outputBuffers, indexingMaps, iteratorTypes,
208         /*doc=*/"",
209         /*libraryCall=*/"",
210         /*symbolSource=*/IntegerAttr(), bodyBuild);
211 }
212 
213 void IndexedGenericOp::build(
214     OpBuilder &builder, OperationState &result, TypeRange resultTensorTypes,
215     ValueRange inputs, ValueRange outputBuffers, ValueRange initTensors,
216     ArrayRef<AffineMap> indexingMaps, ArrayRef<StringRef> iteratorTypes,
217     function_ref<void(OpBuilder &, Location, ValueRange, ValueRange)>
218         bodyBuild) {
219   build(builder, result, resultTensorTypes, inputs, outputBuffers, initTensors,
220         indexingMaps, iteratorTypes,
221         /*doc=*/"",
222         /*libraryCall=*/"",
223         /*symbolSource=*/IntegerAttr(), bodyBuild);
224 }
225 
226 template <typename GenericOpType>
227 static void printGenericOp(OpAsmPrinter &p, GenericOpType op) {
228   p << op.getOperationName() << " ";
229 
230   // Print extra attributes.
231   auto genericAttrNames = op.linalgTraitAttrNames();
232 
233   llvm::StringSet<> genericAttrNamesSet;
234   genericAttrNamesSet.insert(genericAttrNames.begin(), genericAttrNames.end());
235   SmallVector<NamedAttribute, 8> genericAttrs;
236   for (auto attr : op.getAttrs())
237     if (genericAttrNamesSet.count(attr.first.strref()) > 0)
238       genericAttrs.push_back(attr);
239   if (!genericAttrs.empty()) {
240     auto genericDictAttr = DictionaryAttr::get(genericAttrs, op.getContext());
241     p << genericDictAttr;
242   }
243 
244   // Printing is shared with named ops, except for the region and attributes
245   printCommonStructuredOpParts(p, op);
246 
247   genericAttrNames.push_back("operand_segment_sizes");
248   genericAttrNamesSet.insert(genericAttrNames.back());
249 
250   bool hasExtraAttrs = false;
251   for (NamedAttribute n : op.getAttrs()) {
252     if ((hasExtraAttrs = !genericAttrNamesSet.contains(n.first.strref())))
253       break;
254   }
255   if (hasExtraAttrs) {
256     p << " attrs = ";
257     p.printOptionalAttrDict(op.getAttrs(), /*elidedAttrs=*/genericAttrNames);
258   }
259 
260   // Print region.
261   if (!op.region().empty())
262     p.printRegion(op.region());
263 
264   // Print results.
265   printNamedStructuredOpResults(p, op.result_tensors().getTypes());
266 }
267 
268 static void print(OpAsmPrinter &p, GenericOp op) { printGenericOp(p, op); }
269 
270 static void print(OpAsmPrinter &p, IndexedGenericOp op) {
271   printGenericOp(p, op);
272 }
273 
274 static ParseResult parseGenericOp(OpAsmParser &parser, OperationState &result) {
275   DictionaryAttr dictAttr;
276   // Parse the core linalg traits that must check into a dictAttr.
277   // The name is unimportant as we will overwrite result.attributes.
278   // The core linalg traits must contain the information necessary to pass the
279   // verifier.
280   if (parser.parseAttribute(dictAttr, "_", result.attributes))
281     return failure();
282   result.attributes.assign(dictAttr.getValue().begin(),
283                            dictAttr.getValue().end());
284 
285   // Parsing is shared with named ops, except for the region.
286   SmallVector<Type, 1> inputTypes, outputBufferTypes, initTensorTypes;
287   if (parseCommonStructuredOpParts(parser, result, inputTypes,
288                                    outputBufferTypes, initTensorTypes))
289     return failure();
290 
291   // Optional attributes may be added.
292   if (succeeded(parser.parseOptionalKeyword("attrs")))
293     if (failed(parser.parseEqual()) ||
294         failed(parser.parseOptionalAttrDict(result.attributes)))
295       return failure();
296 
297   SmallVector<OpAsmParser::OperandType, 8> regionOperands;
298   std::unique_ptr<Region> region = std::make_unique<Region>();
299   SmallVector<Type, 8> operandTypes, regionTypes;
300   if (parser.parseRegion(*region, regionOperands, regionTypes))
301     return failure();
302   result.addRegion(std::move(region));
303 
304   // Generic ops may specify that a subset of its outputs are tensors. Such
305   // outputs are specified in the result type.
306   // TODO: may need to move output parsing before region parsing.
307   // Need to wait for declarative assembly resolution to decide.
308   SmallVector<Type, 1> outputTensorsTypes;
309   if (parseNamedStructuredOpResults(parser, outputTensorsTypes))
310     return failure();
311   result.addTypes(outputTensorsTypes);
312 
313   return success();
314 }
315 
316 namespace {
317 template <typename GenericOpType>
318 struct BlockArgsVerifier {
319   static LogicalResult verify(GenericOpType op, Block &block);
320 };
321 
322 template <typename GenericOpType>
323 LogicalResult BlockArgsVerifier<GenericOpType>::verify(GenericOpType op,
324                                                        Block &block) {
325   auto nOperands = op.getNumOperands();
326   if (block.getNumArguments() != nOperands)
327     return op.emitOpError("expected number of block arguments to match number "
328                           "of operands");
329 
330   // Note: the number and type of yield values are checked in the YieldOp.
331   auto nInputViews = op.getNumInputs();
332   for (unsigned i = 0; i < nOperands; ++i) {
333     auto viewType = op.getShapedType(i);
334     if (viewType.getElementType() != block.getArgument(i).getType())
335       return op.emitOpError("expected block argument ")
336              << (i + 1) << " of the same type as elemental type of "
337              << ((i < nInputViews) ? "input " : "output ")
338              << "operand: " << viewType;
339   }
340   return success();
341 }
342 
343 template <>
344 LogicalResult BlockArgsVerifier<IndexedGenericOp>::verify(IndexedGenericOp op,
345                                                           Block &block) {
346   auto nInputViews = op.getNumInputs();
347   auto nLoops = op.getNumLoops();
348   auto nOperands = op.getNumOperands();
349   if (block.getNumArguments() != nOperands + nLoops)
350     return op.emitOpError(
351         "expected number of block arguments to match number of operands + "
352         "number of loops");
353 
354   // Note: the number and type of yield values are checked in the YieldOp.
355   for (unsigned i = 0; i < nLoops; ++i)
356     if (!block.getArgument(i).getType().isIndex())
357       return op.emitOpError("expected block argument ")
358              << (i + 1) << " to be an index";
359 
360   for (unsigned i = 0; i < nOperands; ++i) {
361     unsigned memrefArgIndex = i + nLoops;
362     auto viewType = op.getShapedType(i);
363     if (viewType.getElementType() !=
364         block.getArgument(memrefArgIndex).getType())
365       return op.emitOpError("expected block argument ")
366              << (memrefArgIndex + 1)
367              << " of the same type as elemental type of "
368              << ((i < nInputViews) ? "input " : "output ")
369              << "operand: " << viewType;
370   }
371   return success();
372 }
373 } // namespace
374 
375 template <typename GenericOpType>
376 static LogicalResult verifyGenericOp(GenericOpType op) {
377   auto nInputViews = op.getNumInputs();
378   auto nLoops = op.getNumLoops();
379 
380   if (op.inputs().size() + op.output_buffers().size() +
381           op.init_tensors().size() + op.getNumResults() ==
382       0)
383     return op.emitOpError("expected at least 1 Shaped operand or return");
384 
385   auto &region = op.region();
386   if (!llvm::hasSingleElement(region))
387     return op.emitOpError("expected region with 1 block");
388   if (failed(BlockArgsVerifier<GenericOpType>::verify(op, region.front())))
389     return failure();
390 
391   auto symbolSourceAttr =
392       op.template getAttrOfType<IntegerAttr>("symbol_source");
393   int64_t expectedNumSymbols = 0;
394   if (symbolSourceAttr) {
395     unsigned index = symbolSourceAttr.getInt();
396     if (index >= op.getNumOperands())
397       return op.emitOpError("symbol_source index out of range");
398     expectedNumSymbols = op.getShapedType(index).getRank();
399   }
400 
401   SmallVector<AffineMap, 4> indexingMaps;
402   indexingMaps.reserve(op.indexing_maps().size());
403   for (auto en : llvm::enumerate(op.indexing_maps())) {
404     auto idx = en.index();
405     auto m = en.value().template cast<AffineMapAttr>().getValue();
406     indexingMaps.push_back(m); // Save reference to map for further checks.
407     auto view = (idx < nInputViews) ? op.getInputShapedType(idx)
408                                     : op.getOutputShapedType(idx - nInputViews);
409 
410     if (m.getNumSymbols() != expectedNumSymbols)
411       return op.emitOpError("expected the number of symbols in indexing_map #")
412              << idx << " to match rank of operand `symbol_source`";
413 
414     if (m.getNumDims() != nLoops)
415       return op.emitOpError("expected indexing_map #")
416              << idx << " to have " << nLoops
417              << " dim(s) to match the number of loops";
418 
419     if (m.getNumResults() != view.getRank())
420       return op.emitOpError("expected indexing_map #")
421              << idx << " results to match view rank: " << view;
422   }
423 
424   auto concatMap = concatAffineMaps(indexingMaps);
425   // TODO: Bound inference for maps with symbols
426   if (!concatMap.getNumSymbols() && !inversePermutation(concatMap))
427     return op.emitOpError("expected the concatenation of maps in indexing_map "
428                           "to be invertible");
429 
430   return success();
431 }
432 
433 static LogicalResult verify(GenericOp op) { return verifyGenericOp(op); }
434 
435 static LogicalResult verify(IndexedGenericOp op) { return verifyGenericOp(op); }
436 
437 //===----------------------------------------------------------------------===//
438 // ReshapeOp
439 //===----------------------------------------------------------------------===//
440 
441 /// Collapse reassociation maps that are used in pair of reshape ops where one
442 /// is a producer and other is the consumer. Only valid to use this method when
443 /// both the producer and consumer are collapsing dimensions or both are
444 /// expanding dimensions.
445 ///
446 /// For example,
447 ///   mapsProducer = [affine_map<(d0, d1, d2, d3, d4) -> (d0, d1)>,
448 ///                   affine_map<(d0, d1, d2, d3, d4) -> (d2)>,
449 ///                   affine_map<(d0, d1, d2, d3, d4) -> (d3, d4)>]
450 ///   mapsConsumer = [affine_map<(d0, d1, d2) -> (d0, d1)>,
451 ///                   affine_map<(d0, d1, d2) -> (d2)>]
452 ///
453 /// is folded into
454 ///
455 ///   result = [affine_map<(d0, d1, d2, d3, d4) -> (d0, d1, d2)>,
456 ///             affine_map<(d0, d1, d2, d3, d4) -> (d3, d4)>]
457 static ArrayAttr collapseReassociationMaps(ArrayRef<AffineMap> mapsProducer,
458                                            ArrayRef<AffineMap> mapsConsumer,
459                                            MLIRContext *context) {
460   if (mapsProducer.empty() || mapsConsumer.empty() ||
461       mapsProducer[0].getNumDims() < mapsConsumer[0].getNumDims() ||
462       mapsProducer.size() != mapsConsumer[0].getNumDims())
463     return nullptr;
464   unsigned numLhsDims = mapsProducer[0].getNumDims();
465   unsigned currDim = 0;
466   SmallVector<AffineExpr, 4> reassociations;
467   SmallVector<Attribute, 4> reassociationMaps;
468   for (AffineMap rhs : mapsConsumer) {
469     for (AffineExpr rhsExpr : rhs.getResults()) {
470       AffineDimExpr dimExpr = rhsExpr.cast<AffineDimExpr>();
471       for (int i = 0, e = mapsProducer[dimExpr.getPosition()].getNumResults();
472            i < e; ++i) {
473         reassociations.push_back(getAffineDimExpr(currDim++, context));
474       }
475     }
476     reassociationMaps.push_back(AffineMapAttr::get(AffineMap::get(
477         numLhsDims, /*numSymbols =*/0, reassociations, context)));
478     reassociations.clear();
479   }
480   return ArrayAttr::get(reassociationMaps, context);
481 }
482 
483 namespace {
484 /// Pattern to collapse producer/consumer reshape ops that are both collapsing
485 /// dimensions or are both expanding dimensions.
486 template <typename ReshapeOpTy>
487 struct CollapseReshapeOps : public OpRewritePattern<ReshapeOpTy> {
488   using OpRewritePattern<ReshapeOpTy>::OpRewritePattern;
489   LogicalResult matchAndRewrite(ReshapeOpTy reshapeOp,
490                                 PatternRewriter &rewriter) const override {
491     auto srcReshapeOp = reshapeOp.src().template getDefiningOp<ReshapeOpTy>();
492     if (!srcReshapeOp)
493       return failure();
494 
495     auto areReshapeOpsFoldable = [](ShapedType largerType,
496                                     ShapedType intermediateType,
497                                     ShapedType smallerType) -> bool {
498       return largerType.getRank() > intermediateType.getRank() &&
499              intermediateType.getRank() > smallerType.getRank() &&
500              smallerType.getRank() > 0;
501     };
502     // Check if producer and consumer are both expanding dims.
503     if (areReshapeOpsFoldable(reshapeOp.getResultType(), reshapeOp.getSrcType(),
504                               srcReshapeOp.getSrcType())) {
505       rewriter.replaceOpWithNewOp<ReshapeOpTy>(
506           reshapeOp, reshapeOp.getResultType(), srcReshapeOp.src(),
507           collapseReassociationMaps(reshapeOp.getReassociationMaps(),
508                                     srcReshapeOp.getReassociationMaps(),
509                                     rewriter.getContext()));
510       return success();
511     }
512     // Check if producer and consumer are both collapsing dims.
513     else if (areReshapeOpsFoldable(srcReshapeOp.getSrcType(),
514                                    reshapeOp.getSrcType(),
515                                    reshapeOp.getResultType())) {
516       rewriter.replaceOpWithNewOp<ReshapeOpTy>(
517           reshapeOp, reshapeOp.getResultType(), srcReshapeOp.src(),
518           collapseReassociationMaps(srcReshapeOp.getReassociationMaps(),
519                                     reshapeOp.getReassociationMaps(),
520                                     rewriter.getContext()));
521       return success();
522     }
523     return failure();
524   }
525 };
526 } // namespace
527 
528 template <typename ReshapeOpTy>
529 static OpFoldResult foldReshapeOp(ReshapeOpTy reshapeOp,
530                                   ArrayRef<Attribute> operands) {
531   // Fold producer-consumer reshape ops that where the operand type of the
532   // producer is same as the return type of the consumer. This can only be
533   // verified if the shapes in question are static.
534   ReshapeOpTy reshapeSrcOp =
535       reshapeOp.src().template getDefiningOp<ReshapeOpTy>();
536   if (reshapeSrcOp && reshapeSrcOp.getSrcType().hasStaticShape() &&
537       reshapeOp.getResultType().hasStaticShape() &&
538       reshapeSrcOp.getSrcType() == reshapeOp.getResultType())
539     return reshapeSrcOp.src();
540   // Reshape of a constant can be replaced with a new constant.
541   if (auto elements = operands.front().dyn_cast_or_null<DenseElementsAttr>()) {
542     return elements.reshape(
543         reshapeOp.getResult().getType().template cast<ShapedType>());
544   }
545   return nullptr;
546 }
547 
548 /// Return true if the reassociation specification is valid, false otherwise.
549 /// When false, the `invalidIndex` integer pointer is optionally filled with the
550 /// index of the offending reassociation map.
551 static bool isReassociationValid(ArrayRef<AffineMap> reassociation,
552                                  int *invalidIndex = nullptr) {
553   if (reassociation.empty())
554     return true;
555   unsigned nDims = reassociation[0].getNumDims();
556   unsigned nextExpectedDim = 0;
557   for (auto it : llvm::enumerate(reassociation)) {
558     auto m = it.value();
559     if (m.getNumDims() != nDims || m.getNumSymbols() != 0) {
560       if (invalidIndex)
561         *invalidIndex = it.index();
562       return false;
563     }
564     for (auto e : m.getResults()) {
565       auto d = e.dyn_cast<AffineDimExpr>();
566       if (!d || d.getPosition() != nextExpectedDim++) {
567         if (invalidIndex)
568           *invalidIndex = it.index();
569         return false;
570       }
571     }
572   }
573   if (nextExpectedDim != nDims) {
574     if (invalidIndex)
575       *invalidIndex = reassociation.size() - 1;
576     return false;
577   }
578   return true;
579 }
580 
581 /// Detect whether memref dims [dim, dim + extent) can be reshaped without
582 /// copies.
583 static bool isReshapableDimBand(unsigned dim, unsigned extent,
584                                 ArrayRef<int64_t> sizes,
585                                 ArrayRef<AffineExpr> strides) {
586   assert(sizes.size() == strides.size() && "mismatched ranks");
587   // off by 1 indexing to avoid out of bounds
588   //                       V
589   for (auto idx = dim, e = dim + extent; idx + 1 < e; ++idx) {
590     // Only bands of static shapes are reshapable. This is due to the fact that
591     // there is no relation between dynamic sizes and dynamic strides: we do not
592     // have enough information to know whether a "-1" size corresponds to the
593     // proper symbol in the AffineExpr of a stride.
594     if (ShapedType::isDynamic(sizes[dim + 1]))
595       return false;
596     // TODO: Refine this by passing the proper nDims and nSymbols so we can
597     // simplify on the fly and catch more reshapable cases.
598     if (strides[idx] != strides[idx + 1] * sizes[idx + 1])
599       return false;
600   }
601   return true;
602 }
603 
604 /// Compute the MemRefType obtained by applying the `reassociation` (which is
605 /// expected to be valid) to `type`.
606 /// If `type` is Contiguous MemRefType, this always produce a contiguous
607 /// MemRefType.
608 static MemRefType
609 computeReshapeCollapsedType(MemRefType type,
610                             ArrayRef<AffineMap> reassociation) {
611   auto sizes = type.getShape();
612   AffineExpr offset;
613   SmallVector<AffineExpr, 4> strides;
614   auto status = getStridesAndOffset(type, strides, offset);
615   (void)status;
616   assert(succeeded(status) && "expected strided memref");
617 
618   SmallVector<int64_t, 4> newSizes;
619   newSizes.reserve(reassociation.size());
620   SmallVector<AffineExpr, 4> newStrides;
621   newStrides.reserve(reassociation.size());
622 
623   // Use the fact that reassociation is valid to simplify the logic: only use
624   // each map's rank.
625   assert(isReassociationValid(reassociation) && "invalid reassociation");
626   unsigned currentDim = 0;
627   for (AffineMap m : reassociation) {
628     unsigned dim = m.getNumResults();
629     int64_t size = 1;
630     AffineExpr stride = strides[currentDim + dim - 1];
631     if (!isReshapableDimBand(currentDim, dim, sizes, strides)) {
632       size = ShapedType::kDynamicSize;
633       stride = AffineExpr();
634     } else {
635       for (unsigned d = 0; d < dim; ++d)
636         size *= sizes[currentDim + d];
637     }
638     newSizes.push_back(size);
639     newStrides.push_back(stride);
640     currentDim += dim;
641   }
642 
643   // Early-exit: if `type` is contiguous, the result must be contiguous.
644   if (canonicalizeStridedLayout(type).getAffineMaps().empty())
645     return MemRefType::Builder(type).setShape(newSizes).setAffineMaps({});
646 
647   // Convert back to int64_t because we don't have enough information to create
648   // new strided layouts from AffineExpr only. This corresponds to a case where
649   // copies may be necessary.
650   int64_t intOffset = ShapedType::kDynamicStrideOrOffset;
651   if (auto o = offset.dyn_cast<AffineConstantExpr>())
652     intOffset = o.getValue();
653   SmallVector<int64_t, 4> intStrides;
654   intStrides.reserve(strides.size());
655   for (auto stride : newStrides) {
656     if (auto cst = stride.dyn_cast_or_null<AffineConstantExpr>())
657       intStrides.push_back(cst.getValue());
658     else
659       intStrides.push_back(ShapedType::kDynamicStrideOrOffset);
660   }
661   auto layout =
662       makeStridedLinearLayoutMap(intStrides, intOffset, type.getContext());
663   return canonicalizeStridedLayout(
664       MemRefType::Builder(type).setShape(newSizes).setAffineMaps({layout}));
665 }
666 
667 /// Helper functions assert Attribute of the proper type in attr and returns the
668 /// corresponding vector.
669 /// TODO: this should be evolved into a generic
670 /// `getRangeOfType<AffineMap>(ArrayAttr attrs)` that does not copy.
671 static SmallVector<AffineMap, 4> getAffineMaps(ArrayAttr attrs) {
672   return llvm::to_vector<8>(llvm::map_range(
673       attrs, [](Attribute a) { return a.cast<AffineMapAttr>().getValue(); }));
674 }
675 
676 template <typename AffineExprTy>
677 unsigned getMaxPosOfType(ArrayRef<ReassociationExprs> exprArrays) {
678   unsigned pos = 0;
679   for (const auto &exprs : exprArrays) {
680     for (auto expr : exprs) {
681       expr.walk([&pos](AffineExpr e) {
682         if (auto d = e.dyn_cast<AffineExprTy>())
683           pos = std::max(pos, d.getPosition());
684       });
685     }
686   }
687   return pos;
688 }
689 
690 static SmallVector<AffineMap, 4>
691 getSymbolLessAffineMaps(ArrayRef<ReassociationExprs> reassociation) {
692   unsigned maxDim = getMaxPosOfType<AffineDimExpr>(reassociation);
693   assert(getMaxPosOfType<AffineSymbolExpr>(reassociation) == 0 &&
694          "Expected symbol-less expressions");
695   SmallVector<AffineMap, 4> maps;
696   maps.reserve(reassociation.size());
697   for (const auto &exprs : reassociation) {
698     assert(!exprs.empty());
699     maps.push_back(AffineMap::get(maxDim + 1, 0, exprs, exprs[0].getContext()));
700   }
701   return maps;
702 }
703 
704 static SmallVector<SmallVector<AffineExpr, 2>, 2>
705 convertReassociationIndicesToMaps(
706     OpBuilder &b, ArrayRef<ReassociationIndices> reassociationIndices) {
707   SmallVector<SmallVector<AffineExpr, 2>, 2> reassociationMaps;
708   for (const auto &indicies : reassociationIndices) {
709     SmallVector<AffineExpr, 2> reassociationMap;
710     reassociationMap.reserve(indicies.size());
711     for (int64_t index : indicies)
712       reassociationMap.push_back(b.getAffineDimExpr(index));
713     reassociationMaps.push_back(std::move(reassociationMap));
714   }
715   return reassociationMaps;
716 }
717 
718 void mlir::linalg::ReshapeOp::build(OpBuilder &b, OperationState &result,
719                                     Value src,
720                                     ArrayRef<ReassociationExprs> reassociation,
721                                     ArrayRef<NamedAttribute> attrs) {
722   auto maps = getSymbolLessAffineMaps(reassociation);
723   auto memRefType = src.getType().cast<MemRefType>();
724   auto resultType = computeReshapeCollapsedType(memRefType, maps);
725   build(b, result, resultType, src, attrs);
726   result.addAttribute(ReshapeOp::getReassociationAttrName(),
727                       b.getAffineMapArrayAttr(maps));
728 }
729 
730 void mlir::linalg::ReshapeOp::build(OpBuilder &b, OperationState &result,
731                                     Type resultType, Value src,
732                                     ArrayRef<ReassociationExprs> reassociation,
733                                     ArrayRef<NamedAttribute> attrs) {
734   auto maps = getSymbolLessAffineMaps(reassociation);
735   build(b, result, resultType, src, attrs);
736   result.addAttribute(ReshapeOp::getReassociationAttrName(),
737                       b.getAffineMapArrayAttr(maps));
738 }
739 
740 Value mlir::linalg::ReshapeOp::getViewSource() { return src(); }
741 
742 // Common verifier for reshape-like types. Fills `expandedType` and
743 // `collapsedType` with the proper `src` or `result` type.
744 template <typename Op, typename T>
745 static LogicalResult verifyReshapeLikeTypes(Op op, T &expandedType,
746                                             T &collapsedType) {
747   expandedType = op.getSrcType();
748   collapsedType = op.getResultType();
749   unsigned expandedRank = expandedType.getRank();
750   unsigned collapsedRank = collapsedType.getRank();
751   bool isCollapse = expandedRank > collapsedRank;
752   if (!isCollapse) {
753     std::swap(expandedRank, collapsedRank);
754     std::swap(expandedType, collapsedType);
755   }
756   if (expandedRank == 0)
757     return op.emitOpError("expected non-zero memref ranks");
758   if (expandedRank == collapsedRank)
759     return op.emitOpError("expected to collapse or expand dims");
760 
761   if (collapsedRank == 0) {
762     // If collapsed rank is 0, then expanded type must be static shaped and of
763     // sizes 1.
764     if (llvm::any_of(expandedType.getShape(),
765                      [](int64_t dim) -> bool { return dim != 1; }))
766       return op.emitOpError(
767           "invalid to reshape tensor/memref with non-unit extent dimensions to "
768           "zero-rank tensor/memref");
769     return success();
770   }
771   if (collapsedRank != op.reassociation().size())
772     return op.emitOpError("expected rank of the collapsed type(")
773            << collapsedRank << ") to be the number of reassociation maps("
774            << op.reassociation().size() << ")";
775   auto maps = getAffineMaps(op.reassociation());
776   for (auto it : llvm::enumerate(maps))
777     if (it.value().getNumDims() != expandedRank)
778       return op.emitOpError("expected reassociation map #")
779              << it.index() << " of same rank as expanded memref("
780              << expandedRank << "), but got " << it.value().getNumDims();
781   int invalidIdx = 0;
782   if (!isReassociationValid(maps, &invalidIdx))
783     return op.emitOpError("expected reassociation map #")
784            << invalidIdx << " to be valid and contiguous";
785   return success();
786 }
787 
788 static LogicalResult verify(ReshapeOp op) {
789   MemRefType expandedType, collapsedType;
790   if (failed(verifyReshapeLikeTypes(op, expandedType, collapsedType)))
791     return failure();
792   auto maps = getAffineMaps(op.reassociation());
793   MemRefType expectedType = computeReshapeCollapsedType(expandedType, maps);
794   if (collapsedType != expectedType)
795     return op.emitOpError("expected collapsed type to be ")
796            << expectedType << ", but got " << collapsedType;
797   return success();
798 }
799 
800 void ReshapeOp::getCanonicalizationPatterns(OwningRewritePatternList &results,
801                                             MLIRContext *context) {
802   results.insert<CollapseReshapeOps<ReshapeOp>>(context);
803 }
804 
805 //===----------------------------------------------------------------------===//
806 // TensorReshapeOp
807 //===----------------------------------------------------------------------===//
808 
809 /// Compute the RankedTensorType obtained by applying `reassociation` to `type`.
810 static RankedTensorType
811 computeTensorReshapeCollapsedType(RankedTensorType type,
812                                   ArrayRef<AffineMap> reassociation) {
813   auto shape = type.getShape();
814   SmallVector<int64_t, 4> newShape;
815   newShape.reserve(reassociation.size());
816 
817   // Use the fact that reassociation is valid to simplify the logic: only use
818   // each map's rank.
819   assert(isReassociationValid(reassociation) && "invalid reassociation");
820   unsigned currentDim = 0;
821   for (AffineMap m : reassociation) {
822     unsigned dim = m.getNumResults();
823     auto band = shape.slice(currentDim, dim);
824     int64_t size = 1;
825     if (llvm::is_contained(band, ShapedType::kDynamicSize))
826       size = ShapedType::kDynamicSize;
827     else
828       for (unsigned d = 0; d < dim; ++d)
829         size *= shape[currentDim + d];
830     newShape.push_back(size);
831     currentDim += dim;
832   }
833 
834   return RankedTensorType::get(newShape, type.getElementType());
835 }
836 
837 void mlir::linalg::TensorReshapeOp::build(
838     OpBuilder &b, OperationState &result, Value src,
839     ArrayRef<ReassociationExprs> reassociation,
840     ArrayRef<NamedAttribute> attrs) {
841   auto maps = getSymbolLessAffineMaps(reassociation);
842   auto resultType = computeTensorReshapeCollapsedType(
843       src.getType().cast<RankedTensorType>(), maps);
844   build(b, result, resultType, src, attrs);
845   result.addAttribute(TensorReshapeOp::getReassociationAttrName(),
846                       b.getAffineMapArrayAttr(maps));
847 }
848 
849 void mlir::linalg::TensorReshapeOp::build(
850     OpBuilder &b, OperationState &result, Type resultType, Value src,
851     ArrayRef<ReassociationExprs> reassociation,
852     ArrayRef<NamedAttribute> attrs) {
853   auto maps = getSymbolLessAffineMaps(reassociation);
854   build(b, result, resultType, src, attrs);
855   result.addAttribute(TensorReshapeOp::getReassociationAttrName(),
856                       b.getAffineMapArrayAttr(maps));
857 }
858 
859 static LogicalResult verify(TensorReshapeOp op) {
860   RankedTensorType expandedType, collapsedType;
861   if (failed(verifyReshapeLikeTypes(op, expandedType, collapsedType)))
862     return failure();
863   auto maps = getAffineMaps(op.reassociation());
864   // TODO: expanding a ? with a non-constant is under-specified. Error
865   // out.
866   RankedTensorType expectedType =
867       computeTensorReshapeCollapsedType(expandedType, maps);
868   if (collapsedType != expectedType)
869     return op.emitOpError("expected collapsed type to be ")
870            << expectedType << ", but got " << collapsedType;
871   return success();
872 }
873 
874 namespace {
875 /// Reshape of a splat constant can be replaced with a constant of the result
876 /// type.
877 struct FoldReshapeWithConstant : OpRewritePattern<TensorReshapeOp> {
878   using OpRewritePattern<TensorReshapeOp>::OpRewritePattern;
879   LogicalResult matchAndRewrite(TensorReshapeOp reshapeOp,
880                                 PatternRewriter &rewriter) const override {
881     DenseElementsAttr attr;
882     if (!matchPattern(reshapeOp.src(), m_Constant(&attr)))
883       return failure();
884     if (!attr || !attr.isSplat())
885       return failure();
886     DenseElementsAttr newAttr = DenseElementsAttr::getFromRawBuffer(
887         reshapeOp.getResultType(), attr.getRawData(), true);
888     rewriter.replaceOpWithNewOp<ConstantOp>(reshapeOp, newAttr);
889     return success();
890   }
891 };
892 } // namespace
893 
894 void TensorReshapeOp::getCanonicalizationPatterns(
895     OwningRewritePatternList &results, MLIRContext *context) {
896   results.insert<CollapseReshapeOps<TensorReshapeOp>, FoldReshapeWithConstant>(
897       context);
898 }
899 
900 //===----------------------------------------------------------------------===//
901 // SliceOp
902 //===----------------------------------------------------------------------===//
903 void mlir::linalg::SliceOp::build(OpBuilder &b, OperationState &result,
904                                   Value base, ValueRange indexings) {
905   result.addOperands(base);
906   result.addOperands(indexings);
907 
908   auto memRefType = base.getType().cast<MemRefType>();
909   int64_t offset;
910   SmallVector<int64_t, 4> strides;
911   auto res = getStridesAndOffset(memRefType, strides, offset);
912   assert(succeeded(res) && strides.size() == indexings.size());
913   (void)res;
914 
915   unsigned rank = memRefType.getRank();
916   // TODO: propagate static size and stride information when available.
917   SmallVector<int64_t, 4> sizes(rank, -1); // -1 encodes dynamic size.
918   result.addTypes({MemRefType::Builder(memRefType)
919                        .setShape(sizes)
920                        .setAffineMaps(makeStridedLinearLayoutMap(
921                            strides, offset, b.getContext()))});
922 }
923 
924 static void print(OpAsmPrinter &p, SliceOp op) {
925   auto indexings = op.indexings();
926   p << SliceOp::getOperationName() << " " << op.view() << "[" << indexings
927     << "] ";
928   p.printOptionalAttrDict(op.getAttrs());
929   p << " : " << op.getBaseViewType();
930   if (!indexings.empty())
931     p << ", " << op.indexings().getTypes();
932   p << ", " << op.getType();
933 }
934 
935 static ParseResult parseSliceOp(OpAsmParser &parser, OperationState &result) {
936   OpAsmParser::OperandType baseInfo;
937   SmallVector<OpAsmParser::OperandType, 8> operands;
938   SmallVector<Type, 8> types;
939   if (parser.parseOperand(baseInfo) ||
940       parser.parseOperandList(operands, OpAsmParser::Delimiter::Square) ||
941       parser.parseOptionalAttrDict(result.attributes) ||
942       parser.parseColonTypeList(types))
943     return failure();
944 
945   if (types.size() < 2)
946     return parser.emitError(parser.getCurrentLocation(),
947                             "expected at least input and result view types");
948 
949   ArrayRef<Type> indexingTypes = ArrayRef<Type>(types).drop_front().drop_back();
950   return failure(
951       parser.resolveOperand(baseInfo, types.front(), result.operands) ||
952       (!operands.empty() &&
953        parser.resolveOperands(operands, indexingTypes,
954                               operands.front().location, result.operands)) ||
955       parser.addTypeToList(types.back(), result.types));
956 }
957 
958 static LogicalResult verify(SliceOp op) {
959   unsigned rank = op.getBaseViewRank();
960   if (rank != llvm::size(op.indexings()))
961     return op.emitOpError("expected ")
962            << rank << " indexings, got " << llvm::size(op.indexings());
963   unsigned index = 0;
964   for (auto indexing : op.indexings()) {
965     if (indexing.getType().isa<IndexType>())
966       --rank;
967     ++index;
968   }
969   if (op.getRank() != rank)
970     return op.emitOpError() << "expected rank of the view(" << op.getRank()
971                             << ") to be the number of ranges(" << rank << ")";
972   return success();
973 }
974 
975 Value SliceOp::getViewSource() { return view(); }
976 
977 //===----------------------------------------------------------------------===//
978 // YieldOp
979 //===----------------------------------------------------------------------===//
980 
981 static void print(OpAsmPrinter &p, linalg::YieldOp op) {
982   p << op.getOperationName();
983   if (op.getNumOperands() > 0)
984     p << ' ' << op.getOperands();
985   p.printOptionalAttrDict(op.getAttrs());
986   if (op.getNumOperands() > 0)
987     p << " : " << op.getOperandTypes();
988 }
989 
990 static ParseResult parseYieldOp(OpAsmParser &parser, OperationState &result) {
991   SmallVector<OpAsmParser::OperandType, 2> opInfo;
992   SmallVector<Type, 2> types;
993   llvm::SMLoc loc = parser.getCurrentLocation();
994   return failure(parser.parseOperandList(opInfo) ||
995                  parser.parseOptionalAttrDict(result.attributes) ||
996                  (!opInfo.empty() && parser.parseColonTypeList(types)) ||
997                  parser.resolveOperands(opInfo, types, loc, result.operands));
998 }
999 
1000 // Check the operand number and types must match the element types of the
1001 // LinalgOp interface's shaped operands.
1002 static LogicalResult verifyYield(linalg::YieldOp op,
1003                                  LinalgOp linalgOpInterface) {
1004   auto nOutputs = linalgOpInterface.getNumOutputs();
1005   if (op.getNumOperands() != nOutputs)
1006     return op.emitOpError("expected number of yield values (")
1007            << nOutputs << ") to match the number of operands of the enclosing "
1008            << "LinalgOp (" << op.getNumOperands() << ")";
1009 
1010   for (unsigned i = 0; i != nOutputs; ++i) {
1011     auto elementType =
1012         linalgOpInterface.getOutputShapedType(i).getElementType();
1013     if (op.getOperand(i).getType() != elementType)
1014       return op.emitOpError("type of yield operand ")
1015              << (i + 1) << " (" << op.getOperand(i).getType()
1016              << ") doesn't match "
1017              << "the element type of the enclosing linalg.generic op ("
1018              << elementType << ")";
1019   }
1020   return success();
1021 }
1022 
1023 static LogicalResult verify(linalg::YieldOp op) {
1024   auto *parentOp = op.getParentOp();
1025   if (parentOp->getNumRegions() != 1 || parentOp->getRegion(0).empty())
1026     return op.emitOpError("expected single non-empty parent region");
1027 
1028   if (auto linalgOp = dyn_cast<LinalgOp>(parentOp))
1029     return verifyYield(op, cast<LinalgOp>(parentOp));
1030 
1031   return op.emitOpError("expected parent op with LinalgOp interface");
1032 }
1033 
1034 /////// Operations corresponding to library calls defined with Tablegen ////////
1035 
1036 static LogicalResult verify(FillOp op) {
1037   auto viewType = op.getOutputShapedType(0);
1038   auto fillType = op.value().getType();
1039   if (viewType.getElementType() != fillType)
1040     return op.emitOpError("expects fill type to match view elemental type");
1041   return success();
1042 }
1043 
1044 static LogicalResult verify(CopyOp op) {
1045   auto outputViewType = op.getOutputShapedType(0);
1046   auto inputViewType = op.getInputShapedType(0);
1047   if (inputViewType.getElementType() != outputViewType.getElementType())
1048     return op.emitOpError("expects views of the same type");
1049   if (inputViewType.getRank() != outputViewType.getRank())
1050     return op.emitOpError("expects views of the same rank");
1051   auto rank = op.getNumParallelLoops();
1052   auto inputPermutationMap = op.inputPermutation();
1053   if (inputPermutationMap) {
1054     if (inputPermutationMap->getNumInputs() != rank)
1055       return op.emitOpError("expects optional input_permutation map of rank ")
1056              << rank;
1057     if (!inputPermutationMap->isPermutation())
1058       return op.emitOpError(
1059           "expects optional input_permutation map to be a permutation");
1060   }
1061   auto outputPermutationMap = op.outputPermutation();
1062   if (outputPermutationMap) {
1063     if (outputPermutationMap->getNumInputs() != rank)
1064       return op.emitOpError("expects optional output_permutation map of rank ")
1065              << rank;
1066     if (!outputPermutationMap->isPermutation())
1067       return op.emitOpError(
1068           "expects optional output_permutation map to be a permutation");
1069   }
1070   if (rank == 0 && inputPermutationMap)
1071     return op.emitOpError("expected no input permutation when rank == 0");
1072   if (rank == 0 && outputPermutationMap)
1073     return op.emitOpError("expected no output permutation when rank == 0");
1074   return success();
1075 }
1076 
1077 template <typename LinalgPoolingOp>
1078 static LogicalResult verifyStrideOrDilation(LinalgPoolingOp op,
1079                                             ArrayRef<Attribute> attrs,
1080                                             bool isStride) {
1081   auto strideOrDilation = isStride ? "stride" : "dilation";
1082   if (attrs.size() != op.getNumWindowLoops())
1083     return op.emitOpError("expects num ")
1084            << strideOrDilation
1085            << "s equal to number of window dimensions: " << attrs.size()
1086            << " vs " << op.getNumWindowLoops();
1087   return success();
1088 }
1089 
1090 static LogicalResult verify(ConvOp op) {
1091   auto oType = op.output().getType().cast<MemRefType>();
1092   auto fType = op.filter().getType().cast<MemRefType>();
1093   auto iType = op.input().getType().cast<MemRefType>();
1094   if (oType.getElementType() != iType.getElementType() ||
1095       oType.getElementType() != fType.getElementType())
1096     return op.emitOpError("expects memref elemental types to match");
1097   if (oType.getRank() != iType.getRank() || oType.getRank() != fType.getRank())
1098     return op.emitOpError("expects memref ranks to match");
1099   if (oType.getRank() <= 2)
1100     return op.emitOpError("expects memref ranks to be greater than 2");
1101   if (auto strides = op.strides()) {
1102     if (failed(
1103             verifyStrideOrDilation(op, strides->getValue(), /*isStride=*/true)))
1104       return failure();
1105   }
1106   if (auto dilations = op.dilations()) {
1107     if (failed(verifyStrideOrDilation(op, dilations->getValue(),
1108                                       /*isStride=*/false)))
1109       return failure();
1110   }
1111   return success();
1112 }
1113 
1114 template <typename PoolingOp>
1115 static LogicalResult verifySingleInputPoolingOp(PoolingOp op) {
1116   auto inputType = op.input().getType().template cast<MemRefType>();
1117   auto outputType = op.output().getType().template cast<MemRefType>();
1118   if (outputType.getElementType() != inputType.getElementType())
1119     return op.emitOpError("expects memref elemental types to match");
1120 
1121   auto windowDimsType = op.windowDims().getType().template cast<MemRefType>();
1122   if (outputType.getRank() != inputType.getRank() ||
1123       outputType.getRank() != windowDimsType.getRank())
1124     return op.emitOpError("expects memref ranks to match");
1125 
1126   if (auto strides = op.strides()) {
1127     if (failed(
1128             verifyStrideOrDilation(op, strides->getValue(), /*isStride=*/true)))
1129       return failure();
1130   }
1131   if (auto dilations = op.dilations()) {
1132     if (failed(verifyStrideOrDilation(op, dilations->getValue(),
1133                                       /*isStride=*/false)))
1134       return failure();
1135   }
1136   return success();
1137 }
1138 
1139 static LogicalResult verify(PoolingMaxOp op) {
1140   return verifySingleInputPoolingOp(op);
1141 }
1142 static LogicalResult verify(PoolingMinOp op) {
1143   return verifySingleInputPoolingOp(op);
1144 }
1145 static LogicalResult verify(PoolingSumOp op) {
1146   return verifySingleInputPoolingOp(op);
1147 }
1148 
1149 #include "mlir/Dialect/Linalg/IR/LinalgStructuredOpsInterfaces.cpp.inc"
1150 
1151 #include "mlir/Dialect/Linalg/IR/LinalgNamedStructuredOps.cpp.inc"
1152 
1153 #define GET_OP_CLASSES
1154 #include "mlir/Dialect/Linalg/IR/LinalgOps.cpp.inc"
1155 
1156 #define GET_OP_CLASSES
1157 #include "mlir/Dialect/Linalg/IR/LinalgStructuredOps.cpp.inc"
1158 
1159 /// Return the dims that are `iteratorTypeName` loops in the LinalgOp `op`.
1160 /// Assumes `op` is a LinalgOp.
1161 void mlir::linalg::getDimsOfType(Operation *op, StringRef iteratorTypeName,
1162                                  SmallVectorImpl<AffineExpr> &res) {
1163   if (!cast<LinalgOp>(op).iterator_types())
1164     return;
1165 
1166   unsigned dim = 0;
1167   MLIRContext *ctx = op->getContext();
1168   for (auto tn :
1169        cast<LinalgOp>(op).iterator_types().getAsValueRange<StringAttr>()) {
1170     if (tn == iteratorTypeName)
1171       res.push_back(getAffineDimExpr(dim, ctx));
1172     ++dim;
1173   }
1174 }
1175 
1176 AffineMap mlir::linalg::extractOrIdentityMap(Optional<AffineMap> maybeMap,
1177                                              unsigned rank,
1178                                              MLIRContext *context) {
1179   if (maybeMap)
1180     return maybeMap.getValue();
1181   if (rank == 0)
1182     return AffineMap::get(context);
1183   return AffineMap::getMultiDimIdentityMap(rank, context);
1184 }
1185 
1186 SmallVector<AffineExpr, 4>
1187 mlir::linalg::makeAffineDimExprs(unsigned num, unsigned &startIdx,
1188                                  MLIRContext *context) {
1189   SmallVector<AffineExpr, 4> res;
1190   res.reserve(num);
1191   for (unsigned i = 0; i < num; ++i)
1192     res.push_back(getAffineDimExpr(startIdx++, context));
1193   return res;
1194 }
1195 
1196 template <typename PoolingOp>
1197 SmallVector<AffineExpr, 4>
1198 mlir::linalg::weightedPoolingInputIndex(PoolingOp op,
1199                                         ArrayRef<AffineExpr> outputDims,
1200                                         ArrayRef<AffineExpr> windowDims) {
1201   assert(outputDims.size() == windowDims.size());
1202   SmallVector<AffineExpr, 4> res;
1203   res.reserve(outputDims.size());
1204   for (unsigned i = 0, e = outputDims.size(); i < e; ++i) {
1205     // TODO: add a level of indirection to linalg.generic.
1206     auto expr = op.getStride(i) * outputDims[i] +
1207                 op.getDilation(i) * windowDims[i] - op.getLowPad(i);
1208     res.push_back(expr);
1209   }
1210   return res;
1211 }
1212 
1213 #define INSTANTIATE_WEIGHTED_POOLING_INPUT_INDEX(OP_TYPE)                      \
1214   template SmallVector<AffineExpr, 4>                                          \
1215   mlir::linalg::weightedPoolingInputIndex<OP_TYPE>(                            \
1216       OP_TYPE op, ArrayRef<AffineExpr> outputDims,                             \
1217       ArrayRef<AffineExpr> windowDims);
1218 
1219 INSTANTIATE_WEIGHTED_POOLING_INPUT_INDEX(ConvOp)
1220 INSTANTIATE_WEIGHTED_POOLING_INPUT_INDEX(PoolingMaxOp)
1221 INSTANTIATE_WEIGHTED_POOLING_INPUT_INDEX(PoolingMinOp)
1222 INSTANTIATE_WEIGHTED_POOLING_INPUT_INDEX(PoolingSumOp)
1223 
1224 SmallVector<AffineExpr, 4> mlir::linalg::concat(ArrayRef<AffineExpr> a,
1225                                                 ArrayRef<AffineExpr> b) {
1226   auto rangeA = llvm::make_range(a.begin(), a.end());
1227   auto rangeB = llvm::make_range(b.begin(), b.end());
1228   auto concatRanges = llvm::concat<const AffineExpr>(rangeA, rangeB);
1229   return llvm::to_vector<4>(concatRanges);
1230 }
1231 
1232 static void appendMangledType(llvm::raw_string_ostream &ss, Type t) {
1233   if (auto memref = t.dyn_cast<MemRefType>()) {
1234     ss << "view";
1235     for (auto size : memref.getShape())
1236       if (size < 0)
1237         ss << "sx";
1238       else
1239         ss << size << "x";
1240     appendMangledType(ss, memref.getElementType());
1241   } else if (auto vec = t.dyn_cast<VectorType>()) {
1242     ss << "vector";
1243     llvm::interleave(
1244         vec.getShape(), [&](int64_t i) { ss << i; }, [&]() { ss << "x"; });
1245     appendMangledType(ss, vec.getElementType());
1246   } else if (t.isSignlessIntOrIndexOrFloat()) {
1247     ss << t;
1248   } else {
1249     llvm_unreachable("Invalid type for linalg library name mangling");
1250   }
1251 }
1252 
1253 std::string mlir::linalg::generateLibraryCallName(Operation *op) {
1254   assert(isa<LinalgOp>(op));
1255   std::string name(op->getName().getStringRef().str());
1256   name.reserve(128);
1257   std::replace(name.begin(), name.end(), '.', '_');
1258   llvm::raw_string_ostream ss(name);
1259   ss << "_";
1260   auto types = op->getOperandTypes();
1261   llvm::interleave(
1262       types.begin(), types.end(), [&](Type t) { appendMangledType(ss, t); },
1263       [&]() { ss << "_"; });
1264   return ss.str();
1265 }
1266 
1267 // TODO: Consider making all this boilerplate easy to autogenerate
1268 // with Tablegen. This seems a desirable property in the context of
1269 // OpInterfaces where a Linalg "named" op **isa** LinalgOp.
1270 OpFoldResult ReshapeOp::fold(ArrayRef<Attribute> operands) {
1271   if (succeeded(foldMemRefCast(*this)))
1272     return getResult();
1273   return foldReshapeOp(*this, operands);
1274 }
1275 OpFoldResult SliceOp::fold(ArrayRef<Attribute>) {
1276   if (succeeded(foldMemRefCast(*this)))
1277     return getResult();
1278   return {};
1279 }
1280 OpFoldResult TensorReshapeOp::fold(ArrayRef<Attribute> operands) {
1281   return foldReshapeOp(*this, operands);
1282 }
1283 
1284 //===----------------------------------------------------------------------===//
1285 // Auto-generated Linalg named ops.
1286 //===----------------------------------------------------------------------===//
1287 
1288 template <typename NamedStructuredOpType>
1289 static void buildNamedStructuredOpRegionAndAttributesImpl(
1290     OpBuilder &opBuilder, Region &region, TypeRange inputTypes,
1291     TypeRange outputBufferTypes, TypeRange initTensorTypes,
1292     TypeRange resultTypes,
1293     std::function<void(unsigned, unsigned)> errorHandler) {
1294   // TODO: atm all operands go through getElementTypeOrSelf,
1295   // reconsider when we have evidence we need to.
1296   SmallVector<Type, 8> argTypes;
1297   for (auto containers : {inputTypes, outputBufferTypes, resultTypes})
1298     for (auto t : containers)
1299       argTypes.push_back(getElementTypeOrSelf(t));
1300 
1301   // RAII.
1302   OpBuilder::InsertionGuard guard(opBuilder);
1303   Block *body = opBuilder.createBlock(&region, {}, argTypes);
1304   unsigned actual = body->getNumArguments();
1305   unsigned expected = NamedStructuredOpType::getNumRegionArgs();
1306   if (expected != actual)
1307     return errorHandler(expected, actual);
1308 
1309   opBuilder.setInsertionPointToStart(body);
1310   mlir::edsc::ScopedContext scope(opBuilder, opBuilder.getUnknownLoc());
1311   NamedStructuredOpType::regionBuilder(*body);
1312 
1313   // indexing_maps is an auto-generated method.
1314 
1315   // iterator_types is an auto-generated method.
1316 }
1317 
1318 template <typename NamedStructuredOpType>
1319 void buildNamedStructuredOpRegionAndAttributes(OpBuilder &opBuilder,
1320                                                OperationState &result,
1321                                                TypeRange inputTypes,
1322                                                TypeRange outputBufferTypes,
1323                                                TypeRange initTensorTypes,
1324                                                TypeRange resultTypes) {
1325   Region &region = *result.addRegion();
1326   buildNamedStructuredOpRegionAndAttributesImpl<NamedStructuredOpType>(
1327       opBuilder, region, inputTypes, outputBufferTypes, initTensorTypes,
1328       resultTypes, [&](unsigned expected, unsigned actual) {
1329         llvm::errs() << "region expects " << expected << " args, got "
1330                      << actual;
1331         assert(expected != actual && "incorrect number of arguments");
1332       });
1333 }
1334 
1335 template <typename NamedStructuredOpType>
1336 static ParseResult
1337 parseNamedStructuredOpRegion(OpAsmParser &parser, Region &region,
1338                              TypeRange inputTypes, TypeRange outputBufferTypes,
1339                              TypeRange initTensorTypes, TypeRange resultTypes) {
1340   ParseResult res = success();
1341   OpBuilder opBuilder(parser.getBuilder().getContext());
1342   buildNamedStructuredOpRegionAndAttributesImpl<NamedStructuredOpType>(
1343       opBuilder, region, inputTypes, outputBufferTypes, initTensorTypes,
1344       resultTypes, [&](unsigned expected, unsigned actual) {
1345         res = parser.emitError(parser.getCurrentLocation(),
1346                                llvm::formatv("region expects {0} args, got {1}",
1347                                              expected, actual));
1348       });
1349   return res;
1350 }
1351 
1352 static ParseResult
1353 parseNamedStructuredOpResults(OpAsmParser &parser,
1354                               SmallVectorImpl<Type> &resultTypes) {
1355   if (succeeded(parser.parseOptionalArrow()))
1356     if (parser.parseTypeList(resultTypes))
1357       return failure();
1358   return success();
1359 }
1360 
1361 static ParseResult
1362 parseCommonStructuredOpParts(OpAsmParser &parser, OperationState &result,
1363                              SmallVectorImpl<Type> &inputTypes,
1364                              SmallVectorImpl<Type> &outputBufferTypes,
1365                              SmallVectorImpl<Type> &initTensorTypes) {
1366   llvm::SMLoc inputsOperandsLoc, outputBuffersOperandsLoc,
1367       initTensorsOperandsLoc;
1368   SmallVector<OpAsmParser::OperandType, 4> inputsOperands,
1369       outputBuffersOperands, initTensorsOperands;
1370 
1371   parser.parseOptionalAttrDict(result.attributes);
1372 
1373   if (succeeded(parser.parseOptionalKeyword("ins"))) {
1374     if (parser.parseLParen())
1375       return failure();
1376 
1377     inputsOperandsLoc = parser.getCurrentLocation();
1378     if (parser.parseOperandList(inputsOperands) ||
1379         parser.parseColonTypeList(inputTypes) || parser.parseRParen())
1380       return failure();
1381   }
1382 
1383   if (succeeded(parser.parseOptionalKeyword("outs"))) {
1384     outputBuffersOperandsLoc = parser.getCurrentLocation();
1385     if (parser.parseLParen() ||
1386         parser.parseOperandList(outputBuffersOperands) ||
1387         parser.parseColonTypeList(outputBufferTypes) || parser.parseRParen())
1388       return failure();
1389   }
1390   if (succeeded(parser.parseOptionalKeyword("init"))) {
1391     initTensorsOperandsLoc = parser.getCurrentLocation();
1392     if (parser.parseLParen() || parser.parseOperandList(initTensorsOperands) ||
1393         parser.parseColonTypeList(initTensorTypes) || parser.parseRParen())
1394       return failure();
1395   }
1396 
1397   if (parser.resolveOperands(inputsOperands, inputTypes, inputsOperandsLoc,
1398                              result.operands) ||
1399       parser.resolveOperands(outputBuffersOperands, outputBufferTypes,
1400                              outputBuffersOperandsLoc, result.operands) ||
1401       parser.resolveOperands(initTensorsOperands, initTensorTypes,
1402                              initTensorsOperandsLoc, result.operands))
1403     return failure();
1404 
1405   result.addAttribute("operand_segment_sizes",
1406                       parser.getBuilder().getI32VectorAttr(
1407                           {static_cast<int32_t>(inputsOperands.size()),
1408                            static_cast<int32_t>(outputBuffersOperands.size()),
1409                            static_cast<int32_t>(initTensorsOperands.size())}));
1410   return success();
1411 }
1412 
1413 template <typename NamedStructuredOpType>
1414 static ParseResult parseNamedStructuredOp(OpAsmParser &parser,
1415                                           OperationState &result) {
1416   SmallVector<Type, 1> inputTypes, outputBufferTypes, initTensorTypes;
1417   if (parseCommonStructuredOpParts(parser, result, inputTypes,
1418                                    outputBufferTypes, initTensorTypes))
1419     return failure();
1420 
1421   // TODO: consider merging results parsing into region parsing.
1422   // Need to wait for declarative assembly resolution to decide.
1423   SmallVector<Type, 1> outputTensorsTypes;
1424   if (parseNamedStructuredOpResults(parser, outputTensorsTypes))
1425     return failure();
1426   result.addTypes(outputTensorsTypes);
1427 
1428   std::unique_ptr<Region> region = std::make_unique<Region>();
1429   if (parseNamedStructuredOpRegion<NamedStructuredOpType>(
1430           parser, *region, inputTypes, outputBufferTypes, initTensorTypes,
1431           outputTensorsTypes))
1432     return failure();
1433   result.addRegion(std::move(region));
1434 
1435   return success();
1436 }
1437 
1438 static void printNamedStructuredOpResults(OpAsmPrinter &p,
1439                                           TypeRange resultTypes) {
1440   if (resultTypes.empty())
1441     return;
1442   p.printOptionalArrowTypeList(resultTypes);
1443 }
1444 
1445 template <typename NamedStructuredOpType>
1446 static void printCommonStructuredOpParts(OpAsmPrinter &p,
1447                                          NamedStructuredOpType op) {
1448   p << " ins(" << op.inputs() << " : " << op.inputs().getTypes() << ")";
1449   if (!op.output_buffers().empty())
1450     p << " outs(" << op.output_buffers() << " : "
1451       << op.output_buffers().getTypes() << ")";
1452   if (!op.init_tensors().empty())
1453     p << " init(" << op.init_tensors() << " : " << op.init_tensors().getTypes()
1454       << ") ";
1455 }
1456 
1457 template <typename NamedStructuredOpType>
1458 static void printNamedStructuredOp(OpAsmPrinter &p, NamedStructuredOpType op) {
1459   p << op.getOperationName();
1460   p.printOptionalAttrDict(op.getAttrs(),
1461                           /*elidedAttrs=*/{"operand_segment_sizes"});
1462 
1463   // Printing is shared with generic ops, except for the region and attributes.
1464   printCommonStructuredOpParts(p, op);
1465 
1466   // Results printing.
1467   printNamedStructuredOpResults(p, op.result_tensors().getTypes());
1468 
1469   // Region is elided.
1470 }
1471 
1472 template <typename NamedStructuredOpType>
1473 static LogicalResult verifyNamedStructuredOp(NamedStructuredOpType op) {
1474   return verifyGenericOp<NamedStructuredOpType>(op);
1475 }
1476 
1477 namespace {
1478 struct EraseDeadLinalgOp : public RewritePattern {
1479   EraseDeadLinalgOp(PatternBenefit benefit = 1)
1480       : RewritePattern(benefit, MatchAnyOpTypeTag()) {}
1481 
1482   LogicalResult matchAndRewrite(Operation *op,
1483                                 PatternRewriter &rewriter) const override {
1484     auto linalgOp = dyn_cast<LinalgOp>(op);
1485     if (!linalgOp)
1486       return failure();
1487     for (Value v : linalgOp.getInputsAndOutputBuffers()) {
1488       // Linalg "inputs" may be either tensor or memref type.
1489       // tensor<0xelt_type> is a convention that may not always mean
1490       // "0 iterations". Only erase in cases we see memref<...x0x...>.
1491       auto mt = v.getType().dyn_cast<MemRefType>();
1492       if (!mt)
1493         continue;
1494       if (llvm::is_contained(mt.getShape(), 0)) {
1495         rewriter.eraseOp(linalgOp);
1496         return success();
1497       }
1498     }
1499     return failure();
1500   }
1501 };
1502 
1503 struct FoldTensorCastOp : public RewritePattern {
1504   FoldTensorCastOp(PatternBenefit benefit = 1)
1505       : RewritePattern(benefit, MatchAnyOpTypeTag()) {}
1506 
1507   LogicalResult matchAndRewrite(Operation *op,
1508                                 PatternRewriter &rewriter) const override {
1509     auto linalgOp = dyn_cast<LinalgOp>(op);
1510     if (!linalgOp)
1511       return failure();
1512 
1513     // If no operand comes from a TensorCastOp and can be folded then fail.
1514     bool hasTensorCastOperand =
1515         llvm::any_of(linalgOp.getShapedOperands(), [&](Value v) {
1516           if (v.isa<BlockArgument>())
1517             return false;
1518           auto castOp = v.getDefiningOp<TensorCastOp>();
1519           return castOp && canFoldIntoConsumerOp(castOp);
1520         });
1521     if (!hasTensorCastOperand)
1522       return failure();
1523 
1524     SmallVector<Type, 4> newResultTypes;
1525     newResultTypes.reserve(op->getNumResults());
1526     SmallVector<Value, 4> newOperands;
1527     newOperands.reserve(op->getNumOperands());
1528     // Inputs may fold.
1529     for (Value v : linalgOp.getInputs()) {
1530       auto tensorCastOp = v.getDefiningOp<TensorCastOp>();
1531       newOperands.push_back(
1532           canFoldIntoConsumerOp(tensorCastOp) ? tensorCastOp.source() : v);
1533     }
1534     // Output buffers are memrefs, they don't fold.
1535     newOperands.append(linalgOp.getOutputBuffers().begin(),
1536                        linalgOp.getOutputBuffers().end());
1537     // Init tensors may fold, in which case the resultType must also change.
1538     for (Value v : linalgOp.getInitTensors()) {
1539       auto tensorCastOp = v.getDefiningOp<TensorCastOp>();
1540       bool fold = canFoldIntoConsumerOp(tensorCastOp);
1541       newOperands.push_back(fold ? tensorCastOp.getOperand() : v);
1542       newResultTypes.push_back(newOperands.back().getType());
1543     }
1544     auto extraOperands = linalgOp.getAssumedNonShapedOperands();
1545     newOperands.append(extraOperands.begin(), extraOperands.end());
1546     // Clone op.
1547     Operation *newOp =
1548         linalgOp.clone(rewriter, op->getLoc(), newResultTypes, newOperands);
1549     rewriter.replaceOp(op, newOp->getResults());
1550 
1551     return success();
1552   }
1553 };
1554 } // namespace
1555 
1556 #define CANONICALIZERS_AND_FOLDERS(XXX)                                        \
1557   void XXX::getCanonicalizationPatterns(OwningRewritePatternList &results,     \
1558                                         MLIRContext *context) {                \
1559     results.insert<EraseDeadLinalgOp>();                                       \
1560     results.insert<FoldTensorCastOp>();                                        \
1561   }                                                                            \
1562                                                                                \
1563   LogicalResult XXX::fold(ArrayRef<Attribute>,                                 \
1564                           SmallVectorImpl<OpFoldResult> &) {                   \
1565     return foldMemRefCast(*this);                                              \
1566   }
1567 
1568 CANONICALIZERS_AND_FOLDERS(ConvOp)
1569 CANONICALIZERS_AND_FOLDERS(PoolingMaxOp)
1570 CANONICALIZERS_AND_FOLDERS(PoolingMinOp)
1571 CANONICALIZERS_AND_FOLDERS(PoolingSumOp)
1572 CANONICALIZERS_AND_FOLDERS(CopyOp)
1573 CANONICALIZERS_AND_FOLDERS(FillOp)
1574 CANONICALIZERS_AND_FOLDERS(GenericOp)
1575 CANONICALIZERS_AND_FOLDERS(IndexedGenericOp)
1576 
1577 // TODO: Determine whether we can generate the folders and verifiers.
1578 CANONICALIZERS_AND_FOLDERS(BatchMatmulOp)
1579 CANONICALIZERS_AND_FOLDERS(DotOp)
1580 CANONICALIZERS_AND_FOLDERS(MatmulOp)
1581 CANONICALIZERS_AND_FOLDERS(MatvecOp)
1582 CANONICALIZERS_AND_FOLDERS(VecmatOp)
1583 CANONICALIZERS_AND_FOLDERS(ConvWOp)
1584 CANONICALIZERS_AND_FOLDERS(ConvNWCOp)
1585 CANONICALIZERS_AND_FOLDERS(ConvNCWOp)
1586 CANONICALIZERS_AND_FOLDERS(ConvHWOp)
1587 CANONICALIZERS_AND_FOLDERS(ConvNHWCOp)
1588 CANONICALIZERS_AND_FOLDERS(ConvNCHWOp)
1589 CANONICALIZERS_AND_FOLDERS(ConvDHWOp)
1590 CANONICALIZERS_AND_FOLDERS(ConvNDHWCOp)
1591 CANONICALIZERS_AND_FOLDERS(ConvNCDHWOp)
1592