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