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