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