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