1 //===- AffineOps.cpp - MLIR Affine 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 #include "mlir/Dialect/Affine/IR/AffineOps.h"
10 #include "mlir/Dialect/Affine/IR/AffineValueMap.h"
11 #include "mlir/Dialect/MemRef/IR/MemRef.h"
12 #include "mlir/Dialect/StandardOps/IR/Ops.h"
13 #include "mlir/IR/BlockAndValueMapping.h"
14 #include "mlir/IR/BuiltinOps.h"
15 #include "mlir/IR/IntegerSet.h"
16 #include "mlir/IR/Matchers.h"
17 #include "mlir/IR/OpImplementation.h"
18 #include "mlir/IR/PatternMatch.h"
19 #include "mlir/Transforms/InliningUtils.h"
20 #include "llvm/ADT/SetVector.h"
21 #include "llvm/ADT/SmallBitVector.h"
22 #include "llvm/ADT/TypeSwitch.h"
23 #include "llvm/Support/Debug.h"
24 
25 using namespace mlir;
26 using llvm::dbgs;
27 
28 #define DEBUG_TYPE "affine-analysis"
29 
30 /// A utility function to check if a value is defined at the top level of
31 /// `region` or is an argument of `region`. A value of index type defined at the
32 /// top level of a `AffineScope` region is always a valid symbol for all
33 /// uses in that region.
34 static bool isTopLevelValue(Value value, Region *region) {
35   if (auto arg = value.dyn_cast<BlockArgument>())
36     return arg.getParentRegion() == region;
37   return value.getDefiningOp()->getParentRegion() == region;
38 }
39 
40 /// Checks if `value` known to be a legal affine dimension or symbol in `src`
41 /// region remains legal if the operation that uses it is inlined into `dest`
42 /// with the given value mapping. `legalityCheck` is either `isValidDim` or
43 /// `isValidSymbol`, depending on the value being required to remain a valid
44 /// dimension or symbol.
45 static bool
46 remainsLegalAfterInline(Value value, Region *src, Region *dest,
47                         const BlockAndValueMapping &mapping,
48                         function_ref<bool(Value, Region *)> legalityCheck) {
49   // If the value is a valid dimension for any other reason than being
50   // a top-level value, it will remain valid: constants get inlined
51   // with the function, transitive affine applies also get inlined and
52   // will be checked themselves, etc.
53   if (!isTopLevelValue(value, src))
54     return true;
55 
56   // If it's a top-level value because it's a block operand, i.e. a
57   // function argument, check whether the value replacing it after
58   // inlining is a valid dimension in the new region.
59   if (value.isa<BlockArgument>())
60     return legalityCheck(mapping.lookup(value), dest);
61 
62   // If it's a top-level value beacuse it's defined in the region,
63   // it can only be inlined if the defining op is a constant or a
64   // `dim`, which can appear anywhere and be valid, since the defining
65   // op won't be top-level anymore after inlining.
66   Attribute operandCst;
67   return matchPattern(value.getDefiningOp(), m_Constant(&operandCst)) ||
68          value.getDefiningOp<memref::DimOp>();
69 }
70 
71 /// Checks if all values known to be legal affine dimensions or symbols in `src`
72 /// remain so if their respective users are inlined into `dest`.
73 static bool
74 remainsLegalAfterInline(ValueRange values, Region *src, Region *dest,
75                         const BlockAndValueMapping &mapping,
76                         function_ref<bool(Value, Region *)> legalityCheck) {
77   return llvm::all_of(values, [&](Value v) {
78     return remainsLegalAfterInline(v, src, dest, mapping, legalityCheck);
79   });
80 }
81 
82 /// Checks if an affine read or write operation remains legal after inlining
83 /// from `src` to `dest`.
84 template <typename OpTy>
85 static bool remainsLegalAfterInline(OpTy op, Region *src, Region *dest,
86                                     const BlockAndValueMapping &mapping) {
87   static_assert(llvm::is_one_of<OpTy, AffineReadOpInterface,
88                                 AffineWriteOpInterface>::value,
89                 "only ops with affine read/write interface are supported");
90 
91   AffineMap map = op.getAffineMap();
92   ValueRange dimOperands = op.getMapOperands().take_front(map.getNumDims());
93   ValueRange symbolOperands =
94       op.getMapOperands().take_back(map.getNumSymbols());
95   if (!remainsLegalAfterInline(
96           dimOperands, src, dest, mapping,
97           static_cast<bool (*)(Value, Region *)>(isValidDim)))
98     return false;
99   if (!remainsLegalAfterInline(
100           symbolOperands, src, dest, mapping,
101           static_cast<bool (*)(Value, Region *)>(isValidSymbol)))
102     return false;
103   return true;
104 }
105 
106 /// Checks if an affine apply operation remains legal after inlining from `src`
107 /// to `dest`.
108 template <>
109 bool remainsLegalAfterInline(AffineApplyOp op, Region *src, Region *dest,
110                              const BlockAndValueMapping &mapping) {
111   // If it's a valid dimension, we need to check that it remains so.
112   if (isValidDim(op.getResult(), src))
113     return remainsLegalAfterInline(
114         op.getMapOperands(), src, dest, mapping,
115         static_cast<bool (*)(Value, Region *)>(isValidDim));
116 
117   // Otherwise it must be a valid symbol, check that it remains so.
118   return remainsLegalAfterInline(
119       op.getMapOperands(), src, dest, mapping,
120       static_cast<bool (*)(Value, Region *)>(isValidSymbol));
121 }
122 
123 //===----------------------------------------------------------------------===//
124 // AffineDialect Interfaces
125 //===----------------------------------------------------------------------===//
126 
127 namespace {
128 /// This class defines the interface for handling inlining with affine
129 /// operations.
130 struct AffineInlinerInterface : public DialectInlinerInterface {
131   using DialectInlinerInterface::DialectInlinerInterface;
132 
133   //===--------------------------------------------------------------------===//
134   // Analysis Hooks
135   //===--------------------------------------------------------------------===//
136 
137   /// Returns true if the given region 'src' can be inlined into the region
138   /// 'dest' that is attached to an operation registered to the current dialect.
139   /// 'wouldBeCloned' is set if the region is cloned into its new location
140   /// rather than moved, indicating there may be other users.
141   bool isLegalToInline(Region *dest, Region *src, bool wouldBeCloned,
142                        BlockAndValueMapping &valueMapping) const final {
143     // We can inline into affine loops and conditionals if this doesn't break
144     // affine value categorization rules.
145     Operation *destOp = dest->getParentOp();
146     if (!isa<AffineParallelOp, AffineForOp, AffineIfOp>(destOp))
147       return false;
148 
149     // Multi-block regions cannot be inlined into affine constructs, all of
150     // which require single-block regions.
151     if (!llvm::hasSingleElement(*src))
152       return false;
153 
154     // Side-effecting operations that the affine dialect cannot understand
155     // should not be inlined.
156     Block &srcBlock = src->front();
157     for (Operation &op : srcBlock) {
158       // Ops with no side effects are fine,
159       if (auto iface = dyn_cast<MemoryEffectOpInterface>(op)) {
160         if (iface.hasNoEffect())
161           continue;
162       }
163 
164       // Assuming the inlined region is valid, we only need to check if the
165       // inlining would change it.
166       bool remainsValid =
167           llvm::TypeSwitch<Operation *, bool>(&op)
168               .Case<AffineApplyOp, AffineReadOpInterface,
169                     AffineWriteOpInterface>([&](auto op) {
170                 return remainsLegalAfterInline(op, src, dest, valueMapping);
171               })
172               .Default([](Operation *) {
173                 // Conservatively disallow inlining ops we cannot reason about.
174                 return false;
175               });
176 
177       if (!remainsValid)
178         return false;
179     }
180 
181     return true;
182   }
183 
184   /// Returns true if the given operation 'op', that is registered to this
185   /// dialect, can be inlined into the given region, false otherwise.
186   bool isLegalToInline(Operation *op, Region *region, bool wouldBeCloned,
187                        BlockAndValueMapping &valueMapping) const final {
188     // Always allow inlining affine operations into a region that is marked as
189     // affine scope, or into affine loops and conditionals. There are some edge
190     // cases when inlining *into* affine structures, but that is handled in the
191     // other 'isLegalToInline' hook above.
192     Operation *parentOp = region->getParentOp();
193     return parentOp->hasTrait<OpTrait::AffineScope>() ||
194            isa<AffineForOp, AffineParallelOp, AffineIfOp>(parentOp);
195   }
196 
197   /// Affine regions should be analyzed recursively.
198   bool shouldAnalyzeRecursively(Operation *op) const final { return true; }
199 };
200 } // end anonymous namespace
201 
202 //===----------------------------------------------------------------------===//
203 // AffineDialect
204 //===----------------------------------------------------------------------===//
205 
206 void AffineDialect::initialize() {
207   addOperations<AffineDmaStartOp, AffineDmaWaitOp,
208 #define GET_OP_LIST
209 #include "mlir/Dialect/Affine/IR/AffineOps.cpp.inc"
210                 >();
211   addInterfaces<AffineInlinerInterface>();
212 }
213 
214 /// Materialize a single constant operation from a given attribute value with
215 /// the desired resultant type.
216 Operation *AffineDialect::materializeConstant(OpBuilder &builder,
217                                               Attribute value, Type type,
218                                               Location loc) {
219   return builder.create<ConstantOp>(loc, type, value);
220 }
221 
222 /// A utility function to check if a value is defined at the top level of an
223 /// op with trait `AffineScope`. If the value is defined in an unlinked region,
224 /// conservatively assume it is not top-level. A value of index type defined at
225 /// the top level is always a valid symbol.
226 bool mlir::isTopLevelValue(Value value) {
227   if (auto arg = value.dyn_cast<BlockArgument>()) {
228     // The block owning the argument may be unlinked, e.g. when the surrounding
229     // region has not yet been attached to an Op, at which point the parent Op
230     // is null.
231     Operation *parentOp = arg.getOwner()->getParentOp();
232     return parentOp && parentOp->hasTrait<OpTrait::AffineScope>();
233   }
234   // The defining Op may live in an unlinked block so its parent Op may be null.
235   Operation *parentOp = value.getDefiningOp()->getParentOp();
236   return parentOp && parentOp->hasTrait<OpTrait::AffineScope>();
237 }
238 
239 /// Returns the closest region enclosing `op` that is held by an operation with
240 /// trait `AffineScope`; `nullptr` if there is no such region.
241 //  TODO: getAffineScope should be publicly exposed for affine passes/utilities.
242 static Region *getAffineScope(Operation *op) {
243   auto *curOp = op;
244   while (auto *parentOp = curOp->getParentOp()) {
245     if (parentOp->hasTrait<OpTrait::AffineScope>())
246       return curOp->getParentRegion();
247     curOp = parentOp;
248   }
249   return nullptr;
250 }
251 
252 // A Value can be used as a dimension id iff it meets one of the following
253 // conditions:
254 // *) It is valid as a symbol.
255 // *) It is an induction variable.
256 // *) It is the result of affine apply operation with dimension id arguments.
257 bool mlir::isValidDim(Value value) {
258   // The value must be an index type.
259   if (!value.getType().isIndex())
260     return false;
261 
262   if (auto *defOp = value.getDefiningOp())
263     return isValidDim(value, getAffineScope(defOp));
264 
265   // This value has to be a block argument for an op that has the
266   // `AffineScope` trait or for an affine.for or affine.parallel.
267   auto *parentOp = value.cast<BlockArgument>().getOwner()->getParentOp();
268   return parentOp && (parentOp->hasTrait<OpTrait::AffineScope>() ||
269                       isa<AffineForOp, AffineParallelOp>(parentOp));
270 }
271 
272 // Value can be used as a dimension id iff it meets one of the following
273 // conditions:
274 // *) It is valid as a symbol.
275 // *) It is an induction variable.
276 // *) It is the result of an affine apply operation with dimension id operands.
277 bool mlir::isValidDim(Value value, Region *region) {
278   // The value must be an index type.
279   if (!value.getType().isIndex())
280     return false;
281 
282   // All valid symbols are okay.
283   if (isValidSymbol(value, region))
284     return true;
285 
286   auto *op = value.getDefiningOp();
287   if (!op) {
288     // This value has to be a block argument for an affine.for or an
289     // affine.parallel.
290     auto *parentOp = value.cast<BlockArgument>().getOwner()->getParentOp();
291     return isa<AffineForOp, AffineParallelOp>(parentOp);
292   }
293 
294   // Affine apply operation is ok if all of its operands are ok.
295   if (auto applyOp = dyn_cast<AffineApplyOp>(op))
296     return applyOp.isValidDim(region);
297   // The dim op is okay if its operand memref/tensor is defined at the top
298   // level.
299   if (auto dimOp = dyn_cast<memref::DimOp>(op))
300     return isTopLevelValue(dimOp.memrefOrTensor());
301   return false;
302 }
303 
304 /// Returns true if the 'index' dimension of the `memref` defined by
305 /// `memrefDefOp` is a statically  shaped one or defined using a valid symbol
306 /// for `region`.
307 template <typename AnyMemRefDefOp>
308 static bool isMemRefSizeValidSymbol(AnyMemRefDefOp memrefDefOp, unsigned index,
309                                     Region *region) {
310   auto memRefType = memrefDefOp.getType();
311   // Statically shaped.
312   if (!memRefType.isDynamicDim(index))
313     return true;
314   // Get the position of the dimension among dynamic dimensions;
315   unsigned dynamicDimPos = memRefType.getDynamicDimIndex(index);
316   return isValidSymbol(*(memrefDefOp.getDynamicSizes().begin() + dynamicDimPos),
317                        region);
318 }
319 
320 /// Returns true if the result of the dim op is a valid symbol for `region`.
321 static bool isDimOpValidSymbol(memref::DimOp dimOp, Region *region) {
322   // The dim op is okay if its operand memref is defined at the top level.
323   if (isTopLevelValue(dimOp.memrefOrTensor()))
324     return true;
325 
326   // Conservatively handle remaining BlockArguments as non-valid symbols.
327   // E.g. scf.for iterArgs.
328   if (dimOp.memrefOrTensor().isa<BlockArgument>())
329     return false;
330 
331   // The dim op is also okay if its operand memref is a view/subview whose
332   // corresponding size is a valid symbol.
333   Optional<int64_t> index = dimOp.getConstantIndex();
334   assert(index.hasValue() &&
335          "expect only `dim` operations with a constant index");
336   int64_t i = index.getValue();
337   return TypeSwitch<Operation *, bool>(dimOp.memrefOrTensor().getDefiningOp())
338       .Case<memref::ViewOp, memref::SubViewOp, memref::AllocOp>(
339           [&](auto op) { return isMemRefSizeValidSymbol(op, i, region); })
340       .Default([](Operation *) { return false; });
341 }
342 
343 // A value can be used as a symbol (at all its use sites) iff it meets one of
344 // the following conditions:
345 // *) It is a constant.
346 // *) Its defining op or block arg appearance is immediately enclosed by an op
347 //    with `AffineScope` trait.
348 // *) It is the result of an affine.apply operation with symbol operands.
349 // *) It is a result of the dim op on a memref whose corresponding size is a
350 //    valid symbol.
351 bool mlir::isValidSymbol(Value value) {
352   // The value must be an index type.
353   if (!value.getType().isIndex())
354     return false;
355 
356   // Check that the value is a top level value.
357   if (isTopLevelValue(value))
358     return true;
359 
360   if (auto *defOp = value.getDefiningOp())
361     return isValidSymbol(value, getAffineScope(defOp));
362 
363   return false;
364 }
365 
366 /// A value can be used as a symbol for `region` iff it meets onf of the the
367 /// following conditions:
368 /// *) It is a constant.
369 /// *) It is the result of an affine apply operation with symbol arguments.
370 /// *) It is a result of the dim op on a memref whose corresponding size is
371 ///    a valid symbol.
372 /// *) It is defined at the top level of 'region' or is its argument.
373 /// *) It dominates `region`'s parent op.
374 /// If `region` is null, conservatively assume the symbol definition scope does
375 /// not exist and only accept the values that would be symbols regardless of
376 /// the surrounding region structure, i.e. the first three cases above.
377 bool mlir::isValidSymbol(Value value, Region *region) {
378   // The value must be an index type.
379   if (!value.getType().isIndex())
380     return false;
381 
382   // A top-level value is a valid symbol.
383   if (region && ::isTopLevelValue(value, region))
384     return true;
385 
386   auto *defOp = value.getDefiningOp();
387   if (!defOp) {
388     // A block argument that is not a top-level value is a valid symbol if it
389     // dominates region's parent op.
390     Operation *regionOp = region ? region->getParentOp() : nullptr;
391     if (regionOp && !regionOp->hasTrait<OpTrait::IsIsolatedFromAbove>())
392       if (auto *parentOpRegion = region->getParentOp()->getParentRegion())
393         return isValidSymbol(value, parentOpRegion);
394     return false;
395   }
396 
397   // Constant operation is ok.
398   Attribute operandCst;
399   if (matchPattern(defOp, m_Constant(&operandCst)))
400     return true;
401 
402   // Affine apply operation is ok if all of its operands are ok.
403   if (auto applyOp = dyn_cast<AffineApplyOp>(defOp))
404     return applyOp.isValidSymbol(region);
405 
406   // Dim op results could be valid symbols at any level.
407   if (auto dimOp = dyn_cast<memref::DimOp>(defOp))
408     return isDimOpValidSymbol(dimOp, region);
409 
410   // Check for values dominating `region`'s parent op.
411   Operation *regionOp = region ? region->getParentOp() : nullptr;
412   if (regionOp && !regionOp->hasTrait<OpTrait::IsIsolatedFromAbove>())
413     if (auto *parentRegion = region->getParentOp()->getParentRegion())
414       return isValidSymbol(value, parentRegion);
415 
416   return false;
417 }
418 
419 // Returns true if 'value' is a valid index to an affine operation (e.g.
420 // affine.load, affine.store, affine.dma_start, affine.dma_wait) where
421 // `region` provides the polyhedral symbol scope. Returns false otherwise.
422 static bool isValidAffineIndexOperand(Value value, Region *region) {
423   return isValidDim(value, region) || isValidSymbol(value, region);
424 }
425 
426 /// Prints dimension and symbol list.
427 static void printDimAndSymbolList(Operation::operand_iterator begin,
428                                   Operation::operand_iterator end,
429                                   unsigned numDims, OpAsmPrinter &printer) {
430   OperandRange operands(begin, end);
431   printer << '(' << operands.take_front(numDims) << ')';
432   if (operands.size() > numDims)
433     printer << '[' << operands.drop_front(numDims) << ']';
434 }
435 
436 /// Parses dimension and symbol list and returns true if parsing failed.
437 ParseResult mlir::parseDimAndSymbolList(OpAsmParser &parser,
438                                         SmallVectorImpl<Value> &operands,
439                                         unsigned &numDims) {
440   SmallVector<OpAsmParser::OperandType, 8> opInfos;
441   if (parser.parseOperandList(opInfos, OpAsmParser::Delimiter::Paren))
442     return failure();
443   // Store number of dimensions for validation by caller.
444   numDims = opInfos.size();
445 
446   // Parse the optional symbol operands.
447   auto indexTy = parser.getBuilder().getIndexType();
448   return failure(parser.parseOperandList(
449                      opInfos, OpAsmParser::Delimiter::OptionalSquare) ||
450                  parser.resolveOperands(opInfos, indexTy, operands));
451 }
452 
453 /// Utility function to verify that a set of operands are valid dimension and
454 /// symbol identifiers. The operands should be laid out such that the dimension
455 /// operands are before the symbol operands. This function returns failure if
456 /// there was an invalid operand. An operation is provided to emit any necessary
457 /// errors.
458 template <typename OpTy>
459 static LogicalResult
460 verifyDimAndSymbolIdentifiers(OpTy &op, Operation::operand_range operands,
461                               unsigned numDims) {
462   unsigned opIt = 0;
463   for (auto operand : operands) {
464     if (opIt++ < numDims) {
465       if (!isValidDim(operand, getAffineScope(op)))
466         return op.emitOpError("operand cannot be used as a dimension id");
467     } else if (!isValidSymbol(operand, getAffineScope(op))) {
468       return op.emitOpError("operand cannot be used as a symbol");
469     }
470   }
471   return success();
472 }
473 
474 //===----------------------------------------------------------------------===//
475 // AffineApplyOp
476 //===----------------------------------------------------------------------===//
477 
478 AffineValueMap AffineApplyOp::getAffineValueMap() {
479   return AffineValueMap(getAffineMap(), getOperands(), getResult());
480 }
481 
482 static ParseResult parseAffineApplyOp(OpAsmParser &parser,
483                                       OperationState &result) {
484   auto &builder = parser.getBuilder();
485   auto indexTy = builder.getIndexType();
486 
487   AffineMapAttr mapAttr;
488   unsigned numDims;
489   if (parser.parseAttribute(mapAttr, "map", result.attributes) ||
490       parseDimAndSymbolList(parser, result.operands, numDims) ||
491       parser.parseOptionalAttrDict(result.attributes))
492     return failure();
493   auto map = mapAttr.getValue();
494 
495   if (map.getNumDims() != numDims ||
496       numDims + map.getNumSymbols() != result.operands.size()) {
497     return parser.emitError(parser.getNameLoc(),
498                             "dimension or symbol index mismatch");
499   }
500 
501   result.types.append(map.getNumResults(), indexTy);
502   return success();
503 }
504 
505 static void print(OpAsmPrinter &p, AffineApplyOp op) {
506   p << AffineApplyOp::getOperationName() << " " << op.mapAttr();
507   printDimAndSymbolList(op.operand_begin(), op.operand_end(),
508                         op.getAffineMap().getNumDims(), p);
509   p.printOptionalAttrDict(op->getAttrs(), /*elidedAttrs=*/{"map"});
510 }
511 
512 static LogicalResult verify(AffineApplyOp op) {
513   // Check input and output dimensions match.
514   auto map = op.map();
515 
516   // Verify that operand count matches affine map dimension and symbol count.
517   if (op.getNumOperands() != map.getNumDims() + map.getNumSymbols())
518     return op.emitOpError(
519         "operand count and affine map dimension and symbol count must match");
520 
521   // Verify that the map only produces one result.
522   if (map.getNumResults() != 1)
523     return op.emitOpError("mapping must produce one value");
524 
525   return success();
526 }
527 
528 // The result of the affine apply operation can be used as a dimension id if all
529 // its operands are valid dimension ids.
530 bool AffineApplyOp::isValidDim() {
531   return llvm::all_of(getOperands(),
532                       [](Value op) { return mlir::isValidDim(op); });
533 }
534 
535 // The result of the affine apply operation can be used as a dimension id if all
536 // its operands are valid dimension ids with the parent operation of `region`
537 // defining the polyhedral scope for symbols.
538 bool AffineApplyOp::isValidDim(Region *region) {
539   return llvm::all_of(getOperands(),
540                       [&](Value op) { return ::isValidDim(op, region); });
541 }
542 
543 // The result of the affine apply operation can be used as a symbol if all its
544 // operands are symbols.
545 bool AffineApplyOp::isValidSymbol() {
546   return llvm::all_of(getOperands(),
547                       [](Value op) { return mlir::isValidSymbol(op); });
548 }
549 
550 // The result of the affine apply operation can be used as a symbol in `region`
551 // if all its operands are symbols in `region`.
552 bool AffineApplyOp::isValidSymbol(Region *region) {
553   return llvm::all_of(getOperands(), [&](Value operand) {
554     return mlir::isValidSymbol(operand, region);
555   });
556 }
557 
558 OpFoldResult AffineApplyOp::fold(ArrayRef<Attribute> operands) {
559   auto map = getAffineMap();
560 
561   // Fold dims and symbols to existing values.
562   auto expr = map.getResult(0);
563   if (auto dim = expr.dyn_cast<AffineDimExpr>())
564     return getOperand(dim.getPosition());
565   if (auto sym = expr.dyn_cast<AffineSymbolExpr>())
566     return getOperand(map.getNumDims() + sym.getPosition());
567 
568   // Otherwise, default to folding the map.
569   SmallVector<Attribute, 1> result;
570   if (failed(map.constantFold(operands, result)))
571     return {};
572   return result[0];
573 }
574 
575 /// Replace all occurrences of AffineExpr at position `pos` in `map` by the
576 /// defining AffineApplyOp expression and operands.
577 /// When `dimOrSymbolPosition < dims.size()`, AffineDimExpr@[pos] is replaced.
578 /// When `dimOrSymbolPosition >= dims.size()`,
579 /// AffineSymbolExpr@[pos - dims.size()] is replaced.
580 /// Mutate `map`,`dims` and `syms` in place as follows:
581 ///   1. `dims` and `syms` are only appended to.
582 ///   2. `map` dim and symbols are gradually shifted to higer positions.
583 ///   3. Old `dim` and `sym` entries are replaced by nullptr
584 /// This avoids the need for any bookkeeping.
585 static LogicalResult replaceDimOrSym(AffineMap *map,
586                                      unsigned dimOrSymbolPosition,
587                                      SmallVectorImpl<Value> &dims,
588                                      SmallVectorImpl<Value> &syms) {
589   bool isDimReplacement = (dimOrSymbolPosition < dims.size());
590   unsigned pos = isDimReplacement ? dimOrSymbolPosition
591                                   : dimOrSymbolPosition - dims.size();
592   Value &v = isDimReplacement ? dims[pos] : syms[pos];
593   if (!v)
594     return failure();
595 
596   auto affineApply = v.getDefiningOp<AffineApplyOp>();
597   if (!affineApply)
598     return failure();
599 
600   // At this point we will perform a replacement of `v`, set the entry in `dim`
601   // or `sym` to nullptr immediately.
602   v = nullptr;
603 
604   // Compute the map, dims and symbols coming from the AffineApplyOp.
605   AffineMap composeMap = affineApply.getAffineMap();
606   assert(composeMap.getNumResults() == 1 && "affine.apply with >1 results");
607   AffineExpr composeExpr =
608       composeMap.shiftDims(dims.size()).shiftSymbols(syms.size()).getResult(0);
609   ValueRange composeDims =
610       affineApply.getMapOperands().take_front(composeMap.getNumDims());
611   ValueRange composeSyms =
612       affineApply.getMapOperands().take_back(composeMap.getNumSymbols());
613 
614   // Perform the replacement and append the dims and symbols where relevant.
615   MLIRContext *ctx = map->getContext();
616   AffineExpr toReplace = isDimReplacement ? getAffineDimExpr(pos, ctx)
617                                           : getAffineSymbolExpr(pos, ctx);
618   *map = map->replace(toReplace, composeExpr, dims.size(), syms.size());
619   dims.append(composeDims.begin(), composeDims.end());
620   syms.append(composeSyms.begin(), composeSyms.end());
621 
622   return success();
623 }
624 
625 /// Iterate over `operands` and fold away all those produced by an AffineApplyOp
626 /// iteratively. Perform canonicalization of map and operands as well as
627 /// AffineMap simplification. `map` and `operands` are mutated in place.
628 static void composeAffineMapAndOperands(AffineMap *map,
629                                         SmallVectorImpl<Value> *operands) {
630   if (map->getNumResults() == 0) {
631     canonicalizeMapAndOperands(map, operands);
632     *map = simplifyAffineMap(*map);
633     return;
634   }
635 
636   MLIRContext *ctx = map->getContext();
637   SmallVector<Value, 4> dims(operands->begin(),
638                              operands->begin() + map->getNumDims());
639   SmallVector<Value, 4> syms(operands->begin() + map->getNumDims(),
640                              operands->end());
641 
642   // Iterate over dims and symbols coming from AffineApplyOp and replace until
643   // exhaustion. This iteratively mutates `map`, `dims` and `syms`. Both `dims`
644   // and `syms` can only increase by construction.
645   // The implementation uses a `while` loop to support the case of symbols
646   // that may be constructed from dims ;this may be overkill.
647   while (true) {
648     bool changed = false;
649     for (unsigned pos = 0; pos != dims.size() + syms.size(); ++pos)
650       if ((changed |= succeeded(replaceDimOrSym(map, pos, dims, syms))))
651         break;
652     if (!changed)
653       break;
654   }
655 
656   // Clear operands so we can fill them anew.
657   operands->clear();
658 
659   // At this point we may have introduced null operands, prune them out before
660   // canonicalizing map and operands.
661   unsigned nDims = 0, nSyms = 0;
662   SmallVector<AffineExpr, 4> dimReplacements, symReplacements;
663   dimReplacements.reserve(dims.size());
664   symReplacements.reserve(syms.size());
665   for (auto *container : {&dims, &syms}) {
666     bool isDim = (container == &dims);
667     auto &repls = isDim ? dimReplacements : symReplacements;
668     for (auto en : llvm::enumerate(*container)) {
669       Value v = en.value();
670       if (!v) {
671         assert(isDim ? !map->isFunctionOfDim(en.index())
672                      : !map->isFunctionOfSymbol(en.index()) &&
673                            "map is function of unexpected expr@pos");
674         repls.push_back(getAffineConstantExpr(0, ctx));
675         continue;
676       }
677       repls.push_back(isDim ? getAffineDimExpr(nDims++, ctx)
678                             : getAffineSymbolExpr(nSyms++, ctx));
679       operands->push_back(v);
680     }
681   }
682   *map = map->replaceDimsAndSymbols(dimReplacements, symReplacements, nDims,
683                                     nSyms);
684 
685   // Canonicalize and simplify before returning.
686   canonicalizeMapAndOperands(map, operands);
687   *map = simplifyAffineMap(*map);
688 }
689 
690 void mlir::fullyComposeAffineMapAndOperands(AffineMap *map,
691                                             SmallVectorImpl<Value> *operands) {
692   while (llvm::any_of(*operands, [](Value v) {
693     return isa_and_nonnull<AffineApplyOp>(v.getDefiningOp());
694   })) {
695     composeAffineMapAndOperands(map, operands);
696   }
697 }
698 
699 AffineApplyOp mlir::makeComposedAffineApply(OpBuilder &b, Location loc,
700                                             AffineMap map,
701                                             ArrayRef<Value> operands) {
702   AffineMap normalizedMap = map;
703   SmallVector<Value, 8> normalizedOperands(operands.begin(), operands.end());
704   composeAffineMapAndOperands(&normalizedMap, &normalizedOperands);
705   assert(normalizedMap);
706   return b.create<AffineApplyOp>(loc, normalizedMap, normalizedOperands);
707 }
708 
709 // A symbol may appear as a dim in affine.apply operations. This function
710 // canonicalizes dims that are valid symbols into actual symbols.
711 template <class MapOrSet>
712 static void canonicalizePromotedSymbols(MapOrSet *mapOrSet,
713                                         SmallVectorImpl<Value> *operands) {
714   if (!mapOrSet || operands->empty())
715     return;
716 
717   assert(mapOrSet->getNumInputs() == operands->size() &&
718          "map/set inputs must match number of operands");
719 
720   auto *context = mapOrSet->getContext();
721   SmallVector<Value, 8> resultOperands;
722   resultOperands.reserve(operands->size());
723   SmallVector<Value, 8> remappedSymbols;
724   remappedSymbols.reserve(operands->size());
725   unsigned nextDim = 0;
726   unsigned nextSym = 0;
727   unsigned oldNumSyms = mapOrSet->getNumSymbols();
728   SmallVector<AffineExpr, 8> dimRemapping(mapOrSet->getNumDims());
729   for (unsigned i = 0, e = mapOrSet->getNumInputs(); i != e; ++i) {
730     if (i < mapOrSet->getNumDims()) {
731       if (isValidSymbol((*operands)[i])) {
732         // This is a valid symbol that appears as a dim, canonicalize it.
733         dimRemapping[i] = getAffineSymbolExpr(oldNumSyms + nextSym++, context);
734         remappedSymbols.push_back((*operands)[i]);
735       } else {
736         dimRemapping[i] = getAffineDimExpr(nextDim++, context);
737         resultOperands.push_back((*operands)[i]);
738       }
739     } else {
740       resultOperands.push_back((*operands)[i]);
741     }
742   }
743 
744   resultOperands.append(remappedSymbols.begin(), remappedSymbols.end());
745   *operands = resultOperands;
746   *mapOrSet = mapOrSet->replaceDimsAndSymbols(dimRemapping, {}, nextDim,
747                                               oldNumSyms + nextSym);
748 
749   assert(mapOrSet->getNumInputs() == operands->size() &&
750          "map/set inputs must match number of operands");
751 }
752 
753 // Works for either an affine map or an integer set.
754 template <class MapOrSet>
755 static void canonicalizeMapOrSetAndOperands(MapOrSet *mapOrSet,
756                                             SmallVectorImpl<Value> *operands) {
757   static_assert(llvm::is_one_of<MapOrSet, AffineMap, IntegerSet>::value,
758                 "Argument must be either of AffineMap or IntegerSet type");
759 
760   if (!mapOrSet || operands->empty())
761     return;
762 
763   assert(mapOrSet->getNumInputs() == operands->size() &&
764          "map/set inputs must match number of operands");
765 
766   canonicalizePromotedSymbols<MapOrSet>(mapOrSet, operands);
767 
768   // Check to see what dims are used.
769   llvm::SmallBitVector usedDims(mapOrSet->getNumDims());
770   llvm::SmallBitVector usedSyms(mapOrSet->getNumSymbols());
771   mapOrSet->walkExprs([&](AffineExpr expr) {
772     if (auto dimExpr = expr.dyn_cast<AffineDimExpr>())
773       usedDims[dimExpr.getPosition()] = true;
774     else if (auto symExpr = expr.dyn_cast<AffineSymbolExpr>())
775       usedSyms[symExpr.getPosition()] = true;
776   });
777 
778   auto *context = mapOrSet->getContext();
779 
780   SmallVector<Value, 8> resultOperands;
781   resultOperands.reserve(operands->size());
782 
783   llvm::SmallDenseMap<Value, AffineExpr, 8> seenDims;
784   SmallVector<AffineExpr, 8> dimRemapping(mapOrSet->getNumDims());
785   unsigned nextDim = 0;
786   for (unsigned i = 0, e = mapOrSet->getNumDims(); i != e; ++i) {
787     if (usedDims[i]) {
788       // Remap dim positions for duplicate operands.
789       auto it = seenDims.find((*operands)[i]);
790       if (it == seenDims.end()) {
791         dimRemapping[i] = getAffineDimExpr(nextDim++, context);
792         resultOperands.push_back((*operands)[i]);
793         seenDims.insert(std::make_pair((*operands)[i], dimRemapping[i]));
794       } else {
795         dimRemapping[i] = it->second;
796       }
797     }
798   }
799   llvm::SmallDenseMap<Value, AffineExpr, 8> seenSymbols;
800   SmallVector<AffineExpr, 8> symRemapping(mapOrSet->getNumSymbols());
801   unsigned nextSym = 0;
802   for (unsigned i = 0, e = mapOrSet->getNumSymbols(); i != e; ++i) {
803     if (!usedSyms[i])
804       continue;
805     // Handle constant operands (only needed for symbolic operands since
806     // constant operands in dimensional positions would have already been
807     // promoted to symbolic positions above).
808     IntegerAttr operandCst;
809     if (matchPattern((*operands)[i + mapOrSet->getNumDims()],
810                      m_Constant(&operandCst))) {
811       symRemapping[i] =
812           getAffineConstantExpr(operandCst.getValue().getSExtValue(), context);
813       continue;
814     }
815     // Remap symbol positions for duplicate operands.
816     auto it = seenSymbols.find((*operands)[i + mapOrSet->getNumDims()]);
817     if (it == seenSymbols.end()) {
818       symRemapping[i] = getAffineSymbolExpr(nextSym++, context);
819       resultOperands.push_back((*operands)[i + mapOrSet->getNumDims()]);
820       seenSymbols.insert(std::make_pair((*operands)[i + mapOrSet->getNumDims()],
821                                         symRemapping[i]));
822     } else {
823       symRemapping[i] = it->second;
824     }
825   }
826   *mapOrSet = mapOrSet->replaceDimsAndSymbols(dimRemapping, symRemapping,
827                                               nextDim, nextSym);
828   *operands = resultOperands;
829 }
830 
831 void mlir::canonicalizeMapAndOperands(AffineMap *map,
832                                       SmallVectorImpl<Value> *operands) {
833   canonicalizeMapOrSetAndOperands<AffineMap>(map, operands);
834 }
835 
836 void mlir::canonicalizeSetAndOperands(IntegerSet *set,
837                                       SmallVectorImpl<Value> *operands) {
838   canonicalizeMapOrSetAndOperands<IntegerSet>(set, operands);
839 }
840 
841 namespace {
842 /// Simplify AffineApply, AffineLoad, and AffineStore operations by composing
843 /// maps that supply results into them.
844 ///
845 template <typename AffineOpTy>
846 struct SimplifyAffineOp : public OpRewritePattern<AffineOpTy> {
847   using OpRewritePattern<AffineOpTy>::OpRewritePattern;
848 
849   /// Replace the affine op with another instance of it with the supplied
850   /// map and mapOperands.
851   void replaceAffineOp(PatternRewriter &rewriter, AffineOpTy affineOp,
852                        AffineMap map, ArrayRef<Value> mapOperands) const;
853 
854   LogicalResult matchAndRewrite(AffineOpTy affineOp,
855                                 PatternRewriter &rewriter) const override {
856     static_assert(llvm::is_one_of<AffineOpTy, AffineLoadOp, AffinePrefetchOp,
857                                   AffineStoreOp, AffineApplyOp, AffineMinOp,
858                                   AffineMaxOp>::value,
859                   "affine load/store/apply/prefetch/min/max op expected");
860     auto map = affineOp.getAffineMap();
861     AffineMap oldMap = map;
862     auto oldOperands = affineOp.getMapOperands();
863     SmallVector<Value, 8> resultOperands(oldOperands);
864     composeAffineMapAndOperands(&map, &resultOperands);
865     if (map == oldMap && std::equal(oldOperands.begin(), oldOperands.end(),
866                                     resultOperands.begin()))
867       return failure();
868 
869     replaceAffineOp(rewriter, affineOp, map, resultOperands);
870     return success();
871   }
872 };
873 
874 // Specialize the template to account for the different build signatures for
875 // affine load, store, and apply ops.
876 template <>
877 void SimplifyAffineOp<AffineLoadOp>::replaceAffineOp(
878     PatternRewriter &rewriter, AffineLoadOp load, AffineMap map,
879     ArrayRef<Value> mapOperands) const {
880   rewriter.replaceOpWithNewOp<AffineLoadOp>(load, load.getMemRef(), map,
881                                             mapOperands);
882 }
883 template <>
884 void SimplifyAffineOp<AffinePrefetchOp>::replaceAffineOp(
885     PatternRewriter &rewriter, AffinePrefetchOp prefetch, AffineMap map,
886     ArrayRef<Value> mapOperands) const {
887   rewriter.replaceOpWithNewOp<AffinePrefetchOp>(
888       prefetch, prefetch.memref(), map, mapOperands, prefetch.localityHint(),
889       prefetch.isWrite(), prefetch.isDataCache());
890 }
891 template <>
892 void SimplifyAffineOp<AffineStoreOp>::replaceAffineOp(
893     PatternRewriter &rewriter, AffineStoreOp store, AffineMap map,
894     ArrayRef<Value> mapOperands) const {
895   rewriter.replaceOpWithNewOp<AffineStoreOp>(
896       store, store.getValueToStore(), store.getMemRef(), map, mapOperands);
897 }
898 
899 // Generic version for ops that don't have extra operands.
900 template <typename AffineOpTy>
901 void SimplifyAffineOp<AffineOpTy>::replaceAffineOp(
902     PatternRewriter &rewriter, AffineOpTy op, AffineMap map,
903     ArrayRef<Value> mapOperands) const {
904   rewriter.replaceOpWithNewOp<AffineOpTy>(op, map, mapOperands);
905 }
906 } // end anonymous namespace.
907 
908 void AffineApplyOp::getCanonicalizationPatterns(RewritePatternSet &results,
909                                                 MLIRContext *context) {
910   results.add<SimplifyAffineOp<AffineApplyOp>>(context);
911 }
912 
913 //===----------------------------------------------------------------------===//
914 // Common canonicalization pattern support logic
915 //===----------------------------------------------------------------------===//
916 
917 /// This is a common class used for patterns of the form
918 /// "someop(memrefcast) -> someop".  It folds the source of any memref.cast
919 /// into the root operation directly.
920 static LogicalResult foldMemRefCast(Operation *op) {
921   bool folded = false;
922   for (OpOperand &operand : op->getOpOperands()) {
923     auto cast = operand.get().getDefiningOp<memref::CastOp>();
924     if (cast && !cast.getOperand().getType().isa<UnrankedMemRefType>()) {
925       operand.set(cast.getOperand());
926       folded = true;
927     }
928   }
929   return success(folded);
930 }
931 
932 //===----------------------------------------------------------------------===//
933 // AffineDmaStartOp
934 //===----------------------------------------------------------------------===//
935 
936 // TODO: Check that map operands are loop IVs or symbols.
937 void AffineDmaStartOp::build(OpBuilder &builder, OperationState &result,
938                              Value srcMemRef, AffineMap srcMap,
939                              ValueRange srcIndices, Value destMemRef,
940                              AffineMap dstMap, ValueRange destIndices,
941                              Value tagMemRef, AffineMap tagMap,
942                              ValueRange tagIndices, Value numElements,
943                              Value stride, Value elementsPerStride) {
944   result.addOperands(srcMemRef);
945   result.addAttribute(getSrcMapAttrName(), AffineMapAttr::get(srcMap));
946   result.addOperands(srcIndices);
947   result.addOperands(destMemRef);
948   result.addAttribute(getDstMapAttrName(), AffineMapAttr::get(dstMap));
949   result.addOperands(destIndices);
950   result.addOperands(tagMemRef);
951   result.addAttribute(getTagMapAttrName(), AffineMapAttr::get(tagMap));
952   result.addOperands(tagIndices);
953   result.addOperands(numElements);
954   if (stride) {
955     result.addOperands({stride, elementsPerStride});
956   }
957 }
958 
959 void AffineDmaStartOp::print(OpAsmPrinter &p) {
960   p << "affine.dma_start " << getSrcMemRef() << '[';
961   p.printAffineMapOfSSAIds(getSrcMapAttr(), getSrcIndices());
962   p << "], " << getDstMemRef() << '[';
963   p.printAffineMapOfSSAIds(getDstMapAttr(), getDstIndices());
964   p << "], " << getTagMemRef() << '[';
965   p.printAffineMapOfSSAIds(getTagMapAttr(), getTagIndices());
966   p << "], " << getNumElements();
967   if (isStrided()) {
968     p << ", " << getStride();
969     p << ", " << getNumElementsPerStride();
970   }
971   p << " : " << getSrcMemRefType() << ", " << getDstMemRefType() << ", "
972     << getTagMemRefType();
973 }
974 
975 // Parse AffineDmaStartOp.
976 // Ex:
977 //   affine.dma_start %src[%i, %j], %dst[%k, %l], %tag[%index], %size,
978 //     %stride, %num_elt_per_stride
979 //       : memref<3076 x f32, 0>, memref<1024 x f32, 2>, memref<1 x i32>
980 //
981 ParseResult AffineDmaStartOp::parse(OpAsmParser &parser,
982                                     OperationState &result) {
983   OpAsmParser::OperandType srcMemRefInfo;
984   AffineMapAttr srcMapAttr;
985   SmallVector<OpAsmParser::OperandType, 4> srcMapOperands;
986   OpAsmParser::OperandType dstMemRefInfo;
987   AffineMapAttr dstMapAttr;
988   SmallVector<OpAsmParser::OperandType, 4> dstMapOperands;
989   OpAsmParser::OperandType tagMemRefInfo;
990   AffineMapAttr tagMapAttr;
991   SmallVector<OpAsmParser::OperandType, 4> tagMapOperands;
992   OpAsmParser::OperandType numElementsInfo;
993   SmallVector<OpAsmParser::OperandType, 2> strideInfo;
994 
995   SmallVector<Type, 3> types;
996   auto indexType = parser.getBuilder().getIndexType();
997 
998   // Parse and resolve the following list of operands:
999   // *) dst memref followed by its affine maps operands (in square brackets).
1000   // *) src memref followed by its affine map operands (in square brackets).
1001   // *) tag memref followed by its affine map operands (in square brackets).
1002   // *) number of elements transferred by DMA operation.
1003   if (parser.parseOperand(srcMemRefInfo) ||
1004       parser.parseAffineMapOfSSAIds(srcMapOperands, srcMapAttr,
1005                                     getSrcMapAttrName(), result.attributes) ||
1006       parser.parseComma() || parser.parseOperand(dstMemRefInfo) ||
1007       parser.parseAffineMapOfSSAIds(dstMapOperands, dstMapAttr,
1008                                     getDstMapAttrName(), result.attributes) ||
1009       parser.parseComma() || parser.parseOperand(tagMemRefInfo) ||
1010       parser.parseAffineMapOfSSAIds(tagMapOperands, tagMapAttr,
1011                                     getTagMapAttrName(), result.attributes) ||
1012       parser.parseComma() || parser.parseOperand(numElementsInfo))
1013     return failure();
1014 
1015   // Parse optional stride and elements per stride.
1016   if (parser.parseTrailingOperandList(strideInfo)) {
1017     return failure();
1018   }
1019   if (!strideInfo.empty() && strideInfo.size() != 2) {
1020     return parser.emitError(parser.getNameLoc(),
1021                             "expected two stride related operands");
1022   }
1023   bool isStrided = strideInfo.size() == 2;
1024 
1025   if (parser.parseColonTypeList(types))
1026     return failure();
1027 
1028   if (types.size() != 3)
1029     return parser.emitError(parser.getNameLoc(), "expected three types");
1030 
1031   if (parser.resolveOperand(srcMemRefInfo, types[0], result.operands) ||
1032       parser.resolveOperands(srcMapOperands, indexType, result.operands) ||
1033       parser.resolveOperand(dstMemRefInfo, types[1], result.operands) ||
1034       parser.resolveOperands(dstMapOperands, indexType, result.operands) ||
1035       parser.resolveOperand(tagMemRefInfo, types[2], result.operands) ||
1036       parser.resolveOperands(tagMapOperands, indexType, result.operands) ||
1037       parser.resolveOperand(numElementsInfo, indexType, result.operands))
1038     return failure();
1039 
1040   if (isStrided) {
1041     if (parser.resolveOperands(strideInfo, indexType, result.operands))
1042       return failure();
1043   }
1044 
1045   // Check that src/dst/tag operand counts match their map.numInputs.
1046   if (srcMapOperands.size() != srcMapAttr.getValue().getNumInputs() ||
1047       dstMapOperands.size() != dstMapAttr.getValue().getNumInputs() ||
1048       tagMapOperands.size() != tagMapAttr.getValue().getNumInputs())
1049     return parser.emitError(parser.getNameLoc(),
1050                             "memref operand count not equal to map.numInputs");
1051   return success();
1052 }
1053 
1054 LogicalResult AffineDmaStartOp::verify() {
1055   if (!getOperand(getSrcMemRefOperandIndex()).getType().isa<MemRefType>())
1056     return emitOpError("expected DMA source to be of memref type");
1057   if (!getOperand(getDstMemRefOperandIndex()).getType().isa<MemRefType>())
1058     return emitOpError("expected DMA destination to be of memref type");
1059   if (!getOperand(getTagMemRefOperandIndex()).getType().isa<MemRefType>())
1060     return emitOpError("expected DMA tag to be of memref type");
1061 
1062   // DMAs from different memory spaces supported.
1063   if (getSrcMemorySpace() == getDstMemorySpace()) {
1064     return emitOpError("DMA should be between different memory spaces");
1065   }
1066   unsigned numInputsAllMaps = getSrcMap().getNumInputs() +
1067                               getDstMap().getNumInputs() +
1068                               getTagMap().getNumInputs();
1069   if (getNumOperands() != numInputsAllMaps + 3 + 1 &&
1070       getNumOperands() != numInputsAllMaps + 3 + 1 + 2) {
1071     return emitOpError("incorrect number of operands");
1072   }
1073 
1074   Region *scope = getAffineScope(*this);
1075   for (auto idx : getSrcIndices()) {
1076     if (!idx.getType().isIndex())
1077       return emitOpError("src index to dma_start must have 'index' type");
1078     if (!isValidAffineIndexOperand(idx, scope))
1079       return emitOpError("src index must be a dimension or symbol identifier");
1080   }
1081   for (auto idx : getDstIndices()) {
1082     if (!idx.getType().isIndex())
1083       return emitOpError("dst index to dma_start must have 'index' type");
1084     if (!isValidAffineIndexOperand(idx, scope))
1085       return emitOpError("dst index must be a dimension or symbol identifier");
1086   }
1087   for (auto idx : getTagIndices()) {
1088     if (!idx.getType().isIndex())
1089       return emitOpError("tag index to dma_start must have 'index' type");
1090     if (!isValidAffineIndexOperand(idx, scope))
1091       return emitOpError("tag index must be a dimension or symbol identifier");
1092   }
1093   return success();
1094 }
1095 
1096 LogicalResult AffineDmaStartOp::fold(ArrayRef<Attribute> cstOperands,
1097                                      SmallVectorImpl<OpFoldResult> &results) {
1098   /// dma_start(memrefcast) -> dma_start
1099   return foldMemRefCast(*this);
1100 }
1101 
1102 //===----------------------------------------------------------------------===//
1103 // AffineDmaWaitOp
1104 //===----------------------------------------------------------------------===//
1105 
1106 // TODO: Check that map operands are loop IVs or symbols.
1107 void AffineDmaWaitOp::build(OpBuilder &builder, OperationState &result,
1108                             Value tagMemRef, AffineMap tagMap,
1109                             ValueRange tagIndices, Value numElements) {
1110   result.addOperands(tagMemRef);
1111   result.addAttribute(getTagMapAttrName(), AffineMapAttr::get(tagMap));
1112   result.addOperands(tagIndices);
1113   result.addOperands(numElements);
1114 }
1115 
1116 void AffineDmaWaitOp::print(OpAsmPrinter &p) {
1117   p << "affine.dma_wait " << getTagMemRef() << '[';
1118   SmallVector<Value, 2> operands(getTagIndices());
1119   p.printAffineMapOfSSAIds(getTagMapAttr(), operands);
1120   p << "], ";
1121   p.printOperand(getNumElements());
1122   p << " : " << getTagMemRef().getType();
1123 }
1124 
1125 // Parse AffineDmaWaitOp.
1126 // Eg:
1127 //   affine.dma_wait %tag[%index], %num_elements
1128 //     : memref<1 x i32, (d0) -> (d0), 4>
1129 //
1130 ParseResult AffineDmaWaitOp::parse(OpAsmParser &parser,
1131                                    OperationState &result) {
1132   OpAsmParser::OperandType tagMemRefInfo;
1133   AffineMapAttr tagMapAttr;
1134   SmallVector<OpAsmParser::OperandType, 2> tagMapOperands;
1135   Type type;
1136   auto indexType = parser.getBuilder().getIndexType();
1137   OpAsmParser::OperandType numElementsInfo;
1138 
1139   // Parse tag memref, its map operands, and dma size.
1140   if (parser.parseOperand(tagMemRefInfo) ||
1141       parser.parseAffineMapOfSSAIds(tagMapOperands, tagMapAttr,
1142                                     getTagMapAttrName(), result.attributes) ||
1143       parser.parseComma() || parser.parseOperand(numElementsInfo) ||
1144       parser.parseColonType(type) ||
1145       parser.resolveOperand(tagMemRefInfo, type, result.operands) ||
1146       parser.resolveOperands(tagMapOperands, indexType, result.operands) ||
1147       parser.resolveOperand(numElementsInfo, indexType, result.operands))
1148     return failure();
1149 
1150   if (!type.isa<MemRefType>())
1151     return parser.emitError(parser.getNameLoc(),
1152                             "expected tag to be of memref type");
1153 
1154   if (tagMapOperands.size() != tagMapAttr.getValue().getNumInputs())
1155     return parser.emitError(parser.getNameLoc(),
1156                             "tag memref operand count != to map.numInputs");
1157   return success();
1158 }
1159 
1160 LogicalResult AffineDmaWaitOp::verify() {
1161   if (!getOperand(0).getType().isa<MemRefType>())
1162     return emitOpError("expected DMA tag to be of memref type");
1163   Region *scope = getAffineScope(*this);
1164   for (auto idx : getTagIndices()) {
1165     if (!idx.getType().isIndex())
1166       return emitOpError("index to dma_wait must have 'index' type");
1167     if (!isValidAffineIndexOperand(idx, scope))
1168       return emitOpError("index must be a dimension or symbol identifier");
1169   }
1170   return success();
1171 }
1172 
1173 LogicalResult AffineDmaWaitOp::fold(ArrayRef<Attribute> cstOperands,
1174                                     SmallVectorImpl<OpFoldResult> &results) {
1175   /// dma_wait(memrefcast) -> dma_wait
1176   return foldMemRefCast(*this);
1177 }
1178 
1179 //===----------------------------------------------------------------------===//
1180 // AffineForOp
1181 //===----------------------------------------------------------------------===//
1182 
1183 /// 'bodyBuilder' is used to build the body of affine.for. If iterArgs and
1184 /// bodyBuilder are empty/null, we include default terminator op.
1185 void AffineForOp::build(OpBuilder &builder, OperationState &result,
1186                         ValueRange lbOperands, AffineMap lbMap,
1187                         ValueRange ubOperands, AffineMap ubMap, int64_t step,
1188                         ValueRange iterArgs, BodyBuilderFn bodyBuilder) {
1189   assert(((!lbMap && lbOperands.empty()) ||
1190           lbOperands.size() == lbMap.getNumInputs()) &&
1191          "lower bound operand count does not match the affine map");
1192   assert(((!ubMap && ubOperands.empty()) ||
1193           ubOperands.size() == ubMap.getNumInputs()) &&
1194          "upper bound operand count does not match the affine map");
1195   assert(step > 0 && "step has to be a positive integer constant");
1196 
1197   for (Value val : iterArgs)
1198     result.addTypes(val.getType());
1199 
1200   // Add an attribute for the step.
1201   result.addAttribute(getStepAttrName(),
1202                       builder.getIntegerAttr(builder.getIndexType(), step));
1203 
1204   // Add the lower bound.
1205   result.addAttribute(getLowerBoundAttrName(), AffineMapAttr::get(lbMap));
1206   result.addOperands(lbOperands);
1207 
1208   // Add the upper bound.
1209   result.addAttribute(getUpperBoundAttrName(), AffineMapAttr::get(ubMap));
1210   result.addOperands(ubOperands);
1211 
1212   result.addOperands(iterArgs);
1213   // Create a region and a block for the body.  The argument of the region is
1214   // the loop induction variable.
1215   Region *bodyRegion = result.addRegion();
1216   bodyRegion->push_back(new Block);
1217   Block &bodyBlock = bodyRegion->front();
1218   Value inductionVar = bodyBlock.addArgument(builder.getIndexType());
1219   for (Value val : iterArgs)
1220     bodyBlock.addArgument(val.getType());
1221 
1222   // Create the default terminator if the builder is not provided and if the
1223   // iteration arguments are not provided. Otherwise, leave this to the caller
1224   // because we don't know which values to return from the loop.
1225   if (iterArgs.empty() && !bodyBuilder) {
1226     ensureTerminator(*bodyRegion, builder, result.location);
1227   } else if (bodyBuilder) {
1228     OpBuilder::InsertionGuard guard(builder);
1229     builder.setInsertionPointToStart(&bodyBlock);
1230     bodyBuilder(builder, result.location, inductionVar,
1231                 bodyBlock.getArguments().drop_front());
1232   }
1233 }
1234 
1235 void AffineForOp::build(OpBuilder &builder, OperationState &result, int64_t lb,
1236                         int64_t ub, int64_t step, ValueRange iterArgs,
1237                         BodyBuilderFn bodyBuilder) {
1238   auto lbMap = AffineMap::getConstantMap(lb, builder.getContext());
1239   auto ubMap = AffineMap::getConstantMap(ub, builder.getContext());
1240   return build(builder, result, {}, lbMap, {}, ubMap, step, iterArgs,
1241                bodyBuilder);
1242 }
1243 
1244 static LogicalResult verify(AffineForOp op) {
1245   // Check that the body defines as single block argument for the induction
1246   // variable.
1247   auto *body = op.getBody();
1248   if (body->getNumArguments() == 0 || !body->getArgument(0).getType().isIndex())
1249     return op.emitOpError(
1250         "expected body to have a single index argument for the "
1251         "induction variable");
1252 
1253   // Verify that the bound operands are valid dimension/symbols.
1254   /// Lower bound.
1255   if (op.getLowerBoundMap().getNumInputs() > 0)
1256     if (failed(
1257             verifyDimAndSymbolIdentifiers(op, op.getLowerBoundOperands(),
1258                                           op.getLowerBoundMap().getNumDims())))
1259       return failure();
1260   /// Upper bound.
1261   if (op.getUpperBoundMap().getNumInputs() > 0)
1262     if (failed(
1263             verifyDimAndSymbolIdentifiers(op, op.getUpperBoundOperands(),
1264                                           op.getUpperBoundMap().getNumDims())))
1265       return failure();
1266 
1267   unsigned opNumResults = op.getNumResults();
1268   if (opNumResults == 0)
1269     return success();
1270 
1271   // If ForOp defines values, check that the number and types of the defined
1272   // values match ForOp initial iter operands and backedge basic block
1273   // arguments.
1274   if (op.getNumIterOperands() != opNumResults)
1275     return op.emitOpError(
1276         "mismatch between the number of loop-carried values and results");
1277   if (op.getNumRegionIterArgs() != opNumResults)
1278     return op.emitOpError(
1279         "mismatch between the number of basic block args and results");
1280 
1281   return success();
1282 }
1283 
1284 /// Parse a for operation loop bounds.
1285 static ParseResult parseBound(bool isLower, OperationState &result,
1286                               OpAsmParser &p) {
1287   // 'min' / 'max' prefixes are generally syntactic sugar, but are required if
1288   // the map has multiple results.
1289   bool failedToParsedMinMax =
1290       failed(p.parseOptionalKeyword(isLower ? "max" : "min"));
1291 
1292   auto &builder = p.getBuilder();
1293   auto boundAttrName = isLower ? AffineForOp::getLowerBoundAttrName()
1294                                : AffineForOp::getUpperBoundAttrName();
1295 
1296   // Parse ssa-id as identity map.
1297   SmallVector<OpAsmParser::OperandType, 1> boundOpInfos;
1298   if (p.parseOperandList(boundOpInfos))
1299     return failure();
1300 
1301   if (!boundOpInfos.empty()) {
1302     // Check that only one operand was parsed.
1303     if (boundOpInfos.size() > 1)
1304       return p.emitError(p.getNameLoc(),
1305                          "expected only one loop bound operand");
1306 
1307     // TODO: improve error message when SSA value is not of index type.
1308     // Currently it is 'use of value ... expects different type than prior uses'
1309     if (p.resolveOperand(boundOpInfos.front(), builder.getIndexType(),
1310                          result.operands))
1311       return failure();
1312 
1313     // Create an identity map using symbol id. This representation is optimized
1314     // for storage. Analysis passes may expand it into a multi-dimensional map
1315     // if desired.
1316     AffineMap map = builder.getSymbolIdentityMap();
1317     result.addAttribute(boundAttrName, AffineMapAttr::get(map));
1318     return success();
1319   }
1320 
1321   // Get the attribute location.
1322   llvm::SMLoc attrLoc = p.getCurrentLocation();
1323 
1324   Attribute boundAttr;
1325   if (p.parseAttribute(boundAttr, builder.getIndexType(), boundAttrName,
1326                        result.attributes))
1327     return failure();
1328 
1329   // Parse full form - affine map followed by dim and symbol list.
1330   if (auto affineMapAttr = boundAttr.dyn_cast<AffineMapAttr>()) {
1331     unsigned currentNumOperands = result.operands.size();
1332     unsigned numDims;
1333     if (parseDimAndSymbolList(p, result.operands, numDims))
1334       return failure();
1335 
1336     auto map = affineMapAttr.getValue();
1337     if (map.getNumDims() != numDims)
1338       return p.emitError(
1339           p.getNameLoc(),
1340           "dim operand count and affine map dim count must match");
1341 
1342     unsigned numDimAndSymbolOperands =
1343         result.operands.size() - currentNumOperands;
1344     if (numDims + map.getNumSymbols() != numDimAndSymbolOperands)
1345       return p.emitError(
1346           p.getNameLoc(),
1347           "symbol operand count and affine map symbol count must match");
1348 
1349     // If the map has multiple results, make sure that we parsed the min/max
1350     // prefix.
1351     if (map.getNumResults() > 1 && failedToParsedMinMax) {
1352       if (isLower) {
1353         return p.emitError(attrLoc, "lower loop bound affine map with "
1354                                     "multiple results requires 'max' prefix");
1355       }
1356       return p.emitError(attrLoc, "upper loop bound affine map with multiple "
1357                                   "results requires 'min' prefix");
1358     }
1359     return success();
1360   }
1361 
1362   // Parse custom assembly form.
1363   if (auto integerAttr = boundAttr.dyn_cast<IntegerAttr>()) {
1364     result.attributes.pop_back();
1365     result.addAttribute(
1366         boundAttrName,
1367         AffineMapAttr::get(builder.getConstantAffineMap(integerAttr.getInt())));
1368     return success();
1369   }
1370 
1371   return p.emitError(
1372       p.getNameLoc(),
1373       "expected valid affine map representation for loop bounds");
1374 }
1375 
1376 static ParseResult parseAffineForOp(OpAsmParser &parser,
1377                                     OperationState &result) {
1378   auto &builder = parser.getBuilder();
1379   OpAsmParser::OperandType inductionVariable;
1380   // Parse the induction variable followed by '='.
1381   if (parser.parseRegionArgument(inductionVariable) || parser.parseEqual())
1382     return failure();
1383 
1384   // Parse loop bounds.
1385   if (parseBound(/*isLower=*/true, result, parser) ||
1386       parser.parseKeyword("to", " between bounds") ||
1387       parseBound(/*isLower=*/false, result, parser))
1388     return failure();
1389 
1390   // Parse the optional loop step, we default to 1 if one is not present.
1391   if (parser.parseOptionalKeyword("step")) {
1392     result.addAttribute(
1393         AffineForOp::getStepAttrName(),
1394         builder.getIntegerAttr(builder.getIndexType(), /*value=*/1));
1395   } else {
1396     llvm::SMLoc stepLoc = parser.getCurrentLocation();
1397     IntegerAttr stepAttr;
1398     if (parser.parseAttribute(stepAttr, builder.getIndexType(),
1399                               AffineForOp::getStepAttrName().data(),
1400                               result.attributes))
1401       return failure();
1402 
1403     if (stepAttr.getValue().getSExtValue() < 0)
1404       return parser.emitError(
1405           stepLoc,
1406           "expected step to be representable as a positive signed integer");
1407   }
1408 
1409   // Parse the optional initial iteration arguments.
1410   SmallVector<OpAsmParser::OperandType, 4> regionArgs, operands;
1411   SmallVector<Type, 4> argTypes;
1412   regionArgs.push_back(inductionVariable);
1413 
1414   if (succeeded(parser.parseOptionalKeyword("iter_args"))) {
1415     // Parse assignment list and results type list.
1416     if (parser.parseAssignmentList(regionArgs, operands) ||
1417         parser.parseArrowTypeList(result.types))
1418       return failure();
1419     // Resolve input operands.
1420     for (auto operandType : llvm::zip(operands, result.types))
1421       if (parser.resolveOperand(std::get<0>(operandType),
1422                                 std::get<1>(operandType), result.operands))
1423         return failure();
1424   }
1425   // Induction variable.
1426   Type indexType = builder.getIndexType();
1427   argTypes.push_back(indexType);
1428   // Loop carried variables.
1429   argTypes.append(result.types.begin(), result.types.end());
1430   // Parse the body region.
1431   Region *body = result.addRegion();
1432   if (regionArgs.size() != argTypes.size())
1433     return parser.emitError(
1434         parser.getNameLoc(),
1435         "mismatch between the number of loop-carried values and results");
1436   if (parser.parseRegion(*body, regionArgs, argTypes))
1437     return failure();
1438 
1439   AffineForOp::ensureTerminator(*body, builder, result.location);
1440 
1441   // Parse the optional attribute list.
1442   return parser.parseOptionalAttrDict(result.attributes);
1443 }
1444 
1445 static void printBound(AffineMapAttr boundMap,
1446                        Operation::operand_range boundOperands,
1447                        const char *prefix, OpAsmPrinter &p) {
1448   AffineMap map = boundMap.getValue();
1449 
1450   // Check if this bound should be printed using custom assembly form.
1451   // The decision to restrict printing custom assembly form to trivial cases
1452   // comes from the will to roundtrip MLIR binary -> text -> binary in a
1453   // lossless way.
1454   // Therefore, custom assembly form parsing and printing is only supported for
1455   // zero-operand constant maps and single symbol operand identity maps.
1456   if (map.getNumResults() == 1) {
1457     AffineExpr expr = map.getResult(0);
1458 
1459     // Print constant bound.
1460     if (map.getNumDims() == 0 && map.getNumSymbols() == 0) {
1461       if (auto constExpr = expr.dyn_cast<AffineConstantExpr>()) {
1462         p << constExpr.getValue();
1463         return;
1464       }
1465     }
1466 
1467     // Print bound that consists of a single SSA symbol if the map is over a
1468     // single symbol.
1469     if (map.getNumDims() == 0 && map.getNumSymbols() == 1) {
1470       if (auto symExpr = expr.dyn_cast<AffineSymbolExpr>()) {
1471         p.printOperand(*boundOperands.begin());
1472         return;
1473       }
1474     }
1475   } else {
1476     // Map has multiple results. Print 'min' or 'max' prefix.
1477     p << prefix << ' ';
1478   }
1479 
1480   // Print the map and its operands.
1481   p << boundMap;
1482   printDimAndSymbolList(boundOperands.begin(), boundOperands.end(),
1483                         map.getNumDims(), p);
1484 }
1485 
1486 unsigned AffineForOp::getNumIterOperands() {
1487   AffineMap lbMap = getLowerBoundMapAttr().getValue();
1488   AffineMap ubMap = getUpperBoundMapAttr().getValue();
1489 
1490   return getNumOperands() - lbMap.getNumInputs() - ubMap.getNumInputs();
1491 }
1492 
1493 static void print(OpAsmPrinter &p, AffineForOp op) {
1494   p << op.getOperationName() << ' ';
1495   p.printOperand(op.getBody()->getArgument(0));
1496   p << " = ";
1497   printBound(op.getLowerBoundMapAttr(), op.getLowerBoundOperands(), "max", p);
1498   p << " to ";
1499   printBound(op.getUpperBoundMapAttr(), op.getUpperBoundOperands(), "min", p);
1500 
1501   if (op.getStep() != 1)
1502     p << " step " << op.getStep();
1503 
1504   bool printBlockTerminators = false;
1505   if (op.getNumIterOperands() > 0) {
1506     p << " iter_args(";
1507     auto regionArgs = op.getRegionIterArgs();
1508     auto operands = op.getIterOperands();
1509 
1510     llvm::interleaveComma(llvm::zip(regionArgs, operands), p, [&](auto it) {
1511       p << std::get<0>(it) << " = " << std::get<1>(it);
1512     });
1513     p << ") -> (" << op.getResultTypes() << ")";
1514     printBlockTerminators = true;
1515   }
1516 
1517   p.printRegion(op.region(),
1518                 /*printEntryBlockArgs=*/false, printBlockTerminators);
1519   p.printOptionalAttrDict(op->getAttrs(),
1520                           /*elidedAttrs=*/{op.getLowerBoundAttrName(),
1521                                            op.getUpperBoundAttrName(),
1522                                            op.getStepAttrName()});
1523 }
1524 
1525 /// Fold the constant bounds of a loop.
1526 static LogicalResult foldLoopBounds(AffineForOp forOp) {
1527   auto foldLowerOrUpperBound = [&forOp](bool lower) {
1528     // Check to see if each of the operands is the result of a constant.  If
1529     // so, get the value.  If not, ignore it.
1530     SmallVector<Attribute, 8> operandConstants;
1531     auto boundOperands =
1532         lower ? forOp.getLowerBoundOperands() : forOp.getUpperBoundOperands();
1533     for (auto operand : boundOperands) {
1534       Attribute operandCst;
1535       matchPattern(operand, m_Constant(&operandCst));
1536       operandConstants.push_back(operandCst);
1537     }
1538 
1539     AffineMap boundMap =
1540         lower ? forOp.getLowerBoundMap() : forOp.getUpperBoundMap();
1541     assert(boundMap.getNumResults() >= 1 &&
1542            "bound maps should have at least one result");
1543     SmallVector<Attribute, 4> foldedResults;
1544     if (failed(boundMap.constantFold(operandConstants, foldedResults)))
1545       return failure();
1546 
1547     // Compute the max or min as applicable over the results.
1548     assert(!foldedResults.empty() && "bounds should have at least one result");
1549     auto maxOrMin = foldedResults[0].cast<IntegerAttr>().getValue();
1550     for (unsigned i = 1, e = foldedResults.size(); i < e; i++) {
1551       auto foldedResult = foldedResults[i].cast<IntegerAttr>().getValue();
1552       maxOrMin = lower ? llvm::APIntOps::smax(maxOrMin, foldedResult)
1553                        : llvm::APIntOps::smin(maxOrMin, foldedResult);
1554     }
1555     lower ? forOp.setConstantLowerBound(maxOrMin.getSExtValue())
1556           : forOp.setConstantUpperBound(maxOrMin.getSExtValue());
1557     return success();
1558   };
1559 
1560   // Try to fold the lower bound.
1561   bool folded = false;
1562   if (!forOp.hasConstantLowerBound())
1563     folded |= succeeded(foldLowerOrUpperBound(/*lower=*/true));
1564 
1565   // Try to fold the upper bound.
1566   if (!forOp.hasConstantUpperBound())
1567     folded |= succeeded(foldLowerOrUpperBound(/*lower=*/false));
1568   return success(folded);
1569 }
1570 
1571 /// Canonicalize the bounds of the given loop.
1572 static LogicalResult canonicalizeLoopBounds(AffineForOp forOp) {
1573   SmallVector<Value, 4> lbOperands(forOp.getLowerBoundOperands());
1574   SmallVector<Value, 4> ubOperands(forOp.getUpperBoundOperands());
1575 
1576   auto lbMap = forOp.getLowerBoundMap();
1577   auto ubMap = forOp.getUpperBoundMap();
1578   auto prevLbMap = lbMap;
1579   auto prevUbMap = ubMap;
1580 
1581   composeAffineMapAndOperands(&lbMap, &lbOperands);
1582   canonicalizeMapAndOperands(&lbMap, &lbOperands);
1583   lbMap = removeDuplicateExprs(lbMap);
1584 
1585   composeAffineMapAndOperands(&ubMap, &ubOperands);
1586   canonicalizeMapAndOperands(&ubMap, &ubOperands);
1587   ubMap = removeDuplicateExprs(ubMap);
1588 
1589   // Any canonicalization change always leads to updated map(s).
1590   if (lbMap == prevLbMap && ubMap == prevUbMap)
1591     return failure();
1592 
1593   if (lbMap != prevLbMap)
1594     forOp.setLowerBound(lbOperands, lbMap);
1595   if (ubMap != prevUbMap)
1596     forOp.setUpperBound(ubOperands, ubMap);
1597   return success();
1598 }
1599 
1600 namespace {
1601 /// This is a pattern to fold trivially empty loops.
1602 struct AffineForEmptyLoopFolder : public OpRewritePattern<AffineForOp> {
1603   using OpRewritePattern<AffineForOp>::OpRewritePattern;
1604 
1605   LogicalResult matchAndRewrite(AffineForOp forOp,
1606                                 PatternRewriter &rewriter) const override {
1607     // Check that the body only contains a yield.
1608     if (!llvm::hasSingleElement(*forOp.getBody()))
1609       return failure();
1610     rewriter.eraseOp(forOp);
1611     return success();
1612   }
1613 };
1614 } // end anonymous namespace
1615 
1616 void AffineForOp::getCanonicalizationPatterns(RewritePatternSet &results,
1617                                               MLIRContext *context) {
1618   results.add<AffineForEmptyLoopFolder>(context);
1619 }
1620 
1621 LogicalResult AffineForOp::fold(ArrayRef<Attribute> operands,
1622                                 SmallVectorImpl<OpFoldResult> &results) {
1623   bool folded = succeeded(foldLoopBounds(*this));
1624   folded |= succeeded(canonicalizeLoopBounds(*this));
1625   return success(folded);
1626 }
1627 
1628 AffineBound AffineForOp::getLowerBound() {
1629   auto lbMap = getLowerBoundMap();
1630   return AffineBound(AffineForOp(*this), 0, lbMap.getNumInputs(), lbMap);
1631 }
1632 
1633 AffineBound AffineForOp::getUpperBound() {
1634   auto lbMap = getLowerBoundMap();
1635   auto ubMap = getUpperBoundMap();
1636   return AffineBound(AffineForOp(*this), lbMap.getNumInputs(),
1637                      lbMap.getNumInputs() + ubMap.getNumInputs(), ubMap);
1638 }
1639 
1640 void AffineForOp::setLowerBound(ValueRange lbOperands, AffineMap map) {
1641   assert(lbOperands.size() == map.getNumInputs());
1642   assert(map.getNumResults() >= 1 && "bound map has at least one result");
1643 
1644   SmallVector<Value, 4> newOperands(lbOperands.begin(), lbOperands.end());
1645 
1646   auto ubOperands = getUpperBoundOperands();
1647   newOperands.append(ubOperands.begin(), ubOperands.end());
1648   auto iterOperands = getIterOperands();
1649   newOperands.append(iterOperands.begin(), iterOperands.end());
1650   (*this)->setOperands(newOperands);
1651 
1652   (*this)->setAttr(getLowerBoundAttrName(), AffineMapAttr::get(map));
1653 }
1654 
1655 void AffineForOp::setUpperBound(ValueRange ubOperands, AffineMap map) {
1656   assert(ubOperands.size() == map.getNumInputs());
1657   assert(map.getNumResults() >= 1 && "bound map has at least one result");
1658 
1659   SmallVector<Value, 4> newOperands(getLowerBoundOperands());
1660   newOperands.append(ubOperands.begin(), ubOperands.end());
1661   auto iterOperands = getIterOperands();
1662   newOperands.append(iterOperands.begin(), iterOperands.end());
1663   (*this)->setOperands(newOperands);
1664 
1665   (*this)->setAttr(getUpperBoundAttrName(), AffineMapAttr::get(map));
1666 }
1667 
1668 void AffineForOp::setLowerBoundMap(AffineMap map) {
1669   auto lbMap = getLowerBoundMap();
1670   assert(lbMap.getNumDims() == map.getNumDims() &&
1671          lbMap.getNumSymbols() == map.getNumSymbols());
1672   assert(map.getNumResults() >= 1 && "bound map has at least one result");
1673   (void)lbMap;
1674   (*this)->setAttr(getLowerBoundAttrName(), AffineMapAttr::get(map));
1675 }
1676 
1677 void AffineForOp::setUpperBoundMap(AffineMap map) {
1678   auto ubMap = getUpperBoundMap();
1679   assert(ubMap.getNumDims() == map.getNumDims() &&
1680          ubMap.getNumSymbols() == map.getNumSymbols());
1681   assert(map.getNumResults() >= 1 && "bound map has at least one result");
1682   (void)ubMap;
1683   (*this)->setAttr(getUpperBoundAttrName(), AffineMapAttr::get(map));
1684 }
1685 
1686 bool AffineForOp::hasConstantLowerBound() {
1687   return getLowerBoundMap().isSingleConstant();
1688 }
1689 
1690 bool AffineForOp::hasConstantUpperBound() {
1691   return getUpperBoundMap().isSingleConstant();
1692 }
1693 
1694 int64_t AffineForOp::getConstantLowerBound() {
1695   return getLowerBoundMap().getSingleConstantResult();
1696 }
1697 
1698 int64_t AffineForOp::getConstantUpperBound() {
1699   return getUpperBoundMap().getSingleConstantResult();
1700 }
1701 
1702 void AffineForOp::setConstantLowerBound(int64_t value) {
1703   setLowerBound({}, AffineMap::getConstantMap(value, getContext()));
1704 }
1705 
1706 void AffineForOp::setConstantUpperBound(int64_t value) {
1707   setUpperBound({}, AffineMap::getConstantMap(value, getContext()));
1708 }
1709 
1710 AffineForOp::operand_range AffineForOp::getLowerBoundOperands() {
1711   return {operand_begin(), operand_begin() + getLowerBoundMap().getNumInputs()};
1712 }
1713 
1714 AffineForOp::operand_range AffineForOp::getUpperBoundOperands() {
1715   return {operand_begin() + getLowerBoundMap().getNumInputs(),
1716           operand_begin() + getLowerBoundMap().getNumInputs() +
1717               getUpperBoundMap().getNumInputs()};
1718 }
1719 
1720 bool AffineForOp::matchingBoundOperandList() {
1721   auto lbMap = getLowerBoundMap();
1722   auto ubMap = getUpperBoundMap();
1723   if (lbMap.getNumDims() != ubMap.getNumDims() ||
1724       lbMap.getNumSymbols() != ubMap.getNumSymbols())
1725     return false;
1726 
1727   unsigned numOperands = lbMap.getNumInputs();
1728   for (unsigned i = 0, e = lbMap.getNumInputs(); i < e; i++) {
1729     // Compare Value 's.
1730     if (getOperand(i) != getOperand(numOperands + i))
1731       return false;
1732   }
1733   return true;
1734 }
1735 
1736 Region &AffineForOp::getLoopBody() { return region(); }
1737 
1738 bool AffineForOp::isDefinedOutsideOfLoop(Value value) {
1739   return !region().isAncestor(value.getParentRegion());
1740 }
1741 
1742 LogicalResult AffineForOp::moveOutOfLoop(ArrayRef<Operation *> ops) {
1743   for (auto *op : ops)
1744     op->moveBefore(*this);
1745   return success();
1746 }
1747 
1748 /// Returns true if the provided value is the induction variable of a
1749 /// AffineForOp.
1750 bool mlir::isForInductionVar(Value val) {
1751   return getForInductionVarOwner(val) != AffineForOp();
1752 }
1753 
1754 /// Returns the loop parent of an induction variable. If the provided value is
1755 /// not an induction variable, then return nullptr.
1756 AffineForOp mlir::getForInductionVarOwner(Value val) {
1757   auto ivArg = val.dyn_cast<BlockArgument>();
1758   if (!ivArg || !ivArg.getOwner())
1759     return AffineForOp();
1760   auto *containingInst = ivArg.getOwner()->getParent()->getParentOp();
1761   return dyn_cast<AffineForOp>(containingInst);
1762 }
1763 
1764 /// Extracts the induction variables from a list of AffineForOps and returns
1765 /// them.
1766 void mlir::extractForInductionVars(ArrayRef<AffineForOp> forInsts,
1767                                    SmallVectorImpl<Value> *ivs) {
1768   ivs->reserve(forInsts.size());
1769   for (auto forInst : forInsts)
1770     ivs->push_back(forInst.getInductionVar());
1771 }
1772 
1773 /// Builds an affine loop nest, using "loopCreatorFn" to create individual loop
1774 /// operations.
1775 template <typename BoundListTy, typename LoopCreatorTy>
1776 static void buildAffineLoopNestImpl(
1777     OpBuilder &builder, Location loc, BoundListTy lbs, BoundListTy ubs,
1778     ArrayRef<int64_t> steps,
1779     function_ref<void(OpBuilder &, Location, ValueRange)> bodyBuilderFn,
1780     LoopCreatorTy &&loopCreatorFn) {
1781   assert(lbs.size() == ubs.size() && "Mismatch in number of arguments");
1782   assert(lbs.size() == steps.size() && "Mismatch in number of arguments");
1783 
1784   // If there are no loops to be constructed, construct the body anyway.
1785   OpBuilder::InsertionGuard guard(builder);
1786   if (lbs.empty()) {
1787     if (bodyBuilderFn)
1788       bodyBuilderFn(builder, loc, ValueRange());
1789     return;
1790   }
1791 
1792   // Create the loops iteratively and store the induction variables.
1793   SmallVector<Value, 4> ivs;
1794   ivs.reserve(lbs.size());
1795   for (unsigned i = 0, e = lbs.size(); i < e; ++i) {
1796     // Callback for creating the loop body, always creates the terminator.
1797     auto loopBody = [&](OpBuilder &nestedBuilder, Location nestedLoc, Value iv,
1798                         ValueRange iterArgs) {
1799       ivs.push_back(iv);
1800       // In the innermost loop, call the body builder.
1801       if (i == e - 1 && bodyBuilderFn) {
1802         OpBuilder::InsertionGuard nestedGuard(nestedBuilder);
1803         bodyBuilderFn(nestedBuilder, nestedLoc, ivs);
1804       }
1805       nestedBuilder.create<AffineYieldOp>(nestedLoc);
1806     };
1807 
1808     // Delegate actual loop creation to the callback in order to dispatch
1809     // between constant- and variable-bound loops.
1810     auto loop = loopCreatorFn(builder, loc, lbs[i], ubs[i], steps[i], loopBody);
1811     builder.setInsertionPointToStart(loop.getBody());
1812   }
1813 }
1814 
1815 /// Creates an affine loop from the bounds known to be constants.
1816 static AffineForOp
1817 buildAffineLoopFromConstants(OpBuilder &builder, Location loc, int64_t lb,
1818                              int64_t ub, int64_t step,
1819                              AffineForOp::BodyBuilderFn bodyBuilderFn) {
1820   return builder.create<AffineForOp>(loc, lb, ub, step, /*iterArgs=*/llvm::None,
1821                                      bodyBuilderFn);
1822 }
1823 
1824 /// Creates an affine loop from the bounds that may or may not be constants.
1825 static AffineForOp
1826 buildAffineLoopFromValues(OpBuilder &builder, Location loc, Value lb, Value ub,
1827                           int64_t step,
1828                           AffineForOp::BodyBuilderFn bodyBuilderFn) {
1829   auto lbConst = lb.getDefiningOp<ConstantIndexOp>();
1830   auto ubConst = ub.getDefiningOp<ConstantIndexOp>();
1831   if (lbConst && ubConst)
1832     return buildAffineLoopFromConstants(builder, loc, lbConst.getValue(),
1833                                         ubConst.getValue(), step,
1834                                         bodyBuilderFn);
1835   return builder.create<AffineForOp>(loc, lb, builder.getDimIdentityMap(), ub,
1836                                      builder.getDimIdentityMap(), step,
1837                                      /*iterArgs=*/llvm::None, bodyBuilderFn);
1838 }
1839 
1840 void mlir::buildAffineLoopNest(
1841     OpBuilder &builder, Location loc, ArrayRef<int64_t> lbs,
1842     ArrayRef<int64_t> ubs, ArrayRef<int64_t> steps,
1843     function_ref<void(OpBuilder &, Location, ValueRange)> bodyBuilderFn) {
1844   buildAffineLoopNestImpl(builder, loc, lbs, ubs, steps, bodyBuilderFn,
1845                           buildAffineLoopFromConstants);
1846 }
1847 
1848 void mlir::buildAffineLoopNest(
1849     OpBuilder &builder, Location loc, ValueRange lbs, ValueRange ubs,
1850     ArrayRef<int64_t> steps,
1851     function_ref<void(OpBuilder &, Location, ValueRange)> bodyBuilderFn) {
1852   buildAffineLoopNestImpl(builder, loc, lbs, ubs, steps, bodyBuilderFn,
1853                           buildAffineLoopFromValues);
1854 }
1855 
1856 //===----------------------------------------------------------------------===//
1857 // AffineIfOp
1858 //===----------------------------------------------------------------------===//
1859 
1860 namespace {
1861 /// Remove else blocks that have nothing other than a zero value yield.
1862 struct SimplifyDeadElse : public OpRewritePattern<AffineIfOp> {
1863   using OpRewritePattern<AffineIfOp>::OpRewritePattern;
1864 
1865   LogicalResult matchAndRewrite(AffineIfOp ifOp,
1866                                 PatternRewriter &rewriter) const override {
1867     if (ifOp.elseRegion().empty() ||
1868         !llvm::hasSingleElement(*ifOp.getElseBlock()) || ifOp.getNumResults())
1869       return failure();
1870 
1871     rewriter.startRootUpdate(ifOp);
1872     rewriter.eraseBlock(ifOp.getElseBlock());
1873     rewriter.finalizeRootUpdate(ifOp);
1874     return success();
1875   }
1876 };
1877 } // end anonymous namespace.
1878 
1879 static LogicalResult verify(AffineIfOp op) {
1880   // Verify that we have a condition attribute.
1881   auto conditionAttr =
1882       op->getAttrOfType<IntegerSetAttr>(op.getConditionAttrName());
1883   if (!conditionAttr)
1884     return op.emitOpError(
1885         "requires an integer set attribute named 'condition'");
1886 
1887   // Verify that there are enough operands for the condition.
1888   IntegerSet condition = conditionAttr.getValue();
1889   if (op.getNumOperands() != condition.getNumInputs())
1890     return op.emitOpError(
1891         "operand count and condition integer set dimension and "
1892         "symbol count must match");
1893 
1894   // Verify that the operands are valid dimension/symbols.
1895   if (failed(verifyDimAndSymbolIdentifiers(op, op.getOperands(),
1896                                            condition.getNumDims())))
1897     return failure();
1898 
1899   return success();
1900 }
1901 
1902 static ParseResult parseAffineIfOp(OpAsmParser &parser,
1903                                    OperationState &result) {
1904   // Parse the condition attribute set.
1905   IntegerSetAttr conditionAttr;
1906   unsigned numDims;
1907   if (parser.parseAttribute(conditionAttr, AffineIfOp::getConditionAttrName(),
1908                             result.attributes) ||
1909       parseDimAndSymbolList(parser, result.operands, numDims))
1910     return failure();
1911 
1912   // Verify the condition operands.
1913   auto set = conditionAttr.getValue();
1914   if (set.getNumDims() != numDims)
1915     return parser.emitError(
1916         parser.getNameLoc(),
1917         "dim operand count and integer set dim count must match");
1918   if (numDims + set.getNumSymbols() != result.operands.size())
1919     return parser.emitError(
1920         parser.getNameLoc(),
1921         "symbol operand count and integer set symbol count must match");
1922 
1923   if (parser.parseOptionalArrowTypeList(result.types))
1924     return failure();
1925 
1926   // Create the regions for 'then' and 'else'.  The latter must be created even
1927   // if it remains empty for the validity of the operation.
1928   result.regions.reserve(2);
1929   Region *thenRegion = result.addRegion();
1930   Region *elseRegion = result.addRegion();
1931 
1932   // Parse the 'then' region.
1933   if (parser.parseRegion(*thenRegion, {}, {}))
1934     return failure();
1935   AffineIfOp::ensureTerminator(*thenRegion, parser.getBuilder(),
1936                                result.location);
1937 
1938   // If we find an 'else' keyword then parse the 'else' region.
1939   if (!parser.parseOptionalKeyword("else")) {
1940     if (parser.parseRegion(*elseRegion, {}, {}))
1941       return failure();
1942     AffineIfOp::ensureTerminator(*elseRegion, parser.getBuilder(),
1943                                  result.location);
1944   }
1945 
1946   // Parse the optional attribute list.
1947   if (parser.parseOptionalAttrDict(result.attributes))
1948     return failure();
1949 
1950   return success();
1951 }
1952 
1953 static void print(OpAsmPrinter &p, AffineIfOp op) {
1954   auto conditionAttr =
1955       op->getAttrOfType<IntegerSetAttr>(op.getConditionAttrName());
1956   p << "affine.if " << conditionAttr;
1957   printDimAndSymbolList(op.operand_begin(), op.operand_end(),
1958                         conditionAttr.getValue().getNumDims(), p);
1959   p.printOptionalArrowTypeList(op.getResultTypes());
1960   p.printRegion(op.thenRegion(),
1961                 /*printEntryBlockArgs=*/false,
1962                 /*printBlockTerminators=*/op.getNumResults());
1963 
1964   // Print the 'else' regions if it has any blocks.
1965   auto &elseRegion = op.elseRegion();
1966   if (!elseRegion.empty()) {
1967     p << " else";
1968     p.printRegion(elseRegion,
1969                   /*printEntryBlockArgs=*/false,
1970                   /*printBlockTerminators=*/op.getNumResults());
1971   }
1972 
1973   // Print the attribute list.
1974   p.printOptionalAttrDict(op->getAttrs(),
1975                           /*elidedAttrs=*/op.getConditionAttrName());
1976 }
1977 
1978 IntegerSet AffineIfOp::getIntegerSet() {
1979   return (*this)
1980       ->getAttrOfType<IntegerSetAttr>(getConditionAttrName())
1981       .getValue();
1982 }
1983 void AffineIfOp::setIntegerSet(IntegerSet newSet) {
1984   (*this)->setAttr(getConditionAttrName(), IntegerSetAttr::get(newSet));
1985 }
1986 
1987 void AffineIfOp::setConditional(IntegerSet set, ValueRange operands) {
1988   setIntegerSet(set);
1989   (*this)->setOperands(operands);
1990 }
1991 
1992 void AffineIfOp::build(OpBuilder &builder, OperationState &result,
1993                        TypeRange resultTypes, IntegerSet set, ValueRange args,
1994                        bool withElseRegion) {
1995   assert(resultTypes.empty() || withElseRegion);
1996   result.addTypes(resultTypes);
1997   result.addOperands(args);
1998   result.addAttribute(getConditionAttrName(), IntegerSetAttr::get(set));
1999 
2000   Region *thenRegion = result.addRegion();
2001   thenRegion->push_back(new Block());
2002   if (resultTypes.empty())
2003     AffineIfOp::ensureTerminator(*thenRegion, builder, result.location);
2004 
2005   Region *elseRegion = result.addRegion();
2006   if (withElseRegion) {
2007     elseRegion->push_back(new Block());
2008     if (resultTypes.empty())
2009       AffineIfOp::ensureTerminator(*elseRegion, builder, result.location);
2010   }
2011 }
2012 
2013 void AffineIfOp::build(OpBuilder &builder, OperationState &result,
2014                        IntegerSet set, ValueRange args, bool withElseRegion) {
2015   AffineIfOp::build(builder, result, /*resultTypes=*/{}, set, args,
2016                     withElseRegion);
2017 }
2018 
2019 /// Canonicalize an affine if op's conditional (integer set + operands).
2020 LogicalResult AffineIfOp::fold(ArrayRef<Attribute>,
2021                                SmallVectorImpl<OpFoldResult> &) {
2022   auto set = getIntegerSet();
2023   SmallVector<Value, 4> operands(getOperands());
2024   canonicalizeSetAndOperands(&set, &operands);
2025 
2026   // Any canonicalization change always leads to either a reduction in the
2027   // number of operands or a change in the number of symbolic operands
2028   // (promotion of dims to symbols).
2029   if (operands.size() < getIntegerSet().getNumInputs() ||
2030       set.getNumSymbols() > getIntegerSet().getNumSymbols()) {
2031     setConditional(set, operands);
2032     return success();
2033   }
2034 
2035   return failure();
2036 }
2037 
2038 void AffineIfOp::getCanonicalizationPatterns(RewritePatternSet &results,
2039                                              MLIRContext *context) {
2040   results.add<SimplifyDeadElse>(context);
2041 }
2042 
2043 //===----------------------------------------------------------------------===//
2044 // AffineLoadOp
2045 //===----------------------------------------------------------------------===//
2046 
2047 void AffineLoadOp::build(OpBuilder &builder, OperationState &result,
2048                          AffineMap map, ValueRange operands) {
2049   assert(operands.size() == 1 + map.getNumInputs() && "inconsistent operands");
2050   result.addOperands(operands);
2051   if (map)
2052     result.addAttribute(getMapAttrName(), AffineMapAttr::get(map));
2053   auto memrefType = operands[0].getType().cast<MemRefType>();
2054   result.types.push_back(memrefType.getElementType());
2055 }
2056 
2057 void AffineLoadOp::build(OpBuilder &builder, OperationState &result,
2058                          Value memref, AffineMap map, ValueRange mapOperands) {
2059   assert(map.getNumInputs() == mapOperands.size() && "inconsistent index info");
2060   result.addOperands(memref);
2061   result.addOperands(mapOperands);
2062   auto memrefType = memref.getType().cast<MemRefType>();
2063   result.addAttribute(getMapAttrName(), AffineMapAttr::get(map));
2064   result.types.push_back(memrefType.getElementType());
2065 }
2066 
2067 void AffineLoadOp::build(OpBuilder &builder, OperationState &result,
2068                          Value memref, ValueRange indices) {
2069   auto memrefType = memref.getType().cast<MemRefType>();
2070   int64_t rank = memrefType.getRank();
2071   // Create identity map for memrefs with at least one dimension or () -> ()
2072   // for zero-dimensional memrefs.
2073   auto map =
2074       rank ? builder.getMultiDimIdentityMap(rank) : builder.getEmptyAffineMap();
2075   build(builder, result, memref, map, indices);
2076 }
2077 
2078 static ParseResult parseAffineLoadOp(OpAsmParser &parser,
2079                                      OperationState &result) {
2080   auto &builder = parser.getBuilder();
2081   auto indexTy = builder.getIndexType();
2082 
2083   MemRefType type;
2084   OpAsmParser::OperandType memrefInfo;
2085   AffineMapAttr mapAttr;
2086   SmallVector<OpAsmParser::OperandType, 1> mapOperands;
2087   return failure(
2088       parser.parseOperand(memrefInfo) ||
2089       parser.parseAffineMapOfSSAIds(mapOperands, mapAttr,
2090                                     AffineLoadOp::getMapAttrName(),
2091                                     result.attributes) ||
2092       parser.parseOptionalAttrDict(result.attributes) ||
2093       parser.parseColonType(type) ||
2094       parser.resolveOperand(memrefInfo, type, result.operands) ||
2095       parser.resolveOperands(mapOperands, indexTy, result.operands) ||
2096       parser.addTypeToList(type.getElementType(), result.types));
2097 }
2098 
2099 static void print(OpAsmPrinter &p, AffineLoadOp op) {
2100   p << "affine.load " << op.getMemRef() << '[';
2101   if (AffineMapAttr mapAttr =
2102           op->getAttrOfType<AffineMapAttr>(op.getMapAttrName()))
2103     p.printAffineMapOfSSAIds(mapAttr, op.getMapOperands());
2104   p << ']';
2105   p.printOptionalAttrDict(op->getAttrs(),
2106                           /*elidedAttrs=*/{op.getMapAttrName()});
2107   p << " : " << op.getMemRefType();
2108 }
2109 
2110 /// Verify common indexing invariants of affine.load, affine.store,
2111 /// affine.vector_load and affine.vector_store.
2112 static LogicalResult
2113 verifyMemoryOpIndexing(Operation *op, AffineMapAttr mapAttr,
2114                        Operation::operand_range mapOperands,
2115                        MemRefType memrefType, unsigned numIndexOperands) {
2116   if (mapAttr) {
2117     AffineMap map = mapAttr.getValue();
2118     if (map.getNumResults() != memrefType.getRank())
2119       return op->emitOpError("affine map num results must equal memref rank");
2120     if (map.getNumInputs() != numIndexOperands)
2121       return op->emitOpError("expects as many subscripts as affine map inputs");
2122   } else {
2123     if (memrefType.getRank() != numIndexOperands)
2124       return op->emitOpError(
2125           "expects the number of subscripts to be equal to memref rank");
2126   }
2127 
2128   Region *scope = getAffineScope(op);
2129   for (auto idx : mapOperands) {
2130     if (!idx.getType().isIndex())
2131       return op->emitOpError("index to load must have 'index' type");
2132     if (!isValidAffineIndexOperand(idx, scope))
2133       return op->emitOpError("index must be a dimension or symbol identifier");
2134   }
2135 
2136   return success();
2137 }
2138 
2139 LogicalResult verify(AffineLoadOp op) {
2140   auto memrefType = op.getMemRefType();
2141   if (op.getType() != memrefType.getElementType())
2142     return op.emitOpError("result type must match element type of memref");
2143 
2144   if (failed(verifyMemoryOpIndexing(
2145           op.getOperation(),
2146           op->getAttrOfType<AffineMapAttr>(op.getMapAttrName()),
2147           op.getMapOperands(), memrefType,
2148           /*numIndexOperands=*/op.getNumOperands() - 1)))
2149     return failure();
2150 
2151   return success();
2152 }
2153 
2154 void AffineLoadOp::getCanonicalizationPatterns(RewritePatternSet &results,
2155                                                MLIRContext *context) {
2156   results.add<SimplifyAffineOp<AffineLoadOp>>(context);
2157 }
2158 
2159 OpFoldResult AffineLoadOp::fold(ArrayRef<Attribute> cstOperands) {
2160   /// load(memrefcast) -> load
2161   if (succeeded(foldMemRefCast(*this)))
2162     return getResult();
2163   return OpFoldResult();
2164 }
2165 
2166 //===----------------------------------------------------------------------===//
2167 // AffineStoreOp
2168 //===----------------------------------------------------------------------===//
2169 
2170 void AffineStoreOp::build(OpBuilder &builder, OperationState &result,
2171                           Value valueToStore, Value memref, AffineMap map,
2172                           ValueRange mapOperands) {
2173   assert(map.getNumInputs() == mapOperands.size() && "inconsistent index info");
2174   result.addOperands(valueToStore);
2175   result.addOperands(memref);
2176   result.addOperands(mapOperands);
2177   result.addAttribute(getMapAttrName(), AffineMapAttr::get(map));
2178 }
2179 
2180 // Use identity map.
2181 void AffineStoreOp::build(OpBuilder &builder, OperationState &result,
2182                           Value valueToStore, Value memref,
2183                           ValueRange indices) {
2184   auto memrefType = memref.getType().cast<MemRefType>();
2185   int64_t rank = memrefType.getRank();
2186   // Create identity map for memrefs with at least one dimension or () -> ()
2187   // for zero-dimensional memrefs.
2188   auto map =
2189       rank ? builder.getMultiDimIdentityMap(rank) : builder.getEmptyAffineMap();
2190   build(builder, result, valueToStore, memref, map, indices);
2191 }
2192 
2193 static ParseResult parseAffineStoreOp(OpAsmParser &parser,
2194                                       OperationState &result) {
2195   auto indexTy = parser.getBuilder().getIndexType();
2196 
2197   MemRefType type;
2198   OpAsmParser::OperandType storeValueInfo;
2199   OpAsmParser::OperandType memrefInfo;
2200   AffineMapAttr mapAttr;
2201   SmallVector<OpAsmParser::OperandType, 1> mapOperands;
2202   return failure(parser.parseOperand(storeValueInfo) || parser.parseComma() ||
2203                  parser.parseOperand(memrefInfo) ||
2204                  parser.parseAffineMapOfSSAIds(mapOperands, mapAttr,
2205                                                AffineStoreOp::getMapAttrName(),
2206                                                result.attributes) ||
2207                  parser.parseOptionalAttrDict(result.attributes) ||
2208                  parser.parseColonType(type) ||
2209                  parser.resolveOperand(storeValueInfo, type.getElementType(),
2210                                        result.operands) ||
2211                  parser.resolveOperand(memrefInfo, type, result.operands) ||
2212                  parser.resolveOperands(mapOperands, indexTy, result.operands));
2213 }
2214 
2215 static void print(OpAsmPrinter &p, AffineStoreOp op) {
2216   p << "affine.store " << op.getValueToStore();
2217   p << ", " << op.getMemRef() << '[';
2218   if (AffineMapAttr mapAttr =
2219           op->getAttrOfType<AffineMapAttr>(op.getMapAttrName()))
2220     p.printAffineMapOfSSAIds(mapAttr, op.getMapOperands());
2221   p << ']';
2222   p.printOptionalAttrDict(op->getAttrs(),
2223                           /*elidedAttrs=*/{op.getMapAttrName()});
2224   p << " : " << op.getMemRefType();
2225 }
2226 
2227 LogicalResult verify(AffineStoreOp op) {
2228   // First operand must have same type as memref element type.
2229   auto memrefType = op.getMemRefType();
2230   if (op.getValueToStore().getType() != memrefType.getElementType())
2231     return op.emitOpError(
2232         "first operand must have same type memref element type");
2233 
2234   if (failed(verifyMemoryOpIndexing(
2235           op.getOperation(),
2236           op->getAttrOfType<AffineMapAttr>(op.getMapAttrName()),
2237           op.getMapOperands(), memrefType,
2238           /*numIndexOperands=*/op.getNumOperands() - 2)))
2239     return failure();
2240 
2241   return success();
2242 }
2243 
2244 void AffineStoreOp::getCanonicalizationPatterns(RewritePatternSet &results,
2245                                                 MLIRContext *context) {
2246   results.add<SimplifyAffineOp<AffineStoreOp>>(context);
2247 }
2248 
2249 LogicalResult AffineStoreOp::fold(ArrayRef<Attribute> cstOperands,
2250                                   SmallVectorImpl<OpFoldResult> &results) {
2251   /// store(memrefcast) -> store
2252   return foldMemRefCast(*this);
2253 }
2254 
2255 //===----------------------------------------------------------------------===//
2256 // AffineMinMaxOpBase
2257 //===----------------------------------------------------------------------===//
2258 
2259 template <typename T>
2260 static LogicalResult verifyAffineMinMaxOp(T op) {
2261   // Verify that operand count matches affine map dimension and symbol count.
2262   if (op.getNumOperands() != op.map().getNumDims() + op.map().getNumSymbols())
2263     return op.emitOpError(
2264         "operand count and affine map dimension and symbol count must match");
2265   return success();
2266 }
2267 
2268 template <typename T>
2269 static void printAffineMinMaxOp(OpAsmPrinter &p, T op) {
2270   p << op.getOperationName() << ' ' << op->getAttr(T::getMapAttrName());
2271   auto operands = op.getOperands();
2272   unsigned numDims = op.map().getNumDims();
2273   p << '(' << operands.take_front(numDims) << ')';
2274 
2275   if (operands.size() != numDims)
2276     p << '[' << operands.drop_front(numDims) << ']';
2277   p.printOptionalAttrDict(op->getAttrs(),
2278                           /*elidedAttrs=*/{T::getMapAttrName()});
2279 }
2280 
2281 template <typename T>
2282 static ParseResult parseAffineMinMaxOp(OpAsmParser &parser,
2283                                        OperationState &result) {
2284   auto &builder = parser.getBuilder();
2285   auto indexType = builder.getIndexType();
2286   SmallVector<OpAsmParser::OperandType, 8> dim_infos;
2287   SmallVector<OpAsmParser::OperandType, 8> sym_infos;
2288   AffineMapAttr mapAttr;
2289   return failure(
2290       parser.parseAttribute(mapAttr, T::getMapAttrName(), result.attributes) ||
2291       parser.parseOperandList(dim_infos, OpAsmParser::Delimiter::Paren) ||
2292       parser.parseOperandList(sym_infos,
2293                               OpAsmParser::Delimiter::OptionalSquare) ||
2294       parser.parseOptionalAttrDict(result.attributes) ||
2295       parser.resolveOperands(dim_infos, indexType, result.operands) ||
2296       parser.resolveOperands(sym_infos, indexType, result.operands) ||
2297       parser.addTypeToList(indexType, result.types));
2298 }
2299 
2300 /// Fold an affine min or max operation with the given operands. The operand
2301 /// list may contain nulls, which are interpreted as the operand not being a
2302 /// constant.
2303 template <typename T>
2304 static OpFoldResult foldMinMaxOp(T op, ArrayRef<Attribute> operands) {
2305   static_assert(llvm::is_one_of<T, AffineMinOp, AffineMaxOp>::value,
2306                 "expected affine min or max op");
2307 
2308   // Fold the affine map.
2309   // TODO: Fold more cases:
2310   // min(some_affine, some_affine + constant, ...), etc.
2311   SmallVector<int64_t, 2> results;
2312   auto foldedMap = op.map().partialConstantFold(operands, &results);
2313 
2314   // If some of the map results are not constant, try changing the map in-place.
2315   if (results.empty()) {
2316     // If the map is the same, report that folding did not happen.
2317     if (foldedMap == op.map())
2318       return {};
2319     op->setAttr("map", AffineMapAttr::get(foldedMap));
2320     return op.getResult();
2321   }
2322 
2323   // Otherwise, completely fold the op into a constant.
2324   auto resultIt = std::is_same<T, AffineMinOp>::value
2325                       ? std::min_element(results.begin(), results.end())
2326                       : std::max_element(results.begin(), results.end());
2327   if (resultIt == results.end())
2328     return {};
2329   return IntegerAttr::get(IndexType::get(op.getContext()), *resultIt);
2330 }
2331 
2332 /// Remove duplicated expressions in affine min/max ops.
2333 template <typename T>
2334 struct DeduplicateAffineMinMaxExpressions : public OpRewritePattern<T> {
2335   using OpRewritePattern<T>::OpRewritePattern;
2336 
2337   LogicalResult matchAndRewrite(T affineOp,
2338                                 PatternRewriter &rewriter) const override {
2339     AffineMap oldMap = affineOp.getAffineMap();
2340 
2341     SmallVector<AffineExpr, 4> newExprs;
2342     for (AffineExpr expr : oldMap.getResults()) {
2343       // This is a linear scan over newExprs, but it should be fine given that
2344       // we typically just have a few expressions per op.
2345       if (!llvm::is_contained(newExprs, expr))
2346         newExprs.push_back(expr);
2347     }
2348 
2349     if (newExprs.size() == oldMap.getNumResults())
2350       return failure();
2351 
2352     auto newMap = AffineMap::get(oldMap.getNumDims(), oldMap.getNumSymbols(),
2353                                  newExprs, rewriter.getContext());
2354     rewriter.replaceOpWithNewOp<T>(affineOp, newMap, affineOp.getMapOperands());
2355 
2356     return success();
2357   }
2358 };
2359 
2360 /// Merge an affine min/max op to its consumers if its consumer is also an
2361 /// affine min/max op.
2362 ///
2363 /// This pattern requires the producer affine min/max op is bound to a
2364 /// dimension/symbol that is used as a standalone expression in the consumer
2365 /// affine op's map.
2366 ///
2367 /// For example, a pattern like the following:
2368 ///
2369 ///   %0 = affine.min affine_map<()[s0] -> (s0 + 16, s0 * 8)> ()[%sym1]
2370 ///   %1 = affine.min affine_map<(d0)[s0] -> (s0 + 4, d0)> (%0)[%sym2]
2371 ///
2372 /// Can be turned into:
2373 ///
2374 ///   %1 = affine.min affine_map<
2375 ///          ()[s0, s1] -> (s0 + 4, s1 + 16, s1 * 8)> ()[%sym2, %sym1]
2376 template <typename T>
2377 struct MergeAffineMinMaxOp : public OpRewritePattern<T> {
2378   using OpRewritePattern<T>::OpRewritePattern;
2379 
2380   LogicalResult matchAndRewrite(T affineOp,
2381                                 PatternRewriter &rewriter) const override {
2382     AffineMap oldMap = affineOp.getAffineMap();
2383     ValueRange dimOperands =
2384         affineOp.getMapOperands().take_front(oldMap.getNumDims());
2385     ValueRange symOperands =
2386         affineOp.getMapOperands().take_back(oldMap.getNumSymbols());
2387 
2388     auto newDimOperands = llvm::to_vector<8>(dimOperands);
2389     auto newSymOperands = llvm::to_vector<8>(symOperands);
2390     SmallVector<AffineExpr, 4> newExprs;
2391     SmallVector<T, 4> producerOps;
2392 
2393     // Go over each expression to see whether it's a single dimension/symbol
2394     // with the corresponding operand which is the result of another affine
2395     // min/max op. If So it can be merged into this affine op.
2396     for (AffineExpr expr : oldMap.getResults()) {
2397       if (auto symExpr = expr.dyn_cast<AffineSymbolExpr>()) {
2398         Value symValue = symOperands[symExpr.getPosition()];
2399         if (auto producerOp = symValue.getDefiningOp<T>()) {
2400           producerOps.push_back(producerOp);
2401           continue;
2402         }
2403       } else if (auto dimExpr = expr.dyn_cast<AffineDimExpr>()) {
2404         Value dimValue = dimOperands[dimExpr.getPosition()];
2405         if (auto producerOp = dimValue.getDefiningOp<T>()) {
2406           producerOps.push_back(producerOp);
2407           continue;
2408         }
2409       }
2410       // For the above cases we will remove the expression by merging the
2411       // producer affine min/max's affine expressions. Otherwise we need to
2412       // keep the existing expression.
2413       newExprs.push_back(expr);
2414     }
2415 
2416     if (producerOps.empty())
2417       return failure();
2418 
2419     unsigned numUsedDims = oldMap.getNumDims();
2420     unsigned numUsedSyms = oldMap.getNumSymbols();
2421 
2422     // Now go over all producer affine ops and merge their expressions.
2423     for (T producerOp : producerOps) {
2424       AffineMap producerMap = producerOp.getAffineMap();
2425       unsigned numProducerDims = producerMap.getNumDims();
2426       unsigned numProducerSyms = producerMap.getNumSymbols();
2427 
2428       // Collect all dimension/symbol values.
2429       ValueRange dimValues =
2430           producerOp.getMapOperands().take_front(numProducerDims);
2431       ValueRange symValues =
2432           producerOp.getMapOperands().take_back(numProducerSyms);
2433       newDimOperands.append(dimValues.begin(), dimValues.end());
2434       newSymOperands.append(symValues.begin(), symValues.end());
2435 
2436       // For expressions we need to shift to avoid overlap.
2437       for (AffineExpr expr : producerMap.getResults()) {
2438         newExprs.push_back(expr.shiftDims(numProducerDims, numUsedDims)
2439                                .shiftSymbols(numProducerSyms, numUsedSyms));
2440       }
2441 
2442       numUsedDims += numProducerDims;
2443       numUsedSyms += numProducerSyms;
2444     }
2445 
2446     auto newMap = AffineMap::get(numUsedDims, numUsedSyms, newExprs,
2447                                  rewriter.getContext());
2448     auto newOperands =
2449         llvm::to_vector<8>(llvm::concat<Value>(newDimOperands, newSymOperands));
2450     rewriter.replaceOpWithNewOp<T>(affineOp, newMap, newOperands);
2451 
2452     return success();
2453   }
2454 };
2455 
2456 //===----------------------------------------------------------------------===//
2457 // AffineMinOp
2458 //===----------------------------------------------------------------------===//
2459 //
2460 //   %0 = affine.min (d0) -> (1000, d0 + 512) (%i0)
2461 //
2462 
2463 OpFoldResult AffineMinOp::fold(ArrayRef<Attribute> operands) {
2464   return foldMinMaxOp(*this, operands);
2465 }
2466 
2467 void AffineMinOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2468                                               MLIRContext *context) {
2469   patterns.add<DeduplicateAffineMinMaxExpressions<AffineMinOp>,
2470                MergeAffineMinMaxOp<AffineMinOp>, SimplifyAffineOp<AffineMinOp>>(
2471       context);
2472 }
2473 
2474 //===----------------------------------------------------------------------===//
2475 // AffineMaxOp
2476 //===----------------------------------------------------------------------===//
2477 //
2478 //   %0 = affine.max (d0) -> (1000, d0 + 512) (%i0)
2479 //
2480 
2481 OpFoldResult AffineMaxOp::fold(ArrayRef<Attribute> operands) {
2482   return foldMinMaxOp(*this, operands);
2483 }
2484 
2485 void AffineMaxOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2486                                               MLIRContext *context) {
2487   patterns.add<DeduplicateAffineMinMaxExpressions<AffineMaxOp>,
2488                MergeAffineMinMaxOp<AffineMaxOp>, SimplifyAffineOp<AffineMaxOp>>(
2489       context);
2490 }
2491 
2492 //===----------------------------------------------------------------------===//
2493 // AffinePrefetchOp
2494 //===----------------------------------------------------------------------===//
2495 
2496 //
2497 // affine.prefetch %0[%i, %j + 5], read, locality<3>, data : memref<400x400xi32>
2498 //
2499 static ParseResult parseAffinePrefetchOp(OpAsmParser &parser,
2500                                          OperationState &result) {
2501   auto &builder = parser.getBuilder();
2502   auto indexTy = builder.getIndexType();
2503 
2504   MemRefType type;
2505   OpAsmParser::OperandType memrefInfo;
2506   IntegerAttr hintInfo;
2507   auto i32Type = parser.getBuilder().getIntegerType(32);
2508   StringRef readOrWrite, cacheType;
2509 
2510   AffineMapAttr mapAttr;
2511   SmallVector<OpAsmParser::OperandType, 1> mapOperands;
2512   if (parser.parseOperand(memrefInfo) ||
2513       parser.parseAffineMapOfSSAIds(mapOperands, mapAttr,
2514                                     AffinePrefetchOp::getMapAttrName(),
2515                                     result.attributes) ||
2516       parser.parseComma() || parser.parseKeyword(&readOrWrite) ||
2517       parser.parseComma() || parser.parseKeyword("locality") ||
2518       parser.parseLess() ||
2519       parser.parseAttribute(hintInfo, i32Type,
2520                             AffinePrefetchOp::getLocalityHintAttrName(),
2521                             result.attributes) ||
2522       parser.parseGreater() || parser.parseComma() ||
2523       parser.parseKeyword(&cacheType) ||
2524       parser.parseOptionalAttrDict(result.attributes) ||
2525       parser.parseColonType(type) ||
2526       parser.resolveOperand(memrefInfo, type, result.operands) ||
2527       parser.resolveOperands(mapOperands, indexTy, result.operands))
2528     return failure();
2529 
2530   if (!readOrWrite.equals("read") && !readOrWrite.equals("write"))
2531     return parser.emitError(parser.getNameLoc(),
2532                             "rw specifier has to be 'read' or 'write'");
2533   result.addAttribute(
2534       AffinePrefetchOp::getIsWriteAttrName(),
2535       parser.getBuilder().getBoolAttr(readOrWrite.equals("write")));
2536 
2537   if (!cacheType.equals("data") && !cacheType.equals("instr"))
2538     return parser.emitError(parser.getNameLoc(),
2539                             "cache type has to be 'data' or 'instr'");
2540 
2541   result.addAttribute(
2542       AffinePrefetchOp::getIsDataCacheAttrName(),
2543       parser.getBuilder().getBoolAttr(cacheType.equals("data")));
2544 
2545   return success();
2546 }
2547 
2548 static void print(OpAsmPrinter &p, AffinePrefetchOp op) {
2549   p << AffinePrefetchOp::getOperationName() << " " << op.memref() << '[';
2550   AffineMapAttr mapAttr = op->getAttrOfType<AffineMapAttr>(op.getMapAttrName());
2551   if (mapAttr) {
2552     SmallVector<Value, 2> operands(op.getMapOperands());
2553     p.printAffineMapOfSSAIds(mapAttr, operands);
2554   }
2555   p << ']' << ", " << (op.isWrite() ? "write" : "read") << ", "
2556     << "locality<" << op.localityHint() << ">, "
2557     << (op.isDataCache() ? "data" : "instr");
2558   p.printOptionalAttrDict(
2559       op->getAttrs(),
2560       /*elidedAttrs=*/{op.getMapAttrName(), op.getLocalityHintAttrName(),
2561                        op.getIsDataCacheAttrName(), op.getIsWriteAttrName()});
2562   p << " : " << op.getMemRefType();
2563 }
2564 
2565 static LogicalResult verify(AffinePrefetchOp op) {
2566   auto mapAttr = op->getAttrOfType<AffineMapAttr>(op.getMapAttrName());
2567   if (mapAttr) {
2568     AffineMap map = mapAttr.getValue();
2569     if (map.getNumResults() != op.getMemRefType().getRank())
2570       return op.emitOpError("affine.prefetch affine map num results must equal"
2571                             " memref rank");
2572     if (map.getNumInputs() + 1 != op.getNumOperands())
2573       return op.emitOpError("too few operands");
2574   } else {
2575     if (op.getNumOperands() != 1)
2576       return op.emitOpError("too few operands");
2577   }
2578 
2579   Region *scope = getAffineScope(op);
2580   for (auto idx : op.getMapOperands()) {
2581     if (!isValidAffineIndexOperand(idx, scope))
2582       return op.emitOpError("index must be a dimension or symbol identifier");
2583   }
2584   return success();
2585 }
2586 
2587 void AffinePrefetchOp::getCanonicalizationPatterns(RewritePatternSet &results,
2588                                                    MLIRContext *context) {
2589   // prefetch(memrefcast) -> prefetch
2590   results.add<SimplifyAffineOp<AffinePrefetchOp>>(context);
2591 }
2592 
2593 LogicalResult AffinePrefetchOp::fold(ArrayRef<Attribute> cstOperands,
2594                                      SmallVectorImpl<OpFoldResult> &results) {
2595   /// prefetch(memrefcast) -> prefetch
2596   return foldMemRefCast(*this);
2597 }
2598 
2599 //===----------------------------------------------------------------------===//
2600 // AffineParallelOp
2601 //===----------------------------------------------------------------------===//
2602 
2603 void AffineParallelOp::build(OpBuilder &builder, OperationState &result,
2604                              TypeRange resultTypes,
2605                              ArrayRef<AtomicRMWKind> reductions,
2606                              ArrayRef<int64_t> ranges) {
2607   SmallVector<AffineMap> lbs(ranges.size(), builder.getConstantAffineMap(0));
2608   auto ubs = llvm::to_vector<4>(llvm::map_range(ranges, [&](int64_t value) {
2609     return builder.getConstantAffineMap(value);
2610   }));
2611   SmallVector<int64_t> steps(ranges.size(), 1);
2612   build(builder, result, resultTypes, reductions, lbs, /*lbArgs=*/{}, ubs,
2613         /*ubArgs=*/{}, steps);
2614 }
2615 
2616 void AffineParallelOp::build(OpBuilder &builder, OperationState &result,
2617                              TypeRange resultTypes,
2618                              ArrayRef<AtomicRMWKind> reductions,
2619                              ArrayRef<AffineMap> lbMaps, ValueRange lbArgs,
2620                              ArrayRef<AffineMap> ubMaps, ValueRange ubArgs,
2621                              ArrayRef<int64_t> steps) {
2622   assert(!lbMaps.empty() && "expected the lower bound map to be non-empty");
2623   assert(!ubMaps.empty() && "expected the upper bound map to be non-empty");
2624   assert(llvm::all_of(lbMaps,
2625                       [lbMaps](AffineMap m) {
2626                         return m.getNumDims() == lbMaps[0].getNumDims() &&
2627                                m.getNumSymbols() == lbMaps[0].getNumSymbols();
2628                       }) &&
2629          "expected all lower bounds maps to have the same number of dimensions "
2630          "and symbols");
2631   assert(llvm::all_of(ubMaps,
2632                       [ubMaps](AffineMap m) {
2633                         return m.getNumDims() == ubMaps[0].getNumDims() &&
2634                                m.getNumSymbols() == ubMaps[0].getNumSymbols();
2635                       }) &&
2636          "expected all upper bounds maps to have the same number of dimensions "
2637          "and symbols");
2638   assert(lbMaps[0].getNumInputs() == lbArgs.size() &&
2639          "expected lower bound maps to have as many inputs as lower bound "
2640          "operands");
2641   assert(ubMaps[0].getNumInputs() == ubArgs.size() &&
2642          "expected upper bound maps to have as many inputs as upper bound "
2643          "operands");
2644 
2645   result.addTypes(resultTypes);
2646 
2647   // Convert the reductions to integer attributes.
2648   SmallVector<Attribute, 4> reductionAttrs;
2649   for (AtomicRMWKind reduction : reductions)
2650     reductionAttrs.push_back(
2651         builder.getI64IntegerAttr(static_cast<int64_t>(reduction)));
2652   result.addAttribute(getReductionsAttrName(),
2653                       builder.getArrayAttr(reductionAttrs));
2654 
2655   // Concatenates maps defined in the same input space (same dimensions and
2656   // symbols), assumes there is at least one map.
2657   auto concatMapsSameInput = [](ArrayRef<AffineMap> maps,
2658                                 SmallVectorImpl<int32_t> &groups) {
2659     SmallVector<AffineExpr> exprs;
2660     groups.reserve(groups.size() + maps.size());
2661     exprs.reserve(maps.size());
2662     for (AffineMap m : maps) {
2663       llvm::append_range(exprs, m.getResults());
2664       groups.push_back(m.getNumResults());
2665     }
2666     assert(!maps.empty() && "expected a non-empty list of maps");
2667     return AffineMap::get(maps[0].getNumDims(), maps[0].getNumSymbols(), exprs,
2668                           maps[0].getContext());
2669   };
2670 
2671   // Set up the bounds.
2672   SmallVector<int32_t> lbGroups, ubGroups;
2673   AffineMap lbMap = concatMapsSameInput(lbMaps, lbGroups);
2674   AffineMap ubMap = concatMapsSameInput(ubMaps, ubGroups);
2675   result.addAttribute(getLowerBoundsMapAttrName(), AffineMapAttr::get(lbMap));
2676   result.addAttribute(getLowerBoundsGroupsAttrName(),
2677                       builder.getI32VectorAttr(lbGroups));
2678   result.addAttribute(getUpperBoundsMapAttrName(), AffineMapAttr::get(ubMap));
2679   result.addAttribute(getUpperBoundsGroupsAttrName(),
2680                       builder.getI32VectorAttr(ubGroups));
2681   result.addAttribute(getStepsAttrName(), builder.getI64ArrayAttr(steps));
2682   result.addOperands(lbArgs);
2683   result.addOperands(ubArgs);
2684 
2685   // Create a region and a block for the body.
2686   auto *bodyRegion = result.addRegion();
2687   auto *body = new Block();
2688   // Add all the block arguments.
2689   for (unsigned i = 0, e = steps.size(); i < e; ++i)
2690     body->addArgument(IndexType::get(builder.getContext()));
2691   bodyRegion->push_back(body);
2692   if (resultTypes.empty())
2693     ensureTerminator(*bodyRegion, builder, result.location);
2694 }
2695 
2696 Region &AffineParallelOp::getLoopBody() { return region(); }
2697 
2698 bool AffineParallelOp::isDefinedOutsideOfLoop(Value value) {
2699   return !region().isAncestor(value.getParentRegion());
2700 }
2701 
2702 LogicalResult AffineParallelOp::moveOutOfLoop(ArrayRef<Operation *> ops) {
2703   for (Operation *op : ops)
2704     op->moveBefore(*this);
2705   return success();
2706 }
2707 
2708 unsigned AffineParallelOp::getNumDims() { return steps().size(); }
2709 
2710 AffineParallelOp::operand_range AffineParallelOp::getLowerBoundsOperands() {
2711   return getOperands().take_front(lowerBoundsMap().getNumInputs());
2712 }
2713 
2714 AffineParallelOp::operand_range AffineParallelOp::getUpperBoundsOperands() {
2715   return getOperands().drop_front(lowerBoundsMap().getNumInputs());
2716 }
2717 
2718 AffineMap AffineParallelOp::getLowerBoundMap(unsigned pos) {
2719   unsigned start = 0;
2720   for (unsigned i = 0; i < pos; ++i)
2721     start += lowerBoundsGroups().getValue<int32_t>(i);
2722   return lowerBoundsMap().getSliceMap(
2723       start, lowerBoundsGroups().getValue<int32_t>(pos));
2724 }
2725 
2726 AffineMap AffineParallelOp::getUpperBoundMap(unsigned pos) {
2727   unsigned start = 0;
2728   for (unsigned i = 0; i < pos; ++i)
2729     start += upperBoundsGroups().getValue<int32_t>(i);
2730   return upperBoundsMap().getSliceMap(
2731       start, upperBoundsGroups().getValue<int32_t>(pos));
2732 }
2733 
2734 AffineValueMap AffineParallelOp::getLowerBoundsValueMap() {
2735   return AffineValueMap(lowerBoundsMap(), getLowerBoundsOperands());
2736 }
2737 
2738 AffineValueMap AffineParallelOp::getUpperBoundsValueMap() {
2739   return AffineValueMap(upperBoundsMap(), getUpperBoundsOperands());
2740 }
2741 
2742 Optional<SmallVector<int64_t, 8>> AffineParallelOp::getConstantRanges() {
2743   if (hasMinMaxBounds())
2744     return llvm::None;
2745 
2746   // Try to convert all the ranges to constant expressions.
2747   SmallVector<int64_t, 8> out;
2748   AffineValueMap rangesValueMap;
2749   AffineValueMap::difference(getUpperBoundsValueMap(), getLowerBoundsValueMap(),
2750                              &rangesValueMap);
2751   out.reserve(rangesValueMap.getNumResults());
2752   for (unsigned i = 0, e = rangesValueMap.getNumResults(); i < e; ++i) {
2753     auto expr = rangesValueMap.getResult(i);
2754     auto cst = expr.dyn_cast<AffineConstantExpr>();
2755     if (!cst)
2756       return llvm::None;
2757     out.push_back(cst.getValue());
2758   }
2759   return out;
2760 }
2761 
2762 Block *AffineParallelOp::getBody() { return &region().front(); }
2763 
2764 OpBuilder AffineParallelOp::getBodyBuilder() {
2765   return OpBuilder(getBody(), std::prev(getBody()->end()));
2766 }
2767 
2768 void AffineParallelOp::setLowerBounds(ValueRange lbOperands, AffineMap map) {
2769   assert(lbOperands.size() == map.getNumInputs() &&
2770          "operands to map must match number of inputs");
2771   assert(map.getNumResults() >= 1 && "bounds map has at least one result");
2772 
2773   auto ubOperands = getUpperBoundsOperands();
2774 
2775   SmallVector<Value, 4> newOperands(lbOperands);
2776   newOperands.append(ubOperands.begin(), ubOperands.end());
2777   (*this)->setOperands(newOperands);
2778 
2779   lowerBoundsMapAttr(AffineMapAttr::get(map));
2780 }
2781 
2782 void AffineParallelOp::setUpperBounds(ValueRange ubOperands, AffineMap map) {
2783   assert(ubOperands.size() == map.getNumInputs() &&
2784          "operands to map must match number of inputs");
2785   assert(map.getNumResults() >= 1 && "bounds map has at least one result");
2786 
2787   SmallVector<Value, 4> newOperands(getLowerBoundsOperands());
2788   newOperands.append(ubOperands.begin(), ubOperands.end());
2789   (*this)->setOperands(newOperands);
2790 
2791   upperBoundsMapAttr(AffineMapAttr::get(map));
2792 }
2793 
2794 void AffineParallelOp::setLowerBoundsMap(AffineMap map) {
2795   AffineMap lbMap = lowerBoundsMap();
2796   assert(lbMap.getNumDims() == map.getNumDims() &&
2797          lbMap.getNumSymbols() == map.getNumSymbols());
2798   (void)lbMap;
2799   lowerBoundsMapAttr(AffineMapAttr::get(map));
2800 }
2801 
2802 void AffineParallelOp::setUpperBoundsMap(AffineMap map) {
2803   AffineMap ubMap = upperBoundsMap();
2804   assert(ubMap.getNumDims() == map.getNumDims() &&
2805          ubMap.getNumSymbols() == map.getNumSymbols());
2806   (void)ubMap;
2807   upperBoundsMapAttr(AffineMapAttr::get(map));
2808 }
2809 
2810 SmallVector<int64_t, 8> AffineParallelOp::getSteps() {
2811   SmallVector<int64_t, 8> result;
2812   for (Attribute attr : steps()) {
2813     result.push_back(attr.cast<IntegerAttr>().getInt());
2814   }
2815   return result;
2816 }
2817 
2818 void AffineParallelOp::setSteps(ArrayRef<int64_t> newSteps) {
2819   stepsAttr(getBodyBuilder().getI64ArrayAttr(newSteps));
2820 }
2821 
2822 static LogicalResult verify(AffineParallelOp op) {
2823   auto numDims = op.getNumDims();
2824   if (op.lowerBoundsGroups().getNumElements() != numDims ||
2825       op.upperBoundsGroups().getNumElements() != numDims ||
2826       op.steps().size() != numDims ||
2827       op.getBody()->getNumArguments() != numDims) {
2828     return op.emitOpError()
2829            << "the number of region arguments ("
2830            << op.getBody()->getNumArguments()
2831            << ") and the number of map groups for lower ("
2832            << op.lowerBoundsGroups().getNumElements() << ") and upper bound ("
2833            << op.upperBoundsGroups().getNumElements()
2834            << "), and the number of steps (" << op.steps().size()
2835            << ") must all match";
2836   }
2837 
2838   unsigned expectedNumLBResults = 0;
2839   for (APInt v : op.lowerBoundsGroups())
2840     expectedNumLBResults += v.getZExtValue();
2841   if (expectedNumLBResults != op.lowerBoundsMap().getNumResults())
2842     return op.emitOpError() << "expected lower bounds map to have "
2843                             << expectedNumLBResults << " results";
2844   unsigned expectedNumUBResults = 0;
2845   for (APInt v : op.upperBoundsGroups())
2846     expectedNumUBResults += v.getZExtValue();
2847   if (expectedNumUBResults != op.upperBoundsMap().getNumResults())
2848     return op.emitOpError() << "expected upper bounds map to have "
2849                             << expectedNumUBResults << " results";
2850 
2851   if (op.reductions().size() != op.getNumResults())
2852     return op.emitOpError("a reduction must be specified for each output");
2853 
2854   // Verify reduction  ops are all valid
2855   for (Attribute attr : op.reductions()) {
2856     auto intAttr = attr.dyn_cast<IntegerAttr>();
2857     if (!intAttr || !symbolizeAtomicRMWKind(intAttr.getInt()))
2858       return op.emitOpError("invalid reduction attribute");
2859   }
2860 
2861   // Verify that the bound operands are valid dimension/symbols.
2862   /// Lower bounds.
2863   if (failed(verifyDimAndSymbolIdentifiers(op, op.getLowerBoundsOperands(),
2864                                            op.lowerBoundsMap().getNumDims())))
2865     return failure();
2866   /// Upper bounds.
2867   if (failed(verifyDimAndSymbolIdentifiers(op, op.getUpperBoundsOperands(),
2868                                            op.upperBoundsMap().getNumDims())))
2869     return failure();
2870   return success();
2871 }
2872 
2873 LogicalResult AffineValueMap::canonicalize() {
2874   SmallVector<Value, 4> newOperands{operands};
2875   auto newMap = getAffineMap();
2876   composeAffineMapAndOperands(&newMap, &newOperands);
2877   if (newMap == getAffineMap() && newOperands == operands)
2878     return failure();
2879   reset(newMap, newOperands);
2880   return success();
2881 }
2882 
2883 /// Canonicalize the bounds of the given loop.
2884 static LogicalResult canonicalizeLoopBounds(AffineParallelOp op) {
2885   AffineValueMap lb = op.getLowerBoundsValueMap();
2886   bool lbCanonicalized = succeeded(lb.canonicalize());
2887 
2888   AffineValueMap ub = op.getUpperBoundsValueMap();
2889   bool ubCanonicalized = succeeded(ub.canonicalize());
2890 
2891   // Any canonicalization change always leads to updated map(s).
2892   if (!lbCanonicalized && !ubCanonicalized)
2893     return failure();
2894 
2895   if (lbCanonicalized)
2896     op.setLowerBounds(lb.getOperands(), lb.getAffineMap());
2897   if (ubCanonicalized)
2898     op.setUpperBounds(ub.getOperands(), ub.getAffineMap());
2899 
2900   return success();
2901 }
2902 
2903 LogicalResult AffineParallelOp::fold(ArrayRef<Attribute> operands,
2904                                      SmallVectorImpl<OpFoldResult> &results) {
2905   return canonicalizeLoopBounds(*this);
2906 }
2907 
2908 /// Prints a lower(upper) bound of an affine parallel loop with max(min)
2909 /// conditions in it. `mapAttr` is a flat list of affine expressions and `group`
2910 /// identifies which of the those expressions form max/min groups. `operands`
2911 /// are the SSA values of dimensions and symbols and `keyword` is either "min"
2912 /// or "max".
2913 static void printMinMaxBound(OpAsmPrinter &p, AffineMapAttr mapAttr,
2914                              DenseIntElementsAttr group, ValueRange operands,
2915                              StringRef keyword) {
2916   AffineMap map = mapAttr.getValue();
2917   unsigned numDims = map.getNumDims();
2918   ValueRange dimOperands = operands.take_front(numDims);
2919   ValueRange symOperands = operands.drop_front(numDims);
2920   unsigned start = 0;
2921   for (llvm::APInt groupSize : group) {
2922     if (start != 0)
2923       p << ", ";
2924 
2925     unsigned size = groupSize.getZExtValue();
2926     if (size == 1) {
2927       p.printAffineExprOfSSAIds(map.getResult(start), dimOperands, symOperands);
2928       ++start;
2929     } else {
2930       p << keyword << '(';
2931       AffineMap submap = map.getSliceMap(start, size);
2932       p.printAffineMapOfSSAIds(AffineMapAttr::get(submap), operands);
2933       p << ')';
2934       start += size;
2935     }
2936   }
2937 }
2938 
2939 static void print(OpAsmPrinter &p, AffineParallelOp op) {
2940   p << op.getOperationName() << " (" << op.getBody()->getArguments() << ") = (";
2941   printMinMaxBound(p, op.lowerBoundsMapAttr(), op.lowerBoundsGroupsAttr(),
2942                    op.getLowerBoundsOperands(), "max");
2943   p << ") to (";
2944   printMinMaxBound(p, op.upperBoundsMapAttr(), op.upperBoundsGroupsAttr(),
2945                    op.getUpperBoundsOperands(), "min");
2946   p << ')';
2947   SmallVector<int64_t, 8> steps = op.getSteps();
2948   bool elideSteps = llvm::all_of(steps, [](int64_t step) { return step == 1; });
2949   if (!elideSteps) {
2950     p << " step (";
2951     llvm::interleaveComma(steps, p);
2952     p << ')';
2953   }
2954   if (op.getNumResults()) {
2955     p << " reduce (";
2956     llvm::interleaveComma(op.reductions(), p, [&](auto &attr) {
2957       AtomicRMWKind sym =
2958           *symbolizeAtomicRMWKind(attr.template cast<IntegerAttr>().getInt());
2959       p << "\"" << stringifyAtomicRMWKind(sym) << "\"";
2960     });
2961     p << ") -> (" << op.getResultTypes() << ")";
2962   }
2963 
2964   p.printRegion(op.region(), /*printEntryBlockArgs=*/false,
2965                 /*printBlockTerminators=*/op.getNumResults());
2966   p.printOptionalAttrDict(
2967       op->getAttrs(),
2968       /*elidedAttrs=*/{AffineParallelOp::getReductionsAttrName(),
2969                        AffineParallelOp::getLowerBoundsMapAttrName(),
2970                        AffineParallelOp::getLowerBoundsGroupsAttrName(),
2971                        AffineParallelOp::getUpperBoundsMapAttrName(),
2972                        AffineParallelOp::getUpperBoundsGroupsAttrName(),
2973                        AffineParallelOp::getStepsAttrName()});
2974 }
2975 
2976 /// Given a list of lists of parsed operands, populates `uniqueOperands` with
2977 /// unique operands. Also populates `replacements with affine expressions of
2978 /// `kind` that can be used to update affine maps previously accepting a
2979 /// `operands` to accept `uniqueOperands` instead.
2980 static void deduplicateAndResolveOperands(
2981     OpAsmParser &parser,
2982     ArrayRef<SmallVector<OpAsmParser::OperandType>> operands,
2983     SmallVectorImpl<Value> &uniqueOperands,
2984     SmallVectorImpl<AffineExpr> &replacements, AffineExprKind kind) {
2985   assert((kind == AffineExprKind::DimId || kind == AffineExprKind::SymbolId) &&
2986          "expected operands to be dim or symbol expression");
2987 
2988   Type indexType = parser.getBuilder().getIndexType();
2989   for (const auto &list : operands) {
2990     SmallVector<Value> valueOperands;
2991     parser.resolveOperands(list, indexType, valueOperands);
2992     for (Value operand : valueOperands) {
2993       unsigned pos = std::distance(uniqueOperands.begin(),
2994                                    llvm::find(uniqueOperands, operand));
2995       if (pos == uniqueOperands.size())
2996         uniqueOperands.push_back(operand);
2997       replacements.push_back(
2998           kind == AffineExprKind::DimId
2999               ? getAffineDimExpr(pos, parser.getBuilder().getContext())
3000               : getAffineSymbolExpr(pos, parser.getBuilder().getContext()));
3001     }
3002   }
3003 }
3004 
3005 namespace {
3006 enum class MinMaxKind { Min, Max };
3007 } // namespace
3008 
3009 /// Parses an affine map that can contain a min/max for groups of its results,
3010 /// e.g., max(expr-1, expr-2), expr-3, max(expr-4, expr-5, expr-6). Populates
3011 /// `result` attributes with the map (flat list of expressions) and the grouping
3012 /// (list of integers that specify how many expressions to put into each
3013 /// min/max) attributes. Deduplicates repeated operands.
3014 ///
3015 /// parallel-bound       ::= `(` parallel-group-list `)`
3016 /// parallel-group-list  ::= parallel-group (`,` parallel-group-list)?
3017 /// parallel-group       ::= simple-group | min-max-group
3018 /// simple-group         ::= expr-of-ssa-ids
3019 /// min-max-group        ::= ( `min` | `max` ) `(` expr-of-ssa-ids-list `)`
3020 /// expr-of-ssa-ids-list ::= expr-of-ssa-ids (`,` expr-of-ssa-id-list)?
3021 ///
3022 /// Examples:
3023 ///   (%0, min(%1 + %2, %3), %4, min(%5 floordiv 32, %6))
3024 ///   (%0, max(%1 - 2 * %2))
3025 static ParseResult parseAffineMapWithMinMax(OpAsmParser &parser,
3026                                             OperationState &result,
3027                                             MinMaxKind kind) {
3028   constexpr llvm::StringLiteral tmpAttrName = "__pseudo_bound_map";
3029 
3030   StringRef mapName = kind == MinMaxKind::Min
3031                           ? AffineParallelOp::getUpperBoundsMapAttrName()
3032                           : AffineParallelOp::getLowerBoundsMapAttrName();
3033   StringRef groupsName = kind == MinMaxKind::Min
3034                              ? AffineParallelOp::getUpperBoundsGroupsAttrName()
3035                              : AffineParallelOp::getLowerBoundsGroupsAttrName();
3036 
3037   if (failed(parser.parseLParen()))
3038     return failure();
3039 
3040   if (succeeded(parser.parseOptionalRParen())) {
3041     result.addAttribute(
3042         mapName, AffineMapAttr::get(parser.getBuilder().getEmptyAffineMap()));
3043     result.addAttribute(groupsName, parser.getBuilder().getI32VectorAttr({}));
3044     return success();
3045   }
3046 
3047   SmallVector<AffineExpr> flatExprs;
3048   SmallVector<SmallVector<OpAsmParser::OperandType>> flatDimOperands;
3049   SmallVector<SmallVector<OpAsmParser::OperandType>> flatSymOperands;
3050   SmallVector<int32_t> numMapsPerGroup;
3051   SmallVector<OpAsmParser::OperandType> mapOperands;
3052   do {
3053     if (succeeded(parser.parseOptionalKeyword(
3054             kind == MinMaxKind::Min ? "min" : "max"))) {
3055       mapOperands.clear();
3056       AffineMapAttr map;
3057       if (failed(parser.parseAffineMapOfSSAIds(mapOperands, map, tmpAttrName,
3058                                                result.attributes,
3059                                                OpAsmParser::Delimiter::Paren)))
3060         return failure();
3061       result.attributes.erase(tmpAttrName);
3062       llvm::append_range(flatExprs, map.getValue().getResults());
3063       auto operandsRef = llvm::makeArrayRef(mapOperands);
3064       auto dimsRef = operandsRef.take_front(map.getValue().getNumDims());
3065       SmallVector<OpAsmParser::OperandType> dims(dimsRef.begin(),
3066                                                  dimsRef.end());
3067       auto symsRef = operandsRef.drop_front(map.getValue().getNumDims());
3068       SmallVector<OpAsmParser::OperandType> syms(symsRef.begin(),
3069                                                  symsRef.end());
3070       flatDimOperands.append(map.getValue().getNumResults(), dims);
3071       flatSymOperands.append(map.getValue().getNumResults(), syms);
3072       numMapsPerGroup.push_back(map.getValue().getNumResults());
3073     } else {
3074       if (failed(parser.parseAffineExprOfSSAIds(flatDimOperands.emplace_back(),
3075                                                 flatSymOperands.emplace_back(),
3076                                                 flatExprs.emplace_back())))
3077         return failure();
3078       numMapsPerGroup.push_back(1);
3079     }
3080   } while (succeeded(parser.parseOptionalComma()));
3081 
3082   if (failed(parser.parseRParen()))
3083     return failure();
3084 
3085   unsigned totalNumDims = 0;
3086   unsigned totalNumSyms = 0;
3087   for (unsigned i = 0, e = flatExprs.size(); i < e; ++i) {
3088     unsigned numDims = flatDimOperands[i].size();
3089     unsigned numSyms = flatSymOperands[i].size();
3090     flatExprs[i] = flatExprs[i]
3091                        .shiftDims(numDims, totalNumDims)
3092                        .shiftSymbols(numSyms, totalNumSyms);
3093     totalNumDims += numDims;
3094     totalNumSyms += numSyms;
3095   }
3096 
3097   // Deduplicate map operands.
3098   SmallVector<Value> dimOperands, symOperands;
3099   SmallVector<AffineExpr> dimRplacements, symRepacements;
3100   deduplicateAndResolveOperands(parser, flatDimOperands, dimOperands,
3101                                 dimRplacements, AffineExprKind::DimId);
3102   deduplicateAndResolveOperands(parser, flatSymOperands, symOperands,
3103                                 symRepacements, AffineExprKind::SymbolId);
3104 
3105   result.operands.append(dimOperands.begin(), dimOperands.end());
3106   result.operands.append(symOperands.begin(), symOperands.end());
3107 
3108   Builder &builder = parser.getBuilder();
3109   auto flatMap = AffineMap::get(totalNumDims, totalNumSyms, flatExprs,
3110                                 parser.getBuilder().getContext());
3111   flatMap = flatMap.replaceDimsAndSymbols(
3112       dimRplacements, symRepacements, dimOperands.size(), symOperands.size());
3113 
3114   result.addAttribute(mapName, AffineMapAttr::get(flatMap));
3115   result.addAttribute(groupsName, builder.getI32VectorAttr(numMapsPerGroup));
3116   return success();
3117 }
3118 
3119 //
3120 // operation ::= `affine.parallel` `(` ssa-ids `)` `=` parallel-bound
3121 //               `to` parallel-bound steps? region attr-dict?
3122 // steps     ::= `steps` `(` integer-literals `)`
3123 //
3124 static ParseResult parseAffineParallelOp(OpAsmParser &parser,
3125                                          OperationState &result) {
3126   auto &builder = parser.getBuilder();
3127   auto indexType = builder.getIndexType();
3128   SmallVector<OpAsmParser::OperandType, 4> ivs;
3129   if (parser.parseRegionArgumentList(ivs, /*requiredOperandCount=*/-1,
3130                                      OpAsmParser::Delimiter::Paren) ||
3131       parser.parseEqual() ||
3132       parseAffineMapWithMinMax(parser, result, MinMaxKind::Max) ||
3133       parser.parseKeyword("to") ||
3134       parseAffineMapWithMinMax(parser, result, MinMaxKind::Min))
3135     return failure();
3136 
3137   AffineMapAttr stepsMapAttr;
3138   NamedAttrList stepsAttrs;
3139   SmallVector<OpAsmParser::OperandType, 4> stepsMapOperands;
3140   if (failed(parser.parseOptionalKeyword("step"))) {
3141     SmallVector<int64_t, 4> steps(ivs.size(), 1);
3142     result.addAttribute(AffineParallelOp::getStepsAttrName(),
3143                         builder.getI64ArrayAttr(steps));
3144   } else {
3145     if (parser.parseAffineMapOfSSAIds(stepsMapOperands, stepsMapAttr,
3146                                       AffineParallelOp::getStepsAttrName(),
3147                                       stepsAttrs,
3148                                       OpAsmParser::Delimiter::Paren))
3149       return failure();
3150 
3151     // Convert steps from an AffineMap into an I64ArrayAttr.
3152     SmallVector<int64_t, 4> steps;
3153     auto stepsMap = stepsMapAttr.getValue();
3154     for (const auto &result : stepsMap.getResults()) {
3155       auto constExpr = result.dyn_cast<AffineConstantExpr>();
3156       if (!constExpr)
3157         return parser.emitError(parser.getNameLoc(),
3158                                 "steps must be constant integers");
3159       steps.push_back(constExpr.getValue());
3160     }
3161     result.addAttribute(AffineParallelOp::getStepsAttrName(),
3162                         builder.getI64ArrayAttr(steps));
3163   }
3164 
3165   // Parse optional clause of the form: `reduce ("addf", "maxf")`, where the
3166   // quoted strings are a member of the enum AtomicRMWKind.
3167   SmallVector<Attribute, 4> reductions;
3168   if (succeeded(parser.parseOptionalKeyword("reduce"))) {
3169     if (parser.parseLParen())
3170       return failure();
3171     do {
3172       // Parse a single quoted string via the attribute parsing, and then
3173       // verify it is a member of the enum and convert to it's integer
3174       // representation.
3175       StringAttr attrVal;
3176       NamedAttrList attrStorage;
3177       auto loc = parser.getCurrentLocation();
3178       if (parser.parseAttribute(attrVal, builder.getNoneType(), "reduce",
3179                                 attrStorage))
3180         return failure();
3181       llvm::Optional<AtomicRMWKind> reduction =
3182           symbolizeAtomicRMWKind(attrVal.getValue());
3183       if (!reduction)
3184         return parser.emitError(loc, "invalid reduction value: ") << attrVal;
3185       reductions.push_back(builder.getI64IntegerAttr(
3186           static_cast<int64_t>(reduction.getValue())));
3187       // While we keep getting commas, keep parsing.
3188     } while (succeeded(parser.parseOptionalComma()));
3189     if (parser.parseRParen())
3190       return failure();
3191   }
3192   result.addAttribute(AffineParallelOp::getReductionsAttrName(),
3193                       builder.getArrayAttr(reductions));
3194 
3195   // Parse return types of reductions (if any)
3196   if (parser.parseOptionalArrowTypeList(result.types))
3197     return failure();
3198 
3199   // Now parse the body.
3200   Region *body = result.addRegion();
3201   SmallVector<Type, 4> types(ivs.size(), indexType);
3202   if (parser.parseRegion(*body, ivs, types) ||
3203       parser.parseOptionalAttrDict(result.attributes))
3204     return failure();
3205 
3206   // Add a terminator if none was parsed.
3207   AffineParallelOp::ensureTerminator(*body, builder, result.location);
3208   return success();
3209 }
3210 
3211 //===----------------------------------------------------------------------===//
3212 // AffineYieldOp
3213 //===----------------------------------------------------------------------===//
3214 
3215 static LogicalResult verify(AffineYieldOp op) {
3216   auto *parentOp = op->getParentOp();
3217   auto results = parentOp->getResults();
3218   auto operands = op.getOperands();
3219 
3220   if (!isa<AffineParallelOp, AffineIfOp, AffineForOp>(parentOp))
3221     return op.emitOpError() << "only terminates affine.if/for/parallel regions";
3222   if (parentOp->getNumResults() != op.getNumOperands())
3223     return op.emitOpError() << "parent of yield must have same number of "
3224                                "results as the yield operands";
3225   for (auto it : llvm::zip(results, operands)) {
3226     if (std::get<0>(it).getType() != std::get<1>(it).getType())
3227       return op.emitOpError()
3228              << "types mismatch between yield op and its parent";
3229   }
3230 
3231   return success();
3232 }
3233 
3234 //===----------------------------------------------------------------------===//
3235 // AffineVectorLoadOp
3236 //===----------------------------------------------------------------------===//
3237 
3238 void AffineVectorLoadOp::build(OpBuilder &builder, OperationState &result,
3239                                VectorType resultType, AffineMap map,
3240                                ValueRange operands) {
3241   assert(operands.size() == 1 + map.getNumInputs() && "inconsistent operands");
3242   result.addOperands(operands);
3243   if (map)
3244     result.addAttribute(getMapAttrName(), AffineMapAttr::get(map));
3245   result.types.push_back(resultType);
3246 }
3247 
3248 void AffineVectorLoadOp::build(OpBuilder &builder, OperationState &result,
3249                                VectorType resultType, Value memref,
3250                                AffineMap map, ValueRange mapOperands) {
3251   assert(map.getNumInputs() == mapOperands.size() && "inconsistent index info");
3252   result.addOperands(memref);
3253   result.addOperands(mapOperands);
3254   result.addAttribute(getMapAttrName(), AffineMapAttr::get(map));
3255   result.types.push_back(resultType);
3256 }
3257 
3258 void AffineVectorLoadOp::build(OpBuilder &builder, OperationState &result,
3259                                VectorType resultType, Value memref,
3260                                ValueRange indices) {
3261   auto memrefType = memref.getType().cast<MemRefType>();
3262   int64_t rank = memrefType.getRank();
3263   // Create identity map for memrefs with at least one dimension or () -> ()
3264   // for zero-dimensional memrefs.
3265   auto map =
3266       rank ? builder.getMultiDimIdentityMap(rank) : builder.getEmptyAffineMap();
3267   build(builder, result, resultType, memref, map, indices);
3268 }
3269 
3270 static ParseResult parseAffineVectorLoadOp(OpAsmParser &parser,
3271                                            OperationState &result) {
3272   auto &builder = parser.getBuilder();
3273   auto indexTy = builder.getIndexType();
3274 
3275   MemRefType memrefType;
3276   VectorType resultType;
3277   OpAsmParser::OperandType memrefInfo;
3278   AffineMapAttr mapAttr;
3279   SmallVector<OpAsmParser::OperandType, 1> mapOperands;
3280   return failure(
3281       parser.parseOperand(memrefInfo) ||
3282       parser.parseAffineMapOfSSAIds(mapOperands, mapAttr,
3283                                     AffineVectorLoadOp::getMapAttrName(),
3284                                     result.attributes) ||
3285       parser.parseOptionalAttrDict(result.attributes) ||
3286       parser.parseColonType(memrefType) || parser.parseComma() ||
3287       parser.parseType(resultType) ||
3288       parser.resolveOperand(memrefInfo, memrefType, result.operands) ||
3289       parser.resolveOperands(mapOperands, indexTy, result.operands) ||
3290       parser.addTypeToList(resultType, result.types));
3291 }
3292 
3293 static void print(OpAsmPrinter &p, AffineVectorLoadOp op) {
3294   p << "affine.vector_load " << op.getMemRef() << '[';
3295   if (AffineMapAttr mapAttr =
3296           op->getAttrOfType<AffineMapAttr>(op.getMapAttrName()))
3297     p.printAffineMapOfSSAIds(mapAttr, op.getMapOperands());
3298   p << ']';
3299   p.printOptionalAttrDict(op->getAttrs(),
3300                           /*elidedAttrs=*/{op.getMapAttrName()});
3301   p << " : " << op.getMemRefType() << ", " << op.getType();
3302 }
3303 
3304 /// Verify common invariants of affine.vector_load and affine.vector_store.
3305 static LogicalResult verifyVectorMemoryOp(Operation *op, MemRefType memrefType,
3306                                           VectorType vectorType) {
3307   // Check that memref and vector element types match.
3308   if (memrefType.getElementType() != vectorType.getElementType())
3309     return op->emitOpError(
3310         "requires memref and vector types of the same elemental type");
3311   return success();
3312 }
3313 
3314 static LogicalResult verify(AffineVectorLoadOp op) {
3315   MemRefType memrefType = op.getMemRefType();
3316   if (failed(verifyMemoryOpIndexing(
3317           op.getOperation(),
3318           op->getAttrOfType<AffineMapAttr>(op.getMapAttrName()),
3319           op.getMapOperands(), memrefType,
3320           /*numIndexOperands=*/op.getNumOperands() - 1)))
3321     return failure();
3322 
3323   if (failed(verifyVectorMemoryOp(op.getOperation(), memrefType,
3324                                   op.getVectorType())))
3325     return failure();
3326 
3327   return success();
3328 }
3329 
3330 //===----------------------------------------------------------------------===//
3331 // AffineVectorStoreOp
3332 //===----------------------------------------------------------------------===//
3333 
3334 void AffineVectorStoreOp::build(OpBuilder &builder, OperationState &result,
3335                                 Value valueToStore, Value memref, AffineMap map,
3336                                 ValueRange mapOperands) {
3337   assert(map.getNumInputs() == mapOperands.size() && "inconsistent index info");
3338   result.addOperands(valueToStore);
3339   result.addOperands(memref);
3340   result.addOperands(mapOperands);
3341   result.addAttribute(getMapAttrName(), AffineMapAttr::get(map));
3342 }
3343 
3344 // Use identity map.
3345 void AffineVectorStoreOp::build(OpBuilder &builder, OperationState &result,
3346                                 Value valueToStore, Value memref,
3347                                 ValueRange indices) {
3348   auto memrefType = memref.getType().cast<MemRefType>();
3349   int64_t rank = memrefType.getRank();
3350   // Create identity map for memrefs with at least one dimension or () -> ()
3351   // for zero-dimensional memrefs.
3352   auto map =
3353       rank ? builder.getMultiDimIdentityMap(rank) : builder.getEmptyAffineMap();
3354   build(builder, result, valueToStore, memref, map, indices);
3355 }
3356 
3357 static ParseResult parseAffineVectorStoreOp(OpAsmParser &parser,
3358                                             OperationState &result) {
3359   auto indexTy = parser.getBuilder().getIndexType();
3360 
3361   MemRefType memrefType;
3362   VectorType resultType;
3363   OpAsmParser::OperandType storeValueInfo;
3364   OpAsmParser::OperandType memrefInfo;
3365   AffineMapAttr mapAttr;
3366   SmallVector<OpAsmParser::OperandType, 1> mapOperands;
3367   return failure(
3368       parser.parseOperand(storeValueInfo) || parser.parseComma() ||
3369       parser.parseOperand(memrefInfo) ||
3370       parser.parseAffineMapOfSSAIds(mapOperands, mapAttr,
3371                                     AffineVectorStoreOp::getMapAttrName(),
3372                                     result.attributes) ||
3373       parser.parseOptionalAttrDict(result.attributes) ||
3374       parser.parseColonType(memrefType) || parser.parseComma() ||
3375       parser.parseType(resultType) ||
3376       parser.resolveOperand(storeValueInfo, resultType, result.operands) ||
3377       parser.resolveOperand(memrefInfo, memrefType, result.operands) ||
3378       parser.resolveOperands(mapOperands, indexTy, result.operands));
3379 }
3380 
3381 static void print(OpAsmPrinter &p, AffineVectorStoreOp op) {
3382   p << "affine.vector_store " << op.getValueToStore();
3383   p << ", " << op.getMemRef() << '[';
3384   if (AffineMapAttr mapAttr =
3385           op->getAttrOfType<AffineMapAttr>(op.getMapAttrName()))
3386     p.printAffineMapOfSSAIds(mapAttr, op.getMapOperands());
3387   p << ']';
3388   p.printOptionalAttrDict(op->getAttrs(),
3389                           /*elidedAttrs=*/{op.getMapAttrName()});
3390   p << " : " << op.getMemRefType() << ", " << op.getValueToStore().getType();
3391 }
3392 
3393 static LogicalResult verify(AffineVectorStoreOp op) {
3394   MemRefType memrefType = op.getMemRefType();
3395   if (failed(verifyMemoryOpIndexing(
3396           op.getOperation(),
3397           op->getAttrOfType<AffineMapAttr>(op.getMapAttrName()),
3398           op.getMapOperands(), memrefType,
3399           /*numIndexOperands=*/op.getNumOperands() - 2)))
3400     return failure();
3401 
3402   if (failed(verifyVectorMemoryOp(op.getOperation(), memrefType,
3403                                   op.getVectorType())))
3404     return failure();
3405 
3406   return success();
3407 }
3408 
3409 //===----------------------------------------------------------------------===//
3410 // TableGen'd op method definitions
3411 //===----------------------------------------------------------------------===//
3412 
3413 #define GET_OP_CLASSES
3414 #include "mlir/Dialect/Affine/IR/AffineOps.cpp.inc"
3415