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