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) {
946   bool folded = false;
947   for (OpOperand &operand : op->getOpOperands()) {
948     auto cast = operand.get().getDefiningOp<memref::CastOp>();
949     if (cast && !cast.getOperand().getType().isa<UnrankedMemRefType>()) {
950       operand.set(cast.getOperand());
951       folded = true;
952     }
953   }
954   return success(folded);
955 }
956 
957 //===----------------------------------------------------------------------===//
958 // AffineDmaStartOp
959 //===----------------------------------------------------------------------===//
960 
961 // TODO: Check that map operands are loop IVs or symbols.
962 void AffineDmaStartOp::build(OpBuilder &builder, OperationState &result,
963                              Value srcMemRef, AffineMap srcMap,
964                              ValueRange srcIndices, Value destMemRef,
965                              AffineMap dstMap, ValueRange destIndices,
966                              Value tagMemRef, AffineMap tagMap,
967                              ValueRange tagIndices, Value numElements,
968                              Value stride, Value elementsPerStride) {
969   result.addOperands(srcMemRef);
970   result.addAttribute(getSrcMapAttrName(), AffineMapAttr::get(srcMap));
971   result.addOperands(srcIndices);
972   result.addOperands(destMemRef);
973   result.addAttribute(getDstMapAttrName(), AffineMapAttr::get(dstMap));
974   result.addOperands(destIndices);
975   result.addOperands(tagMemRef);
976   result.addAttribute(getTagMapAttrName(), AffineMapAttr::get(tagMap));
977   result.addOperands(tagIndices);
978   result.addOperands(numElements);
979   if (stride) {
980     result.addOperands({stride, elementsPerStride});
981   }
982 }
983 
984 void AffineDmaStartOp::print(OpAsmPrinter &p) {
985   p << "affine.dma_start " << getSrcMemRef() << '[';
986   p.printAffineMapOfSSAIds(getSrcMapAttr(), getSrcIndices());
987   p << "], " << getDstMemRef() << '[';
988   p.printAffineMapOfSSAIds(getDstMapAttr(), getDstIndices());
989   p << "], " << getTagMemRef() << '[';
990   p.printAffineMapOfSSAIds(getTagMapAttr(), getTagIndices());
991   p << "], " << getNumElements();
992   if (isStrided()) {
993     p << ", " << getStride();
994     p << ", " << getNumElementsPerStride();
995   }
996   p << " : " << getSrcMemRefType() << ", " << getDstMemRefType() << ", "
997     << getTagMemRefType();
998 }
999 
1000 // Parse AffineDmaStartOp.
1001 // Ex:
1002 //   affine.dma_start %src[%i, %j], %dst[%k, %l], %tag[%index], %size,
1003 //     %stride, %num_elt_per_stride
1004 //       : memref<3076 x f32, 0>, memref<1024 x f32, 2>, memref<1 x i32>
1005 //
1006 ParseResult AffineDmaStartOp::parse(OpAsmParser &parser,
1007                                     OperationState &result) {
1008   OpAsmParser::OperandType srcMemRefInfo;
1009   AffineMapAttr srcMapAttr;
1010   SmallVector<OpAsmParser::OperandType, 4> srcMapOperands;
1011   OpAsmParser::OperandType dstMemRefInfo;
1012   AffineMapAttr dstMapAttr;
1013   SmallVector<OpAsmParser::OperandType, 4> dstMapOperands;
1014   OpAsmParser::OperandType tagMemRefInfo;
1015   AffineMapAttr tagMapAttr;
1016   SmallVector<OpAsmParser::OperandType, 4> tagMapOperands;
1017   OpAsmParser::OperandType numElementsInfo;
1018   SmallVector<OpAsmParser::OperandType, 2> strideInfo;
1019 
1020   SmallVector<Type, 3> types;
1021   auto indexType = parser.getBuilder().getIndexType();
1022 
1023   // Parse and resolve the following list of operands:
1024   // *) dst memref followed by its affine maps operands (in square brackets).
1025   // *) src memref followed by its affine map operands (in square brackets).
1026   // *) tag memref followed by its affine map operands (in square brackets).
1027   // *) number of elements transferred by DMA operation.
1028   if (parser.parseOperand(srcMemRefInfo) ||
1029       parser.parseAffineMapOfSSAIds(srcMapOperands, srcMapAttr,
1030                                     getSrcMapAttrName(), result.attributes) ||
1031       parser.parseComma() || parser.parseOperand(dstMemRefInfo) ||
1032       parser.parseAffineMapOfSSAIds(dstMapOperands, dstMapAttr,
1033                                     getDstMapAttrName(), result.attributes) ||
1034       parser.parseComma() || parser.parseOperand(tagMemRefInfo) ||
1035       parser.parseAffineMapOfSSAIds(tagMapOperands, tagMapAttr,
1036                                     getTagMapAttrName(), result.attributes) ||
1037       parser.parseComma() || parser.parseOperand(numElementsInfo))
1038     return failure();
1039 
1040   // Parse optional stride and elements per stride.
1041   if (parser.parseTrailingOperandList(strideInfo)) {
1042     return failure();
1043   }
1044   if (!strideInfo.empty() && strideInfo.size() != 2) {
1045     return parser.emitError(parser.getNameLoc(),
1046                             "expected two stride related operands");
1047   }
1048   bool isStrided = strideInfo.size() == 2;
1049 
1050   if (parser.parseColonTypeList(types))
1051     return failure();
1052 
1053   if (types.size() != 3)
1054     return parser.emitError(parser.getNameLoc(), "expected three types");
1055 
1056   if (parser.resolveOperand(srcMemRefInfo, types[0], result.operands) ||
1057       parser.resolveOperands(srcMapOperands, indexType, result.operands) ||
1058       parser.resolveOperand(dstMemRefInfo, types[1], result.operands) ||
1059       parser.resolveOperands(dstMapOperands, indexType, result.operands) ||
1060       parser.resolveOperand(tagMemRefInfo, types[2], result.operands) ||
1061       parser.resolveOperands(tagMapOperands, indexType, result.operands) ||
1062       parser.resolveOperand(numElementsInfo, indexType, result.operands))
1063     return failure();
1064 
1065   if (isStrided) {
1066     if (parser.resolveOperands(strideInfo, indexType, result.operands))
1067       return failure();
1068   }
1069 
1070   // Check that src/dst/tag operand counts match their map.numInputs.
1071   if (srcMapOperands.size() != srcMapAttr.getValue().getNumInputs() ||
1072       dstMapOperands.size() != dstMapAttr.getValue().getNumInputs() ||
1073       tagMapOperands.size() != tagMapAttr.getValue().getNumInputs())
1074     return parser.emitError(parser.getNameLoc(),
1075                             "memref operand count not equal to map.numInputs");
1076   return success();
1077 }
1078 
1079 LogicalResult AffineDmaStartOp::verify() {
1080   if (!getOperand(getSrcMemRefOperandIndex()).getType().isa<MemRefType>())
1081     return emitOpError("expected DMA source to be of memref type");
1082   if (!getOperand(getDstMemRefOperandIndex()).getType().isa<MemRefType>())
1083     return emitOpError("expected DMA destination to be of memref type");
1084   if (!getOperand(getTagMemRefOperandIndex()).getType().isa<MemRefType>())
1085     return emitOpError("expected DMA tag to be of memref type");
1086 
1087   unsigned numInputsAllMaps = getSrcMap().getNumInputs() +
1088                               getDstMap().getNumInputs() +
1089                               getTagMap().getNumInputs();
1090   if (getNumOperands() != numInputsAllMaps + 3 + 1 &&
1091       getNumOperands() != numInputsAllMaps + 3 + 1 + 2) {
1092     return emitOpError("incorrect number of operands");
1093   }
1094 
1095   Region *scope = getAffineScope(*this);
1096   for (auto idx : getSrcIndices()) {
1097     if (!idx.getType().isIndex())
1098       return emitOpError("src index to dma_start must have 'index' type");
1099     if (!isValidAffineIndexOperand(idx, scope))
1100       return emitOpError("src index must be a dimension or symbol identifier");
1101   }
1102   for (auto idx : getDstIndices()) {
1103     if (!idx.getType().isIndex())
1104       return emitOpError("dst index to dma_start must have 'index' type");
1105     if (!isValidAffineIndexOperand(idx, scope))
1106       return emitOpError("dst index must be a dimension or symbol identifier");
1107   }
1108   for (auto idx : getTagIndices()) {
1109     if (!idx.getType().isIndex())
1110       return emitOpError("tag index to dma_start must have 'index' type");
1111     if (!isValidAffineIndexOperand(idx, scope))
1112       return emitOpError("tag index must be a dimension or symbol identifier");
1113   }
1114   return success();
1115 }
1116 
1117 LogicalResult AffineDmaStartOp::fold(ArrayRef<Attribute> cstOperands,
1118                                      SmallVectorImpl<OpFoldResult> &results) {
1119   /// dma_start(memrefcast) -> dma_start
1120   return foldMemRefCast(*this);
1121 }
1122 
1123 //===----------------------------------------------------------------------===//
1124 // AffineDmaWaitOp
1125 //===----------------------------------------------------------------------===//
1126 
1127 // TODO: Check that map operands are loop IVs or symbols.
1128 void AffineDmaWaitOp::build(OpBuilder &builder, OperationState &result,
1129                             Value tagMemRef, AffineMap tagMap,
1130                             ValueRange tagIndices, Value numElements) {
1131   result.addOperands(tagMemRef);
1132   result.addAttribute(getTagMapAttrName(), AffineMapAttr::get(tagMap));
1133   result.addOperands(tagIndices);
1134   result.addOperands(numElements);
1135 }
1136 
1137 void AffineDmaWaitOp::print(OpAsmPrinter &p) {
1138   p << "affine.dma_wait " << getTagMemRef() << '[';
1139   SmallVector<Value, 2> operands(getTagIndices());
1140   p.printAffineMapOfSSAIds(getTagMapAttr(), operands);
1141   p << "], ";
1142   p.printOperand(getNumElements());
1143   p << " : " << getTagMemRef().getType();
1144 }
1145 
1146 // Parse AffineDmaWaitOp.
1147 // Eg:
1148 //   affine.dma_wait %tag[%index], %num_elements
1149 //     : memref<1 x i32, (d0) -> (d0), 4>
1150 //
1151 ParseResult AffineDmaWaitOp::parse(OpAsmParser &parser,
1152                                    OperationState &result) {
1153   OpAsmParser::OperandType tagMemRefInfo;
1154   AffineMapAttr tagMapAttr;
1155   SmallVector<OpAsmParser::OperandType, 2> tagMapOperands;
1156   Type type;
1157   auto indexType = parser.getBuilder().getIndexType();
1158   OpAsmParser::OperandType numElementsInfo;
1159 
1160   // Parse tag memref, its map operands, and dma size.
1161   if (parser.parseOperand(tagMemRefInfo) ||
1162       parser.parseAffineMapOfSSAIds(tagMapOperands, tagMapAttr,
1163                                     getTagMapAttrName(), result.attributes) ||
1164       parser.parseComma() || parser.parseOperand(numElementsInfo) ||
1165       parser.parseColonType(type) ||
1166       parser.resolveOperand(tagMemRefInfo, type, result.operands) ||
1167       parser.resolveOperands(tagMapOperands, indexType, result.operands) ||
1168       parser.resolveOperand(numElementsInfo, indexType, result.operands))
1169     return failure();
1170 
1171   if (!type.isa<MemRefType>())
1172     return parser.emitError(parser.getNameLoc(),
1173                             "expected tag to be of memref type");
1174 
1175   if (tagMapOperands.size() != tagMapAttr.getValue().getNumInputs())
1176     return parser.emitError(parser.getNameLoc(),
1177                             "tag memref operand count != to map.numInputs");
1178   return success();
1179 }
1180 
1181 LogicalResult AffineDmaWaitOp::verify() {
1182   if (!getOperand(0).getType().isa<MemRefType>())
1183     return emitOpError("expected DMA tag to be of memref type");
1184   Region *scope = getAffineScope(*this);
1185   for (auto idx : getTagIndices()) {
1186     if (!idx.getType().isIndex())
1187       return emitOpError("index to dma_wait must have 'index' type");
1188     if (!isValidAffineIndexOperand(idx, scope))
1189       return emitOpError("index must be a dimension or symbol identifier");
1190   }
1191   return success();
1192 }
1193 
1194 LogicalResult AffineDmaWaitOp::fold(ArrayRef<Attribute> cstOperands,
1195                                     SmallVectorImpl<OpFoldResult> &results) {
1196   /// dma_wait(memrefcast) -> dma_wait
1197   return foldMemRefCast(*this);
1198 }
1199 
1200 //===----------------------------------------------------------------------===//
1201 // AffineForOp
1202 //===----------------------------------------------------------------------===//
1203 
1204 /// 'bodyBuilder' is used to build the body of affine.for. If iterArgs and
1205 /// bodyBuilder are empty/null, we include default terminator op.
1206 void AffineForOp::build(OpBuilder &builder, OperationState &result,
1207                         ValueRange lbOperands, AffineMap lbMap,
1208                         ValueRange ubOperands, AffineMap ubMap, int64_t step,
1209                         ValueRange iterArgs, BodyBuilderFn bodyBuilder) {
1210   assert(((!lbMap && lbOperands.empty()) ||
1211           lbOperands.size() == lbMap.getNumInputs()) &&
1212          "lower bound operand count does not match the affine map");
1213   assert(((!ubMap && ubOperands.empty()) ||
1214           ubOperands.size() == ubMap.getNumInputs()) &&
1215          "upper bound operand count does not match the affine map");
1216   assert(step > 0 && "step has to be a positive integer constant");
1217 
1218   for (Value val : iterArgs)
1219     result.addTypes(val.getType());
1220 
1221   // Add an attribute for the step.
1222   result.addAttribute(getStepAttrName(),
1223                       builder.getIntegerAttr(builder.getIndexType(), step));
1224 
1225   // Add the lower bound.
1226   result.addAttribute(getLowerBoundAttrName(), AffineMapAttr::get(lbMap));
1227   result.addOperands(lbOperands);
1228 
1229   // Add the upper bound.
1230   result.addAttribute(getUpperBoundAttrName(), AffineMapAttr::get(ubMap));
1231   result.addOperands(ubOperands);
1232 
1233   result.addOperands(iterArgs);
1234   // Create a region and a block for the body.  The argument of the region is
1235   // the loop induction variable.
1236   Region *bodyRegion = result.addRegion();
1237   bodyRegion->push_back(new Block);
1238   Block &bodyBlock = bodyRegion->front();
1239   Value inductionVar = bodyBlock.addArgument(builder.getIndexType());
1240   for (Value val : iterArgs)
1241     bodyBlock.addArgument(val.getType());
1242 
1243   // Create the default terminator if the builder is not provided and if the
1244   // iteration arguments are not provided. Otherwise, leave this to the caller
1245   // because we don't know which values to return from the loop.
1246   if (iterArgs.empty() && !bodyBuilder) {
1247     ensureTerminator(*bodyRegion, builder, result.location);
1248   } else if (bodyBuilder) {
1249     OpBuilder::InsertionGuard guard(builder);
1250     builder.setInsertionPointToStart(&bodyBlock);
1251     bodyBuilder(builder, result.location, inductionVar,
1252                 bodyBlock.getArguments().drop_front());
1253   }
1254 }
1255 
1256 void AffineForOp::build(OpBuilder &builder, OperationState &result, int64_t lb,
1257                         int64_t ub, int64_t step, ValueRange iterArgs,
1258                         BodyBuilderFn bodyBuilder) {
1259   auto lbMap = AffineMap::getConstantMap(lb, builder.getContext());
1260   auto ubMap = AffineMap::getConstantMap(ub, builder.getContext());
1261   return build(builder, result, {}, lbMap, {}, ubMap, step, iterArgs,
1262                bodyBuilder);
1263 }
1264 
1265 static LogicalResult verify(AffineForOp op) {
1266   // Check that the body defines as single block argument for the induction
1267   // variable.
1268   auto *body = op.getBody();
1269   if (body->getNumArguments() == 0 || !body->getArgument(0).getType().isIndex())
1270     return op.emitOpError(
1271         "expected body to have a single index argument for the "
1272         "induction variable");
1273 
1274   // Verify that the bound operands are valid dimension/symbols.
1275   /// Lower bound.
1276   if (op.getLowerBoundMap().getNumInputs() > 0)
1277     if (failed(
1278             verifyDimAndSymbolIdentifiers(op, op.getLowerBoundOperands(),
1279                                           op.getLowerBoundMap().getNumDims())))
1280       return failure();
1281   /// Upper bound.
1282   if (op.getUpperBoundMap().getNumInputs() > 0)
1283     if (failed(
1284             verifyDimAndSymbolIdentifiers(op, op.getUpperBoundOperands(),
1285                                           op.getUpperBoundMap().getNumDims())))
1286       return failure();
1287 
1288   unsigned opNumResults = op.getNumResults();
1289   if (opNumResults == 0)
1290     return success();
1291 
1292   // If ForOp defines values, check that the number and types of the defined
1293   // values match ForOp initial iter operands and backedge basic block
1294   // arguments.
1295   if (op.getNumIterOperands() != opNumResults)
1296     return op.emitOpError(
1297         "mismatch between the number of loop-carried values and results");
1298   if (op.getNumRegionIterArgs() != opNumResults)
1299     return op.emitOpError(
1300         "mismatch between the number of basic block args and results");
1301 
1302   return success();
1303 }
1304 
1305 /// Parse a for operation loop bounds.
1306 static ParseResult parseBound(bool isLower, OperationState &result,
1307                               OpAsmParser &p) {
1308   // 'min' / 'max' prefixes are generally syntactic sugar, but are required if
1309   // the map has multiple results.
1310   bool failedToParsedMinMax =
1311       failed(p.parseOptionalKeyword(isLower ? "max" : "min"));
1312 
1313   auto &builder = p.getBuilder();
1314   auto boundAttrName = isLower ? AffineForOp::getLowerBoundAttrName()
1315                                : AffineForOp::getUpperBoundAttrName();
1316 
1317   // Parse ssa-id as identity map.
1318   SmallVector<OpAsmParser::OperandType, 1> boundOpInfos;
1319   if (p.parseOperandList(boundOpInfos))
1320     return failure();
1321 
1322   if (!boundOpInfos.empty()) {
1323     // Check that only one operand was parsed.
1324     if (boundOpInfos.size() > 1)
1325       return p.emitError(p.getNameLoc(),
1326                          "expected only one loop bound operand");
1327 
1328     // TODO: improve error message when SSA value is not of index type.
1329     // Currently it is 'use of value ... expects different type than prior uses'
1330     if (p.resolveOperand(boundOpInfos.front(), builder.getIndexType(),
1331                          result.operands))
1332       return failure();
1333 
1334     // Create an identity map using symbol id. This representation is optimized
1335     // for storage. Analysis passes may expand it into a multi-dimensional map
1336     // if desired.
1337     AffineMap map = builder.getSymbolIdentityMap();
1338     result.addAttribute(boundAttrName, AffineMapAttr::get(map));
1339     return success();
1340   }
1341 
1342   // Get the attribute location.
1343   llvm::SMLoc attrLoc = p.getCurrentLocation();
1344 
1345   Attribute boundAttr;
1346   if (p.parseAttribute(boundAttr, builder.getIndexType(), boundAttrName,
1347                        result.attributes))
1348     return failure();
1349 
1350   // Parse full form - affine map followed by dim and symbol list.
1351   if (auto affineMapAttr = boundAttr.dyn_cast<AffineMapAttr>()) {
1352     unsigned currentNumOperands = result.operands.size();
1353     unsigned numDims;
1354     if (parseDimAndSymbolList(p, result.operands, numDims))
1355       return failure();
1356 
1357     auto map = affineMapAttr.getValue();
1358     if (map.getNumDims() != numDims)
1359       return p.emitError(
1360           p.getNameLoc(),
1361           "dim operand count and affine map dim count must match");
1362 
1363     unsigned numDimAndSymbolOperands =
1364         result.operands.size() - currentNumOperands;
1365     if (numDims + map.getNumSymbols() != numDimAndSymbolOperands)
1366       return p.emitError(
1367           p.getNameLoc(),
1368           "symbol operand count and affine map symbol count must match");
1369 
1370     // If the map has multiple results, make sure that we parsed the min/max
1371     // prefix.
1372     if (map.getNumResults() > 1 && failedToParsedMinMax) {
1373       if (isLower) {
1374         return p.emitError(attrLoc, "lower loop bound affine map with "
1375                                     "multiple results requires 'max' prefix");
1376       }
1377       return p.emitError(attrLoc, "upper loop bound affine map with multiple "
1378                                   "results requires 'min' prefix");
1379     }
1380     return success();
1381   }
1382 
1383   // Parse custom assembly form.
1384   if (auto integerAttr = boundAttr.dyn_cast<IntegerAttr>()) {
1385     result.attributes.pop_back();
1386     result.addAttribute(
1387         boundAttrName,
1388         AffineMapAttr::get(builder.getConstantAffineMap(integerAttr.getInt())));
1389     return success();
1390   }
1391 
1392   return p.emitError(
1393       p.getNameLoc(),
1394       "expected valid affine map representation for loop bounds");
1395 }
1396 
1397 static ParseResult parseAffineForOp(OpAsmParser &parser,
1398                                     OperationState &result) {
1399   auto &builder = parser.getBuilder();
1400   OpAsmParser::OperandType inductionVariable;
1401   // Parse the induction variable followed by '='.
1402   if (parser.parseRegionArgument(inductionVariable) || parser.parseEqual())
1403     return failure();
1404 
1405   // Parse loop bounds.
1406   if (parseBound(/*isLower=*/true, result, parser) ||
1407       parser.parseKeyword("to", " between bounds") ||
1408       parseBound(/*isLower=*/false, result, parser))
1409     return failure();
1410 
1411   // Parse the optional loop step, we default to 1 if one is not present.
1412   if (parser.parseOptionalKeyword("step")) {
1413     result.addAttribute(
1414         AffineForOp::getStepAttrName(),
1415         builder.getIntegerAttr(builder.getIndexType(), /*value=*/1));
1416   } else {
1417     llvm::SMLoc stepLoc = parser.getCurrentLocation();
1418     IntegerAttr stepAttr;
1419     if (parser.parseAttribute(stepAttr, builder.getIndexType(),
1420                               AffineForOp::getStepAttrName().data(),
1421                               result.attributes))
1422       return failure();
1423 
1424     if (stepAttr.getValue().getSExtValue() < 0)
1425       return parser.emitError(
1426           stepLoc,
1427           "expected step to be representable as a positive signed integer");
1428   }
1429 
1430   // Parse the optional initial iteration arguments.
1431   SmallVector<OpAsmParser::OperandType, 4> regionArgs, operands;
1432   SmallVector<Type, 4> argTypes;
1433   regionArgs.push_back(inductionVariable);
1434 
1435   if (succeeded(parser.parseOptionalKeyword("iter_args"))) {
1436     // Parse assignment list and results type list.
1437     if (parser.parseAssignmentList(regionArgs, operands) ||
1438         parser.parseArrowTypeList(result.types))
1439       return failure();
1440     // Resolve input operands.
1441     for (auto operandType : llvm::zip(operands, result.types))
1442       if (parser.resolveOperand(std::get<0>(operandType),
1443                                 std::get<1>(operandType), result.operands))
1444         return failure();
1445   }
1446   // Induction variable.
1447   Type indexType = builder.getIndexType();
1448   argTypes.push_back(indexType);
1449   // Loop carried variables.
1450   argTypes.append(result.types.begin(), result.types.end());
1451   // Parse the body region.
1452   Region *body = result.addRegion();
1453   if (regionArgs.size() != argTypes.size())
1454     return parser.emitError(
1455         parser.getNameLoc(),
1456         "mismatch between the number of loop-carried values and results");
1457   if (parser.parseRegion(*body, regionArgs, argTypes))
1458     return failure();
1459 
1460   AffineForOp::ensureTerminator(*body, builder, result.location);
1461 
1462   // Parse the optional attribute list.
1463   return parser.parseOptionalAttrDict(result.attributes);
1464 }
1465 
1466 static void printBound(AffineMapAttr boundMap,
1467                        Operation::operand_range boundOperands,
1468                        const char *prefix, OpAsmPrinter &p) {
1469   AffineMap map = boundMap.getValue();
1470 
1471   // Check if this bound should be printed using custom assembly form.
1472   // The decision to restrict printing custom assembly form to trivial cases
1473   // comes from the will to roundtrip MLIR binary -> text -> binary in a
1474   // lossless way.
1475   // Therefore, custom assembly form parsing and printing is only supported for
1476   // zero-operand constant maps and single symbol operand identity maps.
1477   if (map.getNumResults() == 1) {
1478     AffineExpr expr = map.getResult(0);
1479 
1480     // Print constant bound.
1481     if (map.getNumDims() == 0 && map.getNumSymbols() == 0) {
1482       if (auto constExpr = expr.dyn_cast<AffineConstantExpr>()) {
1483         p << constExpr.getValue();
1484         return;
1485       }
1486     }
1487 
1488     // Print bound that consists of a single SSA symbol if the map is over a
1489     // single symbol.
1490     if (map.getNumDims() == 0 && map.getNumSymbols() == 1) {
1491       if (auto symExpr = expr.dyn_cast<AffineSymbolExpr>()) {
1492         p.printOperand(*boundOperands.begin());
1493         return;
1494       }
1495     }
1496   } else {
1497     // Map has multiple results. Print 'min' or 'max' prefix.
1498     p << prefix << ' ';
1499   }
1500 
1501   // Print the map and its operands.
1502   p << boundMap;
1503   printDimAndSymbolList(boundOperands.begin(), boundOperands.end(),
1504                         map.getNumDims(), p);
1505 }
1506 
1507 unsigned AffineForOp::getNumIterOperands() {
1508   AffineMap lbMap = getLowerBoundMapAttr().getValue();
1509   AffineMap ubMap = getUpperBoundMapAttr().getValue();
1510 
1511   return getNumOperands() - lbMap.getNumInputs() - ubMap.getNumInputs();
1512 }
1513 
1514 static void print(OpAsmPrinter &p, AffineForOp op) {
1515   p << op.getOperationName() << ' ';
1516   p.printOperand(op.getBody()->getArgument(0));
1517   p << " = ";
1518   printBound(op.getLowerBoundMapAttr(), op.getLowerBoundOperands(), "max", p);
1519   p << " to ";
1520   printBound(op.getUpperBoundMapAttr(), op.getUpperBoundOperands(), "min", p);
1521 
1522   if (op.getStep() != 1)
1523     p << " step " << op.getStep();
1524 
1525   bool printBlockTerminators = false;
1526   if (op.getNumIterOperands() > 0) {
1527     p << " iter_args(";
1528     auto regionArgs = op.getRegionIterArgs();
1529     auto operands = op.getIterOperands();
1530 
1531     llvm::interleaveComma(llvm::zip(regionArgs, operands), p, [&](auto it) {
1532       p << std::get<0>(it) << " = " << std::get<1>(it);
1533     });
1534     p << ") -> (" << op.getResultTypes() << ")";
1535     printBlockTerminators = true;
1536   }
1537 
1538   p.printRegion(op.region(),
1539                 /*printEntryBlockArgs=*/false, printBlockTerminators);
1540   p.printOptionalAttrDict(op->getAttrs(),
1541                           /*elidedAttrs=*/{op.getLowerBoundAttrName(),
1542                                            op.getUpperBoundAttrName(),
1543                                            op.getStepAttrName()});
1544 }
1545 
1546 /// Fold the constant bounds of a loop.
1547 static LogicalResult foldLoopBounds(AffineForOp forOp) {
1548   auto foldLowerOrUpperBound = [&forOp](bool lower) {
1549     // Check to see if each of the operands is the result of a constant.  If
1550     // so, get the value.  If not, ignore it.
1551     SmallVector<Attribute, 8> operandConstants;
1552     auto boundOperands =
1553         lower ? forOp.getLowerBoundOperands() : forOp.getUpperBoundOperands();
1554     for (auto operand : boundOperands) {
1555       Attribute operandCst;
1556       matchPattern(operand, m_Constant(&operandCst));
1557       operandConstants.push_back(operandCst);
1558     }
1559 
1560     AffineMap boundMap =
1561         lower ? forOp.getLowerBoundMap() : forOp.getUpperBoundMap();
1562     assert(boundMap.getNumResults() >= 1 &&
1563            "bound maps should have at least one result");
1564     SmallVector<Attribute, 4> foldedResults;
1565     if (failed(boundMap.constantFold(operandConstants, foldedResults)))
1566       return failure();
1567 
1568     // Compute the max or min as applicable over the results.
1569     assert(!foldedResults.empty() && "bounds should have at least one result");
1570     auto maxOrMin = foldedResults[0].cast<IntegerAttr>().getValue();
1571     for (unsigned i = 1, e = foldedResults.size(); i < e; i++) {
1572       auto foldedResult = foldedResults[i].cast<IntegerAttr>().getValue();
1573       maxOrMin = lower ? llvm::APIntOps::smax(maxOrMin, foldedResult)
1574                        : llvm::APIntOps::smin(maxOrMin, foldedResult);
1575     }
1576     lower ? forOp.setConstantLowerBound(maxOrMin.getSExtValue())
1577           : forOp.setConstantUpperBound(maxOrMin.getSExtValue());
1578     return success();
1579   };
1580 
1581   // Try to fold the lower bound.
1582   bool folded = false;
1583   if (!forOp.hasConstantLowerBound())
1584     folded |= succeeded(foldLowerOrUpperBound(/*lower=*/true));
1585 
1586   // Try to fold the upper bound.
1587   if (!forOp.hasConstantUpperBound())
1588     folded |= succeeded(foldLowerOrUpperBound(/*lower=*/false));
1589   return success(folded);
1590 }
1591 
1592 /// Canonicalize the bounds of the given loop.
1593 static LogicalResult canonicalizeLoopBounds(AffineForOp forOp) {
1594   SmallVector<Value, 4> lbOperands(forOp.getLowerBoundOperands());
1595   SmallVector<Value, 4> ubOperands(forOp.getUpperBoundOperands());
1596 
1597   auto lbMap = forOp.getLowerBoundMap();
1598   auto ubMap = forOp.getUpperBoundMap();
1599   auto prevLbMap = lbMap;
1600   auto prevUbMap = ubMap;
1601 
1602   composeAffineMapAndOperands(&lbMap, &lbOperands);
1603   canonicalizeMapAndOperands(&lbMap, &lbOperands);
1604   lbMap = removeDuplicateExprs(lbMap);
1605 
1606   composeAffineMapAndOperands(&ubMap, &ubOperands);
1607   canonicalizeMapAndOperands(&ubMap, &ubOperands);
1608   ubMap = removeDuplicateExprs(ubMap);
1609 
1610   // Any canonicalization change always leads to updated map(s).
1611   if (lbMap == prevLbMap && ubMap == prevUbMap)
1612     return failure();
1613 
1614   if (lbMap != prevLbMap)
1615     forOp.setLowerBound(lbOperands, lbMap);
1616   if (ubMap != prevUbMap)
1617     forOp.setUpperBound(ubOperands, ubMap);
1618   return success();
1619 }
1620 
1621 namespace {
1622 /// This is a pattern to fold trivially empty loops.
1623 struct AffineForEmptyLoopFolder : public OpRewritePattern<AffineForOp> {
1624   using OpRewritePattern<AffineForOp>::OpRewritePattern;
1625 
1626   LogicalResult matchAndRewrite(AffineForOp forOp,
1627                                 PatternRewriter &rewriter) const override {
1628     // Check that the body only contains a yield.
1629     if (!llvm::hasSingleElement(*forOp.getBody()))
1630       return failure();
1631     rewriter.eraseOp(forOp);
1632     return success();
1633   }
1634 };
1635 } // end anonymous namespace
1636 
1637 void AffineForOp::getCanonicalizationPatterns(RewritePatternSet &results,
1638                                               MLIRContext *context) {
1639   results.add<AffineForEmptyLoopFolder>(context);
1640 }
1641 
1642 LogicalResult AffineForOp::fold(ArrayRef<Attribute> operands,
1643                                 SmallVectorImpl<OpFoldResult> &results) {
1644   bool folded = succeeded(foldLoopBounds(*this));
1645   folded |= succeeded(canonicalizeLoopBounds(*this));
1646   return success(folded);
1647 }
1648 
1649 AffineBound AffineForOp::getLowerBound() {
1650   auto lbMap = getLowerBoundMap();
1651   return AffineBound(AffineForOp(*this), 0, lbMap.getNumInputs(), lbMap);
1652 }
1653 
1654 AffineBound AffineForOp::getUpperBound() {
1655   auto lbMap = getLowerBoundMap();
1656   auto ubMap = getUpperBoundMap();
1657   return AffineBound(AffineForOp(*this), lbMap.getNumInputs(),
1658                      lbMap.getNumInputs() + ubMap.getNumInputs(), ubMap);
1659 }
1660 
1661 void AffineForOp::setLowerBound(ValueRange lbOperands, AffineMap map) {
1662   assert(lbOperands.size() == map.getNumInputs());
1663   assert(map.getNumResults() >= 1 && "bound map has at least one result");
1664 
1665   SmallVector<Value, 4> newOperands(lbOperands.begin(), lbOperands.end());
1666 
1667   auto ubOperands = getUpperBoundOperands();
1668   newOperands.append(ubOperands.begin(), ubOperands.end());
1669   auto iterOperands = getIterOperands();
1670   newOperands.append(iterOperands.begin(), iterOperands.end());
1671   (*this)->setOperands(newOperands);
1672 
1673   (*this)->setAttr(getLowerBoundAttrName(), AffineMapAttr::get(map));
1674 }
1675 
1676 void AffineForOp::setUpperBound(ValueRange ubOperands, AffineMap map) {
1677   assert(ubOperands.size() == map.getNumInputs());
1678   assert(map.getNumResults() >= 1 && "bound map has at least one result");
1679 
1680   SmallVector<Value, 4> newOperands(getLowerBoundOperands());
1681   newOperands.append(ubOperands.begin(), ubOperands.end());
1682   auto iterOperands = getIterOperands();
1683   newOperands.append(iterOperands.begin(), iterOperands.end());
1684   (*this)->setOperands(newOperands);
1685 
1686   (*this)->setAttr(getUpperBoundAttrName(), AffineMapAttr::get(map));
1687 }
1688 
1689 void AffineForOp::setLowerBoundMap(AffineMap map) {
1690   auto lbMap = getLowerBoundMap();
1691   assert(lbMap.getNumDims() == map.getNumDims() &&
1692          lbMap.getNumSymbols() == map.getNumSymbols());
1693   assert(map.getNumResults() >= 1 && "bound map has at least one result");
1694   (void)lbMap;
1695   (*this)->setAttr(getLowerBoundAttrName(), AffineMapAttr::get(map));
1696 }
1697 
1698 void AffineForOp::setUpperBoundMap(AffineMap map) {
1699   auto ubMap = getUpperBoundMap();
1700   assert(ubMap.getNumDims() == map.getNumDims() &&
1701          ubMap.getNumSymbols() == map.getNumSymbols());
1702   assert(map.getNumResults() >= 1 && "bound map has at least one result");
1703   (void)ubMap;
1704   (*this)->setAttr(getUpperBoundAttrName(), AffineMapAttr::get(map));
1705 }
1706 
1707 bool AffineForOp::hasConstantLowerBound() {
1708   return getLowerBoundMap().isSingleConstant();
1709 }
1710 
1711 bool AffineForOp::hasConstantUpperBound() {
1712   return getUpperBoundMap().isSingleConstant();
1713 }
1714 
1715 int64_t AffineForOp::getConstantLowerBound() {
1716   return getLowerBoundMap().getSingleConstantResult();
1717 }
1718 
1719 int64_t AffineForOp::getConstantUpperBound() {
1720   return getUpperBoundMap().getSingleConstantResult();
1721 }
1722 
1723 void AffineForOp::setConstantLowerBound(int64_t value) {
1724   setLowerBound({}, AffineMap::getConstantMap(value, getContext()));
1725 }
1726 
1727 void AffineForOp::setConstantUpperBound(int64_t value) {
1728   setUpperBound({}, AffineMap::getConstantMap(value, getContext()));
1729 }
1730 
1731 AffineForOp::operand_range AffineForOp::getLowerBoundOperands() {
1732   return {operand_begin(), operand_begin() + getLowerBoundMap().getNumInputs()};
1733 }
1734 
1735 AffineForOp::operand_range AffineForOp::getUpperBoundOperands() {
1736   return {operand_begin() + getLowerBoundMap().getNumInputs(),
1737           operand_begin() + getLowerBoundMap().getNumInputs() +
1738               getUpperBoundMap().getNumInputs()};
1739 }
1740 
1741 bool AffineForOp::matchingBoundOperandList() {
1742   auto lbMap = getLowerBoundMap();
1743   auto ubMap = getUpperBoundMap();
1744   if (lbMap.getNumDims() != ubMap.getNumDims() ||
1745       lbMap.getNumSymbols() != ubMap.getNumSymbols())
1746     return false;
1747 
1748   unsigned numOperands = lbMap.getNumInputs();
1749   for (unsigned i = 0, e = lbMap.getNumInputs(); i < e; i++) {
1750     // Compare Value 's.
1751     if (getOperand(i) != getOperand(numOperands + i))
1752       return false;
1753   }
1754   return true;
1755 }
1756 
1757 Region &AffineForOp::getLoopBody() { return region(); }
1758 
1759 bool AffineForOp::isDefinedOutsideOfLoop(Value value) {
1760   return !region().isAncestor(value.getParentRegion());
1761 }
1762 
1763 LogicalResult AffineForOp::moveOutOfLoop(ArrayRef<Operation *> ops) {
1764   for (auto *op : ops)
1765     op->moveBefore(*this);
1766   return success();
1767 }
1768 
1769 /// Returns true if the provided value is the induction variable of a
1770 /// AffineForOp.
1771 bool mlir::isForInductionVar(Value val) {
1772   return getForInductionVarOwner(val) != AffineForOp();
1773 }
1774 
1775 /// Returns the loop parent of an induction variable. If the provided value is
1776 /// not an induction variable, then return nullptr.
1777 AffineForOp mlir::getForInductionVarOwner(Value val) {
1778   auto ivArg = val.dyn_cast<BlockArgument>();
1779   if (!ivArg || !ivArg.getOwner())
1780     return AffineForOp();
1781   auto *containingInst = ivArg.getOwner()->getParent()->getParentOp();
1782   return dyn_cast<AffineForOp>(containingInst);
1783 }
1784 
1785 /// Extracts the induction variables from a list of AffineForOps and returns
1786 /// them.
1787 void mlir::extractForInductionVars(ArrayRef<AffineForOp> forInsts,
1788                                    SmallVectorImpl<Value> *ivs) {
1789   ivs->reserve(forInsts.size());
1790   for (auto forInst : forInsts)
1791     ivs->push_back(forInst.getInductionVar());
1792 }
1793 
1794 /// Builds an affine loop nest, using "loopCreatorFn" to create individual loop
1795 /// operations.
1796 template <typename BoundListTy, typename LoopCreatorTy>
1797 static void buildAffineLoopNestImpl(
1798     OpBuilder &builder, Location loc, BoundListTy lbs, BoundListTy ubs,
1799     ArrayRef<int64_t> steps,
1800     function_ref<void(OpBuilder &, Location, ValueRange)> bodyBuilderFn,
1801     LoopCreatorTy &&loopCreatorFn) {
1802   assert(lbs.size() == ubs.size() && "Mismatch in number of arguments");
1803   assert(lbs.size() == steps.size() && "Mismatch in number of arguments");
1804 
1805   // If there are no loops to be constructed, construct the body anyway.
1806   OpBuilder::InsertionGuard guard(builder);
1807   if (lbs.empty()) {
1808     if (bodyBuilderFn)
1809       bodyBuilderFn(builder, loc, ValueRange());
1810     return;
1811   }
1812 
1813   // Create the loops iteratively and store the induction variables.
1814   SmallVector<Value, 4> ivs;
1815   ivs.reserve(lbs.size());
1816   for (unsigned i = 0, e = lbs.size(); i < e; ++i) {
1817     // Callback for creating the loop body, always creates the terminator.
1818     auto loopBody = [&](OpBuilder &nestedBuilder, Location nestedLoc, Value iv,
1819                         ValueRange iterArgs) {
1820       ivs.push_back(iv);
1821       // In the innermost loop, call the body builder.
1822       if (i == e - 1 && bodyBuilderFn) {
1823         OpBuilder::InsertionGuard nestedGuard(nestedBuilder);
1824         bodyBuilderFn(nestedBuilder, nestedLoc, ivs);
1825       }
1826       nestedBuilder.create<AffineYieldOp>(nestedLoc);
1827     };
1828 
1829     // Delegate actual loop creation to the callback in order to dispatch
1830     // between constant- and variable-bound loops.
1831     auto loop = loopCreatorFn(builder, loc, lbs[i], ubs[i], steps[i], loopBody);
1832     builder.setInsertionPointToStart(loop.getBody());
1833   }
1834 }
1835 
1836 /// Creates an affine loop from the bounds known to be constants.
1837 static AffineForOp
1838 buildAffineLoopFromConstants(OpBuilder &builder, Location loc, int64_t lb,
1839                              int64_t ub, int64_t step,
1840                              AffineForOp::BodyBuilderFn bodyBuilderFn) {
1841   return builder.create<AffineForOp>(loc, lb, ub, step, /*iterArgs=*/llvm::None,
1842                                      bodyBuilderFn);
1843 }
1844 
1845 /// Creates an affine loop from the bounds that may or may not be constants.
1846 static AffineForOp
1847 buildAffineLoopFromValues(OpBuilder &builder, Location loc, Value lb, Value ub,
1848                           int64_t step,
1849                           AffineForOp::BodyBuilderFn bodyBuilderFn) {
1850   auto lbConst = lb.getDefiningOp<ConstantIndexOp>();
1851   auto ubConst = ub.getDefiningOp<ConstantIndexOp>();
1852   if (lbConst && ubConst)
1853     return buildAffineLoopFromConstants(builder, loc, lbConst.getValue(),
1854                                         ubConst.getValue(), step,
1855                                         bodyBuilderFn);
1856   return builder.create<AffineForOp>(loc, lb, builder.getDimIdentityMap(), ub,
1857                                      builder.getDimIdentityMap(), step,
1858                                      /*iterArgs=*/llvm::None, bodyBuilderFn);
1859 }
1860 
1861 void mlir::buildAffineLoopNest(
1862     OpBuilder &builder, Location loc, ArrayRef<int64_t> lbs,
1863     ArrayRef<int64_t> ubs, ArrayRef<int64_t> steps,
1864     function_ref<void(OpBuilder &, Location, ValueRange)> bodyBuilderFn) {
1865   buildAffineLoopNestImpl(builder, loc, lbs, ubs, steps, bodyBuilderFn,
1866                           buildAffineLoopFromConstants);
1867 }
1868 
1869 void mlir::buildAffineLoopNest(
1870     OpBuilder &builder, Location loc, ValueRange lbs, ValueRange ubs,
1871     ArrayRef<int64_t> steps,
1872     function_ref<void(OpBuilder &, Location, ValueRange)> bodyBuilderFn) {
1873   buildAffineLoopNestImpl(builder, loc, lbs, ubs, steps, bodyBuilderFn,
1874                           buildAffineLoopFromValues);
1875 }
1876 
1877 //===----------------------------------------------------------------------===//
1878 // AffineIfOp
1879 //===----------------------------------------------------------------------===//
1880 
1881 namespace {
1882 /// Remove else blocks that have nothing other than a zero value yield.
1883 struct SimplifyDeadElse : public OpRewritePattern<AffineIfOp> {
1884   using OpRewritePattern<AffineIfOp>::OpRewritePattern;
1885 
1886   LogicalResult matchAndRewrite(AffineIfOp ifOp,
1887                                 PatternRewriter &rewriter) const override {
1888     if (ifOp.elseRegion().empty() ||
1889         !llvm::hasSingleElement(*ifOp.getElseBlock()) || ifOp.getNumResults())
1890       return failure();
1891 
1892     rewriter.startRootUpdate(ifOp);
1893     rewriter.eraseBlock(ifOp.getElseBlock());
1894     rewriter.finalizeRootUpdate(ifOp);
1895     return success();
1896   }
1897 };
1898 } // end anonymous namespace.
1899 
1900 static LogicalResult verify(AffineIfOp op) {
1901   // Verify that we have a condition attribute.
1902   auto conditionAttr =
1903       op->getAttrOfType<IntegerSetAttr>(op.getConditionAttrName());
1904   if (!conditionAttr)
1905     return op.emitOpError(
1906         "requires an integer set attribute named 'condition'");
1907 
1908   // Verify that there are enough operands for the condition.
1909   IntegerSet condition = conditionAttr.getValue();
1910   if (op.getNumOperands() != condition.getNumInputs())
1911     return op.emitOpError(
1912         "operand count and condition integer set dimension and "
1913         "symbol count must match");
1914 
1915   // Verify that the operands are valid dimension/symbols.
1916   if (failed(verifyDimAndSymbolIdentifiers(op, op.getOperands(),
1917                                            condition.getNumDims())))
1918     return failure();
1919 
1920   return success();
1921 }
1922 
1923 static ParseResult parseAffineIfOp(OpAsmParser &parser,
1924                                    OperationState &result) {
1925   // Parse the condition attribute set.
1926   IntegerSetAttr conditionAttr;
1927   unsigned numDims;
1928   if (parser.parseAttribute(conditionAttr, AffineIfOp::getConditionAttrName(),
1929                             result.attributes) ||
1930       parseDimAndSymbolList(parser, result.operands, numDims))
1931     return failure();
1932 
1933   // Verify the condition operands.
1934   auto set = conditionAttr.getValue();
1935   if (set.getNumDims() != numDims)
1936     return parser.emitError(
1937         parser.getNameLoc(),
1938         "dim operand count and integer set dim count must match");
1939   if (numDims + set.getNumSymbols() != result.operands.size())
1940     return parser.emitError(
1941         parser.getNameLoc(),
1942         "symbol operand count and integer set symbol count must match");
1943 
1944   if (parser.parseOptionalArrowTypeList(result.types))
1945     return failure();
1946 
1947   // Create the regions for 'then' and 'else'.  The latter must be created even
1948   // if it remains empty for the validity of the operation.
1949   result.regions.reserve(2);
1950   Region *thenRegion = result.addRegion();
1951   Region *elseRegion = result.addRegion();
1952 
1953   // Parse the 'then' region.
1954   if (parser.parseRegion(*thenRegion, {}, {}))
1955     return failure();
1956   AffineIfOp::ensureTerminator(*thenRegion, parser.getBuilder(),
1957                                result.location);
1958 
1959   // If we find an 'else' keyword then parse the 'else' region.
1960   if (!parser.parseOptionalKeyword("else")) {
1961     if (parser.parseRegion(*elseRegion, {}, {}))
1962       return failure();
1963     AffineIfOp::ensureTerminator(*elseRegion, parser.getBuilder(),
1964                                  result.location);
1965   }
1966 
1967   // Parse the optional attribute list.
1968   if (parser.parseOptionalAttrDict(result.attributes))
1969     return failure();
1970 
1971   return success();
1972 }
1973 
1974 static void print(OpAsmPrinter &p, AffineIfOp op) {
1975   auto conditionAttr =
1976       op->getAttrOfType<IntegerSetAttr>(op.getConditionAttrName());
1977   p << "affine.if " << conditionAttr;
1978   printDimAndSymbolList(op.operand_begin(), op.operand_end(),
1979                         conditionAttr.getValue().getNumDims(), p);
1980   p.printOptionalArrowTypeList(op.getResultTypes());
1981   p.printRegion(op.thenRegion(),
1982                 /*printEntryBlockArgs=*/false,
1983                 /*printBlockTerminators=*/op.getNumResults());
1984 
1985   // Print the 'else' regions if it has any blocks.
1986   auto &elseRegion = op.elseRegion();
1987   if (!elseRegion.empty()) {
1988     p << " else";
1989     p.printRegion(elseRegion,
1990                   /*printEntryBlockArgs=*/false,
1991                   /*printBlockTerminators=*/op.getNumResults());
1992   }
1993 
1994   // Print the attribute list.
1995   p.printOptionalAttrDict(op->getAttrs(),
1996                           /*elidedAttrs=*/op.getConditionAttrName());
1997 }
1998 
1999 IntegerSet AffineIfOp::getIntegerSet() {
2000   return (*this)
2001       ->getAttrOfType<IntegerSetAttr>(getConditionAttrName())
2002       .getValue();
2003 }
2004 void AffineIfOp::setIntegerSet(IntegerSet newSet) {
2005   (*this)->setAttr(getConditionAttrName(), IntegerSetAttr::get(newSet));
2006 }
2007 
2008 void AffineIfOp::setConditional(IntegerSet set, ValueRange operands) {
2009   setIntegerSet(set);
2010   (*this)->setOperands(operands);
2011 }
2012 
2013 void AffineIfOp::build(OpBuilder &builder, OperationState &result,
2014                        TypeRange resultTypes, IntegerSet set, ValueRange args,
2015                        bool withElseRegion) {
2016   assert(resultTypes.empty() || withElseRegion);
2017   result.addTypes(resultTypes);
2018   result.addOperands(args);
2019   result.addAttribute(getConditionAttrName(), IntegerSetAttr::get(set));
2020 
2021   Region *thenRegion = result.addRegion();
2022   thenRegion->push_back(new Block());
2023   if (resultTypes.empty())
2024     AffineIfOp::ensureTerminator(*thenRegion, builder, result.location);
2025 
2026   Region *elseRegion = result.addRegion();
2027   if (withElseRegion) {
2028     elseRegion->push_back(new Block());
2029     if (resultTypes.empty())
2030       AffineIfOp::ensureTerminator(*elseRegion, builder, result.location);
2031   }
2032 }
2033 
2034 void AffineIfOp::build(OpBuilder &builder, OperationState &result,
2035                        IntegerSet set, ValueRange args, bool withElseRegion) {
2036   AffineIfOp::build(builder, result, /*resultTypes=*/{}, set, args,
2037                     withElseRegion);
2038 }
2039 
2040 /// Canonicalize an affine if op's conditional (integer set + operands).
2041 LogicalResult AffineIfOp::fold(ArrayRef<Attribute>,
2042                                SmallVectorImpl<OpFoldResult> &) {
2043   auto set = getIntegerSet();
2044   SmallVector<Value, 4> operands(getOperands());
2045   canonicalizeSetAndOperands(&set, &operands);
2046 
2047   // Any canonicalization change always leads to either a reduction in the
2048   // number of operands or a change in the number of symbolic operands
2049   // (promotion of dims to symbols).
2050   if (operands.size() < getIntegerSet().getNumInputs() ||
2051       set.getNumSymbols() > getIntegerSet().getNumSymbols()) {
2052     setConditional(set, operands);
2053     return success();
2054   }
2055 
2056   return failure();
2057 }
2058 
2059 void AffineIfOp::getCanonicalizationPatterns(RewritePatternSet &results,
2060                                              MLIRContext *context) {
2061   results.add<SimplifyDeadElse>(context);
2062 }
2063 
2064 //===----------------------------------------------------------------------===//
2065 // AffineLoadOp
2066 //===----------------------------------------------------------------------===//
2067 
2068 void AffineLoadOp::build(OpBuilder &builder, OperationState &result,
2069                          AffineMap map, ValueRange operands) {
2070   assert(operands.size() == 1 + map.getNumInputs() && "inconsistent operands");
2071   result.addOperands(operands);
2072   if (map)
2073     result.addAttribute(getMapAttrName(), AffineMapAttr::get(map));
2074   auto memrefType = operands[0].getType().cast<MemRefType>();
2075   result.types.push_back(memrefType.getElementType());
2076 }
2077 
2078 void AffineLoadOp::build(OpBuilder &builder, OperationState &result,
2079                          Value memref, AffineMap map, ValueRange mapOperands) {
2080   assert(map.getNumInputs() == mapOperands.size() && "inconsistent index info");
2081   result.addOperands(memref);
2082   result.addOperands(mapOperands);
2083   auto memrefType = memref.getType().cast<MemRefType>();
2084   result.addAttribute(getMapAttrName(), AffineMapAttr::get(map));
2085   result.types.push_back(memrefType.getElementType());
2086 }
2087 
2088 void AffineLoadOp::build(OpBuilder &builder, OperationState &result,
2089                          Value memref, ValueRange indices) {
2090   auto memrefType = memref.getType().cast<MemRefType>();
2091   int64_t rank = memrefType.getRank();
2092   // Create identity map for memrefs with at least one dimension or () -> ()
2093   // for zero-dimensional memrefs.
2094   auto map =
2095       rank ? builder.getMultiDimIdentityMap(rank) : builder.getEmptyAffineMap();
2096   build(builder, result, memref, map, indices);
2097 }
2098 
2099 static ParseResult parseAffineLoadOp(OpAsmParser &parser,
2100                                      OperationState &result) {
2101   auto &builder = parser.getBuilder();
2102   auto indexTy = builder.getIndexType();
2103 
2104   MemRefType type;
2105   OpAsmParser::OperandType memrefInfo;
2106   AffineMapAttr mapAttr;
2107   SmallVector<OpAsmParser::OperandType, 1> mapOperands;
2108   return failure(
2109       parser.parseOperand(memrefInfo) ||
2110       parser.parseAffineMapOfSSAIds(mapOperands, mapAttr,
2111                                     AffineLoadOp::getMapAttrName(),
2112                                     result.attributes) ||
2113       parser.parseOptionalAttrDict(result.attributes) ||
2114       parser.parseColonType(type) ||
2115       parser.resolveOperand(memrefInfo, type, result.operands) ||
2116       parser.resolveOperands(mapOperands, indexTy, result.operands) ||
2117       parser.addTypeToList(type.getElementType(), result.types));
2118 }
2119 
2120 static void print(OpAsmPrinter &p, AffineLoadOp op) {
2121   p << "affine.load " << op.getMemRef() << '[';
2122   if (AffineMapAttr mapAttr =
2123           op->getAttrOfType<AffineMapAttr>(op.getMapAttrName()))
2124     p.printAffineMapOfSSAIds(mapAttr, op.getMapOperands());
2125   p << ']';
2126   p.printOptionalAttrDict(op->getAttrs(),
2127                           /*elidedAttrs=*/{op.getMapAttrName()});
2128   p << " : " << op.getMemRefType();
2129 }
2130 
2131 /// Verify common indexing invariants of affine.load, affine.store,
2132 /// affine.vector_load and affine.vector_store.
2133 static LogicalResult
2134 verifyMemoryOpIndexing(Operation *op, AffineMapAttr mapAttr,
2135                        Operation::operand_range mapOperands,
2136                        MemRefType memrefType, unsigned numIndexOperands) {
2137   if (mapAttr) {
2138     AffineMap map = mapAttr.getValue();
2139     if (map.getNumResults() != memrefType.getRank())
2140       return op->emitOpError("affine map num results must equal memref rank");
2141     if (map.getNumInputs() != numIndexOperands)
2142       return op->emitOpError("expects as many subscripts as affine map inputs");
2143   } else {
2144     if (memrefType.getRank() != numIndexOperands)
2145       return op->emitOpError(
2146           "expects the number of subscripts to be equal to memref rank");
2147   }
2148 
2149   Region *scope = getAffineScope(op);
2150   for (auto idx : mapOperands) {
2151     if (!idx.getType().isIndex())
2152       return op->emitOpError("index to load must have 'index' type");
2153     if (!isValidAffineIndexOperand(idx, scope))
2154       return op->emitOpError("index must be a dimension or symbol identifier");
2155   }
2156 
2157   return success();
2158 }
2159 
2160 LogicalResult verify(AffineLoadOp op) {
2161   auto memrefType = op.getMemRefType();
2162   if (op.getType() != memrefType.getElementType())
2163     return op.emitOpError("result type must match element type of memref");
2164 
2165   if (failed(verifyMemoryOpIndexing(
2166           op.getOperation(),
2167           op->getAttrOfType<AffineMapAttr>(op.getMapAttrName()),
2168           op.getMapOperands(), memrefType,
2169           /*numIndexOperands=*/op.getNumOperands() - 1)))
2170     return failure();
2171 
2172   return success();
2173 }
2174 
2175 void AffineLoadOp::getCanonicalizationPatterns(RewritePatternSet &results,
2176                                                MLIRContext *context) {
2177   results.add<SimplifyAffineOp<AffineLoadOp>>(context);
2178 }
2179 
2180 OpFoldResult AffineLoadOp::fold(ArrayRef<Attribute> cstOperands) {
2181   /// load(memrefcast) -> load
2182   if (succeeded(foldMemRefCast(*this)))
2183     return getResult();
2184   return OpFoldResult();
2185 }
2186 
2187 //===----------------------------------------------------------------------===//
2188 // AffineStoreOp
2189 //===----------------------------------------------------------------------===//
2190 
2191 void AffineStoreOp::build(OpBuilder &builder, OperationState &result,
2192                           Value valueToStore, Value memref, AffineMap map,
2193                           ValueRange mapOperands) {
2194   assert(map.getNumInputs() == mapOperands.size() && "inconsistent index info");
2195   result.addOperands(valueToStore);
2196   result.addOperands(memref);
2197   result.addOperands(mapOperands);
2198   result.addAttribute(getMapAttrName(), AffineMapAttr::get(map));
2199 }
2200 
2201 // Use identity map.
2202 void AffineStoreOp::build(OpBuilder &builder, OperationState &result,
2203                           Value valueToStore, Value memref,
2204                           ValueRange indices) {
2205   auto memrefType = memref.getType().cast<MemRefType>();
2206   int64_t rank = memrefType.getRank();
2207   // Create identity map for memrefs with at least one dimension or () -> ()
2208   // for zero-dimensional memrefs.
2209   auto map =
2210       rank ? builder.getMultiDimIdentityMap(rank) : builder.getEmptyAffineMap();
2211   build(builder, result, valueToStore, memref, map, indices);
2212 }
2213 
2214 static ParseResult parseAffineStoreOp(OpAsmParser &parser,
2215                                       OperationState &result) {
2216   auto indexTy = parser.getBuilder().getIndexType();
2217 
2218   MemRefType type;
2219   OpAsmParser::OperandType storeValueInfo;
2220   OpAsmParser::OperandType memrefInfo;
2221   AffineMapAttr mapAttr;
2222   SmallVector<OpAsmParser::OperandType, 1> mapOperands;
2223   return failure(parser.parseOperand(storeValueInfo) || parser.parseComma() ||
2224                  parser.parseOperand(memrefInfo) ||
2225                  parser.parseAffineMapOfSSAIds(mapOperands, mapAttr,
2226                                                AffineStoreOp::getMapAttrName(),
2227                                                result.attributes) ||
2228                  parser.parseOptionalAttrDict(result.attributes) ||
2229                  parser.parseColonType(type) ||
2230                  parser.resolveOperand(storeValueInfo, type.getElementType(),
2231                                        result.operands) ||
2232                  parser.resolveOperand(memrefInfo, type, result.operands) ||
2233                  parser.resolveOperands(mapOperands, indexTy, result.operands));
2234 }
2235 
2236 static void print(OpAsmPrinter &p, AffineStoreOp op) {
2237   p << "affine.store " << op.getValueToStore();
2238   p << ", " << op.getMemRef() << '[';
2239   if (AffineMapAttr mapAttr =
2240           op->getAttrOfType<AffineMapAttr>(op.getMapAttrName()))
2241     p.printAffineMapOfSSAIds(mapAttr, op.getMapOperands());
2242   p << ']';
2243   p.printOptionalAttrDict(op->getAttrs(),
2244                           /*elidedAttrs=*/{op.getMapAttrName()});
2245   p << " : " << op.getMemRefType();
2246 }
2247 
2248 LogicalResult verify(AffineStoreOp op) {
2249   // First operand must have same type as memref element type.
2250   auto memrefType = op.getMemRefType();
2251   if (op.getValueToStore().getType() != memrefType.getElementType())
2252     return op.emitOpError(
2253         "first operand must have same type memref element type");
2254 
2255   if (failed(verifyMemoryOpIndexing(
2256           op.getOperation(),
2257           op->getAttrOfType<AffineMapAttr>(op.getMapAttrName()),
2258           op.getMapOperands(), memrefType,
2259           /*numIndexOperands=*/op.getNumOperands() - 2)))
2260     return failure();
2261 
2262   return success();
2263 }
2264 
2265 void AffineStoreOp::getCanonicalizationPatterns(RewritePatternSet &results,
2266                                                 MLIRContext *context) {
2267   results.add<SimplifyAffineOp<AffineStoreOp>>(context);
2268 }
2269 
2270 LogicalResult AffineStoreOp::fold(ArrayRef<Attribute> cstOperands,
2271                                   SmallVectorImpl<OpFoldResult> &results) {
2272   /// store(memrefcast) -> store
2273   return foldMemRefCast(*this);
2274 }
2275 
2276 //===----------------------------------------------------------------------===//
2277 // AffineMinMaxOpBase
2278 //===----------------------------------------------------------------------===//
2279 
2280 template <typename T>
2281 static LogicalResult verifyAffineMinMaxOp(T op) {
2282   // Verify that operand count matches affine map dimension and symbol count.
2283   if (op.getNumOperands() != op.map().getNumDims() + op.map().getNumSymbols())
2284     return op.emitOpError(
2285         "operand count and affine map dimension and symbol count must match");
2286   return success();
2287 }
2288 
2289 template <typename T>
2290 static void printAffineMinMaxOp(OpAsmPrinter &p, T op) {
2291   p << op.getOperationName() << ' ' << op->getAttr(T::getMapAttrName());
2292   auto operands = op.getOperands();
2293   unsigned numDims = op.map().getNumDims();
2294   p << '(' << operands.take_front(numDims) << ')';
2295 
2296   if (operands.size() != numDims)
2297     p << '[' << operands.drop_front(numDims) << ']';
2298   p.printOptionalAttrDict(op->getAttrs(),
2299                           /*elidedAttrs=*/{T::getMapAttrName()});
2300 }
2301 
2302 template <typename T>
2303 static ParseResult parseAffineMinMaxOp(OpAsmParser &parser,
2304                                        OperationState &result) {
2305   auto &builder = parser.getBuilder();
2306   auto indexType = builder.getIndexType();
2307   SmallVector<OpAsmParser::OperandType, 8> dimInfos;
2308   SmallVector<OpAsmParser::OperandType, 8> symInfos;
2309   AffineMapAttr mapAttr;
2310   return failure(
2311       parser.parseAttribute(mapAttr, T::getMapAttrName(), result.attributes) ||
2312       parser.parseOperandList(dimInfos, OpAsmParser::Delimiter::Paren) ||
2313       parser.parseOperandList(symInfos,
2314                               OpAsmParser::Delimiter::OptionalSquare) ||
2315       parser.parseOptionalAttrDict(result.attributes) ||
2316       parser.resolveOperands(dimInfos, indexType, result.operands) ||
2317       parser.resolveOperands(symInfos, indexType, result.operands) ||
2318       parser.addTypeToList(indexType, result.types));
2319 }
2320 
2321 /// Fold an affine min or max operation with the given operands. The operand
2322 /// list may contain nulls, which are interpreted as the operand not being a
2323 /// constant.
2324 template <typename T>
2325 static OpFoldResult foldMinMaxOp(T op, ArrayRef<Attribute> operands) {
2326   static_assert(llvm::is_one_of<T, AffineMinOp, AffineMaxOp>::value,
2327                 "expected affine min or max op");
2328 
2329   // Fold the affine map.
2330   // TODO: Fold more cases:
2331   // min(some_affine, some_affine + constant, ...), etc.
2332   SmallVector<int64_t, 2> results;
2333   auto foldedMap = op.map().partialConstantFold(operands, &results);
2334 
2335   // If some of the map results are not constant, try changing the map in-place.
2336   if (results.empty()) {
2337     // If the map is the same, report that folding did not happen.
2338     if (foldedMap == op.map())
2339       return {};
2340     op->setAttr("map", AffineMapAttr::get(foldedMap));
2341     return op.getResult();
2342   }
2343 
2344   // Otherwise, completely fold the op into a constant.
2345   auto resultIt = std::is_same<T, AffineMinOp>::value
2346                       ? std::min_element(results.begin(), results.end())
2347                       : std::max_element(results.begin(), results.end());
2348   if (resultIt == results.end())
2349     return {};
2350   return IntegerAttr::get(IndexType::get(op.getContext()), *resultIt);
2351 }
2352 
2353 /// Remove duplicated expressions in affine min/max ops.
2354 template <typename T>
2355 struct DeduplicateAffineMinMaxExpressions : public OpRewritePattern<T> {
2356   using OpRewritePattern<T>::OpRewritePattern;
2357 
2358   LogicalResult matchAndRewrite(T affineOp,
2359                                 PatternRewriter &rewriter) const override {
2360     AffineMap oldMap = affineOp.getAffineMap();
2361 
2362     SmallVector<AffineExpr, 4> newExprs;
2363     for (AffineExpr expr : oldMap.getResults()) {
2364       // This is a linear scan over newExprs, but it should be fine given that
2365       // we typically just have a few expressions per op.
2366       if (!llvm::is_contained(newExprs, expr))
2367         newExprs.push_back(expr);
2368     }
2369 
2370     if (newExprs.size() == oldMap.getNumResults())
2371       return failure();
2372 
2373     auto newMap = AffineMap::get(oldMap.getNumDims(), oldMap.getNumSymbols(),
2374                                  newExprs, rewriter.getContext());
2375     rewriter.replaceOpWithNewOp<T>(affineOp, newMap, affineOp.getMapOperands());
2376 
2377     return success();
2378   }
2379 };
2380 
2381 /// Merge an affine min/max op to its consumers if its consumer is also an
2382 /// affine min/max op.
2383 ///
2384 /// This pattern requires the producer affine min/max op is bound to a
2385 /// dimension/symbol that is used as a standalone expression in the consumer
2386 /// affine op's map.
2387 ///
2388 /// For example, a pattern like the following:
2389 ///
2390 ///   %0 = affine.min affine_map<()[s0] -> (s0 + 16, s0 * 8)> ()[%sym1]
2391 ///   %1 = affine.min affine_map<(d0)[s0] -> (s0 + 4, d0)> (%0)[%sym2]
2392 ///
2393 /// Can be turned into:
2394 ///
2395 ///   %1 = affine.min affine_map<
2396 ///          ()[s0, s1] -> (s0 + 4, s1 + 16, s1 * 8)> ()[%sym2, %sym1]
2397 template <typename T>
2398 struct MergeAffineMinMaxOp : public OpRewritePattern<T> {
2399   using OpRewritePattern<T>::OpRewritePattern;
2400 
2401   LogicalResult matchAndRewrite(T affineOp,
2402                                 PatternRewriter &rewriter) const override {
2403     AffineMap oldMap = affineOp.getAffineMap();
2404     ValueRange dimOperands =
2405         affineOp.getMapOperands().take_front(oldMap.getNumDims());
2406     ValueRange symOperands =
2407         affineOp.getMapOperands().take_back(oldMap.getNumSymbols());
2408 
2409     auto newDimOperands = llvm::to_vector<8>(dimOperands);
2410     auto newSymOperands = llvm::to_vector<8>(symOperands);
2411     SmallVector<AffineExpr, 4> newExprs;
2412     SmallVector<T, 4> producerOps;
2413 
2414     // Go over each expression to see whether it's a single dimension/symbol
2415     // with the corresponding operand which is the result of another affine
2416     // min/max op. If So it can be merged into this affine op.
2417     for (AffineExpr expr : oldMap.getResults()) {
2418       if (auto symExpr = expr.dyn_cast<AffineSymbolExpr>()) {
2419         Value symValue = symOperands[symExpr.getPosition()];
2420         if (auto producerOp = symValue.getDefiningOp<T>()) {
2421           producerOps.push_back(producerOp);
2422           continue;
2423         }
2424       } else if (auto dimExpr = expr.dyn_cast<AffineDimExpr>()) {
2425         Value dimValue = dimOperands[dimExpr.getPosition()];
2426         if (auto producerOp = dimValue.getDefiningOp<T>()) {
2427           producerOps.push_back(producerOp);
2428           continue;
2429         }
2430       }
2431       // For the above cases we will remove the expression by merging the
2432       // producer affine min/max's affine expressions. Otherwise we need to
2433       // keep the existing expression.
2434       newExprs.push_back(expr);
2435     }
2436 
2437     if (producerOps.empty())
2438       return failure();
2439 
2440     unsigned numUsedDims = oldMap.getNumDims();
2441     unsigned numUsedSyms = oldMap.getNumSymbols();
2442 
2443     // Now go over all producer affine ops and merge their expressions.
2444     for (T producerOp : producerOps) {
2445       AffineMap producerMap = producerOp.getAffineMap();
2446       unsigned numProducerDims = producerMap.getNumDims();
2447       unsigned numProducerSyms = producerMap.getNumSymbols();
2448 
2449       // Collect all dimension/symbol values.
2450       ValueRange dimValues =
2451           producerOp.getMapOperands().take_front(numProducerDims);
2452       ValueRange symValues =
2453           producerOp.getMapOperands().take_back(numProducerSyms);
2454       newDimOperands.append(dimValues.begin(), dimValues.end());
2455       newSymOperands.append(symValues.begin(), symValues.end());
2456 
2457       // For expressions we need to shift to avoid overlap.
2458       for (AffineExpr expr : producerMap.getResults()) {
2459         newExprs.push_back(expr.shiftDims(numProducerDims, numUsedDims)
2460                                .shiftSymbols(numProducerSyms, numUsedSyms));
2461       }
2462 
2463       numUsedDims += numProducerDims;
2464       numUsedSyms += numProducerSyms;
2465     }
2466 
2467     auto newMap = AffineMap::get(numUsedDims, numUsedSyms, newExprs,
2468                                  rewriter.getContext());
2469     auto newOperands =
2470         llvm::to_vector<8>(llvm::concat<Value>(newDimOperands, newSymOperands));
2471     rewriter.replaceOpWithNewOp<T>(affineOp, newMap, newOperands);
2472 
2473     return success();
2474   }
2475 };
2476 
2477 //===----------------------------------------------------------------------===//
2478 // AffineMinOp
2479 //===----------------------------------------------------------------------===//
2480 //
2481 //   %0 = affine.min (d0) -> (1000, d0 + 512) (%i0)
2482 //
2483 
2484 OpFoldResult AffineMinOp::fold(ArrayRef<Attribute> operands) {
2485   return foldMinMaxOp(*this, operands);
2486 }
2487 
2488 void AffineMinOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2489                                               MLIRContext *context) {
2490   patterns.add<DeduplicateAffineMinMaxExpressions<AffineMinOp>,
2491                MergeAffineMinMaxOp<AffineMinOp>, SimplifyAffineOp<AffineMinOp>>(
2492       context);
2493 }
2494 
2495 //===----------------------------------------------------------------------===//
2496 // AffineMaxOp
2497 //===----------------------------------------------------------------------===//
2498 //
2499 //   %0 = affine.max (d0) -> (1000, d0 + 512) (%i0)
2500 //
2501 
2502 OpFoldResult AffineMaxOp::fold(ArrayRef<Attribute> operands) {
2503   return foldMinMaxOp(*this, operands);
2504 }
2505 
2506 void AffineMaxOp::getCanonicalizationPatterns(RewritePatternSet &patterns,
2507                                               MLIRContext *context) {
2508   patterns.add<DeduplicateAffineMinMaxExpressions<AffineMaxOp>,
2509                MergeAffineMinMaxOp<AffineMaxOp>, SimplifyAffineOp<AffineMaxOp>>(
2510       context);
2511 }
2512 
2513 //===----------------------------------------------------------------------===//
2514 // AffinePrefetchOp
2515 //===----------------------------------------------------------------------===//
2516 
2517 //
2518 // affine.prefetch %0[%i, %j + 5], read, locality<3>, data : memref<400x400xi32>
2519 //
2520 static ParseResult parseAffinePrefetchOp(OpAsmParser &parser,
2521                                          OperationState &result) {
2522   auto &builder = parser.getBuilder();
2523   auto indexTy = builder.getIndexType();
2524 
2525   MemRefType type;
2526   OpAsmParser::OperandType memrefInfo;
2527   IntegerAttr hintInfo;
2528   auto i32Type = parser.getBuilder().getIntegerType(32);
2529   StringRef readOrWrite, cacheType;
2530 
2531   AffineMapAttr mapAttr;
2532   SmallVector<OpAsmParser::OperandType, 1> mapOperands;
2533   if (parser.parseOperand(memrefInfo) ||
2534       parser.parseAffineMapOfSSAIds(mapOperands, mapAttr,
2535                                     AffinePrefetchOp::getMapAttrName(),
2536                                     result.attributes) ||
2537       parser.parseComma() || parser.parseKeyword(&readOrWrite) ||
2538       parser.parseComma() || parser.parseKeyword("locality") ||
2539       parser.parseLess() ||
2540       parser.parseAttribute(hintInfo, i32Type,
2541                             AffinePrefetchOp::getLocalityHintAttrName(),
2542                             result.attributes) ||
2543       parser.parseGreater() || parser.parseComma() ||
2544       parser.parseKeyword(&cacheType) ||
2545       parser.parseOptionalAttrDict(result.attributes) ||
2546       parser.parseColonType(type) ||
2547       parser.resolveOperand(memrefInfo, type, result.operands) ||
2548       parser.resolveOperands(mapOperands, indexTy, result.operands))
2549     return failure();
2550 
2551   if (!readOrWrite.equals("read") && !readOrWrite.equals("write"))
2552     return parser.emitError(parser.getNameLoc(),
2553                             "rw specifier has to be 'read' or 'write'");
2554   result.addAttribute(
2555       AffinePrefetchOp::getIsWriteAttrName(),
2556       parser.getBuilder().getBoolAttr(readOrWrite.equals("write")));
2557 
2558   if (!cacheType.equals("data") && !cacheType.equals("instr"))
2559     return parser.emitError(parser.getNameLoc(),
2560                             "cache type has to be 'data' or 'instr'");
2561 
2562   result.addAttribute(
2563       AffinePrefetchOp::getIsDataCacheAttrName(),
2564       parser.getBuilder().getBoolAttr(cacheType.equals("data")));
2565 
2566   return success();
2567 }
2568 
2569 static void print(OpAsmPrinter &p, AffinePrefetchOp op) {
2570   p << AffinePrefetchOp::getOperationName() << " " << op.memref() << '[';
2571   AffineMapAttr mapAttr = op->getAttrOfType<AffineMapAttr>(op.getMapAttrName());
2572   if (mapAttr) {
2573     SmallVector<Value, 2> operands(op.getMapOperands());
2574     p.printAffineMapOfSSAIds(mapAttr, operands);
2575   }
2576   p << ']' << ", " << (op.isWrite() ? "write" : "read") << ", "
2577     << "locality<" << op.localityHint() << ">, "
2578     << (op.isDataCache() ? "data" : "instr");
2579   p.printOptionalAttrDict(
2580       op->getAttrs(),
2581       /*elidedAttrs=*/{op.getMapAttrName(), op.getLocalityHintAttrName(),
2582                        op.getIsDataCacheAttrName(), op.getIsWriteAttrName()});
2583   p << " : " << op.getMemRefType();
2584 }
2585 
2586 static LogicalResult verify(AffinePrefetchOp op) {
2587   auto mapAttr = op->getAttrOfType<AffineMapAttr>(op.getMapAttrName());
2588   if (mapAttr) {
2589     AffineMap map = mapAttr.getValue();
2590     if (map.getNumResults() != op.getMemRefType().getRank())
2591       return op.emitOpError("affine.prefetch affine map num results must equal"
2592                             " memref rank");
2593     if (map.getNumInputs() + 1 != op.getNumOperands())
2594       return op.emitOpError("too few operands");
2595   } else {
2596     if (op.getNumOperands() != 1)
2597       return op.emitOpError("too few operands");
2598   }
2599 
2600   Region *scope = getAffineScope(op);
2601   for (auto idx : op.getMapOperands()) {
2602     if (!isValidAffineIndexOperand(idx, scope))
2603       return op.emitOpError("index must be a dimension or symbol identifier");
2604   }
2605   return success();
2606 }
2607 
2608 void AffinePrefetchOp::getCanonicalizationPatterns(RewritePatternSet &results,
2609                                                    MLIRContext *context) {
2610   // prefetch(memrefcast) -> prefetch
2611   results.add<SimplifyAffineOp<AffinePrefetchOp>>(context);
2612 }
2613 
2614 LogicalResult AffinePrefetchOp::fold(ArrayRef<Attribute> cstOperands,
2615                                      SmallVectorImpl<OpFoldResult> &results) {
2616   /// prefetch(memrefcast) -> prefetch
2617   return foldMemRefCast(*this);
2618 }
2619 
2620 //===----------------------------------------------------------------------===//
2621 // AffineParallelOp
2622 //===----------------------------------------------------------------------===//
2623 
2624 void AffineParallelOp::build(OpBuilder &builder, OperationState &result,
2625                              TypeRange resultTypes,
2626                              ArrayRef<AtomicRMWKind> reductions,
2627                              ArrayRef<int64_t> ranges) {
2628   SmallVector<AffineMap> lbs(ranges.size(), builder.getConstantAffineMap(0));
2629   auto ubs = llvm::to_vector<4>(llvm::map_range(ranges, [&](int64_t value) {
2630     return builder.getConstantAffineMap(value);
2631   }));
2632   SmallVector<int64_t> steps(ranges.size(), 1);
2633   build(builder, result, resultTypes, reductions, lbs, /*lbArgs=*/{}, ubs,
2634         /*ubArgs=*/{}, steps);
2635 }
2636 
2637 void AffineParallelOp::build(OpBuilder &builder, OperationState &result,
2638                              TypeRange resultTypes,
2639                              ArrayRef<AtomicRMWKind> reductions,
2640                              ArrayRef<AffineMap> lbMaps, ValueRange lbArgs,
2641                              ArrayRef<AffineMap> ubMaps, ValueRange ubArgs,
2642                              ArrayRef<int64_t> steps) {
2643   assert(llvm::all_of(lbMaps,
2644                       [lbMaps](AffineMap m) {
2645                         return m.getNumDims() == lbMaps[0].getNumDims() &&
2646                                m.getNumSymbols() == lbMaps[0].getNumSymbols();
2647                       }) &&
2648          "expected all lower bounds maps to have the same number of dimensions "
2649          "and symbols");
2650   assert(llvm::all_of(ubMaps,
2651                       [ubMaps](AffineMap m) {
2652                         return m.getNumDims() == ubMaps[0].getNumDims() &&
2653                                m.getNumSymbols() == ubMaps[0].getNumSymbols();
2654                       }) &&
2655          "expected all upper bounds maps to have the same number of dimensions "
2656          "and symbols");
2657   assert((lbMaps.empty() || lbMaps[0].getNumInputs() == lbArgs.size()) &&
2658          "expected lower bound maps to have as many inputs as lower bound "
2659          "operands");
2660   assert((ubMaps.empty() || ubMaps[0].getNumInputs() == ubArgs.size()) &&
2661          "expected upper bound maps to have as many inputs as upper bound "
2662          "operands");
2663 
2664   result.addTypes(resultTypes);
2665 
2666   // Convert the reductions to integer attributes.
2667   SmallVector<Attribute, 4> reductionAttrs;
2668   for (AtomicRMWKind reduction : reductions)
2669     reductionAttrs.push_back(
2670         builder.getI64IntegerAttr(static_cast<int64_t>(reduction)));
2671   result.addAttribute(getReductionsAttrName(),
2672                       builder.getArrayAttr(reductionAttrs));
2673 
2674   // Concatenates maps defined in the same input space (same dimensions and
2675   // symbols), assumes there is at least one map.
2676   auto concatMapsSameInput = [&builder](ArrayRef<AffineMap> maps,
2677                                         SmallVectorImpl<int32_t> &groups) {
2678     if (maps.empty())
2679       return AffineMap::get(builder.getContext());
2680     SmallVector<AffineExpr> exprs;
2681     groups.reserve(groups.size() + maps.size());
2682     exprs.reserve(maps.size());
2683     for (AffineMap m : maps) {
2684       llvm::append_range(exprs, m.getResults());
2685       groups.push_back(m.getNumResults());
2686     }
2687     return AffineMap::get(maps[0].getNumDims(), maps[0].getNumSymbols(), exprs,
2688                           maps[0].getContext());
2689   };
2690 
2691   // Set up the bounds.
2692   SmallVector<int32_t> lbGroups, ubGroups;
2693   AffineMap lbMap = concatMapsSameInput(lbMaps, lbGroups);
2694   AffineMap ubMap = concatMapsSameInput(ubMaps, ubGroups);
2695   result.addAttribute(getLowerBoundsMapAttrName(), AffineMapAttr::get(lbMap));
2696   result.addAttribute(getLowerBoundsGroupsAttrName(),
2697                       builder.getI32TensorAttr(lbGroups));
2698   result.addAttribute(getUpperBoundsMapAttrName(), AffineMapAttr::get(ubMap));
2699   result.addAttribute(getUpperBoundsGroupsAttrName(),
2700                       builder.getI32TensorAttr(ubGroups));
2701   result.addAttribute(getStepsAttrName(), builder.getI64ArrayAttr(steps));
2702   result.addOperands(lbArgs);
2703   result.addOperands(ubArgs);
2704 
2705   // Create a region and a block for the body.
2706   auto *bodyRegion = result.addRegion();
2707   auto *body = new Block();
2708   // Add all the block arguments.
2709   for (unsigned i = 0, e = steps.size(); i < e; ++i)
2710     body->addArgument(IndexType::get(builder.getContext()));
2711   bodyRegion->push_back(body);
2712   if (resultTypes.empty())
2713     ensureTerminator(*bodyRegion, builder, result.location);
2714 }
2715 
2716 Region &AffineParallelOp::getLoopBody() { return region(); }
2717 
2718 bool AffineParallelOp::isDefinedOutsideOfLoop(Value value) {
2719   return !region().isAncestor(value.getParentRegion());
2720 }
2721 
2722 LogicalResult AffineParallelOp::moveOutOfLoop(ArrayRef<Operation *> ops) {
2723   for (Operation *op : ops)
2724     op->moveBefore(*this);
2725   return success();
2726 }
2727 
2728 unsigned AffineParallelOp::getNumDims() { return steps().size(); }
2729 
2730 AffineParallelOp::operand_range AffineParallelOp::getLowerBoundsOperands() {
2731   return getOperands().take_front(lowerBoundsMap().getNumInputs());
2732 }
2733 
2734 AffineParallelOp::operand_range AffineParallelOp::getUpperBoundsOperands() {
2735   return getOperands().drop_front(lowerBoundsMap().getNumInputs());
2736 }
2737 
2738 AffineMap AffineParallelOp::getLowerBoundMap(unsigned pos) {
2739   unsigned start = 0;
2740   for (unsigned i = 0; i < pos; ++i)
2741     start += lowerBoundsGroups().getValue<int32_t>(i);
2742   return lowerBoundsMap().getSliceMap(
2743       start, lowerBoundsGroups().getValue<int32_t>(pos));
2744 }
2745 
2746 AffineMap AffineParallelOp::getUpperBoundMap(unsigned pos) {
2747   unsigned start = 0;
2748   for (unsigned i = 0; i < pos; ++i)
2749     start += upperBoundsGroups().getValue<int32_t>(i);
2750   return upperBoundsMap().getSliceMap(
2751       start, upperBoundsGroups().getValue<int32_t>(pos));
2752 }
2753 
2754 AffineValueMap AffineParallelOp::getLowerBoundsValueMap() {
2755   return AffineValueMap(lowerBoundsMap(), getLowerBoundsOperands());
2756 }
2757 
2758 AffineValueMap AffineParallelOp::getUpperBoundsValueMap() {
2759   return AffineValueMap(upperBoundsMap(), getUpperBoundsOperands());
2760 }
2761 
2762 Optional<SmallVector<int64_t, 8>> AffineParallelOp::getConstantRanges() {
2763   if (hasMinMaxBounds())
2764     return llvm::None;
2765 
2766   // Try to convert all the ranges to constant expressions.
2767   SmallVector<int64_t, 8> out;
2768   AffineValueMap rangesValueMap;
2769   AffineValueMap::difference(getUpperBoundsValueMap(), getLowerBoundsValueMap(),
2770                              &rangesValueMap);
2771   out.reserve(rangesValueMap.getNumResults());
2772   for (unsigned i = 0, e = rangesValueMap.getNumResults(); i < e; ++i) {
2773     auto expr = rangesValueMap.getResult(i);
2774     auto cst = expr.dyn_cast<AffineConstantExpr>();
2775     if (!cst)
2776       return llvm::None;
2777     out.push_back(cst.getValue());
2778   }
2779   return out;
2780 }
2781 
2782 Block *AffineParallelOp::getBody() { return &region().front(); }
2783 
2784 OpBuilder AffineParallelOp::getBodyBuilder() {
2785   return OpBuilder(getBody(), std::prev(getBody()->end()));
2786 }
2787 
2788 void AffineParallelOp::setLowerBounds(ValueRange lbOperands, AffineMap map) {
2789   assert(lbOperands.size() == map.getNumInputs() &&
2790          "operands to map must match number of inputs");
2791 
2792   auto ubOperands = getUpperBoundsOperands();
2793 
2794   SmallVector<Value, 4> newOperands(lbOperands);
2795   newOperands.append(ubOperands.begin(), ubOperands.end());
2796   (*this)->setOperands(newOperands);
2797 
2798   lowerBoundsMapAttr(AffineMapAttr::get(map));
2799 }
2800 
2801 void AffineParallelOp::setUpperBounds(ValueRange ubOperands, AffineMap map) {
2802   assert(ubOperands.size() == map.getNumInputs() &&
2803          "operands to map must match number of inputs");
2804 
2805   SmallVector<Value, 4> newOperands(getLowerBoundsOperands());
2806   newOperands.append(ubOperands.begin(), ubOperands.end());
2807   (*this)->setOperands(newOperands);
2808 
2809   upperBoundsMapAttr(AffineMapAttr::get(map));
2810 }
2811 
2812 void AffineParallelOp::setLowerBoundsMap(AffineMap map) {
2813   AffineMap lbMap = lowerBoundsMap();
2814   assert(lbMap.getNumDims() == map.getNumDims() &&
2815          lbMap.getNumSymbols() == map.getNumSymbols());
2816   (void)lbMap;
2817   lowerBoundsMapAttr(AffineMapAttr::get(map));
2818 }
2819 
2820 void AffineParallelOp::setUpperBoundsMap(AffineMap map) {
2821   AffineMap ubMap = upperBoundsMap();
2822   assert(ubMap.getNumDims() == map.getNumDims() &&
2823          ubMap.getNumSymbols() == map.getNumSymbols());
2824   (void)ubMap;
2825   upperBoundsMapAttr(AffineMapAttr::get(map));
2826 }
2827 
2828 SmallVector<int64_t, 8> AffineParallelOp::getSteps() {
2829   SmallVector<int64_t, 8> result;
2830   for (Attribute attr : steps()) {
2831     result.push_back(attr.cast<IntegerAttr>().getInt());
2832   }
2833   return result;
2834 }
2835 
2836 void AffineParallelOp::setSteps(ArrayRef<int64_t> newSteps) {
2837   stepsAttr(getBodyBuilder().getI64ArrayAttr(newSteps));
2838 }
2839 
2840 static LogicalResult verify(AffineParallelOp op) {
2841   auto numDims = op.getNumDims();
2842   if (op.lowerBoundsGroups().getNumElements() != numDims ||
2843       op.upperBoundsGroups().getNumElements() != numDims ||
2844       op.steps().size() != numDims ||
2845       op.getBody()->getNumArguments() != numDims) {
2846     return op.emitOpError()
2847            << "the number of region arguments ("
2848            << op.getBody()->getNumArguments()
2849            << ") and the number of map groups for lower ("
2850            << op.lowerBoundsGroups().getNumElements() << ") and upper bound ("
2851            << op.upperBoundsGroups().getNumElements()
2852            << "), and the number of steps (" << op.steps().size()
2853            << ") must all match";
2854   }
2855 
2856   unsigned expectedNumLBResults = 0;
2857   for (APInt v : op.lowerBoundsGroups())
2858     expectedNumLBResults += v.getZExtValue();
2859   if (expectedNumLBResults != op.lowerBoundsMap().getNumResults())
2860     return op.emitOpError() << "expected lower bounds map to have "
2861                             << expectedNumLBResults << " results";
2862   unsigned expectedNumUBResults = 0;
2863   for (APInt v : op.upperBoundsGroups())
2864     expectedNumUBResults += v.getZExtValue();
2865   if (expectedNumUBResults != op.upperBoundsMap().getNumResults())
2866     return op.emitOpError() << "expected upper bounds map to have "
2867                             << expectedNumUBResults << " results";
2868 
2869   if (op.reductions().size() != op.getNumResults())
2870     return op.emitOpError("a reduction must be specified for each output");
2871 
2872   // Verify reduction  ops are all valid
2873   for (Attribute attr : op.reductions()) {
2874     auto intAttr = attr.dyn_cast<IntegerAttr>();
2875     if (!intAttr || !symbolizeAtomicRMWKind(intAttr.getInt()))
2876       return op.emitOpError("invalid reduction attribute");
2877   }
2878 
2879   // Verify that the bound operands are valid dimension/symbols.
2880   /// Lower bounds.
2881   if (failed(verifyDimAndSymbolIdentifiers(op, op.getLowerBoundsOperands(),
2882                                            op.lowerBoundsMap().getNumDims())))
2883     return failure();
2884   /// Upper bounds.
2885   if (failed(verifyDimAndSymbolIdentifiers(op, op.getUpperBoundsOperands(),
2886                                            op.upperBoundsMap().getNumDims())))
2887     return failure();
2888   return success();
2889 }
2890 
2891 LogicalResult AffineValueMap::canonicalize() {
2892   SmallVector<Value, 4> newOperands{operands};
2893   auto newMap = getAffineMap();
2894   composeAffineMapAndOperands(&newMap, &newOperands);
2895   if (newMap == getAffineMap() && newOperands == operands)
2896     return failure();
2897   reset(newMap, newOperands);
2898   return success();
2899 }
2900 
2901 /// Canonicalize the bounds of the given loop.
2902 static LogicalResult canonicalizeLoopBounds(AffineParallelOp op) {
2903   AffineValueMap lb = op.getLowerBoundsValueMap();
2904   bool lbCanonicalized = succeeded(lb.canonicalize());
2905 
2906   AffineValueMap ub = op.getUpperBoundsValueMap();
2907   bool ubCanonicalized = succeeded(ub.canonicalize());
2908 
2909   // Any canonicalization change always leads to updated map(s).
2910   if (!lbCanonicalized && !ubCanonicalized)
2911     return failure();
2912 
2913   if (lbCanonicalized)
2914     op.setLowerBounds(lb.getOperands(), lb.getAffineMap());
2915   if (ubCanonicalized)
2916     op.setUpperBounds(ub.getOperands(), ub.getAffineMap());
2917 
2918   return success();
2919 }
2920 
2921 LogicalResult AffineParallelOp::fold(ArrayRef<Attribute> operands,
2922                                      SmallVectorImpl<OpFoldResult> &results) {
2923   return canonicalizeLoopBounds(*this);
2924 }
2925 
2926 /// Prints a lower(upper) bound of an affine parallel loop with max(min)
2927 /// conditions in it. `mapAttr` is a flat list of affine expressions and `group`
2928 /// identifies which of the those expressions form max/min groups. `operands`
2929 /// are the SSA values of dimensions and symbols and `keyword` is either "min"
2930 /// or "max".
2931 static void printMinMaxBound(OpAsmPrinter &p, AffineMapAttr mapAttr,
2932                              DenseIntElementsAttr group, ValueRange operands,
2933                              StringRef keyword) {
2934   AffineMap map = mapAttr.getValue();
2935   unsigned numDims = map.getNumDims();
2936   ValueRange dimOperands = operands.take_front(numDims);
2937   ValueRange symOperands = operands.drop_front(numDims);
2938   unsigned start = 0;
2939   for (llvm::APInt groupSize : group) {
2940     if (start != 0)
2941       p << ", ";
2942 
2943     unsigned size = groupSize.getZExtValue();
2944     if (size == 1) {
2945       p.printAffineExprOfSSAIds(map.getResult(start), dimOperands, symOperands);
2946       ++start;
2947     } else {
2948       p << keyword << '(';
2949       AffineMap submap = map.getSliceMap(start, size);
2950       p.printAffineMapOfSSAIds(AffineMapAttr::get(submap), operands);
2951       p << ')';
2952       start += size;
2953     }
2954   }
2955 }
2956 
2957 static void print(OpAsmPrinter &p, AffineParallelOp op) {
2958   p << op.getOperationName() << " (" << op.getBody()->getArguments() << ") = (";
2959   printMinMaxBound(p, op.lowerBoundsMapAttr(), op.lowerBoundsGroupsAttr(),
2960                    op.getLowerBoundsOperands(), "max");
2961   p << ") to (";
2962   printMinMaxBound(p, op.upperBoundsMapAttr(), op.upperBoundsGroupsAttr(),
2963                    op.getUpperBoundsOperands(), "min");
2964   p << ')';
2965   SmallVector<int64_t, 8> steps = op.getSteps();
2966   bool elideSteps = llvm::all_of(steps, [](int64_t step) { return step == 1; });
2967   if (!elideSteps) {
2968     p << " step (";
2969     llvm::interleaveComma(steps, p);
2970     p << ')';
2971   }
2972   if (op.getNumResults()) {
2973     p << " reduce (";
2974     llvm::interleaveComma(op.reductions(), p, [&](auto &attr) {
2975       AtomicRMWKind sym =
2976           *symbolizeAtomicRMWKind(attr.template cast<IntegerAttr>().getInt());
2977       p << "\"" << stringifyAtomicRMWKind(sym) << "\"";
2978     });
2979     p << ") -> (" << op.getResultTypes() << ")";
2980   }
2981 
2982   p.printRegion(op.region(), /*printEntryBlockArgs=*/false,
2983                 /*printBlockTerminators=*/op.getNumResults());
2984   p.printOptionalAttrDict(
2985       op->getAttrs(),
2986       /*elidedAttrs=*/{AffineParallelOp::getReductionsAttrName(),
2987                        AffineParallelOp::getLowerBoundsMapAttrName(),
2988                        AffineParallelOp::getLowerBoundsGroupsAttrName(),
2989                        AffineParallelOp::getUpperBoundsMapAttrName(),
2990                        AffineParallelOp::getUpperBoundsGroupsAttrName(),
2991                        AffineParallelOp::getStepsAttrName()});
2992 }
2993 
2994 /// Given a list of lists of parsed operands, populates `uniqueOperands` with
2995 /// unique operands. Also populates `replacements with affine expressions of
2996 /// `kind` that can be used to update affine maps previously accepting a
2997 /// `operands` to accept `uniqueOperands` instead.
2998 static void deduplicateAndResolveOperands(
2999     OpAsmParser &parser,
3000     ArrayRef<SmallVector<OpAsmParser::OperandType>> operands,
3001     SmallVectorImpl<Value> &uniqueOperands,
3002     SmallVectorImpl<AffineExpr> &replacements, AffineExprKind kind) {
3003   assert((kind == AffineExprKind::DimId || kind == AffineExprKind::SymbolId) &&
3004          "expected operands to be dim or symbol expression");
3005 
3006   Type indexType = parser.getBuilder().getIndexType();
3007   for (const auto &list : operands) {
3008     SmallVector<Value> valueOperands;
3009     parser.resolveOperands(list, indexType, valueOperands);
3010     for (Value operand : valueOperands) {
3011       unsigned pos = std::distance(uniqueOperands.begin(),
3012                                    llvm::find(uniqueOperands, operand));
3013       if (pos == uniqueOperands.size())
3014         uniqueOperands.push_back(operand);
3015       replacements.push_back(
3016           kind == AffineExprKind::DimId
3017               ? getAffineDimExpr(pos, parser.getBuilder().getContext())
3018               : getAffineSymbolExpr(pos, parser.getBuilder().getContext()));
3019     }
3020   }
3021 }
3022 
3023 namespace {
3024 enum class MinMaxKind { Min, Max };
3025 } // namespace
3026 
3027 /// Parses an affine map that can contain a min/max for groups of its results,
3028 /// e.g., max(expr-1, expr-2), expr-3, max(expr-4, expr-5, expr-6). Populates
3029 /// `result` attributes with the map (flat list of expressions) and the grouping
3030 /// (list of integers that specify how many expressions to put into each
3031 /// min/max) attributes. Deduplicates repeated operands.
3032 ///
3033 /// parallel-bound       ::= `(` parallel-group-list `)`
3034 /// parallel-group-list  ::= parallel-group (`,` parallel-group-list)?
3035 /// parallel-group       ::= simple-group | min-max-group
3036 /// simple-group         ::= expr-of-ssa-ids
3037 /// min-max-group        ::= ( `min` | `max` ) `(` expr-of-ssa-ids-list `)`
3038 /// expr-of-ssa-ids-list ::= expr-of-ssa-ids (`,` expr-of-ssa-id-list)?
3039 ///
3040 /// Examples:
3041 ///   (%0, min(%1 + %2, %3), %4, min(%5 floordiv 32, %6))
3042 ///   (%0, max(%1 - 2 * %2))
3043 static ParseResult parseAffineMapWithMinMax(OpAsmParser &parser,
3044                                             OperationState &result,
3045                                             MinMaxKind kind) {
3046   constexpr llvm::StringLiteral tmpAttrName = "__pseudo_bound_map";
3047 
3048   StringRef mapName = kind == MinMaxKind::Min
3049                           ? AffineParallelOp::getUpperBoundsMapAttrName()
3050                           : AffineParallelOp::getLowerBoundsMapAttrName();
3051   StringRef groupsName = kind == MinMaxKind::Min
3052                              ? AffineParallelOp::getUpperBoundsGroupsAttrName()
3053                              : AffineParallelOp::getLowerBoundsGroupsAttrName();
3054 
3055   if (failed(parser.parseLParen()))
3056     return failure();
3057 
3058   if (succeeded(parser.parseOptionalRParen())) {
3059     result.addAttribute(
3060         mapName, AffineMapAttr::get(parser.getBuilder().getEmptyAffineMap()));
3061     result.addAttribute(groupsName, parser.getBuilder().getI32TensorAttr({}));
3062     return success();
3063   }
3064 
3065   SmallVector<AffineExpr> flatExprs;
3066   SmallVector<SmallVector<OpAsmParser::OperandType>> flatDimOperands;
3067   SmallVector<SmallVector<OpAsmParser::OperandType>> flatSymOperands;
3068   SmallVector<int32_t> numMapsPerGroup;
3069   SmallVector<OpAsmParser::OperandType> mapOperands;
3070   do {
3071     if (succeeded(parser.parseOptionalKeyword(
3072             kind == MinMaxKind::Min ? "min" : "max"))) {
3073       mapOperands.clear();
3074       AffineMapAttr map;
3075       if (failed(parser.parseAffineMapOfSSAIds(mapOperands, map, tmpAttrName,
3076                                                result.attributes,
3077                                                OpAsmParser::Delimiter::Paren)))
3078         return failure();
3079       result.attributes.erase(tmpAttrName);
3080       llvm::append_range(flatExprs, map.getValue().getResults());
3081       auto operandsRef = llvm::makeArrayRef(mapOperands);
3082       auto dimsRef = operandsRef.take_front(map.getValue().getNumDims());
3083       SmallVector<OpAsmParser::OperandType> dims(dimsRef.begin(),
3084                                                  dimsRef.end());
3085       auto symsRef = operandsRef.drop_front(map.getValue().getNumDims());
3086       SmallVector<OpAsmParser::OperandType> syms(symsRef.begin(),
3087                                                  symsRef.end());
3088       flatDimOperands.append(map.getValue().getNumResults(), dims);
3089       flatSymOperands.append(map.getValue().getNumResults(), syms);
3090       numMapsPerGroup.push_back(map.getValue().getNumResults());
3091     } else {
3092       if (failed(parser.parseAffineExprOfSSAIds(flatDimOperands.emplace_back(),
3093                                                 flatSymOperands.emplace_back(),
3094                                                 flatExprs.emplace_back())))
3095         return failure();
3096       numMapsPerGroup.push_back(1);
3097     }
3098   } while (succeeded(parser.parseOptionalComma()));
3099 
3100   if (failed(parser.parseRParen()))
3101     return failure();
3102 
3103   unsigned totalNumDims = 0;
3104   unsigned totalNumSyms = 0;
3105   for (unsigned i = 0, e = flatExprs.size(); i < e; ++i) {
3106     unsigned numDims = flatDimOperands[i].size();
3107     unsigned numSyms = flatSymOperands[i].size();
3108     flatExprs[i] = flatExprs[i]
3109                        .shiftDims(numDims, totalNumDims)
3110                        .shiftSymbols(numSyms, totalNumSyms);
3111     totalNumDims += numDims;
3112     totalNumSyms += numSyms;
3113   }
3114 
3115   // Deduplicate map operands.
3116   SmallVector<Value> dimOperands, symOperands;
3117   SmallVector<AffineExpr> dimRplacements, symRepacements;
3118   deduplicateAndResolveOperands(parser, flatDimOperands, dimOperands,
3119                                 dimRplacements, AffineExprKind::DimId);
3120   deduplicateAndResolveOperands(parser, flatSymOperands, symOperands,
3121                                 symRepacements, AffineExprKind::SymbolId);
3122 
3123   result.operands.append(dimOperands.begin(), dimOperands.end());
3124   result.operands.append(symOperands.begin(), symOperands.end());
3125 
3126   Builder &builder = parser.getBuilder();
3127   auto flatMap = AffineMap::get(totalNumDims, totalNumSyms, flatExprs,
3128                                 parser.getBuilder().getContext());
3129   flatMap = flatMap.replaceDimsAndSymbols(
3130       dimRplacements, symRepacements, dimOperands.size(), symOperands.size());
3131 
3132   result.addAttribute(mapName, AffineMapAttr::get(flatMap));
3133   result.addAttribute(groupsName, builder.getI32TensorAttr(numMapsPerGroup));
3134   return success();
3135 }
3136 
3137 //
3138 // operation ::= `affine.parallel` `(` ssa-ids `)` `=` parallel-bound
3139 //               `to` parallel-bound steps? region attr-dict?
3140 // steps     ::= `steps` `(` integer-literals `)`
3141 //
3142 static ParseResult parseAffineParallelOp(OpAsmParser &parser,
3143                                          OperationState &result) {
3144   auto &builder = parser.getBuilder();
3145   auto indexType = builder.getIndexType();
3146   SmallVector<OpAsmParser::OperandType, 4> ivs;
3147   if (parser.parseRegionArgumentList(ivs, /*requiredOperandCount=*/-1,
3148                                      OpAsmParser::Delimiter::Paren) ||
3149       parser.parseEqual() ||
3150       parseAffineMapWithMinMax(parser, result, MinMaxKind::Max) ||
3151       parser.parseKeyword("to") ||
3152       parseAffineMapWithMinMax(parser, result, MinMaxKind::Min))
3153     return failure();
3154 
3155   AffineMapAttr stepsMapAttr;
3156   NamedAttrList stepsAttrs;
3157   SmallVector<OpAsmParser::OperandType, 4> stepsMapOperands;
3158   if (failed(parser.parseOptionalKeyword("step"))) {
3159     SmallVector<int64_t, 4> steps(ivs.size(), 1);
3160     result.addAttribute(AffineParallelOp::getStepsAttrName(),
3161                         builder.getI64ArrayAttr(steps));
3162   } else {
3163     if (parser.parseAffineMapOfSSAIds(stepsMapOperands, stepsMapAttr,
3164                                       AffineParallelOp::getStepsAttrName(),
3165                                       stepsAttrs,
3166                                       OpAsmParser::Delimiter::Paren))
3167       return failure();
3168 
3169     // Convert steps from an AffineMap into an I64ArrayAttr.
3170     SmallVector<int64_t, 4> steps;
3171     auto stepsMap = stepsMapAttr.getValue();
3172     for (const auto &result : stepsMap.getResults()) {
3173       auto constExpr = result.dyn_cast<AffineConstantExpr>();
3174       if (!constExpr)
3175         return parser.emitError(parser.getNameLoc(),
3176                                 "steps must be constant integers");
3177       steps.push_back(constExpr.getValue());
3178     }
3179     result.addAttribute(AffineParallelOp::getStepsAttrName(),
3180                         builder.getI64ArrayAttr(steps));
3181   }
3182 
3183   // Parse optional clause of the form: `reduce ("addf", "maxf")`, where the
3184   // quoted strings are a member of the enum AtomicRMWKind.
3185   SmallVector<Attribute, 4> reductions;
3186   if (succeeded(parser.parseOptionalKeyword("reduce"))) {
3187     if (parser.parseLParen())
3188       return failure();
3189     do {
3190       // Parse a single quoted string via the attribute parsing, and then
3191       // verify it is a member of the enum and convert to it's integer
3192       // representation.
3193       StringAttr attrVal;
3194       NamedAttrList attrStorage;
3195       auto loc = parser.getCurrentLocation();
3196       if (parser.parseAttribute(attrVal, builder.getNoneType(), "reduce",
3197                                 attrStorage))
3198         return failure();
3199       llvm::Optional<AtomicRMWKind> reduction =
3200           symbolizeAtomicRMWKind(attrVal.getValue());
3201       if (!reduction)
3202         return parser.emitError(loc, "invalid reduction value: ") << attrVal;
3203       reductions.push_back(builder.getI64IntegerAttr(
3204           static_cast<int64_t>(reduction.getValue())));
3205       // While we keep getting commas, keep parsing.
3206     } while (succeeded(parser.parseOptionalComma()));
3207     if (parser.parseRParen())
3208       return failure();
3209   }
3210   result.addAttribute(AffineParallelOp::getReductionsAttrName(),
3211                       builder.getArrayAttr(reductions));
3212 
3213   // Parse return types of reductions (if any)
3214   if (parser.parseOptionalArrowTypeList(result.types))
3215     return failure();
3216 
3217   // Now parse the body.
3218   Region *body = result.addRegion();
3219   SmallVector<Type, 4> types(ivs.size(), indexType);
3220   if (parser.parseRegion(*body, ivs, types) ||
3221       parser.parseOptionalAttrDict(result.attributes))
3222     return failure();
3223 
3224   // Add a terminator if none was parsed.
3225   AffineParallelOp::ensureTerminator(*body, builder, result.location);
3226   return success();
3227 }
3228 
3229 //===----------------------------------------------------------------------===//
3230 // AffineYieldOp
3231 //===----------------------------------------------------------------------===//
3232 
3233 static LogicalResult verify(AffineYieldOp op) {
3234   auto *parentOp = op->getParentOp();
3235   auto results = parentOp->getResults();
3236   auto operands = op.getOperands();
3237 
3238   if (!isa<AffineParallelOp, AffineIfOp, AffineForOp>(parentOp))
3239     return op.emitOpError() << "only terminates affine.if/for/parallel regions";
3240   if (parentOp->getNumResults() != op.getNumOperands())
3241     return op.emitOpError() << "parent of yield must have same number of "
3242                                "results as the yield operands";
3243   for (auto it : llvm::zip(results, operands)) {
3244     if (std::get<0>(it).getType() != std::get<1>(it).getType())
3245       return op.emitOpError()
3246              << "types mismatch between yield op and its parent";
3247   }
3248 
3249   return success();
3250 }
3251 
3252 //===----------------------------------------------------------------------===//
3253 // AffineVectorLoadOp
3254 //===----------------------------------------------------------------------===//
3255 
3256 void AffineVectorLoadOp::build(OpBuilder &builder, OperationState &result,
3257                                VectorType resultType, AffineMap map,
3258                                ValueRange operands) {
3259   assert(operands.size() == 1 + map.getNumInputs() && "inconsistent operands");
3260   result.addOperands(operands);
3261   if (map)
3262     result.addAttribute(getMapAttrName(), AffineMapAttr::get(map));
3263   result.types.push_back(resultType);
3264 }
3265 
3266 void AffineVectorLoadOp::build(OpBuilder &builder, OperationState &result,
3267                                VectorType resultType, Value memref,
3268                                AffineMap map, ValueRange mapOperands) {
3269   assert(map.getNumInputs() == mapOperands.size() && "inconsistent index info");
3270   result.addOperands(memref);
3271   result.addOperands(mapOperands);
3272   result.addAttribute(getMapAttrName(), AffineMapAttr::get(map));
3273   result.types.push_back(resultType);
3274 }
3275 
3276 void AffineVectorLoadOp::build(OpBuilder &builder, OperationState &result,
3277                                VectorType resultType, Value memref,
3278                                ValueRange indices) {
3279   auto memrefType = memref.getType().cast<MemRefType>();
3280   int64_t rank = memrefType.getRank();
3281   // Create identity map for memrefs with at least one dimension or () -> ()
3282   // for zero-dimensional memrefs.
3283   auto map =
3284       rank ? builder.getMultiDimIdentityMap(rank) : builder.getEmptyAffineMap();
3285   build(builder, result, resultType, memref, map, indices);
3286 }
3287 
3288 void AffineVectorLoadOp::getCanonicalizationPatterns(RewritePatternSet &results,
3289                                                      MLIRContext *context) {
3290   results.add<SimplifyAffineOp<AffineVectorLoadOp>>(context);
3291 }
3292 
3293 static ParseResult parseAffineVectorLoadOp(OpAsmParser &parser,
3294                                            OperationState &result) {
3295   auto &builder = parser.getBuilder();
3296   auto indexTy = builder.getIndexType();
3297 
3298   MemRefType memrefType;
3299   VectorType resultType;
3300   OpAsmParser::OperandType memrefInfo;
3301   AffineMapAttr mapAttr;
3302   SmallVector<OpAsmParser::OperandType, 1> mapOperands;
3303   return failure(
3304       parser.parseOperand(memrefInfo) ||
3305       parser.parseAffineMapOfSSAIds(mapOperands, mapAttr,
3306                                     AffineVectorLoadOp::getMapAttrName(),
3307                                     result.attributes) ||
3308       parser.parseOptionalAttrDict(result.attributes) ||
3309       parser.parseColonType(memrefType) || parser.parseComma() ||
3310       parser.parseType(resultType) ||
3311       parser.resolveOperand(memrefInfo, memrefType, result.operands) ||
3312       parser.resolveOperands(mapOperands, indexTy, result.operands) ||
3313       parser.addTypeToList(resultType, result.types));
3314 }
3315 
3316 static void print(OpAsmPrinter &p, AffineVectorLoadOp op) {
3317   p << "affine.vector_load " << op.getMemRef() << '[';
3318   if (AffineMapAttr mapAttr =
3319           op->getAttrOfType<AffineMapAttr>(op.getMapAttrName()))
3320     p.printAffineMapOfSSAIds(mapAttr, op.getMapOperands());
3321   p << ']';
3322   p.printOptionalAttrDict(op->getAttrs(),
3323                           /*elidedAttrs=*/{op.getMapAttrName()});
3324   p << " : " << op.getMemRefType() << ", " << op.getType();
3325 }
3326 
3327 /// Verify common invariants of affine.vector_load and affine.vector_store.
3328 static LogicalResult verifyVectorMemoryOp(Operation *op, MemRefType memrefType,
3329                                           VectorType vectorType) {
3330   // Check that memref and vector element types match.
3331   if (memrefType.getElementType() != vectorType.getElementType())
3332     return op->emitOpError(
3333         "requires memref and vector types of the same elemental type");
3334   return success();
3335 }
3336 
3337 static LogicalResult verify(AffineVectorLoadOp op) {
3338   MemRefType memrefType = op.getMemRefType();
3339   if (failed(verifyMemoryOpIndexing(
3340           op.getOperation(),
3341           op->getAttrOfType<AffineMapAttr>(op.getMapAttrName()),
3342           op.getMapOperands(), memrefType,
3343           /*numIndexOperands=*/op.getNumOperands() - 1)))
3344     return failure();
3345 
3346   if (failed(verifyVectorMemoryOp(op.getOperation(), memrefType,
3347                                   op.getVectorType())))
3348     return failure();
3349 
3350   return success();
3351 }
3352 
3353 //===----------------------------------------------------------------------===//
3354 // AffineVectorStoreOp
3355 //===----------------------------------------------------------------------===//
3356 
3357 void AffineVectorStoreOp::build(OpBuilder &builder, OperationState &result,
3358                                 Value valueToStore, Value memref, AffineMap map,
3359                                 ValueRange mapOperands) {
3360   assert(map.getNumInputs() == mapOperands.size() && "inconsistent index info");
3361   result.addOperands(valueToStore);
3362   result.addOperands(memref);
3363   result.addOperands(mapOperands);
3364   result.addAttribute(getMapAttrName(), AffineMapAttr::get(map));
3365 }
3366 
3367 // Use identity map.
3368 void AffineVectorStoreOp::build(OpBuilder &builder, OperationState &result,
3369                                 Value valueToStore, Value memref,
3370                                 ValueRange indices) {
3371   auto memrefType = memref.getType().cast<MemRefType>();
3372   int64_t rank = memrefType.getRank();
3373   // Create identity map for memrefs with at least one dimension or () -> ()
3374   // for zero-dimensional memrefs.
3375   auto map =
3376       rank ? builder.getMultiDimIdentityMap(rank) : builder.getEmptyAffineMap();
3377   build(builder, result, valueToStore, memref, map, indices);
3378 }
3379 void AffineVectorStoreOp::getCanonicalizationPatterns(
3380     RewritePatternSet &results, MLIRContext *context) {
3381   results.add<SimplifyAffineOp<AffineVectorStoreOp>>(context);
3382 }
3383 
3384 static ParseResult parseAffineVectorStoreOp(OpAsmParser &parser,
3385                                             OperationState &result) {
3386   auto indexTy = parser.getBuilder().getIndexType();
3387 
3388   MemRefType memrefType;
3389   VectorType resultType;
3390   OpAsmParser::OperandType storeValueInfo;
3391   OpAsmParser::OperandType memrefInfo;
3392   AffineMapAttr mapAttr;
3393   SmallVector<OpAsmParser::OperandType, 1> mapOperands;
3394   return failure(
3395       parser.parseOperand(storeValueInfo) || parser.parseComma() ||
3396       parser.parseOperand(memrefInfo) ||
3397       parser.parseAffineMapOfSSAIds(mapOperands, mapAttr,
3398                                     AffineVectorStoreOp::getMapAttrName(),
3399                                     result.attributes) ||
3400       parser.parseOptionalAttrDict(result.attributes) ||
3401       parser.parseColonType(memrefType) || parser.parseComma() ||
3402       parser.parseType(resultType) ||
3403       parser.resolveOperand(storeValueInfo, resultType, result.operands) ||
3404       parser.resolveOperand(memrefInfo, memrefType, result.operands) ||
3405       parser.resolveOperands(mapOperands, indexTy, result.operands));
3406 }
3407 
3408 static void print(OpAsmPrinter &p, AffineVectorStoreOp op) {
3409   p << "affine.vector_store " << op.getValueToStore();
3410   p << ", " << op.getMemRef() << '[';
3411   if (AffineMapAttr mapAttr =
3412           op->getAttrOfType<AffineMapAttr>(op.getMapAttrName()))
3413     p.printAffineMapOfSSAIds(mapAttr, op.getMapOperands());
3414   p << ']';
3415   p.printOptionalAttrDict(op->getAttrs(),
3416                           /*elidedAttrs=*/{op.getMapAttrName()});
3417   p << " : " << op.getMemRefType() << ", " << op.getValueToStore().getType();
3418 }
3419 
3420 static LogicalResult verify(AffineVectorStoreOp op) {
3421   MemRefType memrefType = op.getMemRefType();
3422   if (failed(verifyMemoryOpIndexing(
3423           op.getOperation(),
3424           op->getAttrOfType<AffineMapAttr>(op.getMapAttrName()),
3425           op.getMapOperands(), memrefType,
3426           /*numIndexOperands=*/op.getNumOperands() - 2)))
3427     return failure();
3428 
3429   if (failed(verifyVectorMemoryOp(op.getOperation(), memrefType,
3430                                   op.getVectorType())))
3431     return failure();
3432 
3433   return success();
3434 }
3435 
3436 //===----------------------------------------------------------------------===//
3437 // TableGen'd op method definitions
3438 //===----------------------------------------------------------------------===//
3439 
3440 #define GET_OP_CLASSES
3441 #include "mlir/Dialect/Affine/IR/AffineOps.cpp.inc"
3442