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/IR/LinalgTypes.h"
15 #include "mlir/Dialect/StandardOps/IR/Ops.h"
16 #include "mlir/IR/AffineExpr.h"
17 #include "mlir/IR/AffineMap.h"
18 #include "mlir/IR/Builders.h"
19 #include "mlir/IR/Function.h"
20 #include "mlir/IR/Module.h"
21 #include "mlir/IR/OpImplementation.h"
22 #include "mlir/IR/PatternMatch.h"
23 #include "mlir/IR/StandardTypes.h"
24 #include "mlir/Support/Functional.h"
25 #include "mlir/Support/LLVM.h"
26 #include "mlir/Support/STLExtras.h"
27 
28 #include "llvm/ADT/StringSet.h"
29 #include "llvm/Support/MathExtras.h"
30 #include "llvm/Support/raw_ostream.h"
31 
32 using namespace mlir;
33 using namespace mlir::linalg;
34 
35 /// Determines whether it is possible to fold it away in the parent Linalg op:
36 ///
37 /// ```mlir
38 ///   %1 = memref_cast %0 : memref<8x16xf32> to memref<?x?xf32>
39 ///   %2 = linalg.slice %1 ... : memref<?x?xf32> ...
40 ///   // or
41 ///   %1 = memref_cast %0 : memref<8x16xf32, affine_map<(i, j)->(16 * i + j)>>
42 ///          to memref<?x?xf32>
43 ///   linalg.generic(%1 ...) : memref<?x?xf32> ...
44 /// ```
45 ///
46 /// into
47 ///
48 /// ```mlir
49 ///   %2 = linalg.slice %0 ... : memref<8x16xf32> ...
50 ///   // or
51 ///   linalg.generic(%0 ... : memref<8x16xf32, affine_map<(i, j)->(16 * i + j)>>
52 /// ```
53 ///
54 static bool canFold(MemRefCastOp castOp) {
55   MemRefType sourceType = castOp.source().getType().dyn_cast<MemRefType>();
56   MemRefType resultType = castOp.getType().dyn_cast<MemRefType>();
57 
58   // If we don't have MemRefType as source and destination, bail out.
59   if (!sourceType || !resultType)
60     return false;
61 
62   // If resultType has a map, it needs to be the same as the source type to
63   // canonicalize.
64   if (!resultType.getAffineMaps().empty() &&
65       sourceType.getAffineMaps() != resultType.getAffineMaps())
66     return false;
67 
68   // Ensure that:
69   //   1. source is static
70   //   2. source and target have the same rank (will be extended when needed)
71   //   3. if result is partially static, ensure sizes match.
72   if (!sourceType.hasStaticShape() ||
73       sourceType.getRank() != resultType.getRank())
74     return false;
75 
76   for (auto it : llvm::zip(sourceType.getShape(), resultType.getShape())) {
77     auto sourceSize = std::get<0>(it);
78     auto resultSize = std::get<1>(it);
79     if (ShapedType::isDynamic(resultSize))
80       continue;
81     if (sourceSize != resultSize)
82       return false;
83   }
84 
85   // If source has a map, it can only canonicalize if it is the canonical
86   // strided layout map.
87   if (sourceType.getAffineMaps().empty())
88     return true;
89 
90   int64_t offset;
91   SmallVector<int64_t, 4> strides;
92   auto res = getStridesAndOffset(sourceType, strides, offset);
93   (void)res;
94   assert(succeeded(res));
95   auto stridedMap =
96       makeStridedLinearLayoutMap(strides, offset, castOp.getContext());
97   AffineMap sourceMap = sourceType.getAffineMaps().front();
98   return sourceMap == stridedMap;
99 }
100 
101 /// This is a common class used for patterns of the form
102 /// ```
103 ///    someop(memrefcast) -> someop
104 /// ```
105 /// It folds the source of any memref_cast into the root operation directly.
106 static LogicalResult foldMemRefCast(Operation *op) {
107   bool folded = false;
108   for (OpOperand &operand : op->getOpOperands()) {
109     auto castOp = dyn_cast_or_null<MemRefCastOp>(operand.get().getDefiningOp());
110     if (castOp && canFold(castOp)) {
111       operand.set(castOp.getOperand());
112       folded = true;
113     }
114   }
115   return success(folded);
116 }
117 
118 ///////////////////// Operations defined with Tablegen /////////////////////////
119 // For such operations that do not correspond to library calls (i.e. defined in
120 // LinalgOps.td), we define an overloaded `print` function and a
121 // parse`className` function.
122 
123 //===----------------------------------------------------------------------===//
124 // GenericOps
125 //===----------------------------------------------------------------------===//
126 
127 template <typename GenericOpType>
128 static void printGenericOp(OpAsmPrinter &p, GenericOpType op) {
129   auto attrNames = op.linalgTraitAttrNames();
130   llvm::StringSet<> linalgTraitAttrsSet;
131   linalgTraitAttrsSet.insert(attrNames.begin(), attrNames.end());
132   SmallVector<NamedAttribute, 8> attrs;
133   for (auto attr : op.getAttrs())
134     if (linalgTraitAttrsSet.count(attr.first.strref()) > 0)
135       attrs.push_back(attr);
136 
137   auto dictAttr = DictionaryAttr::get(attrs, op.getContext());
138   p << op.getOperationName() << " " << dictAttr << " " << op.getOperands();
139   if (!op.region().empty())
140     p.printRegion(op.region());
141   p.printOptionalAttrDict(op.getAttrs(), attrNames);
142   p << ": " << op.getOperandTypes();
143   auto outputTensorTypes = op.getResultTypes();
144   if (!outputTensorTypes.empty())
145     p << " -> " << outputTensorTypes;
146 }
147 
148 static void print(OpAsmPrinter &p, GenericOp op) { printGenericOp(p, op); }
149 
150 static void print(OpAsmPrinter &p, IndexedGenericOp op) {
151   printGenericOp(p, op);
152 }
153 
154 static ParseResult parseGenericOp(OpAsmParser &parser, OperationState &result) {
155   SmallVector<OpAsmParser::OperandType, 8> operandsInfo, regionOperandsInfo;
156   DictionaryAttr dictAttr;
157   // Parse the core linalg traits that must check into a dictAttr.
158   // The name is unimportant as we will overwrite result.attributes.
159   // The core linalg traits must contain the information necessary to pass the
160   // verifier.
161   if (parser.parseAttribute(dictAttr, "_", result.attributes) ||
162       parser.parseOperandList(operandsInfo))
163     return failure();
164   result.attributes.assign(dictAttr.getValue().begin(),
165                            dictAttr.getValue().end());
166 
167   Region &region = *result.addRegion();
168   SmallVector<Type, 8> operandTypes, regionTypes;
169   // Optional attributes may be added.
170   // Either Optional getFunAttrName() attribute or region must be specified.
171   if (!dictAttr.get(getFunAttrName()) &&
172       parser.parseOptionalRegion(region, regionOperandsInfo, regionTypes))
173     return failure();
174   if (parser.parseOptionalAttrDict(result.attributes) ||
175       parser.parseColonTypeList(operandTypes))
176     return failure();
177   // Generic ops may specify that a subset of its outputs are tensors. Such
178   // outputs are specified in the result type.
179   SmallVector<Type, 8> tensorResultTypes;
180   if (parser.parseOptionalArrowTypeList(tensorResultTypes))
181     return failure();
182   if (!tensorResultTypes.empty())
183     result.addTypes(tensorResultTypes);
184   return parser.resolveOperands(operandsInfo, operandTypes,
185                                 parser.getCurrentLocation(), result.operands);
186 }
187 
188 template <typename GenericOpType>
189 static LogicalResult verifyBlockArgs(GenericOpType op, Block &block);
190 
191 template <> LogicalResult verifyBlockArgs(GenericOp op, Block &block) {
192   auto nOperands = op.getNumOperands();
193   if (block.getNumArguments() != nOperands)
194     return op.emitOpError("expected number of block arguments to match number "
195                           "of operands");
196 
197   // Note: the number and type of yield values are checked in the YieldOp.
198   auto nInputViews = op.getNumInputs();
199   for (unsigned i = 0; i < nOperands; ++i) {
200     auto viewType = op.getShapedType(i);
201     if (viewType.getElementType() != block.getArgument(i).getType())
202       return op.emitOpError("expected block argument ")
203              << (i + 1) << " of the same type as elemental type of "
204              << ((i < nInputViews) ? "input " : "output ")
205              << "operand: " << viewType;
206   }
207   return success();
208 }
209 
210 template <> LogicalResult verifyBlockArgs(IndexedGenericOp op, Block &block) {
211   auto nInputViews = op.getNumInputs();
212   auto nLoops = op.getNumLoops();
213   auto nOperands = op.getNumOperands();
214   if (block.getNumArguments() != nOperands + nLoops)
215     return op.emitOpError(
216         "expected number of block arguments to match number of operands + "
217         "number of loops");
218 
219   // Note: the number and type of yield values are checked in the YieldOp.
220   for (unsigned i = 0; i < nLoops; ++i)
221     if (!block.getArgument(i).getType().isIndex())
222       return op.emitOpError("expected block argument ")
223              << (i + 1) << " to be an index";
224 
225   for (unsigned i = 0; i < nOperands; ++i) {
226     unsigned memrefArgIndex = i + nLoops;
227     auto viewType = op.getShapedType(i);
228     if (viewType.getElementType() !=
229         block.getArgument(memrefArgIndex).getType())
230       return op.emitOpError("expected block argument ")
231              << (memrefArgIndex + 1)
232              << " of the same type as elemental type of "
233              << ((i < nInputViews) ? "input " : "output ")
234              << "operand: " << viewType;
235   }
236   return success();
237 }
238 
239 template <typename GenericOpType>
240 static LogicalResult verifyFuncArgs(GenericOpType op, FunctionType funType);
241 
242 template <typename GenericOpType>
243 static LogicalResult verifyFuncArgsGeneric(GenericOpType op,
244                                            FunctionType funType) {
245   auto res = verifyFuncArgs(op, funType);
246   if (failed(res))
247     return res;
248 
249   auto nInputs = op.getNumInputs();
250   auto nOutputs = op.getNumOutputs();
251   // linalg.generic output element types are exactly the function results.
252   for (unsigned idx = 0; idx < nOutputs; ++idx) {
253     ShapedType shapedType = op.getShapedType(nInputs + idx);
254     if (funType.getResult(idx) != shapedType.getElementType())
255       return op.emitOpError("expected function result ")
256              << (idx + 1) << " of the same type as elemental type "
257              << shapedType.getElementType() << " of output " << (idx + 1);
258   }
259   return success();
260 }
261 
262 template <> LogicalResult verifyFuncArgs(GenericOp op, FunctionType funType) {
263   auto nOperands = op.getNumOperands();
264   if (funType.getNumInputs() != nOperands)
265     return op.emitOpError(
266         "expected function arguments to match number of operands");
267   if (funType.getNumResults() != op.getNumOutputs())
268     return op.emitOpError("expected function results(")
269            << funType.getNumResults() << ") to match number of outputs("
270            << op.getNumOutputs() << ")";
271 
272   // linalg.generic operands element types are exactly the first function
273   // arguments.
274   for (unsigned idx = 0; idx < nOperands; ++idx) {
275     ShapedType shapedType = op.getShapedType(idx);
276     if (funType.getInput(idx) != shapedType.getElementType())
277       return op.emitOpError("expected function argument ")
278              << (idx + 1) << " of the same type as elemental type "
279              << shapedType.getElementType() << " of operand " << (idx + 1);
280   }
281 
282   return success();
283 }
284 
285 template <>
286 LogicalResult verifyFuncArgs(IndexedGenericOp op, FunctionType funType) {
287   auto nLoops = op.getNumLoops();
288   auto nOutputs = op.getNumOutputs();
289   auto nOperands = op.getNumOperands();
290   if (funType.getNumInputs() != nOperands + nLoops)
291     return op.emitOpError("expected function arguments to match number of "
292                           "loops + number of operands");
293   if (funType.getNumResults() != nOutputs)
294     return op.emitOpError(
295         "expected function results to match number of outputs");
296   for (unsigned i = 0; i < nLoops; ++i)
297     if (!funType.getInput(i).isIndex())
298       return op.emitOpError("expected function argument ")
299              << (i + 1) << " to be an index";
300 
301   // linalg.generic operands element types are exactly the first function
302   // arguments.
303   for (unsigned idx = 0; idx < nOperands; ++idx) {
304     ShapedType shapedType = op.getShapedType(idx);
305     if (funType.getInput(idx + nLoops) != shapedType.getElementType())
306       return op.emitOpError("expected function argument ")
307              << (idx + nLoops + 1) << " of the same type as elemental type "
308              << shapedType.getElementType() << " of input " << (idx + 1);
309   }
310 
311   return success();
312 }
313 
314 template <typename GenericOpType>
315 static LogicalResult verifyGenericOp(GenericOpType op) {
316   auto nInputViews = op.getNumInputs();
317   auto nLoops = op.getNumLoops();
318   auto nInputsAndOutputBuffers = op.getNumInputsAndOutputBuffers();
319   if (nInputsAndOutputBuffers != llvm::size(op.views()))
320     return op.emitOpError("expected exactly ")
321            << nInputsAndOutputBuffers
322            << " inputs (tensor or buffer) and output buffer operands";
323 
324   auto &region = op.region();
325   auto funOp = op.getFunction();
326   auto funType = funOp ? funOp.getType() : FunctionType();
327   if (!region.empty()) {
328     if (region.getBlocks().size() != 1)
329       return op.emitOpError("expected region with 1 block");
330     if (failed(verifyBlockArgs(op, region.getBlocks().front())))
331       return failure();
332   } else {
333     if (!funOp || !funOp.getType())
334       return op.emitOpError(
335           "expected function attribute to refer to a defined symbol");
336     if (failed(verifyFuncArgsGeneric(op, funType)))
337       return failure();
338   }
339 
340   SmallVector<AffineMap, 4> indexingMaps;
341   indexingMaps.reserve(op.indexing_maps().size());
342   for (auto en : llvm::enumerate(op.indexing_maps())) {
343     auto idx = en.index();
344     auto m = en.value().template cast<AffineMapAttr>().getValue();
345     indexingMaps.push_back(m); // Save reference to map for further checks.
346     auto view = (idx < nInputViews) ? op.getInputShapedType(idx)
347                                     : op.getOutputShapedType(idx - nInputViews);
348 
349     if (m.getNumSymbols() != 0)
350       return op.emitOpError("expected indexing_map #")
351              << idx << " to have no symbols";
352 
353     if (m.getNumDims() != nLoops)
354       return op.emitOpError("expected indexing_map #")
355              << idx << " to have " << nLoops
356              << " dim(s) to match the number of loops";
357 
358     if (m.getNumResults() != view.getRank())
359       return op.emitOpError("expected indexing_map #")
360              << idx << " results to match view rank: " << view;
361   }
362 
363   auto concatMap = concatAffineMaps(indexingMaps);
364   auto aggregateMap = inversePermutation(concatMap);
365   if (!aggregateMap)
366     return op.emitOpError("expected the concatenation of maps in indexing_map "
367                           "to be invertible");
368 
369   return success();
370 }
371 
372 static LogicalResult verify(GenericOp op) { return verifyGenericOp(op); }
373 static LogicalResult verify(IndexedGenericOp op) { return verifyGenericOp(op); }
374 
375 //===----------------------------------------------------------------------===//
376 // ReshapeOp
377 //===----------------------------------------------------------------------===//
378 
379 /// Return true if the reassociation specification is valid, false otherwise.
380 /// When false, the `invalidIndex` integer pointer is optionally filled with the
381 /// index of the offending reassociation map.
382 static bool isReassociationValid(ArrayRef<AffineMap> reassociation,
383                                  int *invalidIndex = nullptr) {
384   if (reassociation.empty())
385     return true;
386   unsigned nDims = reassociation[0].getNumDims();
387   unsigned nextExpectedDim = 0;
388   for (auto it : llvm::enumerate(reassociation)) {
389     auto m = it.value();
390     if (m.getNumDims() != nDims || m.getNumSymbols() != 0) {
391       if (invalidIndex)
392         *invalidIndex = it.index();
393       return false;
394     }
395     for (auto e : m.getResults()) {
396       auto d = e.dyn_cast<AffineDimExpr>();
397       if (!d || d.getPosition() != nextExpectedDim++) {
398         if (invalidIndex)
399           *invalidIndex = it.index();
400         return false;
401       }
402     }
403   }
404   if (nextExpectedDim != nDims) {
405     if (invalidIndex)
406       *invalidIndex = reassociation.size() - 1;
407     return false;
408   }
409   return true;
410 }
411 
412 /// Detect whether memref dims [dim, dim + extent) can be reshaped without
413 /// copies.
414 static bool isReshapableDimBand(unsigned dim, unsigned extent,
415                                 ArrayRef<int64_t> sizes,
416                                 ArrayRef<AffineExpr> strides) {
417   assert(sizes.size() == strides.size() && "mismatched ranks");
418   // off by 1 indexing to avoid out of bounds
419   //                       V
420   for (auto idx = dim, e = dim + extent; idx + 1 < e; ++idx) {
421     // Only bands of static shapes are reshapable. This is due to the fact that
422     // there is no relation between dynamic sizes and dynamic strides: we do not
423     // have enough information to know whether a "-1" size corresponds to the
424     // proper symbol in the AffineExpr of a stride.
425     if (ShapedType::isDynamic(sizes[dim + 1]))
426       return false;
427     // TODO(ntv) Refine this by passing the proper nDims and nSymbols so we can
428     // simplify on the fly and catch more reshapable cases.
429     if (strides[idx] != strides[idx + 1] * sizes[idx + 1])
430       return false;
431   }
432   return true;
433 }
434 
435 /// Compute the MemRefType obtained by applying the `reassociation` (which is
436 /// expected to be valid) to `type`.
437 /// If `type` is Contiguous MemRefType, this always produce a contiguous
438 /// MemRefType.
439 static MemRefType
440 computeReshapeCollapsedType(MemRefType type,
441                             ArrayRef<AffineMap> reassociation) {
442   auto sizes = type.getShape();
443   AffineExpr offset;
444   SmallVector<AffineExpr, 4> strides;
445   auto status = getStridesAndOffset(type, strides, offset);
446   (void)status;
447   assert(succeeded(status) && "expected strided memref");
448 
449   SmallVector<int64_t, 4> newSizes;
450   newSizes.reserve(reassociation.size());
451   SmallVector<AffineExpr, 4> newStrides;
452   newStrides.reserve(reassociation.size());
453 
454   // Use the fact that reassociation is valid to simplify the logic: only use
455   // each map's rank.
456   assert(isReassociationValid(reassociation) && "invalid reassociation");
457   unsigned currentDim = 0;
458   for (AffineMap m : reassociation) {
459     unsigned dim = m.getNumResults();
460     int64_t size = 1;
461     AffineExpr stride = strides[currentDim + dim - 1];
462     if (!isReshapableDimBand(currentDim, dim, sizes, strides)) {
463       size = ShapedType::kDynamicSize;
464       stride = AffineExpr();
465     } else {
466       for (unsigned d = 0; d < dim; ++d)
467         size *= sizes[currentDim + d];
468     }
469     newSizes.push_back(size);
470     newStrides.push_back(stride);
471     currentDim += dim;
472   }
473 
474   // Early-exit: if `type` is contiguous, the result must be contiguous.
475   if (canonicalizeStridedLayout(type).getAffineMaps().empty())
476     return MemRefType::Builder(type).setShape(newSizes).setAffineMaps({});
477 
478   // Convert back to int64_t because we don't have enough information to create
479   // new strided layouts from AffineExpr only. This corresponds to a case where
480   // copies may be necessary.
481   int64_t intOffset = ShapedType::kDynamicStrideOrOffset;
482   if (auto o = offset.dyn_cast<AffineConstantExpr>())
483     intOffset = o.getValue();
484   SmallVector<int64_t, 4> intStrides;
485   intStrides.reserve(strides.size());
486   for (auto stride : newStrides) {
487     if (auto cst = stride.dyn_cast_or_null<AffineConstantExpr>())
488       intStrides.push_back(cst.getValue());
489     else
490       intStrides.push_back(ShapedType::kDynamicStrideOrOffset);
491   }
492   auto layout =
493       makeStridedLinearLayoutMap(intStrides, intOffset, type.getContext());
494   return canonicalizeStridedLayout(
495       MemRefType::Builder(type).setShape(newSizes).setAffineMaps({layout}));
496 }
497 
498 /// Helper functions assert Attribute of the proper type in attr and returns the
499 /// corresponding vector.
500 /// TODO(rridle,ntv) this should be evolved into a generic
501 /// `getRangeOfType<AffineMap>(ArrayAttr attrs)` that does not copy.
502 static SmallVector<AffineMap, 4> getAffineMaps(ArrayAttr attrs) {
503   return functional::map(
504       [](Attribute a) { return a.cast<AffineMapAttr>().getValue(); }, attrs);
505 }
506 
507 template <typename AffineExprTy>
508 unsigned getMaxPosOfType(ArrayRef<ArrayRef<AffineExpr>> exprArrays) {
509   unsigned pos = 0;
510   for (auto exprs : exprArrays) {
511     for (auto expr : exprs) {
512       expr.walk([&pos](AffineExpr e) {
513         if (auto d = e.dyn_cast<AffineExprTy>())
514           pos = std::max(pos, d.getPosition());
515       });
516     }
517   }
518   return pos;
519 }
520 
521 static SmallVector<AffineMap, 4>
522 getSymbolLessAffineMaps(ArrayRef<ArrayRef<AffineExpr>> reassociation) {
523   unsigned maxDim = getMaxPosOfType<AffineDimExpr>(reassociation);
524   assert(getMaxPosOfType<AffineSymbolExpr>(reassociation) == 0 &&
525          "Expected symbol-less expressions");
526   SmallVector<AffineMap, 4> maps;
527   maps.reserve(reassociation.size());
528   for (auto exprs : reassociation)
529     maps.push_back(AffineMap::get(maxDim + 1, 0, exprs));
530   return maps;
531 }
532 
533 void mlir::linalg::ReshapeOp::build(
534     Builder *b, OperationState &result, Value src,
535     ArrayRef<ArrayRef<AffineExpr>> reassociation,
536     ArrayRef<NamedAttribute> attrs) {
537   auto maps = getSymbolLessAffineMaps(reassociation);
538   auto memRefType = src.getType().cast<MemRefType>();
539   auto resultType = computeReshapeCollapsedType(memRefType, maps);
540   build(b, result, resultType, src, attrs);
541   result.addAttribute(ReshapeOp::getReassociationAttrName(),
542                       b->getAffineMapArrayAttr(maps));
543 }
544 
545 void mlir::linalg::ReshapeOp::build(
546     Builder *b, OperationState &result, Type resultType, Value src,
547     ArrayRef<ArrayRef<AffineExpr>> reassociation,
548     ArrayRef<NamedAttribute> attrs) {
549   auto maps = getSymbolLessAffineMaps(reassociation);
550   build(b, result, resultType, src, attrs);
551   result.addAttribute(ReshapeOp::getReassociationAttrName(),
552                       b->getAffineMapArrayAttr(maps));
553 }
554 
555 // Common verifier for reshape-like types. Fills `expandedType` and
556 // `collapsedType` with the proper `src` or `result` type.
557 template <typename Op, typename T>
558 LogicalResult verifyReshapeLikeTypes(Op op, T &expandedType, T &collapsedType) {
559   expandedType = op.getSrcType();
560   collapsedType = op.getResultType();
561   unsigned expandedRank = expandedType.getRank();
562   unsigned collapsedRank = collapsedType.getRank();
563   bool isCollapse = expandedRank > collapsedRank;
564   if (!isCollapse) {
565     std::swap(expandedRank, collapsedRank);
566     std::swap(expandedType, collapsedType);
567   }
568   if (expandedRank == 0 || collapsedRank == 0)
569     return op.emitOpError("expected non-zero memref ranks");
570   if (expandedRank == collapsedRank)
571     return op.emitOpError("expected to collapse or expand dims");
572 
573   if (collapsedRank != op.reassociation().size())
574     return op.emitOpError("expected rank of the collapsed type(")
575            << collapsedRank << ") to be the number of reassociation maps("
576            << op.reassociation().size() << ")";
577   auto maps = getAffineMaps(op.reassociation());
578   for (auto it : llvm::enumerate(maps))
579     if (it.value().getNumDims() != expandedRank)
580       return op.emitOpError("expected reassociation map #")
581              << it.index() << " of same rank as expanded memref("
582              << expandedRank << "), but got " << it.value().getNumDims();
583   int invalidIdx = 0;
584   if (!isReassociationValid(maps, &invalidIdx))
585     return op.emitOpError("expected reassociation map #")
586            << invalidIdx << " to be valid and contiguous";
587   return success();
588 }
589 
590 static LogicalResult verify(ReshapeOp op) {
591   MemRefType expandedType, collapsedType;
592   if (failed(verifyReshapeLikeTypes(op, expandedType, collapsedType)))
593     return failure();
594   auto maps = getAffineMaps(op.reassociation());
595   MemRefType expectedType = computeReshapeCollapsedType(expandedType, maps);
596   if (collapsedType != expectedType)
597     return op.emitOpError("expected collapsed type to be ")
598            << expectedType << ", but got " << collapsedType;
599   return success();
600 }
601 
602 //===----------------------------------------------------------------------===//
603 // TensorReshapeOp
604 //===----------------------------------------------------------------------===//
605 
606 /// Compute the RankedTensorType obtained by applying `reassociation` to `type`.
607 static RankedTensorType
608 computeTensorReshapeCollapsedType(RankedTensorType type,
609                                   ArrayRef<AffineMap> reassociation) {
610   auto shape = type.getShape();
611   SmallVector<int64_t, 4> newShape;
612   newShape.reserve(reassociation.size());
613 
614   // Use the fact that reassociation is valid to simplify the logic: only use
615   // each map's rank.
616   assert(isReassociationValid(reassociation) && "invalid reassociation");
617   unsigned currentDim = 0;
618   for (AffineMap m : reassociation) {
619     unsigned dim = m.getNumResults();
620     auto band = shape.drop_front(currentDim).take_front(dim);
621     int64_t size = 1;
622     if (llvm::is_contained(band, ShapedType::kDynamicSize))
623       size = ShapedType::kDynamicSize;
624     else
625       for (unsigned d = 0; d < dim; ++d)
626         size *= shape[currentDim + d];
627     newShape.push_back(size);
628     currentDim += dim;
629   }
630 
631   return RankedTensorType::get(newShape, type.getElementType());
632 }
633 
634 void mlir::linalg::TensorReshapeOp::build(
635     Builder *b, OperationState &result, Value src,
636     ArrayRef<ArrayRef<AffineExpr>> reassociation,
637     ArrayRef<NamedAttribute> attrs) {
638   auto maps = getSymbolLessAffineMaps(reassociation);
639   auto resultType = computeTensorReshapeCollapsedType(
640       src.getType().cast<RankedTensorType>(), maps);
641   build(b, result, resultType, src, attrs);
642   result.addAttribute(TensorReshapeOp::getReassociationAttrName(),
643                       b->getAffineMapArrayAttr(maps));
644 }
645 
646 void mlir::linalg::TensorReshapeOp::build(
647     Builder *b, OperationState &result, Type resultType, Value src,
648     ArrayRef<ArrayRef<AffineExpr>> reassociation,
649     ArrayRef<NamedAttribute> attrs) {
650   auto maps = getSymbolLessAffineMaps(reassociation);
651   build(b, result, resultType, src, attrs);
652   result.addAttribute(TensorReshapeOp::getReassociationAttrName(),
653                       b->getAffineMapArrayAttr(maps));
654 }
655 
656 static LogicalResult verify(TensorReshapeOp op) {
657   RankedTensorType expandedType, collapsedType;
658   if (failed(verifyReshapeLikeTypes(op, expandedType, collapsedType)))
659     return failure();
660   auto maps = getAffineMaps(op.reassociation());
661   // TODO(ntv): expanding a ? with a non-constant is under-specified. Error
662   // out.
663   RankedTensorType expectedType =
664       computeTensorReshapeCollapsedType(expandedType, maps);
665   if (collapsedType != expectedType)
666     return op.emitOpError("expected collapsed type to be ")
667            << expectedType << ", but got " << collapsedType;
668   return success();
669 }
670 
671 //===----------------------------------------------------------------------===//
672 // SliceOp
673 //===----------------------------------------------------------------------===//
674 void mlir::linalg::SliceOp::build(Builder *b, OperationState &result,
675                                   Value base, ValueRange indexings) {
676   result.addOperands(base);
677   result.addOperands(indexings);
678 
679   auto memRefType = base.getType().cast<MemRefType>();
680   int64_t offset;
681   SmallVector<int64_t, 4> strides;
682   auto res = getStridesAndOffset(memRefType, strides, offset);
683   assert(succeeded(res) && strides.size() == indexings.size());
684   (void)res;
685 
686   unsigned rank = memRefType.getRank();
687   // TODO(ntv): propagate static size and stride information when available.
688   SmallVector<int64_t, 4> sizes(rank, -1); // -1 encodes dynamic size.
689   result.addTypes({MemRefType::Builder(memRefType)
690                        .setShape(sizes)
691                        .setAffineMaps(makeStridedLinearLayoutMap(
692                            strides, offset, b->getContext()))});
693 }
694 
695 static void print(OpAsmPrinter &p, SliceOp op) {
696   auto indexings = op.indexings();
697   p << SliceOp::getOperationName() << " " << op.view() << "[" << indexings
698     << "] ";
699   p.printOptionalAttrDict(op.getAttrs());
700   p << " : " << op.getBaseViewType();
701   if (!indexings.empty())
702     p << ", " << op.indexings().getTypes();
703   p << ", " << op.getType();
704 }
705 
706 static ParseResult parseSliceOp(OpAsmParser &parser, OperationState &result) {
707   OpAsmParser::OperandType baseInfo;
708   SmallVector<OpAsmParser::OperandType, 8> operands;
709   SmallVector<Type, 8> types;
710   if (parser.parseOperand(baseInfo) ||
711       parser.parseOperandList(operands, OpAsmParser::Delimiter::Square) ||
712       parser.parseOptionalAttrDict(result.attributes) ||
713       parser.parseColonTypeList(types))
714     return failure();
715 
716   if (types.size() < 2)
717     return parser.emitError(parser.getCurrentLocation(),
718                             "expected at least input and result view types");
719 
720   ArrayRef<Type> indexingTypes = ArrayRef<Type>(types).drop_front().drop_back();
721   return failure(
722       parser.resolveOperand(baseInfo, types.front(), result.operands) ||
723       (!operands.empty() &&
724        parser.resolveOperands(operands, indexingTypes,
725                               operands.front().location, result.operands)) ||
726       parser.addTypeToList(types.back(), result.types));
727 }
728 
729 static LogicalResult verify(SliceOp op) {
730   unsigned rank = op.getBaseViewRank();
731   if (rank != llvm::size(op.indexings()))
732     return op.emitOpError("expected ")
733            << rank << " indexings, got " << llvm::size(op.indexings());
734   unsigned index = 0;
735   for (auto indexing : op.indexings()) {
736     if (indexing.getType().isa<IndexType>())
737       --rank;
738     ++index;
739   }
740   if (op.getRank() != rank)
741     return op.emitOpError() << "expected rank of the view(" << op.getRank()
742                             << ") to be the number of ranges(" << rank << ")";
743   return success();
744 }
745 
746 //===----------------------------------------------------------------------===//
747 // TransposeOp
748 //===----------------------------------------------------------------------===//
749 void mlir::linalg::TransposeOp::build(Builder *b, OperationState &result,
750                                       Value view, AffineMapAttr permutation,
751                                       ArrayRef<NamedAttribute> attrs) {
752   auto permutationMap = permutation.getValue();
753   assert(permutationMap);
754 
755   auto memRefType = view.getType().cast<MemRefType>();
756   auto rank = memRefType.getRank();
757   auto originalSizes = memRefType.getShape();
758   // Compute permuted sizes.
759   SmallVector<int64_t, 4> sizes(rank, 0);
760   for (auto en : llvm::enumerate(permutationMap.getResults()))
761     sizes[en.index()] =
762         originalSizes[en.value().cast<AffineDimExpr>().getPosition()];
763 
764   // Compute permuted strides.
765   int64_t offset;
766   SmallVector<int64_t, 4> strides;
767   auto res = getStridesAndOffset(memRefType, strides, offset);
768   assert(succeeded(res) && strides.size() == static_cast<unsigned>(rank));
769   (void)res;
770   auto map = makeStridedLinearLayoutMap(strides, offset, b->getContext());
771   map = permutationMap ? map.compose(permutationMap) : map;
772   // Compute result type.
773   MemRefType resultType =
774       MemRefType::Builder(memRefType).setShape(sizes).setAffineMaps(map);
775 
776   build(b, result, resultType, view, attrs);
777   result.addAttribute(TransposeOp::getPermutationAttrName(), permutation);
778 }
779 
780 static void print(OpAsmPrinter &p, TransposeOp op) {
781   p << op.getOperationName() << " " << op.view() << " " << op.permutation();
782   p.printOptionalAttrDict(op.getAttrs(),
783                           {TransposeOp::getPermutationAttrName()});
784   p << " : " << op.view().getType();
785 }
786 
787 static ParseResult parseTransposeOp(OpAsmParser &parser,
788                                     OperationState &result) {
789   OpAsmParser::OperandType view;
790   AffineMap permutation;
791   MemRefType type;
792   if (parser.parseOperand(view) || parser.parseAffineMap(permutation) ||
793       parser.parseOptionalAttrDict(result.attributes) ||
794       parser.parseColonType(type) ||
795       parser.resolveOperand(view, type, result.operands) ||
796       parser.addTypeToList(type, result.types))
797     return failure();
798 
799   result.addAttribute(TransposeOp::getPermutationAttrName(),
800                       AffineMapAttr::get(permutation));
801   return success();
802 }
803 
804 //===----------------------------------------------------------------------===//
805 // YieldOp
806 //===----------------------------------------------------------------------===//
807 
808 static void print(OpAsmPrinter &p, YieldOp op) {
809   p << op.getOperationName();
810   if (op.getNumOperands() > 0)
811     p << ' ' << op.getOperands();
812   p.printOptionalAttrDict(op.getAttrs());
813   if (op.getNumOperands() > 0)
814     p << " : " << op.getOperandTypes();
815 }
816 
817 static ParseResult parseYieldOp(OpAsmParser &parser, OperationState &result) {
818   SmallVector<OpAsmParser::OperandType, 2> opInfo;
819   SmallVector<Type, 2> types;
820   llvm::SMLoc loc = parser.getCurrentLocation();
821   return failure(parser.parseOperandList(opInfo) ||
822                  parser.parseOptionalAttrDict(result.attributes) ||
823                  (!opInfo.empty() && parser.parseColonTypeList(types)) ||
824                  parser.resolveOperands(opInfo, types, loc, result.operands));
825 }
826 
827 template <typename GenericOpType>
828 static LogicalResult verifyYield(YieldOp op, GenericOpType genericOp) {
829   // The operand number and types must match the view element types.
830   auto nOutputs = genericOp.getNumOutputs();
831   if (op.getNumOperands() != nOutputs)
832     return op.emitOpError("expected number of yield values (")
833            << nOutputs << ") to match the number of operands of the enclosing "
834            << "linalg.generic op (" << op.getNumOperands() << ")";
835 
836   for (unsigned i = 0; i != nOutputs; ++i) {
837     auto elementType = genericOp.getOutputShapedType(i).getElementType();
838     if (op.getOperand(i).getType() != elementType)
839       return op.emitOpError("type of yield operand ")
840              << (i + 1) << " (" << op.getOperand(i).getType()
841              << ") doesn't match "
842              << "the element type of the enclosing linalg.generic op ("
843              << elementType << ")";
844   }
845   return success();
846 }
847 
848 static LogicalResult verify(YieldOp op) {
849   auto *parentOp = op.getParentOp();
850   if (parentOp->getNumRegions() != 1 || parentOp->getRegion(0).empty())
851     return op.emitOpError("expected single non-empty parent region");
852 
853   auto genericOp = dyn_cast<GenericOp>(parentOp);
854   if (genericOp)
855     return verifyYield(op, genericOp);
856 
857   auto indexedGenericOp = dyn_cast<IndexedGenericOp>(parentOp);
858   if (indexedGenericOp)
859     return verifyYield(op, indexedGenericOp);
860 
861   return op.emitOpError("expected '")
862          << GenericOp::getOperationName() << "' or '"
863          << IndexedGenericOp::getOperationName() << "' parent op";
864 }
865 
866 /////// Operations corresponding to library calls defined with Tablegen ////////
867 
868 static LogicalResult verify(FillOp op) {
869   auto viewType = op.getOutputShapedType(0);
870   auto fillType = op.value().getType();
871   if (viewType.getElementType() != fillType)
872     return op.emitOpError("expects fill type to match view elemental type");
873   return success();
874 }
875 
876 static LogicalResult verify(CopyOp op) {
877   auto outputViewType = op.getOutputShapedType(0);
878   auto inputViewType = op.getInputShapedType(0);
879   if (inputViewType.getElementType() != outputViewType.getElementType())
880     return op.emitOpError("expects views of the same type");
881   if (inputViewType.getRank() != outputViewType.getRank())
882     return op.emitOpError("expects views of the same rank");
883   auto rank = op.getNumParallelLoops();
884   auto inputPermutationMap = op.inputPermutation();
885   if (inputPermutationMap) {
886     if (inputPermutationMap->getNumInputs() != rank)
887       return op.emitOpError("expects optional input_permutation map of rank ")
888              << rank;
889     if (!inputPermutationMap->isPermutation())
890       return op.emitOpError(
891           "expects optional input_permutation map to be a permutation");
892   }
893   auto outputPermutationMap = op.outputPermutation();
894   if (outputPermutationMap) {
895     if (outputPermutationMap->getNumInputs() != rank)
896       return op.emitOpError("expects optional output_permutation map of rank ")
897              << rank;
898     if (!outputPermutationMap->isPermutation())
899       return op.emitOpError(
900           "expects optional output_permutation map to be a permutation");
901   }
902   if (rank == 0 && inputPermutationMap)
903     return op.emitOpError("expected no input permutation when rank == 0");
904   if (rank == 0 && outputPermutationMap)
905     return op.emitOpError("expected no output permutation when rank == 0");
906   return success();
907 }
908 
909 template <typename LinalgPoolingOp>
910 static LogicalResult verifyStrideOrDilation(LinalgPoolingOp op,
911                                             ArrayRef<Attribute> attrs,
912                                             bool isStride) {
913   auto strideOrDilation = isStride ? "stride" : "dilation";
914   if (attrs.size() != op.getNumWindowLoops())
915     return op.emitOpError("expects num ")
916            << strideOrDilation
917            << "s equal to number of window dimensions: " << attrs.size()
918            << " vs " << op.getNumWindowLoops();
919   return success();
920 }
921 
922 static LogicalResult verify(ConvOp op) {
923   auto oType = op.output().getType().cast<MemRefType>();
924   auto fType = op.filter().getType().cast<MemRefType>();
925   auto iType = op.input().getType().cast<MemRefType>();
926   if (oType.getElementType() != iType.getElementType() ||
927       oType.getElementType() != fType.getElementType())
928     return op.emitOpError("expects memref elemental types to match");
929   if (oType.getRank() != iType.getRank() || oType.getRank() != fType.getRank())
930     return op.emitOpError("expects memref ranks to match");
931   if (auto strides = op.strides()) {
932     if (failed(
933             verifyStrideOrDilation(op, strides->getValue(), /*isStride=*/true)))
934       return failure();
935   }
936   if (auto dilations = op.dilations()) {
937     if (failed(verifyStrideOrDilation(op, dilations->getValue(),
938                                       /*isStride=*/false)))
939       return failure();
940   }
941   return success();
942 }
943 
944 template <typename PoolingOp>
945 static LogicalResult verifySingleInputPoolingOp(PoolingOp op) {
946   auto inputType = op.input().getType().template cast<MemRefType>();
947   auto outputType = op.output().getType().template cast<MemRefType>();
948   if (outputType.getElementType() != inputType.getElementType())
949     return op.emitOpError("expects memref elemental types to match");
950 
951   auto windowDimsType = op.windowDims().getType().template cast<MemRefType>();
952   if (outputType.getRank() != inputType.getRank() ||
953       outputType.getRank() != windowDimsType.getRank())
954     return op.emitOpError("expects memref ranks to match");
955 
956   if (auto strides = op.strides()) {
957     if (failed(
958             verifyStrideOrDilation(op, strides->getValue(), /*isStride=*/true)))
959       return failure();
960   }
961   if (auto dilations = op.dilations()) {
962     if (failed(verifyStrideOrDilation(op, dilations->getValue(),
963                                       /*isStride=*/false)))
964       return failure();
965   }
966   return success();
967 }
968 
969 static LogicalResult verify(PoolingMaxOp op) {
970   return verifySingleInputPoolingOp(op);
971 }
972 static LogicalResult verify(PoolingMinOp op) {
973   return verifySingleInputPoolingOp(op);
974 }
975 static LogicalResult verify(PoolingSumOp op) {
976   return verifySingleInputPoolingOp(op);
977 }
978 
979 namespace mlir {
980 namespace linalg {
981 
982 #include "mlir/Dialect/Linalg/IR/LinalgStructuredOpsInterfaces.cpp.inc"
983 
984 #define GET_OP_CLASSES
985 #include "mlir/Dialect/Linalg/IR/LinalgOps.cpp.inc"
986 
987 #define GET_OP_CLASSES
988 #include "mlir/Dialect/Linalg/IR/LinalgStructuredOps.cpp.inc"
989 
990 } // namespace linalg
991 } // namespace mlir
992 
993 AffineMap mlir::linalg::extractOrIdentityMap(Optional<AffineMap> maybeMap,
994                                              unsigned rank,
995                                              MLIRContext *context) {
996   if (maybeMap)
997     return maybeMap.getValue();
998   if (rank == 0)
999     return AffineMap::get(context);
1000   return AffineMap::getMultiDimIdentityMap(rank, context);
1001 }
1002 
1003 SmallVector<AffineExpr, 4>
1004 mlir::linalg::makeAffineDimExprs(unsigned num, unsigned &startIdx,
1005                                  MLIRContext *context) {
1006   SmallVector<AffineExpr, 4> res;
1007   res.reserve(num);
1008   for (unsigned i = 0; i < num; ++i)
1009     res.push_back(getAffineDimExpr(startIdx++, context));
1010   return res;
1011 }
1012 
1013 template <typename PoolingOp>
1014 SmallVector<AffineExpr, 4>
1015 mlir::linalg::weightedPoolingInputIndex(PoolingOp op,
1016                                         ArrayRef<AffineExpr> outputDims,
1017                                         ArrayRef<AffineExpr> windowDims) {
1018   assert(outputDims.size() == windowDims.size());
1019   SmallVector<AffineExpr, 4> res;
1020   res.reserve(outputDims.size());
1021   for (unsigned i = 0, e = outputDims.size(); i < e; ++i) {
1022     // TODO(ntv): add a level of indirection to linalg.generic.
1023     auto expr = op.getStride(i) * outputDims[i] +
1024                 op.getDilation(i) * windowDims[i] - op.getLowPad(i);
1025     res.push_back(expr);
1026   }
1027   return res;
1028 }
1029 
1030 #define INSTANTIATE_WEIGHTED_POOLING_INPUT_INDEX(OP_TYPE)                      \
1031   template SmallVector<AffineExpr, 4>                                          \
1032   mlir::linalg::weightedPoolingInputIndex<OP_TYPE>(                            \
1033       OP_TYPE op, ArrayRef<AffineExpr> outputDims,                             \
1034       ArrayRef<AffineExpr> windowDims);
1035 
1036 INSTANTIATE_WEIGHTED_POOLING_INPUT_INDEX(ConvOp)
1037 INSTANTIATE_WEIGHTED_POOLING_INPUT_INDEX(PoolingMaxOp)
1038 INSTANTIATE_WEIGHTED_POOLING_INPUT_INDEX(PoolingMinOp)
1039 INSTANTIATE_WEIGHTED_POOLING_INPUT_INDEX(PoolingSumOp)
1040 
1041 SmallVector<AffineExpr, 4> mlir::linalg::concat(ArrayRef<AffineExpr> a,
1042                                                 ArrayRef<AffineExpr> b) {
1043   auto rangeA = llvm::make_range(a.begin(), a.end());
1044   auto rangeB = llvm::make_range(b.begin(), b.end());
1045   auto concatRanges = llvm::concat<const AffineExpr>(rangeA, rangeB);
1046   return llvm::to_vector<4>(concatRanges);
1047 }
1048 
1049 static void appendMangledType(llvm::raw_string_ostream &ss, Type t) {
1050   if (auto memref = t.dyn_cast<MemRefType>()) {
1051     ss << "view";
1052     for (auto size : memref.getShape())
1053       if (size < 0)
1054         ss << "sx";
1055       else
1056         ss << size << "x";
1057     appendMangledType(ss, memref.getElementType());
1058   } else if (auto vec = t.dyn_cast<VectorType>()) {
1059     ss << "vector";
1060     interleave(
1061         vec.getShape(), [&](int64_t i) { ss << i; }, [&]() { ss << "x"; });
1062     appendMangledType(ss, vec.getElementType());
1063   } else if (t.isSignlessIntOrIndexOrFloat()) {
1064     ss << t;
1065   } else {
1066     llvm_unreachable("Invalid type for linalg library name mangling");
1067   }
1068 }
1069 
1070 std::string mlir::linalg::generateLibraryCallName(Operation *op) {
1071   assert(isa<LinalgOp>(op));
1072   std::string name(op->getName().getStringRef().str());
1073   name.reserve(128);
1074   std::replace(name.begin(), name.end(), '.', '_');
1075   llvm::raw_string_ostream ss(name);
1076   ss << "_";
1077   auto types = op->getOperandTypes();
1078   interleave(
1079       types.begin(), types.end(), [&](Type t) { appendMangledType(ss, t); },
1080       [&]() { ss << "_"; });
1081   return ss.str();
1082 }
1083 
1084 // TODO(ntv, rriddle): Consider making all this boilerplate easy to autogenerate
1085 // with Tablegen. This seems a desirable property in the context of OpInterfaces
1086 // where a Linalg "named" op **isa** LinalgOp.
1087 LogicalResult ConvOp::fold(ArrayRef<Attribute>,
1088                            SmallVectorImpl<OpFoldResult> &) {
1089   return foldMemRefCast(*this);
1090 }
1091 LogicalResult PoolingMaxOp::fold(ArrayRef<Attribute>,
1092                                  SmallVectorImpl<OpFoldResult> &) {
1093   return foldMemRefCast(*this);
1094 }
1095 LogicalResult PoolingMinOp::fold(ArrayRef<Attribute>,
1096                                  SmallVectorImpl<OpFoldResult> &) {
1097   return foldMemRefCast(*this);
1098 }
1099 LogicalResult PoolingSumOp::fold(ArrayRef<Attribute>,
1100                                  SmallVectorImpl<OpFoldResult> &) {
1101   return foldMemRefCast(*this);
1102 }
1103 LogicalResult CopyOp::fold(ArrayRef<Attribute>,
1104                            SmallVectorImpl<OpFoldResult> &) {
1105   return foldMemRefCast(*this);
1106 }
1107 LogicalResult DotOp::fold(ArrayRef<Attribute>,
1108                           SmallVectorImpl<OpFoldResult> &) {
1109   return foldMemRefCast(*this);
1110 }
1111 LogicalResult FillOp::fold(ArrayRef<Attribute>,
1112                            SmallVectorImpl<OpFoldResult> &) {
1113   return foldMemRefCast(*this);
1114 }
1115 LogicalResult GenericOp::fold(ArrayRef<Attribute>,
1116                               SmallVectorImpl<OpFoldResult> &) {
1117   return foldMemRefCast(*this);
1118 }
1119 LogicalResult IndexedGenericOp::fold(ArrayRef<Attribute>,
1120                                      SmallVectorImpl<OpFoldResult> &) {
1121   return foldMemRefCast(*this);
1122 }
1123 LogicalResult MatvecOp::fold(ArrayRef<Attribute>,
1124                              SmallVectorImpl<OpFoldResult> &) {
1125   return foldMemRefCast(*this);
1126 }
1127 LogicalResult MatmulOp::fold(ArrayRef<Attribute>,
1128                              SmallVectorImpl<OpFoldResult> &) {
1129   return foldMemRefCast(*this);
1130 }
1131 OpFoldResult ReshapeOp::fold(ArrayRef<Attribute>) {
1132   if (succeeded(foldMemRefCast(*this)))
1133     return getResult();
1134   return {};
1135 }
1136 OpFoldResult SliceOp::fold(ArrayRef<Attribute>) {
1137   if (succeeded(foldMemRefCast(*this)))
1138     return getResult();
1139   return {};
1140 }
1141 OpFoldResult TransposeOp::fold(ArrayRef<Attribute>) {
1142   if (succeeded(foldMemRefCast(*this)))
1143     return getResult();
1144   return {};
1145 }
1146