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/StandardOps/IR/Ops.h"
12 #include "mlir/IR/Function.h"
13 #include "mlir/IR/IntegerSet.h"
14 #include "mlir/IR/Matchers.h"
15 #include "mlir/IR/OpImplementation.h"
16 #include "mlir/IR/PatternMatch.h"
17 #include "mlir/Transforms/InliningUtils.h"
18 #include "llvm/ADT/SetVector.h"
19 #include "llvm/ADT/SmallBitVector.h"
20 #include "llvm/Support/Debug.h"
21 
22 using namespace mlir;
23 using llvm::dbgs;
24 
25 #define DEBUG_TYPE "affine-analysis"
26 
27 //===----------------------------------------------------------------------===//
28 // AffineDialect Interfaces
29 //===----------------------------------------------------------------------===//
30 
31 namespace {
32 /// This class defines the interface for handling inlining with affine
33 /// operations.
34 struct AffineInlinerInterface : public DialectInlinerInterface {
35   using DialectInlinerInterface::DialectInlinerInterface;
36 
37   //===--------------------------------------------------------------------===//
38   // Analysis Hooks
39   //===--------------------------------------------------------------------===//
40 
41   /// Returns true if the given region 'src' can be inlined into the region
42   /// 'dest' that is attached to an operation registered to the current dialect.
43   bool isLegalToInline(Region *dest, Region *src,
44                        BlockAndValueMapping &valueMapping) const final {
45     // Conservatively don't allow inlining into affine structures.
46     return false;
47   }
48 
49   /// Returns true if the given operation 'op', that is registered to this
50   /// dialect, can be inlined into the given region, false otherwise.
51   bool isLegalToInline(Operation *op, Region *region,
52                        BlockAndValueMapping &valueMapping) const final {
53     // Always allow inlining affine operations into the top-level region of a
54     // function. There are some edge cases when inlining *into* affine
55     // structures, but that is handled in the other 'isLegalToInline' hook
56     // above.
57     // TODO: We should be able to inline into other regions than functions.
58     return isa<FuncOp>(region->getParentOp());
59   }
60 
61   /// Affine regions should be analyzed recursively.
62   bool shouldAnalyzeRecursively(Operation *op) const final { return true; }
63 };
64 } // end anonymous namespace
65 
66 //===----------------------------------------------------------------------===//
67 // AffineDialect
68 //===----------------------------------------------------------------------===//
69 
70 AffineDialect::AffineDialect(MLIRContext *context)
71     : Dialect(getDialectNamespace(), context) {
72   addOperations<AffineDmaStartOp, AffineDmaWaitOp, AffineLoadOp, AffineStoreOp,
73 #define GET_OP_LIST
74 #include "mlir/Dialect/Affine/IR/AffineOps.cpp.inc"
75                 >();
76   addInterfaces<AffineInlinerInterface>();
77 }
78 
79 /// Materialize a single constant operation from a given attribute value with
80 /// the desired resultant type.
81 Operation *AffineDialect::materializeConstant(OpBuilder &builder,
82                                               Attribute value, Type type,
83                                               Location loc) {
84   return builder.create<ConstantOp>(loc, type, value);
85 }
86 
87 /// A utility function to check if a given region is attached to a function.
88 static bool isFunctionRegion(Region *region) {
89   return llvm::isa<FuncOp>(region->getParentOp());
90 }
91 
92 /// A utility function to check if a value is defined at the top level of a
93 /// function. A value of index type defined at the top level is always a valid
94 /// symbol.
95 bool mlir::isTopLevelValue(Value value) {
96   if (auto arg = value.dyn_cast<BlockArgument>())
97     return isFunctionRegion(arg.getOwner()->getParent());
98   return isFunctionRegion(value.getDefiningOp()->getParentRegion());
99 }
100 
101 // Value can be used as a dimension id if it is valid as a symbol, or
102 // it is an induction variable, or it is a result of affine apply operation
103 // with dimension id arguments.
104 bool mlir::isValidDim(Value value) {
105   // The value must be an index type.
106   if (!value.getType().isIndex())
107     return false;
108 
109   if (auto *op = value.getDefiningOp()) {
110     // Top level operation or constant operation is ok.
111     if (isFunctionRegion(op->getParentRegion()) || isa<ConstantOp>(op))
112       return true;
113     // Affine apply operation is ok if all of its operands are ok.
114     if (auto applyOp = dyn_cast<AffineApplyOp>(op))
115       return applyOp.isValidDim();
116     // The dim op is okay if its operand memref/tensor is defined at the top
117     // level.
118     if (auto dimOp = dyn_cast<DimOp>(op))
119       return isTopLevelValue(dimOp.getOperand());
120     return false;
121   }
122   // This value has to be a block argument of a FuncOp, an 'affine.for', or an
123   // 'affine.parallel'.
124   auto *parentOp = value.cast<BlockArgument>().getOwner()->getParentOp();
125   return isa<FuncOp>(parentOp) || isa<AffineForOp>(parentOp) ||
126          isa<AffineParallelOp>(parentOp);
127 }
128 
129 /// Returns true if the 'index' dimension of the `memref` defined by
130 /// `memrefDefOp` is a statically  shaped one or defined using a valid symbol.
131 template <typename AnyMemRefDefOp>
132 static bool isMemRefSizeValidSymbol(AnyMemRefDefOp memrefDefOp,
133                                     unsigned index) {
134   auto memRefType = memrefDefOp.getType();
135   // Statically shaped.
136   if (!ShapedType::isDynamic(memRefType.getDimSize(index)))
137     return true;
138   // Get the position of the dimension among dynamic dimensions;
139   unsigned dynamicDimPos = memRefType.getDynamicDimIndex(index);
140   return isValidSymbol(
141       *(memrefDefOp.getDynamicSizes().begin() + dynamicDimPos));
142 }
143 
144 /// Returns true if the result of the dim op is a valid symbol.
145 static bool isDimOpValidSymbol(DimOp dimOp) {
146   // The dim op is okay if its operand memref/tensor is defined at the top
147   // level.
148   if (isTopLevelValue(dimOp.getOperand()))
149     return true;
150 
151   // The dim op is also okay if its operand memref/tensor is a view/subview
152   // whose corresponding size is a valid symbol.
153   unsigned index = dimOp.getIndex();
154   if (auto viewOp = dyn_cast<ViewOp>(dimOp.getOperand().getDefiningOp()))
155     return isMemRefSizeValidSymbol<ViewOp>(viewOp, index);
156   if (auto subViewOp = dyn_cast<SubViewOp>(dimOp.getOperand().getDefiningOp()))
157     return isMemRefSizeValidSymbol<SubViewOp>(subViewOp, index);
158   if (auto allocOp = dyn_cast<AllocOp>(dimOp.getOperand().getDefiningOp()))
159     return isMemRefSizeValidSymbol<AllocOp>(allocOp, index);
160   return false;
161 }
162 
163 // Value can be used as a symbol if it is a constant, or it is defined at
164 // the top level, or it is a result of affine apply operation with symbol
165 // arguments, or a result of the dim op on a memref satisfying certain
166 // constraints.
167 bool mlir::isValidSymbol(Value value) {
168   // The value must be an index type.
169   if (!value.getType().isIndex())
170     return false;
171 
172   if (auto *op = value.getDefiningOp()) {
173     // Top level operation or constant operation is ok.
174     if (isFunctionRegion(op->getParentRegion()) || isa<ConstantOp>(op))
175       return true;
176     // Affine apply operation is ok if all of its operands are ok.
177     if (auto applyOp = dyn_cast<AffineApplyOp>(op))
178       return applyOp.isValidSymbol();
179     if (auto dimOp = dyn_cast<DimOp>(op)) {
180       return isDimOpValidSymbol(dimOp);
181     }
182   }
183   // Otherwise, check that the value is a top level value.
184   return isTopLevelValue(value);
185 }
186 
187 // Returns true if 'value' is a valid index to an affine operation (e.g.
188 // affine.load, affine.store, affine.dma_start, affine.dma_wait).
189 // Returns false otherwise.
190 static bool isValidAffineIndexOperand(Value value) {
191   return isValidDim(value) || isValidSymbol(value);
192 }
193 
194 /// Utility function to verify that a set of operands are valid dimension and
195 /// symbol identifiers. The operands should be laid out such that the dimension
196 /// operands are before the symbol operands. This function returns failure if
197 /// there was an invalid operand. An operation is provided to emit any necessary
198 /// errors.
199 template <typename OpTy>
200 static LogicalResult
201 verifyDimAndSymbolIdentifiers(OpTy &op, Operation::operand_range operands,
202                               unsigned numDims) {
203   unsigned opIt = 0;
204   for (auto operand : operands) {
205     if (opIt++ < numDims) {
206       if (!isValidDim(operand))
207         return op.emitOpError("operand cannot be used as a dimension id");
208     } else if (!isValidSymbol(operand)) {
209       return op.emitOpError("operand cannot be used as a symbol");
210     }
211   }
212   return success();
213 }
214 
215 //===----------------------------------------------------------------------===//
216 // AffineApplyOp
217 //===----------------------------------------------------------------------===//
218 
219 AffineValueMap AffineApplyOp::getAffineValueMap() {
220   return AffineValueMap(getAffineMap(), getOperands(), getResult());
221 }
222 
223 static ParseResult parseAffineApplyOp(OpAsmParser &parser,
224                                       OperationState &result) {
225   auto &builder = parser.getBuilder();
226   auto indexTy = builder.getIndexType();
227 
228   AffineMapAttr mapAttr;
229   unsigned numDims;
230   if (parser.parseAttribute(mapAttr, "map", result.attributes) ||
231       parseDimAndSymbolList(parser, result.operands, numDims) ||
232       parser.parseOptionalAttrDict(result.attributes))
233     return failure();
234   auto map = mapAttr.getValue();
235 
236   if (map.getNumDims() != numDims ||
237       numDims + map.getNumSymbols() != result.operands.size()) {
238     return parser.emitError(parser.getNameLoc(),
239                             "dimension or symbol index mismatch");
240   }
241 
242   result.types.append(map.getNumResults(), indexTy);
243   return success();
244 }
245 
246 static void print(OpAsmPrinter &p, AffineApplyOp op) {
247   p << AffineApplyOp::getOperationName() << " " << op.mapAttr();
248   printDimAndSymbolList(op.operand_begin(), op.operand_end(),
249                         op.getAffineMap().getNumDims(), p);
250   p.printOptionalAttrDict(op.getAttrs(), /*elidedAttrs=*/{"map"});
251 }
252 
253 static LogicalResult verify(AffineApplyOp op) {
254   // Check input and output dimensions match.
255   auto map = op.map();
256 
257   // Verify that operand count matches affine map dimension and symbol count.
258   if (op.getNumOperands() != map.getNumDims() + map.getNumSymbols())
259     return op.emitOpError(
260         "operand count and affine map dimension and symbol count must match");
261 
262   // Verify that the map only produces one result.
263   if (map.getNumResults() != 1)
264     return op.emitOpError("mapping must produce one value");
265 
266   return success();
267 }
268 
269 // The result of the affine apply operation can be used as a dimension id if all
270 // its operands are valid dimension ids.
271 bool AffineApplyOp::isValidDim() {
272   return llvm::all_of(getOperands(),
273                       [](Value op) { return mlir::isValidDim(op); });
274 }
275 
276 // The result of the affine apply operation can be used as a symbol if all its
277 // operands are symbols.
278 bool AffineApplyOp::isValidSymbol() {
279   return llvm::all_of(getOperands(),
280                       [](Value op) { return mlir::isValidSymbol(op); });
281 }
282 
283 OpFoldResult AffineApplyOp::fold(ArrayRef<Attribute> operands) {
284   auto map = getAffineMap();
285 
286   // Fold dims and symbols to existing values.
287   auto expr = map.getResult(0);
288   if (auto dim = expr.dyn_cast<AffineDimExpr>())
289     return getOperand(dim.getPosition());
290   if (auto sym = expr.dyn_cast<AffineSymbolExpr>())
291     return getOperand(map.getNumDims() + sym.getPosition());
292 
293   // Otherwise, default to folding the map.
294   SmallVector<Attribute, 1> result;
295   if (failed(map.constantFold(operands, result)))
296     return {};
297   return result[0];
298 }
299 
300 AffineDimExpr AffineApplyNormalizer::renumberOneDim(Value v) {
301   DenseMap<Value, unsigned>::iterator iterPos;
302   bool inserted = false;
303   std::tie(iterPos, inserted) =
304       dimValueToPosition.insert(std::make_pair(v, dimValueToPosition.size()));
305   if (inserted) {
306     reorderedDims.push_back(v);
307   }
308   return getAffineDimExpr(iterPos->second, v.getContext())
309       .cast<AffineDimExpr>();
310 }
311 
312 AffineMap AffineApplyNormalizer::renumber(const AffineApplyNormalizer &other) {
313   SmallVector<AffineExpr, 8> dimRemapping;
314   for (auto v : other.reorderedDims) {
315     auto kvp = other.dimValueToPosition.find(v);
316     if (dimRemapping.size() <= kvp->second)
317       dimRemapping.resize(kvp->second + 1);
318     dimRemapping[kvp->second] = renumberOneDim(kvp->first);
319   }
320   unsigned numSymbols = concatenatedSymbols.size();
321   unsigned numOtherSymbols = other.concatenatedSymbols.size();
322   SmallVector<AffineExpr, 8> symRemapping(numOtherSymbols);
323   for (unsigned idx = 0; idx < numOtherSymbols; ++idx) {
324     symRemapping[idx] =
325         getAffineSymbolExpr(idx + numSymbols, other.affineMap.getContext());
326   }
327   concatenatedSymbols.insert(concatenatedSymbols.end(),
328                              other.concatenatedSymbols.begin(),
329                              other.concatenatedSymbols.end());
330   auto map = other.affineMap;
331   return map.replaceDimsAndSymbols(dimRemapping, symRemapping,
332                                    reorderedDims.size(),
333                                    concatenatedSymbols.size());
334 }
335 
336 // Gather the positions of the operands that are produced by an AffineApplyOp.
337 static llvm::SetVector<unsigned>
338 indicesFromAffineApplyOp(ArrayRef<Value> operands) {
339   llvm::SetVector<unsigned> res;
340   for (auto en : llvm::enumerate(operands))
341     if (isa_and_nonnull<AffineApplyOp>(en.value().getDefiningOp()))
342       res.insert(en.index());
343   return res;
344 }
345 
346 // Support the special case of a symbol coming from an AffineApplyOp that needs
347 // to be composed into the current AffineApplyOp.
348 // This case is handled by rewriting all such symbols into dims for the purpose
349 // of allowing mathematical AffineMap composition.
350 // Returns an AffineMap where symbols that come from an AffineApplyOp have been
351 // rewritten as dims and are ordered after the original dims.
352 // TODO(andydavis,ntv): This promotion makes AffineMap lose track of which
353 // symbols are represented as dims. This loss is static but can still be
354 // recovered dynamically (with `isValidSymbol`). Still this is annoying for the
355 // semi-affine map case. A dynamic canonicalization of all dims that are valid
356 // symbols (a.k.a `canonicalizePromotedSymbols`) into symbols helps and even
357 // results in better simplifications and foldings. But we should evaluate
358 // whether this behavior is what we really want after using more.
359 static AffineMap promoteComposedSymbolsAsDims(AffineMap map,
360                                               ArrayRef<Value> symbols) {
361   if (symbols.empty()) {
362     return map;
363   }
364 
365   // Sanity check on symbols.
366   for (auto sym : symbols) {
367     assert(isValidSymbol(sym) && "Expected only valid symbols");
368     (void)sym;
369   }
370 
371   // Extract the symbol positions that come from an AffineApplyOp and
372   // needs to be rewritten as dims.
373   auto symPositions = indicesFromAffineApplyOp(symbols);
374   if (symPositions.empty()) {
375     return map;
376   }
377 
378   // Create the new map by replacing each symbol at pos by the next new dim.
379   unsigned numDims = map.getNumDims();
380   unsigned numSymbols = map.getNumSymbols();
381   unsigned numNewDims = 0;
382   unsigned numNewSymbols = 0;
383   SmallVector<AffineExpr, 8> symReplacements(numSymbols);
384   for (unsigned i = 0; i < numSymbols; ++i) {
385     symReplacements[i] =
386         symPositions.count(i) > 0
387             ? getAffineDimExpr(numDims + numNewDims++, map.getContext())
388             : getAffineSymbolExpr(numNewSymbols++, map.getContext());
389   }
390   assert(numSymbols >= numNewDims);
391   AffineMap newMap = map.replaceDimsAndSymbols(
392       {}, symReplacements, numDims + numNewDims, numNewSymbols);
393 
394   return newMap;
395 }
396 
397 /// The AffineNormalizer composes AffineApplyOp recursively. Its purpose is to
398 /// keep a correspondence between the mathematical `map` and the `operands` of
399 /// a given AffineApplyOp. This correspondence is maintained by iterating over
400 /// the operands and forming an `auxiliaryMap` that can be composed
401 /// mathematically with `map`. To keep this correspondence in cases where
402 /// symbols are produced by affine.apply operations, we perform a local rewrite
403 /// of symbols as dims.
404 ///
405 /// Rationale for locally rewriting symbols as dims:
406 /// ================================================
407 /// The mathematical composition of AffineMap must always concatenate symbols
408 /// because it does not have enough information to do otherwise. For example,
409 /// composing `(d0)[s0] -> (d0 + s0)` with itself must produce
410 /// `(d0)[s0, s1] -> (d0 + s0 + s1)`.
411 ///
412 /// The result is only equivalent to `(d0)[s0] -> (d0 + 2 * s0)` when
413 /// applied to the same mlir::Value for both s0 and s1.
414 /// As a consequence mathematical composition of AffineMap always concatenates
415 /// symbols.
416 ///
417 /// When AffineMaps are used in AffineApplyOp however, they may specify
418 /// composition via symbols, which is ambiguous mathematically. This corner case
419 /// is handled by locally rewriting such symbols that come from AffineApplyOp
420 /// into dims and composing through dims.
421 /// TODO(andydavis, ntv): Composition via symbols comes at a significant code
422 /// complexity. Alternatively we should investigate whether we want to
423 /// explicitly disallow symbols coming from affine.apply and instead force the
424 /// user to compose symbols beforehand. The annoyances may be small (i.e. 1 or 2
425 /// extra API calls for such uses, which haven't popped up until now) and the
426 /// benefit potentially big: simpler and more maintainable code for a
427 /// non-trivial, recursive, procedure.
428 AffineApplyNormalizer::AffineApplyNormalizer(AffineMap map,
429                                              ArrayRef<Value> operands)
430     : AffineApplyNormalizer() {
431   static_assert(kMaxAffineApplyDepth > 0, "kMaxAffineApplyDepth must be > 0");
432   assert(map.getNumInputs() == operands.size() &&
433          "number of operands does not match the number of map inputs");
434 
435   LLVM_DEBUG(map.print(dbgs() << "\nInput map: "));
436 
437   // Promote symbols that come from an AffineApplyOp to dims by rewriting the
438   // map to always refer to:
439   //   (dims, symbols coming from AffineApplyOp, other symbols).
440   // The order of operands can remain unchanged.
441   // This is a simplification that relies on 2 ordering properties:
442   //   1. rewritten symbols always appear after the original dims in the map;
443   //   2. operands are traversed in order and either dispatched to:
444   //      a. auxiliaryExprs (dims and symbols rewritten as dims);
445   //      b. concatenatedSymbols (all other symbols)
446   // This allows operand order to remain unchanged.
447   unsigned numDimsBeforeRewrite = map.getNumDims();
448   map = promoteComposedSymbolsAsDims(map,
449                                      operands.take_back(map.getNumSymbols()));
450 
451   LLVM_DEBUG(map.print(dbgs() << "\nRewritten map: "));
452 
453   SmallVector<AffineExpr, 8> auxiliaryExprs;
454   bool furtherCompose = (affineApplyDepth() <= kMaxAffineApplyDepth);
455   // We fully spell out the 2 cases below. In this particular instance a little
456   // code duplication greatly improves readability.
457   // Note that the first branch would disappear if we only supported full
458   // composition (i.e. infinite kMaxAffineApplyDepth).
459   if (!furtherCompose) {
460     // 1. Only dispatch dims or symbols.
461     for (auto en : llvm::enumerate(operands)) {
462       auto t = en.value();
463       assert(t.getType().isIndex());
464       bool isDim = (en.index() < map.getNumDims());
465       if (isDim) {
466         // a. The mathematical composition of AffineMap composes dims.
467         auxiliaryExprs.push_back(renumberOneDim(t));
468       } else {
469         // b. The mathematical composition of AffineMap concatenates symbols.
470         //    We do the same for symbol operands.
471         concatenatedSymbols.push_back(t);
472       }
473     }
474   } else {
475     assert(numDimsBeforeRewrite <= operands.size());
476     // 2. Compose AffineApplyOps and dispatch dims or symbols.
477     for (unsigned i = 0, e = operands.size(); i < e; ++i) {
478       auto t = operands[i];
479       auto affineApply = dyn_cast_or_null<AffineApplyOp>(t.getDefiningOp());
480       if (affineApply) {
481         // a. Compose affine.apply operations.
482         LLVM_DEBUG(affineApply.getOperation()->print(
483             dbgs() << "\nCompose AffineApplyOp recursively: "));
484         AffineMap affineApplyMap = affineApply.getAffineMap();
485         SmallVector<Value, 8> affineApplyOperands(
486             affineApply.getOperands().begin(), affineApply.getOperands().end());
487         AffineApplyNormalizer normalizer(affineApplyMap, affineApplyOperands);
488 
489         LLVM_DEBUG(normalizer.affineMap.print(
490             dbgs() << "\nRenumber into current normalizer: "));
491 
492         auto renumberedMap = renumber(normalizer);
493 
494         LLVM_DEBUG(
495             renumberedMap.print(dbgs() << "\nRecursive composition yields: "));
496 
497         auxiliaryExprs.push_back(renumberedMap.getResult(0));
498       } else {
499         if (i < numDimsBeforeRewrite) {
500           // b. The mathematical composition of AffineMap composes dims.
501           auxiliaryExprs.push_back(renumberOneDim(t));
502         } else {
503           // c. The mathematical composition of AffineMap concatenates symbols.
504           //    Note that the map composition will put symbols already present
505           //    in the map before any symbols coming from the auxiliary map, so
506           //    we insert them before any symbols that are due to renumbering,
507           //    and after the proper symbols we have seen already.
508           concatenatedSymbols.insert(
509               std::next(concatenatedSymbols.begin(), numProperSymbols++), t);
510         }
511       }
512     }
513   }
514 
515   // Early exit if `map` is already composed.
516   if (auxiliaryExprs.empty()) {
517     affineMap = map;
518     return;
519   }
520 
521   assert(concatenatedSymbols.size() >= map.getNumSymbols() &&
522          "Unexpected number of concatenated symbols");
523   auto numDims = dimValueToPosition.size();
524   auto numSymbols = concatenatedSymbols.size() - map.getNumSymbols();
525   auto auxiliaryMap = AffineMap::get(numDims, numSymbols, auxiliaryExprs);
526 
527   LLVM_DEBUG(map.print(dbgs() << "\nCompose map: "));
528   LLVM_DEBUG(auxiliaryMap.print(dbgs() << "\nWith map: "));
529   LLVM_DEBUG(map.compose(auxiliaryMap).print(dbgs() << "\nResult: "));
530 
531   // TODO(andydavis,ntv): Disabling simplification results in major speed gains.
532   // Another option is to cache the results as it is expected a lot of redundant
533   // work is performed in practice.
534   affineMap = simplifyAffineMap(map.compose(auxiliaryMap));
535 
536   LLVM_DEBUG(affineMap.print(dbgs() << "\nSimplified result: "));
537   LLVM_DEBUG(dbgs() << "\n");
538 }
539 
540 void AffineApplyNormalizer::normalize(AffineMap *otherMap,
541                                       SmallVectorImpl<Value> *otherOperands) {
542   AffineApplyNormalizer other(*otherMap, *otherOperands);
543   *otherMap = renumber(other);
544 
545   otherOperands->reserve(reorderedDims.size() + concatenatedSymbols.size());
546   otherOperands->assign(reorderedDims.begin(), reorderedDims.end());
547   otherOperands->append(concatenatedSymbols.begin(), concatenatedSymbols.end());
548 }
549 
550 /// Implements `map` and `operands` composition and simplification to support
551 /// `makeComposedAffineApply`. This can be called to achieve the same effects
552 /// on `map` and `operands` without creating an AffineApplyOp that needs to be
553 /// immediately deleted.
554 static void composeAffineMapAndOperands(AffineMap *map,
555                                         SmallVectorImpl<Value> *operands) {
556   AffineApplyNormalizer normalizer(*map, *operands);
557   auto normalizedMap = normalizer.getAffineMap();
558   auto normalizedOperands = normalizer.getOperands();
559   canonicalizeMapAndOperands(&normalizedMap, &normalizedOperands);
560   *map = normalizedMap;
561   *operands = normalizedOperands;
562   assert(*map);
563 }
564 
565 void mlir::fullyComposeAffineMapAndOperands(AffineMap *map,
566                                             SmallVectorImpl<Value> *operands) {
567   while (llvm::any_of(*operands, [](Value v) {
568     return isa_and_nonnull<AffineApplyOp>(v.getDefiningOp());
569   })) {
570     composeAffineMapAndOperands(map, operands);
571   }
572 }
573 
574 AffineApplyOp mlir::makeComposedAffineApply(OpBuilder &b, Location loc,
575                                             AffineMap map,
576                                             ArrayRef<Value> operands) {
577   AffineMap normalizedMap = map;
578   SmallVector<Value, 8> normalizedOperands(operands.begin(), operands.end());
579   composeAffineMapAndOperands(&normalizedMap, &normalizedOperands);
580   assert(normalizedMap);
581   return b.create<AffineApplyOp>(loc, normalizedMap, normalizedOperands);
582 }
583 
584 // A symbol may appear as a dim in affine.apply operations. This function
585 // canonicalizes dims that are valid symbols into actual symbols.
586 template <class MapOrSet>
587 static void canonicalizePromotedSymbols(MapOrSet *mapOrSet,
588                                         SmallVectorImpl<Value> *operands) {
589   if (!mapOrSet || operands->empty())
590     return;
591 
592   assert(mapOrSet->getNumInputs() == operands->size() &&
593          "map/set inputs must match number of operands");
594 
595   auto *context = mapOrSet->getContext();
596   SmallVector<Value, 8> resultOperands;
597   resultOperands.reserve(operands->size());
598   SmallVector<Value, 8> remappedSymbols;
599   remappedSymbols.reserve(operands->size());
600   unsigned nextDim = 0;
601   unsigned nextSym = 0;
602   unsigned oldNumSyms = mapOrSet->getNumSymbols();
603   SmallVector<AffineExpr, 8> dimRemapping(mapOrSet->getNumDims());
604   for (unsigned i = 0, e = mapOrSet->getNumInputs(); i != e; ++i) {
605     if (i < mapOrSet->getNumDims()) {
606       if (isValidSymbol((*operands)[i])) {
607         // This is a valid symbol that appears as a dim, canonicalize it.
608         dimRemapping[i] = getAffineSymbolExpr(oldNumSyms + nextSym++, context);
609         remappedSymbols.push_back((*operands)[i]);
610       } else {
611         dimRemapping[i] = getAffineDimExpr(nextDim++, context);
612         resultOperands.push_back((*operands)[i]);
613       }
614     } else {
615       resultOperands.push_back((*operands)[i]);
616     }
617   }
618 
619   resultOperands.append(remappedSymbols.begin(), remappedSymbols.end());
620   *operands = resultOperands;
621   *mapOrSet = mapOrSet->replaceDimsAndSymbols(dimRemapping, {}, nextDim,
622                                               oldNumSyms + nextSym);
623 
624   assert(mapOrSet->getNumInputs() == operands->size() &&
625          "map/set inputs must match number of operands");
626 }
627 
628 // Works for either an affine map or an integer set.
629 template <class MapOrSet>
630 static void canonicalizeMapOrSetAndOperands(MapOrSet *mapOrSet,
631                                             SmallVectorImpl<Value> *operands) {
632   static_assert(llvm::is_one_of<MapOrSet, AffineMap, IntegerSet>::value,
633                 "Argument must be either of AffineMap or IntegerSet type");
634 
635   if (!mapOrSet || operands->empty())
636     return;
637 
638   assert(mapOrSet->getNumInputs() == operands->size() &&
639          "map/set inputs must match number of operands");
640 
641   canonicalizePromotedSymbols<MapOrSet>(mapOrSet, operands);
642 
643   // Check to see what dims are used.
644   llvm::SmallBitVector usedDims(mapOrSet->getNumDims());
645   llvm::SmallBitVector usedSyms(mapOrSet->getNumSymbols());
646   mapOrSet->walkExprs([&](AffineExpr expr) {
647     if (auto dimExpr = expr.dyn_cast<AffineDimExpr>())
648       usedDims[dimExpr.getPosition()] = true;
649     else if (auto symExpr = expr.dyn_cast<AffineSymbolExpr>())
650       usedSyms[symExpr.getPosition()] = true;
651   });
652 
653   auto *context = mapOrSet->getContext();
654 
655   SmallVector<Value, 8> resultOperands;
656   resultOperands.reserve(operands->size());
657 
658   llvm::SmallDenseMap<Value, AffineExpr, 8> seenDims;
659   SmallVector<AffineExpr, 8> dimRemapping(mapOrSet->getNumDims());
660   unsigned nextDim = 0;
661   for (unsigned i = 0, e = mapOrSet->getNumDims(); i != e; ++i) {
662     if (usedDims[i]) {
663       // Remap dim positions for duplicate operands.
664       auto it = seenDims.find((*operands)[i]);
665       if (it == seenDims.end()) {
666         dimRemapping[i] = getAffineDimExpr(nextDim++, context);
667         resultOperands.push_back((*operands)[i]);
668         seenDims.insert(std::make_pair((*operands)[i], dimRemapping[i]));
669       } else {
670         dimRemapping[i] = it->second;
671       }
672     }
673   }
674   llvm::SmallDenseMap<Value, AffineExpr, 8> seenSymbols;
675   SmallVector<AffineExpr, 8> symRemapping(mapOrSet->getNumSymbols());
676   unsigned nextSym = 0;
677   for (unsigned i = 0, e = mapOrSet->getNumSymbols(); i != e; ++i) {
678     if (!usedSyms[i])
679       continue;
680     // Handle constant operands (only needed for symbolic operands since
681     // constant operands in dimensional positions would have already been
682     // promoted to symbolic positions above).
683     IntegerAttr operandCst;
684     if (matchPattern((*operands)[i + mapOrSet->getNumDims()],
685                      m_Constant(&operandCst))) {
686       symRemapping[i] =
687           getAffineConstantExpr(operandCst.getValue().getSExtValue(), context);
688       continue;
689     }
690     // Remap symbol positions for duplicate operands.
691     auto it = seenSymbols.find((*operands)[i + mapOrSet->getNumDims()]);
692     if (it == seenSymbols.end()) {
693       symRemapping[i] = getAffineSymbolExpr(nextSym++, context);
694       resultOperands.push_back((*operands)[i + mapOrSet->getNumDims()]);
695       seenSymbols.insert(std::make_pair((*operands)[i + mapOrSet->getNumDims()],
696                                         symRemapping[i]));
697     } else {
698       symRemapping[i] = it->second;
699     }
700   }
701   *mapOrSet = mapOrSet->replaceDimsAndSymbols(dimRemapping, symRemapping,
702                                               nextDim, nextSym);
703   *operands = resultOperands;
704 }
705 
706 void mlir::canonicalizeMapAndOperands(AffineMap *map,
707                                       SmallVectorImpl<Value> *operands) {
708   canonicalizeMapOrSetAndOperands<AffineMap>(map, operands);
709 }
710 
711 void mlir::canonicalizeSetAndOperands(IntegerSet *set,
712                                       SmallVectorImpl<Value> *operands) {
713   canonicalizeMapOrSetAndOperands<IntegerSet>(set, operands);
714 }
715 
716 namespace {
717 /// Simplify AffineApply, AffineLoad, and AffineStore operations by composing
718 /// maps that supply results into them.
719 ///
720 template <typename AffineOpTy>
721 struct SimplifyAffineOp : public OpRewritePattern<AffineOpTy> {
722   using OpRewritePattern<AffineOpTy>::OpRewritePattern;
723 
724   /// Replace the affine op with another instance of it with the supplied
725   /// map and mapOperands.
726   void replaceAffineOp(PatternRewriter &rewriter, AffineOpTy affineOp,
727                        AffineMap map, ArrayRef<Value> mapOperands) const;
728 
729   LogicalResult matchAndRewrite(AffineOpTy affineOp,
730                                 PatternRewriter &rewriter) const override {
731     static_assert(llvm::is_one_of<AffineOpTy, AffineLoadOp, AffinePrefetchOp,
732                                   AffineStoreOp, AffineApplyOp, AffineMinOp,
733                                   AffineMaxOp>::value,
734                   "affine load/store/apply/prefetch/min/max op expected");
735     auto map = affineOp.getAffineMap();
736     AffineMap oldMap = map;
737     auto oldOperands = affineOp.getMapOperands();
738     SmallVector<Value, 8> resultOperands(oldOperands);
739     composeAffineMapAndOperands(&map, &resultOperands);
740     if (map == oldMap && std::equal(oldOperands.begin(), oldOperands.end(),
741                                     resultOperands.begin()))
742       return failure();
743 
744     replaceAffineOp(rewriter, affineOp, map, resultOperands);
745     return success();
746   }
747 };
748 
749 // Specialize the template to account for the different build signatures for
750 // affine load, store, and apply ops.
751 template <>
752 void SimplifyAffineOp<AffineLoadOp>::replaceAffineOp(
753     PatternRewriter &rewriter, AffineLoadOp load, AffineMap map,
754     ArrayRef<Value> mapOperands) const {
755   rewriter.replaceOpWithNewOp<AffineLoadOp>(load, load.getMemRef(), map,
756                                             mapOperands);
757 }
758 template <>
759 void SimplifyAffineOp<AffinePrefetchOp>::replaceAffineOp(
760     PatternRewriter &rewriter, AffinePrefetchOp prefetch, AffineMap map,
761     ArrayRef<Value> mapOperands) const {
762   rewriter.replaceOpWithNewOp<AffinePrefetchOp>(
763       prefetch, prefetch.memref(), map, mapOperands,
764       prefetch.localityHint().getZExtValue(), prefetch.isWrite(),
765       prefetch.isDataCache());
766 }
767 template <>
768 void SimplifyAffineOp<AffineStoreOp>::replaceAffineOp(
769     PatternRewriter &rewriter, AffineStoreOp store, AffineMap map,
770     ArrayRef<Value> mapOperands) const {
771   rewriter.replaceOpWithNewOp<AffineStoreOp>(
772       store, store.getValueToStore(), store.getMemRef(), map, mapOperands);
773 }
774 
775 // Generic version for ops that don't have extra operands.
776 template <typename AffineOpTy>
777 void SimplifyAffineOp<AffineOpTy>::replaceAffineOp(
778     PatternRewriter &rewriter, AffineOpTy op, AffineMap map,
779     ArrayRef<Value> mapOperands) const {
780   rewriter.replaceOpWithNewOp<AffineOpTy>(op, map, mapOperands);
781 }
782 } // end anonymous namespace.
783 
784 void AffineApplyOp::getCanonicalizationPatterns(
785     OwningRewritePatternList &results, MLIRContext *context) {
786   results.insert<SimplifyAffineOp<AffineApplyOp>>(context);
787 }
788 
789 //===----------------------------------------------------------------------===//
790 // Common canonicalization pattern support logic
791 //===----------------------------------------------------------------------===//
792 
793 /// This is a common class used for patterns of the form
794 /// "someop(memrefcast) -> someop".  It folds the source of any memref_cast
795 /// into the root operation directly.
796 static LogicalResult foldMemRefCast(Operation *op) {
797   bool folded = false;
798   for (OpOperand &operand : op->getOpOperands()) {
799     auto cast = dyn_cast_or_null<MemRefCastOp>(operand.get().getDefiningOp());
800     if (cast && !cast.getOperand().getType().isa<UnrankedMemRefType>()) {
801       operand.set(cast.getOperand());
802       folded = true;
803     }
804   }
805   return success(folded);
806 }
807 
808 //===----------------------------------------------------------------------===//
809 // AffineDmaStartOp
810 //===----------------------------------------------------------------------===//
811 
812 // TODO(b/133776335) Check that map operands are loop IVs or symbols.
813 void AffineDmaStartOp::build(Builder *builder, OperationState &result,
814                              Value srcMemRef, AffineMap srcMap,
815                              ValueRange srcIndices, Value destMemRef,
816                              AffineMap dstMap, ValueRange destIndices,
817                              Value tagMemRef, AffineMap tagMap,
818                              ValueRange tagIndices, Value numElements,
819                              Value stride, Value elementsPerStride) {
820   result.addOperands(srcMemRef);
821   result.addAttribute(getSrcMapAttrName(), AffineMapAttr::get(srcMap));
822   result.addOperands(srcIndices);
823   result.addOperands(destMemRef);
824   result.addAttribute(getDstMapAttrName(), AffineMapAttr::get(dstMap));
825   result.addOperands(destIndices);
826   result.addOperands(tagMemRef);
827   result.addAttribute(getTagMapAttrName(), AffineMapAttr::get(tagMap));
828   result.addOperands(tagIndices);
829   result.addOperands(numElements);
830   if (stride) {
831     result.addOperands({stride, elementsPerStride});
832   }
833 }
834 
835 void AffineDmaStartOp::print(OpAsmPrinter &p) {
836   p << "affine.dma_start " << getSrcMemRef() << '[';
837   p.printAffineMapOfSSAIds(getSrcMapAttr(), getSrcIndices());
838   p << "], " << getDstMemRef() << '[';
839   p.printAffineMapOfSSAIds(getDstMapAttr(), getDstIndices());
840   p << "], " << getTagMemRef() << '[';
841   p.printAffineMapOfSSAIds(getTagMapAttr(), getTagIndices());
842   p << "], " << getNumElements();
843   if (isStrided()) {
844     p << ", " << getStride();
845     p << ", " << getNumElementsPerStride();
846   }
847   p << " : " << getSrcMemRefType() << ", " << getDstMemRefType() << ", "
848     << getTagMemRefType();
849 }
850 
851 // Parse AffineDmaStartOp.
852 // Ex:
853 //   affine.dma_start %src[%i, %j], %dst[%k, %l], %tag[%index], %size,
854 //     %stride, %num_elt_per_stride
855 //       : memref<3076 x f32, 0>, memref<1024 x f32, 2>, memref<1 x i32>
856 //
857 ParseResult AffineDmaStartOp::parse(OpAsmParser &parser,
858                                     OperationState &result) {
859   OpAsmParser::OperandType srcMemRefInfo;
860   AffineMapAttr srcMapAttr;
861   SmallVector<OpAsmParser::OperandType, 4> srcMapOperands;
862   OpAsmParser::OperandType dstMemRefInfo;
863   AffineMapAttr dstMapAttr;
864   SmallVector<OpAsmParser::OperandType, 4> dstMapOperands;
865   OpAsmParser::OperandType tagMemRefInfo;
866   AffineMapAttr tagMapAttr;
867   SmallVector<OpAsmParser::OperandType, 4> tagMapOperands;
868   OpAsmParser::OperandType numElementsInfo;
869   SmallVector<OpAsmParser::OperandType, 2> strideInfo;
870 
871   SmallVector<Type, 3> types;
872   auto indexType = parser.getBuilder().getIndexType();
873 
874   // Parse and resolve the following list of operands:
875   // *) dst memref followed by its affine maps operands (in square brackets).
876   // *) src memref followed by its affine map operands (in square brackets).
877   // *) tag memref followed by its affine map operands (in square brackets).
878   // *) number of elements transferred by DMA operation.
879   if (parser.parseOperand(srcMemRefInfo) ||
880       parser.parseAffineMapOfSSAIds(srcMapOperands, srcMapAttr,
881                                     getSrcMapAttrName(), result.attributes) ||
882       parser.parseComma() || parser.parseOperand(dstMemRefInfo) ||
883       parser.parseAffineMapOfSSAIds(dstMapOperands, dstMapAttr,
884                                     getDstMapAttrName(), result.attributes) ||
885       parser.parseComma() || parser.parseOperand(tagMemRefInfo) ||
886       parser.parseAffineMapOfSSAIds(tagMapOperands, tagMapAttr,
887                                     getTagMapAttrName(), result.attributes) ||
888       parser.parseComma() || parser.parseOperand(numElementsInfo))
889     return failure();
890 
891   // Parse optional stride and elements per stride.
892   if (parser.parseTrailingOperandList(strideInfo)) {
893     return failure();
894   }
895   if (!strideInfo.empty() && strideInfo.size() != 2) {
896     return parser.emitError(parser.getNameLoc(),
897                             "expected two stride related operands");
898   }
899   bool isStrided = strideInfo.size() == 2;
900 
901   if (parser.parseColonTypeList(types))
902     return failure();
903 
904   if (types.size() != 3)
905     return parser.emitError(parser.getNameLoc(), "expected three types");
906 
907   if (parser.resolveOperand(srcMemRefInfo, types[0], result.operands) ||
908       parser.resolveOperands(srcMapOperands, indexType, result.operands) ||
909       parser.resolveOperand(dstMemRefInfo, types[1], result.operands) ||
910       parser.resolveOperands(dstMapOperands, indexType, result.operands) ||
911       parser.resolveOperand(tagMemRefInfo, types[2], result.operands) ||
912       parser.resolveOperands(tagMapOperands, indexType, result.operands) ||
913       parser.resolveOperand(numElementsInfo, indexType, result.operands))
914     return failure();
915 
916   if (isStrided) {
917     if (parser.resolveOperands(strideInfo, indexType, result.operands))
918       return failure();
919   }
920 
921   // Check that src/dst/tag operand counts match their map.numInputs.
922   if (srcMapOperands.size() != srcMapAttr.getValue().getNumInputs() ||
923       dstMapOperands.size() != dstMapAttr.getValue().getNumInputs() ||
924       tagMapOperands.size() != tagMapAttr.getValue().getNumInputs())
925     return parser.emitError(parser.getNameLoc(),
926                             "memref operand count not equal to map.numInputs");
927   return success();
928 }
929 
930 LogicalResult AffineDmaStartOp::verify() {
931   if (!getOperand(getSrcMemRefOperandIndex()).getType().isa<MemRefType>())
932     return emitOpError("expected DMA source to be of memref type");
933   if (!getOperand(getDstMemRefOperandIndex()).getType().isa<MemRefType>())
934     return emitOpError("expected DMA destination to be of memref type");
935   if (!getOperand(getTagMemRefOperandIndex()).getType().isa<MemRefType>())
936     return emitOpError("expected DMA tag to be of memref type");
937 
938   // DMAs from different memory spaces supported.
939   if (getSrcMemorySpace() == getDstMemorySpace()) {
940     return emitOpError("DMA should be between different memory spaces");
941   }
942   unsigned numInputsAllMaps = getSrcMap().getNumInputs() +
943                               getDstMap().getNumInputs() +
944                               getTagMap().getNumInputs();
945   if (getNumOperands() != numInputsAllMaps + 3 + 1 &&
946       getNumOperands() != numInputsAllMaps + 3 + 1 + 2) {
947     return emitOpError("incorrect number of operands");
948   }
949 
950   for (auto idx : getSrcIndices()) {
951     if (!idx.getType().isIndex())
952       return emitOpError("src index to dma_start must have 'index' type");
953     if (!isValidAffineIndexOperand(idx))
954       return emitOpError("src index must be a dimension or symbol identifier");
955   }
956   for (auto idx : getDstIndices()) {
957     if (!idx.getType().isIndex())
958       return emitOpError("dst index to dma_start must have 'index' type");
959     if (!isValidAffineIndexOperand(idx))
960       return emitOpError("dst index must be a dimension or symbol identifier");
961   }
962   for (auto idx : getTagIndices()) {
963     if (!idx.getType().isIndex())
964       return emitOpError("tag index to dma_start must have 'index' type");
965     if (!isValidAffineIndexOperand(idx))
966       return emitOpError("tag index must be a dimension or symbol identifier");
967   }
968   return success();
969 }
970 
971 LogicalResult AffineDmaStartOp::fold(ArrayRef<Attribute> cstOperands,
972                                      SmallVectorImpl<OpFoldResult> &results) {
973   /// dma_start(memrefcast) -> dma_start
974   return foldMemRefCast(*this);
975 }
976 
977 //===----------------------------------------------------------------------===//
978 // AffineDmaWaitOp
979 //===----------------------------------------------------------------------===//
980 
981 // TODO(b/133776335) Check that map operands are loop IVs or symbols.
982 void AffineDmaWaitOp::build(Builder *builder, OperationState &result,
983                             Value tagMemRef, AffineMap tagMap,
984                             ValueRange tagIndices, Value numElements) {
985   result.addOperands(tagMemRef);
986   result.addAttribute(getTagMapAttrName(), AffineMapAttr::get(tagMap));
987   result.addOperands(tagIndices);
988   result.addOperands(numElements);
989 }
990 
991 void AffineDmaWaitOp::print(OpAsmPrinter &p) {
992   p << "affine.dma_wait " << getTagMemRef() << '[';
993   SmallVector<Value, 2> operands(getTagIndices());
994   p.printAffineMapOfSSAIds(getTagMapAttr(), operands);
995   p << "], ";
996   p.printOperand(getNumElements());
997   p << " : " << getTagMemRef().getType();
998 }
999 
1000 // Parse AffineDmaWaitOp.
1001 // Eg:
1002 //   affine.dma_wait %tag[%index], %num_elements
1003 //     : memref<1 x i32, (d0) -> (d0), 4>
1004 //
1005 ParseResult AffineDmaWaitOp::parse(OpAsmParser &parser,
1006                                    OperationState &result) {
1007   OpAsmParser::OperandType tagMemRefInfo;
1008   AffineMapAttr tagMapAttr;
1009   SmallVector<OpAsmParser::OperandType, 2> tagMapOperands;
1010   Type type;
1011   auto indexType = parser.getBuilder().getIndexType();
1012   OpAsmParser::OperandType numElementsInfo;
1013 
1014   // Parse tag memref, its map operands, and dma size.
1015   if (parser.parseOperand(tagMemRefInfo) ||
1016       parser.parseAffineMapOfSSAIds(tagMapOperands, tagMapAttr,
1017                                     getTagMapAttrName(), result.attributes) ||
1018       parser.parseComma() || parser.parseOperand(numElementsInfo) ||
1019       parser.parseColonType(type) ||
1020       parser.resolveOperand(tagMemRefInfo, type, result.operands) ||
1021       parser.resolveOperands(tagMapOperands, indexType, result.operands) ||
1022       parser.resolveOperand(numElementsInfo, indexType, result.operands))
1023     return failure();
1024 
1025   if (!type.isa<MemRefType>())
1026     return parser.emitError(parser.getNameLoc(),
1027                             "expected tag to be of memref type");
1028 
1029   if (tagMapOperands.size() != tagMapAttr.getValue().getNumInputs())
1030     return parser.emitError(parser.getNameLoc(),
1031                             "tag memref operand count != to map.numInputs");
1032   return success();
1033 }
1034 
1035 LogicalResult AffineDmaWaitOp::verify() {
1036   if (!getOperand(0).getType().isa<MemRefType>())
1037     return emitOpError("expected DMA tag to be of memref type");
1038   for (auto idx : getTagIndices()) {
1039     if (!idx.getType().isIndex())
1040       return emitOpError("index to dma_wait must have 'index' type");
1041     if (!isValidAffineIndexOperand(idx))
1042       return emitOpError("index must be a dimension or symbol identifier");
1043   }
1044   return success();
1045 }
1046 
1047 LogicalResult AffineDmaWaitOp::fold(ArrayRef<Attribute> cstOperands,
1048                                     SmallVectorImpl<OpFoldResult> &results) {
1049   /// dma_wait(memrefcast) -> dma_wait
1050   return foldMemRefCast(*this);
1051 }
1052 
1053 //===----------------------------------------------------------------------===//
1054 // AffineForOp
1055 //===----------------------------------------------------------------------===//
1056 
1057 void AffineForOp::build(Builder *builder, OperationState &result,
1058                         ValueRange lbOperands, AffineMap lbMap,
1059                         ValueRange ubOperands, AffineMap ubMap, int64_t step) {
1060   assert(((!lbMap && lbOperands.empty()) ||
1061           lbOperands.size() == lbMap.getNumInputs()) &&
1062          "lower bound operand count does not match the affine map");
1063   assert(((!ubMap && ubOperands.empty()) ||
1064           ubOperands.size() == ubMap.getNumInputs()) &&
1065          "upper bound operand count does not match the affine map");
1066   assert(step > 0 && "step has to be a positive integer constant");
1067 
1068   // Add an attribute for the step.
1069   result.addAttribute(getStepAttrName(),
1070                       builder->getIntegerAttr(builder->getIndexType(), step));
1071 
1072   // Add the lower bound.
1073   result.addAttribute(getLowerBoundAttrName(), AffineMapAttr::get(lbMap));
1074   result.addOperands(lbOperands);
1075 
1076   // Add the upper bound.
1077   result.addAttribute(getUpperBoundAttrName(), AffineMapAttr::get(ubMap));
1078   result.addOperands(ubOperands);
1079 
1080   // Create a region and a block for the body.  The argument of the region is
1081   // the loop induction variable.
1082   Region *bodyRegion = result.addRegion();
1083   Block *body = new Block();
1084   body->addArgument(IndexType::get(builder->getContext()));
1085   bodyRegion->push_back(body);
1086   ensureTerminator(*bodyRegion, *builder, result.location);
1087 
1088   // Set the operands list as resizable so that we can freely modify the bounds.
1089   result.setOperandListToResizable();
1090 }
1091 
1092 void AffineForOp::build(Builder *builder, OperationState &result, int64_t lb,
1093                         int64_t ub, int64_t step) {
1094   auto lbMap = AffineMap::getConstantMap(lb, builder->getContext());
1095   auto ubMap = AffineMap::getConstantMap(ub, builder->getContext());
1096   return build(builder, result, {}, lbMap, {}, ubMap, step);
1097 }
1098 
1099 static LogicalResult verify(AffineForOp op) {
1100   // Check that the body defines as single block argument for the induction
1101   // variable.
1102   auto *body = op.getBody();
1103   if (body->getNumArguments() != 1 || !body->getArgument(0).getType().isIndex())
1104     return op.emitOpError(
1105         "expected body to have a single index argument for the "
1106         "induction variable");
1107 
1108   // Verify that there are enough operands for the bounds.
1109   AffineMap lowerBoundMap = op.getLowerBoundMap(),
1110             upperBoundMap = op.getUpperBoundMap();
1111   if (op.getNumOperands() !=
1112       (lowerBoundMap.getNumInputs() + upperBoundMap.getNumInputs()))
1113     return op.emitOpError(
1114         "operand count must match with affine map dimension and symbol count");
1115 
1116   // Verify that the bound operands are valid dimension/symbols.
1117   /// Lower bound.
1118   if (failed(verifyDimAndSymbolIdentifiers(op, op.getLowerBoundOperands(),
1119                                            op.getLowerBoundMap().getNumDims())))
1120     return failure();
1121   /// Upper bound.
1122   if (failed(verifyDimAndSymbolIdentifiers(op, op.getUpperBoundOperands(),
1123                                            op.getUpperBoundMap().getNumDims())))
1124     return failure();
1125   return success();
1126 }
1127 
1128 /// Parse a for operation loop bounds.
1129 static ParseResult parseBound(bool isLower, OperationState &result,
1130                               OpAsmParser &p) {
1131   // 'min' / 'max' prefixes are generally syntactic sugar, but are required if
1132   // the map has multiple results.
1133   bool failedToParsedMinMax =
1134       failed(p.parseOptionalKeyword(isLower ? "max" : "min"));
1135 
1136   auto &builder = p.getBuilder();
1137   auto boundAttrName = isLower ? AffineForOp::getLowerBoundAttrName()
1138                                : AffineForOp::getUpperBoundAttrName();
1139 
1140   // Parse ssa-id as identity map.
1141   SmallVector<OpAsmParser::OperandType, 1> boundOpInfos;
1142   if (p.parseOperandList(boundOpInfos))
1143     return failure();
1144 
1145   if (!boundOpInfos.empty()) {
1146     // Check that only one operand was parsed.
1147     if (boundOpInfos.size() > 1)
1148       return p.emitError(p.getNameLoc(),
1149                          "expected only one loop bound operand");
1150 
1151     // TODO: improve error message when SSA value is not of index type.
1152     // Currently it is 'use of value ... expects different type than prior uses'
1153     if (p.resolveOperand(boundOpInfos.front(), builder.getIndexType(),
1154                          result.operands))
1155       return failure();
1156 
1157     // Create an identity map using symbol id. This representation is optimized
1158     // for storage. Analysis passes may expand it into a multi-dimensional map
1159     // if desired.
1160     AffineMap map = builder.getSymbolIdentityMap();
1161     result.addAttribute(boundAttrName, AffineMapAttr::get(map));
1162     return success();
1163   }
1164 
1165   // Get the attribute location.
1166   llvm::SMLoc attrLoc = p.getCurrentLocation();
1167 
1168   Attribute boundAttr;
1169   if (p.parseAttribute(boundAttr, builder.getIndexType(), boundAttrName,
1170                        result.attributes))
1171     return failure();
1172 
1173   // Parse full form - affine map followed by dim and symbol list.
1174   if (auto affineMapAttr = boundAttr.dyn_cast<AffineMapAttr>()) {
1175     unsigned currentNumOperands = result.operands.size();
1176     unsigned numDims;
1177     if (parseDimAndSymbolList(p, result.operands, numDims))
1178       return failure();
1179 
1180     auto map = affineMapAttr.getValue();
1181     if (map.getNumDims() != numDims)
1182       return p.emitError(
1183           p.getNameLoc(),
1184           "dim operand count and affine map dim count must match");
1185 
1186     unsigned numDimAndSymbolOperands =
1187         result.operands.size() - currentNumOperands;
1188     if (numDims + map.getNumSymbols() != numDimAndSymbolOperands)
1189       return p.emitError(
1190           p.getNameLoc(),
1191           "symbol operand count and affine map symbol count must match");
1192 
1193     // If the map has multiple results, make sure that we parsed the min/max
1194     // prefix.
1195     if (map.getNumResults() > 1 && failedToParsedMinMax) {
1196       if (isLower) {
1197         return p.emitError(attrLoc, "lower loop bound affine map with "
1198                                     "multiple results requires 'max' prefix");
1199       }
1200       return p.emitError(attrLoc, "upper loop bound affine map with multiple "
1201                                   "results requires 'min' prefix");
1202     }
1203     return success();
1204   }
1205 
1206   // Parse custom assembly form.
1207   if (auto integerAttr = boundAttr.dyn_cast<IntegerAttr>()) {
1208     result.attributes.pop_back();
1209     result.addAttribute(
1210         boundAttrName,
1211         AffineMapAttr::get(builder.getConstantAffineMap(integerAttr.getInt())));
1212     return success();
1213   }
1214 
1215   return p.emitError(
1216       p.getNameLoc(),
1217       "expected valid affine map representation for loop bounds");
1218 }
1219 
1220 static ParseResult parseAffineForOp(OpAsmParser &parser,
1221                                     OperationState &result) {
1222   auto &builder = parser.getBuilder();
1223   OpAsmParser::OperandType inductionVariable;
1224   // Parse the induction variable followed by '='.
1225   if (parser.parseRegionArgument(inductionVariable) || parser.parseEqual())
1226     return failure();
1227 
1228   // Parse loop bounds.
1229   if (parseBound(/*isLower=*/true, result, parser) ||
1230       parser.parseKeyword("to", " between bounds") ||
1231       parseBound(/*isLower=*/false, result, parser))
1232     return failure();
1233 
1234   // Parse the optional loop step, we default to 1 if one is not present.
1235   if (parser.parseOptionalKeyword("step")) {
1236     result.addAttribute(
1237         AffineForOp::getStepAttrName(),
1238         builder.getIntegerAttr(builder.getIndexType(), /*value=*/1));
1239   } else {
1240     llvm::SMLoc stepLoc = parser.getCurrentLocation();
1241     IntegerAttr stepAttr;
1242     if (parser.parseAttribute(stepAttr, builder.getIndexType(),
1243                               AffineForOp::getStepAttrName().data(),
1244                               result.attributes))
1245       return failure();
1246 
1247     if (stepAttr.getValue().getSExtValue() < 0)
1248       return parser.emitError(
1249           stepLoc,
1250           "expected step to be representable as a positive signed integer");
1251   }
1252 
1253   // Parse the body region.
1254   Region *body = result.addRegion();
1255   if (parser.parseRegion(*body, inductionVariable, builder.getIndexType()))
1256     return failure();
1257 
1258   AffineForOp::ensureTerminator(*body, builder, result.location);
1259 
1260   // Parse the optional attribute list.
1261   if (parser.parseOptionalAttrDict(result.attributes))
1262     return failure();
1263 
1264   // Set the operands list as resizable so that we can freely modify the bounds.
1265   result.setOperandListToResizable();
1266   return success();
1267 }
1268 
1269 static void printBound(AffineMapAttr boundMap,
1270                        Operation::operand_range boundOperands,
1271                        const char *prefix, OpAsmPrinter &p) {
1272   AffineMap map = boundMap.getValue();
1273 
1274   // Check if this bound should be printed using custom assembly form.
1275   // The decision to restrict printing custom assembly form to trivial cases
1276   // comes from the will to roundtrip MLIR binary -> text -> binary in a
1277   // lossless way.
1278   // Therefore, custom assembly form parsing and printing is only supported for
1279   // zero-operand constant maps and single symbol operand identity maps.
1280   if (map.getNumResults() == 1) {
1281     AffineExpr expr = map.getResult(0);
1282 
1283     // Print constant bound.
1284     if (map.getNumDims() == 0 && map.getNumSymbols() == 0) {
1285       if (auto constExpr = expr.dyn_cast<AffineConstantExpr>()) {
1286         p << constExpr.getValue();
1287         return;
1288       }
1289     }
1290 
1291     // Print bound that consists of a single SSA symbol if the map is over a
1292     // single symbol.
1293     if (map.getNumDims() == 0 && map.getNumSymbols() == 1) {
1294       if (auto symExpr = expr.dyn_cast<AffineSymbolExpr>()) {
1295         p.printOperand(*boundOperands.begin());
1296         return;
1297       }
1298     }
1299   } else {
1300     // Map has multiple results. Print 'min' or 'max' prefix.
1301     p << prefix << ' ';
1302   }
1303 
1304   // Print the map and its operands.
1305   p << boundMap;
1306   printDimAndSymbolList(boundOperands.begin(), boundOperands.end(),
1307                         map.getNumDims(), p);
1308 }
1309 
1310 static void print(OpAsmPrinter &p, AffineForOp op) {
1311   p << op.getOperationName() << ' ';
1312   p.printOperand(op.getBody()->getArgument(0));
1313   p << " = ";
1314   printBound(op.getLowerBoundMapAttr(), op.getLowerBoundOperands(), "max", p);
1315   p << " to ";
1316   printBound(op.getUpperBoundMapAttr(), op.getUpperBoundOperands(), "min", p);
1317 
1318   if (op.getStep() != 1)
1319     p << " step " << op.getStep();
1320   p.printRegion(op.region(),
1321                 /*printEntryBlockArgs=*/false,
1322                 /*printBlockTerminators=*/false);
1323   p.printOptionalAttrDict(op.getAttrs(),
1324                           /*elidedAttrs=*/{op.getLowerBoundAttrName(),
1325                                            op.getUpperBoundAttrName(),
1326                                            op.getStepAttrName()});
1327 }
1328 
1329 /// Fold the constant bounds of a loop.
1330 static LogicalResult foldLoopBounds(AffineForOp forOp) {
1331   auto foldLowerOrUpperBound = [&forOp](bool lower) {
1332     // Check to see if each of the operands is the result of a constant.  If
1333     // so, get the value.  If not, ignore it.
1334     SmallVector<Attribute, 8> operandConstants;
1335     auto boundOperands =
1336         lower ? forOp.getLowerBoundOperands() : forOp.getUpperBoundOperands();
1337     for (auto operand : boundOperands) {
1338       Attribute operandCst;
1339       matchPattern(operand, m_Constant(&operandCst));
1340       operandConstants.push_back(operandCst);
1341     }
1342 
1343     AffineMap boundMap =
1344         lower ? forOp.getLowerBoundMap() : forOp.getUpperBoundMap();
1345     assert(boundMap.getNumResults() >= 1 &&
1346            "bound maps should have at least one result");
1347     SmallVector<Attribute, 4> foldedResults;
1348     if (failed(boundMap.constantFold(operandConstants, foldedResults)))
1349       return failure();
1350 
1351     // Compute the max or min as applicable over the results.
1352     assert(!foldedResults.empty() && "bounds should have at least one result");
1353     auto maxOrMin = foldedResults[0].cast<IntegerAttr>().getValue();
1354     for (unsigned i = 1, e = foldedResults.size(); i < e; i++) {
1355       auto foldedResult = foldedResults[i].cast<IntegerAttr>().getValue();
1356       maxOrMin = lower ? llvm::APIntOps::smax(maxOrMin, foldedResult)
1357                        : llvm::APIntOps::smin(maxOrMin, foldedResult);
1358     }
1359     lower ? forOp.setConstantLowerBound(maxOrMin.getSExtValue())
1360           : forOp.setConstantUpperBound(maxOrMin.getSExtValue());
1361     return success();
1362   };
1363 
1364   // Try to fold the lower bound.
1365   bool folded = false;
1366   if (!forOp.hasConstantLowerBound())
1367     folded |= succeeded(foldLowerOrUpperBound(/*lower=*/true));
1368 
1369   // Try to fold the upper bound.
1370   if (!forOp.hasConstantUpperBound())
1371     folded |= succeeded(foldLowerOrUpperBound(/*lower=*/false));
1372   return success(folded);
1373 }
1374 
1375 /// Canonicalize the bounds of the given loop.
1376 static LogicalResult canonicalizeLoopBounds(AffineForOp forOp) {
1377   SmallVector<Value, 4> lbOperands(forOp.getLowerBoundOperands());
1378   SmallVector<Value, 4> ubOperands(forOp.getUpperBoundOperands());
1379 
1380   auto lbMap = forOp.getLowerBoundMap();
1381   auto ubMap = forOp.getUpperBoundMap();
1382   auto prevLbMap = lbMap;
1383   auto prevUbMap = ubMap;
1384 
1385   canonicalizeMapAndOperands(&lbMap, &lbOperands);
1386   lbMap = removeDuplicateExprs(lbMap);
1387 
1388   canonicalizeMapAndOperands(&ubMap, &ubOperands);
1389   ubMap = removeDuplicateExprs(ubMap);
1390 
1391   // Any canonicalization change always leads to updated map(s).
1392   if (lbMap == prevLbMap && ubMap == prevUbMap)
1393     return failure();
1394 
1395   if (lbMap != prevLbMap)
1396     forOp.setLowerBound(lbOperands, lbMap);
1397   if (ubMap != prevUbMap)
1398     forOp.setUpperBound(ubOperands, ubMap);
1399   return success();
1400 }
1401 
1402 namespace {
1403 /// This is a pattern to fold trivially empty loops.
1404 struct AffineForEmptyLoopFolder : public OpRewritePattern<AffineForOp> {
1405   using OpRewritePattern<AffineForOp>::OpRewritePattern;
1406 
1407   LogicalResult matchAndRewrite(AffineForOp forOp,
1408                                 PatternRewriter &rewriter) const override {
1409     // Check that the body only contains a terminator.
1410     if (!has_single_element(*forOp.getBody()))
1411       return failure();
1412     rewriter.eraseOp(forOp);
1413     return success();
1414   }
1415 };
1416 } // end anonymous namespace
1417 
1418 void AffineForOp::getCanonicalizationPatterns(OwningRewritePatternList &results,
1419                                               MLIRContext *context) {
1420   results.insert<AffineForEmptyLoopFolder>(context);
1421 }
1422 
1423 LogicalResult AffineForOp::fold(ArrayRef<Attribute> operands,
1424                                 SmallVectorImpl<OpFoldResult> &results) {
1425   bool folded = succeeded(foldLoopBounds(*this));
1426   folded |= succeeded(canonicalizeLoopBounds(*this));
1427   return success(folded);
1428 }
1429 
1430 AffineBound AffineForOp::getLowerBound() {
1431   auto lbMap = getLowerBoundMap();
1432   return AffineBound(AffineForOp(*this), 0, lbMap.getNumInputs(), lbMap);
1433 }
1434 
1435 AffineBound AffineForOp::getUpperBound() {
1436   auto lbMap = getLowerBoundMap();
1437   auto ubMap = getUpperBoundMap();
1438   return AffineBound(AffineForOp(*this), lbMap.getNumInputs(), getNumOperands(),
1439                      ubMap);
1440 }
1441 
1442 void AffineForOp::setLowerBound(ValueRange lbOperands, AffineMap map) {
1443   assert(lbOperands.size() == map.getNumInputs());
1444   assert(map.getNumResults() >= 1 && "bound map has at least one result");
1445 
1446   SmallVector<Value, 4> newOperands(lbOperands.begin(), lbOperands.end());
1447 
1448   auto ubOperands = getUpperBoundOperands();
1449   newOperands.append(ubOperands.begin(), ubOperands.end());
1450   getOperation()->setOperands(newOperands);
1451 
1452   setAttr(getLowerBoundAttrName(), AffineMapAttr::get(map));
1453 }
1454 
1455 void AffineForOp::setUpperBound(ValueRange ubOperands, AffineMap map) {
1456   assert(ubOperands.size() == map.getNumInputs());
1457   assert(map.getNumResults() >= 1 && "bound map has at least one result");
1458 
1459   SmallVector<Value, 4> newOperands(getLowerBoundOperands());
1460   newOperands.append(ubOperands.begin(), ubOperands.end());
1461   getOperation()->setOperands(newOperands);
1462 
1463   setAttr(getUpperBoundAttrName(), AffineMapAttr::get(map));
1464 }
1465 
1466 void AffineForOp::setLowerBoundMap(AffineMap map) {
1467   auto lbMap = getLowerBoundMap();
1468   assert(lbMap.getNumDims() == map.getNumDims() &&
1469          lbMap.getNumSymbols() == map.getNumSymbols());
1470   assert(map.getNumResults() >= 1 && "bound map has at least one result");
1471   (void)lbMap;
1472   setAttr(getLowerBoundAttrName(), AffineMapAttr::get(map));
1473 }
1474 
1475 void AffineForOp::setUpperBoundMap(AffineMap map) {
1476   auto ubMap = getUpperBoundMap();
1477   assert(ubMap.getNumDims() == map.getNumDims() &&
1478          ubMap.getNumSymbols() == map.getNumSymbols());
1479   assert(map.getNumResults() >= 1 && "bound map has at least one result");
1480   (void)ubMap;
1481   setAttr(getUpperBoundAttrName(), AffineMapAttr::get(map));
1482 }
1483 
1484 bool AffineForOp::hasConstantLowerBound() {
1485   return getLowerBoundMap().isSingleConstant();
1486 }
1487 
1488 bool AffineForOp::hasConstantUpperBound() {
1489   return getUpperBoundMap().isSingleConstant();
1490 }
1491 
1492 int64_t AffineForOp::getConstantLowerBound() {
1493   return getLowerBoundMap().getSingleConstantResult();
1494 }
1495 
1496 int64_t AffineForOp::getConstantUpperBound() {
1497   return getUpperBoundMap().getSingleConstantResult();
1498 }
1499 
1500 void AffineForOp::setConstantLowerBound(int64_t value) {
1501   setLowerBound({}, AffineMap::getConstantMap(value, getContext()));
1502 }
1503 
1504 void AffineForOp::setConstantUpperBound(int64_t value) {
1505   setUpperBound({}, AffineMap::getConstantMap(value, getContext()));
1506 }
1507 
1508 AffineForOp::operand_range AffineForOp::getLowerBoundOperands() {
1509   return {operand_begin(), operand_begin() + getLowerBoundMap().getNumInputs()};
1510 }
1511 
1512 AffineForOp::operand_range AffineForOp::getUpperBoundOperands() {
1513   return {operand_begin() + getLowerBoundMap().getNumInputs(), operand_end()};
1514 }
1515 
1516 bool AffineForOp::matchingBoundOperandList() {
1517   auto lbMap = getLowerBoundMap();
1518   auto ubMap = getUpperBoundMap();
1519   if (lbMap.getNumDims() != ubMap.getNumDims() ||
1520       lbMap.getNumSymbols() != ubMap.getNumSymbols())
1521     return false;
1522 
1523   unsigned numOperands = lbMap.getNumInputs();
1524   for (unsigned i = 0, e = lbMap.getNumInputs(); i < e; i++) {
1525     // Compare Value 's.
1526     if (getOperand(i) != getOperand(numOperands + i))
1527       return false;
1528   }
1529   return true;
1530 }
1531 
1532 Region &AffineForOp::getLoopBody() { return region(); }
1533 
1534 bool AffineForOp::isDefinedOutsideOfLoop(Value value) {
1535   return !region().isAncestor(value.getParentRegion());
1536 }
1537 
1538 LogicalResult AffineForOp::moveOutOfLoop(ArrayRef<Operation *> ops) {
1539   for (auto *op : ops)
1540     op->moveBefore(*this);
1541   return success();
1542 }
1543 
1544 /// Returns if the provided value is the induction variable of a AffineForOp.
1545 bool mlir::isForInductionVar(Value val) {
1546   return getForInductionVarOwner(val) != AffineForOp();
1547 }
1548 
1549 /// Returns the loop parent of an induction variable. If the provided value is
1550 /// not an induction variable, then return nullptr.
1551 AffineForOp mlir::getForInductionVarOwner(Value val) {
1552   auto ivArg = val.dyn_cast<BlockArgument>();
1553   if (!ivArg || !ivArg.getOwner())
1554     return AffineForOp();
1555   auto *containingInst = ivArg.getOwner()->getParent()->getParentOp();
1556   return dyn_cast<AffineForOp>(containingInst);
1557 }
1558 
1559 /// Extracts the induction variables from a list of AffineForOps and returns
1560 /// them.
1561 void mlir::extractForInductionVars(ArrayRef<AffineForOp> forInsts,
1562                                    SmallVectorImpl<Value> *ivs) {
1563   ivs->reserve(forInsts.size());
1564   for (auto forInst : forInsts)
1565     ivs->push_back(forInst.getInductionVar());
1566 }
1567 
1568 //===----------------------------------------------------------------------===//
1569 // AffineIfOp
1570 //===----------------------------------------------------------------------===//
1571 
1572 namespace {
1573 /// Remove else blocks that have nothing other than the terminator.
1574 struct SimplifyDeadElse : public OpRewritePattern<AffineIfOp> {
1575   using OpRewritePattern<AffineIfOp>::OpRewritePattern;
1576 
1577   LogicalResult matchAndRewrite(AffineIfOp ifOp,
1578                                 PatternRewriter &rewriter) const override {
1579     if (ifOp.elseRegion().empty() || !has_single_element(*ifOp.getElseBlock()))
1580       return failure();
1581 
1582     rewriter.startRootUpdate(ifOp);
1583     rewriter.eraseBlock(ifOp.getElseBlock());
1584     rewriter.finalizeRootUpdate(ifOp);
1585     return success();
1586   }
1587 };
1588 } // end anonymous namespace.
1589 
1590 static LogicalResult verify(AffineIfOp op) {
1591   // Verify that we have a condition attribute.
1592   auto conditionAttr =
1593       op.getAttrOfType<IntegerSetAttr>(op.getConditionAttrName());
1594   if (!conditionAttr)
1595     return op.emitOpError(
1596         "requires an integer set attribute named 'condition'");
1597 
1598   // Verify that there are enough operands for the condition.
1599   IntegerSet condition = conditionAttr.getValue();
1600   if (op.getNumOperands() != condition.getNumInputs())
1601     return op.emitOpError(
1602         "operand count and condition integer set dimension and "
1603         "symbol count must match");
1604 
1605   // Verify that the operands are valid dimension/symbols.
1606   if (failed(verifyDimAndSymbolIdentifiers(op, op.getOperands(),
1607                                            condition.getNumDims())))
1608     return failure();
1609 
1610   // Verify that the entry of each child region does not have arguments.
1611   for (auto &region : op.getOperation()->getRegions()) {
1612     for (auto &b : region)
1613       if (b.getNumArguments() != 0)
1614         return op.emitOpError(
1615             "requires that child entry blocks have no arguments");
1616   }
1617   return success();
1618 }
1619 
1620 static ParseResult parseAffineIfOp(OpAsmParser &parser,
1621                                    OperationState &result) {
1622   // Parse the condition attribute set.
1623   IntegerSetAttr conditionAttr;
1624   unsigned numDims;
1625   if (parser.parseAttribute(conditionAttr, AffineIfOp::getConditionAttrName(),
1626                             result.attributes) ||
1627       parseDimAndSymbolList(parser, result.operands, numDims))
1628     return failure();
1629 
1630   // Verify the condition operands.
1631   auto set = conditionAttr.getValue();
1632   if (set.getNumDims() != numDims)
1633     return parser.emitError(
1634         parser.getNameLoc(),
1635         "dim operand count and integer set dim count must match");
1636   if (numDims + set.getNumSymbols() != result.operands.size())
1637     return parser.emitError(
1638         parser.getNameLoc(),
1639         "symbol operand count and integer set symbol count must match");
1640 
1641   // Create the regions for 'then' and 'else'.  The latter must be created even
1642   // if it remains empty for the validity of the operation.
1643   result.regions.reserve(2);
1644   Region *thenRegion = result.addRegion();
1645   Region *elseRegion = result.addRegion();
1646 
1647   // Parse the 'then' region.
1648   if (parser.parseRegion(*thenRegion, {}, {}))
1649     return failure();
1650   AffineIfOp::ensureTerminator(*thenRegion, parser.getBuilder(),
1651                                result.location);
1652 
1653   // If we find an 'else' keyword then parse the 'else' region.
1654   if (!parser.parseOptionalKeyword("else")) {
1655     if (parser.parseRegion(*elseRegion, {}, {}))
1656       return failure();
1657     AffineIfOp::ensureTerminator(*elseRegion, parser.getBuilder(),
1658                                  result.location);
1659   }
1660 
1661   // Parse the optional attribute list.
1662   if (parser.parseOptionalAttrDict(result.attributes))
1663     return failure();
1664 
1665   return success();
1666 }
1667 
1668 static void print(OpAsmPrinter &p, AffineIfOp op) {
1669   auto conditionAttr =
1670       op.getAttrOfType<IntegerSetAttr>(op.getConditionAttrName());
1671   p << "affine.if " << conditionAttr;
1672   printDimAndSymbolList(op.operand_begin(), op.operand_end(),
1673                         conditionAttr.getValue().getNumDims(), p);
1674   p.printRegion(op.thenRegion(),
1675                 /*printEntryBlockArgs=*/false,
1676                 /*printBlockTerminators=*/false);
1677 
1678   // Print the 'else' regions if it has any blocks.
1679   auto &elseRegion = op.elseRegion();
1680   if (!elseRegion.empty()) {
1681     p << " else";
1682     p.printRegion(elseRegion,
1683                   /*printEntryBlockArgs=*/false,
1684                   /*printBlockTerminators=*/false);
1685   }
1686 
1687   // Print the attribute list.
1688   p.printOptionalAttrDict(op.getAttrs(),
1689                           /*elidedAttrs=*/op.getConditionAttrName());
1690 }
1691 
1692 IntegerSet AffineIfOp::getIntegerSet() {
1693   return getAttrOfType<IntegerSetAttr>(getConditionAttrName()).getValue();
1694 }
1695 void AffineIfOp::setIntegerSet(IntegerSet newSet) {
1696   setAttr(getConditionAttrName(), IntegerSetAttr::get(newSet));
1697 }
1698 
1699 void AffineIfOp::setConditional(IntegerSet set, ValueRange operands) {
1700   setIntegerSet(set);
1701   getOperation()->setOperands(operands);
1702 }
1703 
1704 void AffineIfOp::build(Builder *builder, OperationState &result, IntegerSet set,
1705                        ValueRange args, bool withElseRegion) {
1706   result.addOperands(args);
1707   result.addAttribute(getConditionAttrName(), IntegerSetAttr::get(set));
1708   Region *thenRegion = result.addRegion();
1709   Region *elseRegion = result.addRegion();
1710   AffineIfOp::ensureTerminator(*thenRegion, *builder, result.location);
1711   if (withElseRegion)
1712     AffineIfOp::ensureTerminator(*elseRegion, *builder, result.location);
1713 }
1714 
1715 /// Canonicalize an affine if op's conditional (integer set + operands).
1716 LogicalResult AffineIfOp::fold(ArrayRef<Attribute>,
1717                                SmallVectorImpl<OpFoldResult> &) {
1718   auto set = getIntegerSet();
1719   SmallVector<Value, 4> operands(getOperands());
1720   canonicalizeSetAndOperands(&set, &operands);
1721 
1722   // Any canonicalization change always leads to either a reduction in the
1723   // number of operands or a change in the number of symbolic operands
1724   // (promotion of dims to symbols).
1725   if (operands.size() < getIntegerSet().getNumInputs() ||
1726       set.getNumSymbols() > getIntegerSet().getNumSymbols()) {
1727     setConditional(set, operands);
1728     return success();
1729   }
1730 
1731   return failure();
1732 }
1733 
1734 void AffineIfOp::getCanonicalizationPatterns(OwningRewritePatternList &results,
1735                                              MLIRContext *context) {
1736   results.insert<SimplifyDeadElse>(context);
1737 }
1738 
1739 //===----------------------------------------------------------------------===//
1740 // AffineLoadOp
1741 //===----------------------------------------------------------------------===//
1742 
1743 void AffineLoadOp::build(Builder *builder, OperationState &result,
1744                          AffineMap map, ValueRange operands) {
1745   assert(operands.size() == 1 + map.getNumInputs() && "inconsistent operands");
1746   result.addOperands(operands);
1747   if (map)
1748     result.addAttribute(getMapAttrName(), AffineMapAttr::get(map));
1749   auto memrefType = operands[0].getType().cast<MemRefType>();
1750   result.types.push_back(memrefType.getElementType());
1751 }
1752 
1753 void AffineLoadOp::build(Builder *builder, OperationState &result, Value memref,
1754                          AffineMap map, ValueRange mapOperands) {
1755   assert(map.getNumInputs() == mapOperands.size() && "inconsistent index info");
1756   result.addOperands(memref);
1757   result.addOperands(mapOperands);
1758   auto memrefType = memref.getType().cast<MemRefType>();
1759   result.addAttribute(getMapAttrName(), AffineMapAttr::get(map));
1760   result.types.push_back(memrefType.getElementType());
1761 }
1762 
1763 void AffineLoadOp::build(Builder *builder, OperationState &result, Value memref,
1764                          ValueRange indices) {
1765   auto memrefType = memref.getType().cast<MemRefType>();
1766   auto rank = memrefType.getRank();
1767   // Create identity map for memrefs with at least one dimension or () -> ()
1768   // for zero-dimensional memrefs.
1769   auto map = rank ? builder->getMultiDimIdentityMap(rank)
1770                   : builder->getEmptyAffineMap();
1771   build(builder, result, memref, map, indices);
1772 }
1773 
1774 ParseResult AffineLoadOp::parse(OpAsmParser &parser, OperationState &result) {
1775   auto &builder = parser.getBuilder();
1776   auto indexTy = builder.getIndexType();
1777 
1778   MemRefType type;
1779   OpAsmParser::OperandType memrefInfo;
1780   AffineMapAttr mapAttr;
1781   SmallVector<OpAsmParser::OperandType, 1> mapOperands;
1782   return failure(
1783       parser.parseOperand(memrefInfo) ||
1784       parser.parseAffineMapOfSSAIds(mapOperands, mapAttr, getMapAttrName(),
1785                                     result.attributes) ||
1786       parser.parseOptionalAttrDict(result.attributes) ||
1787       parser.parseColonType(type) ||
1788       parser.resolveOperand(memrefInfo, type, result.operands) ||
1789       parser.resolveOperands(mapOperands, indexTy, result.operands) ||
1790       parser.addTypeToList(type.getElementType(), result.types));
1791 }
1792 
1793 void AffineLoadOp::print(OpAsmPrinter &p) {
1794   p << "affine.load " << getMemRef() << '[';
1795   if (AffineMapAttr mapAttr = getAttrOfType<AffineMapAttr>(getMapAttrName()))
1796     p.printAffineMapOfSSAIds(mapAttr, getMapOperands());
1797   p << ']';
1798   p.printOptionalAttrDict(getAttrs(), /*elidedAttrs=*/{getMapAttrName()});
1799   p << " : " << getMemRefType();
1800 }
1801 
1802 LogicalResult AffineLoadOp::verify() {
1803   if (getType() != getMemRefType().getElementType())
1804     return emitOpError("result type must match element type of memref");
1805 
1806   auto mapAttr = getAttrOfType<AffineMapAttr>(getMapAttrName());
1807   if (mapAttr) {
1808     AffineMap map = getAttrOfType<AffineMapAttr>(getMapAttrName()).getValue();
1809     if (map.getNumResults() != getMemRefType().getRank())
1810       return emitOpError("affine.load affine map num results must equal"
1811                          " memref rank");
1812     if (map.getNumInputs() != getNumOperands() - 1)
1813       return emitOpError("expects as many subscripts as affine map inputs");
1814   } else {
1815     if (getMemRefType().getRank() != getNumOperands() - 1)
1816       return emitOpError(
1817           "expects the number of subscripts to be equal to memref rank");
1818   }
1819 
1820   for (auto idx : getMapOperands()) {
1821     if (!idx.getType().isIndex())
1822       return emitOpError("index to load must have 'index' type");
1823     if (!isValidAffineIndexOperand(idx))
1824       return emitOpError("index must be a dimension or symbol identifier");
1825   }
1826   return success();
1827 }
1828 
1829 void AffineLoadOp::getCanonicalizationPatterns(
1830     OwningRewritePatternList &results, MLIRContext *context) {
1831   results.insert<SimplifyAffineOp<AffineLoadOp>>(context);
1832 }
1833 
1834 OpFoldResult AffineLoadOp::fold(ArrayRef<Attribute> cstOperands) {
1835   /// load(memrefcast) -> load
1836   if (succeeded(foldMemRefCast(*this)))
1837     return getResult();
1838   return OpFoldResult();
1839 }
1840 
1841 //===----------------------------------------------------------------------===//
1842 // AffineStoreOp
1843 //===----------------------------------------------------------------------===//
1844 
1845 void AffineStoreOp::build(Builder *builder, OperationState &result,
1846                           Value valueToStore, Value memref, AffineMap map,
1847                           ValueRange mapOperands) {
1848   assert(map.getNumInputs() == mapOperands.size() && "inconsistent index info");
1849   result.addOperands(valueToStore);
1850   result.addOperands(memref);
1851   result.addOperands(mapOperands);
1852   result.addAttribute(getMapAttrName(), AffineMapAttr::get(map));
1853 }
1854 
1855 // Use identity map.
1856 void AffineStoreOp::build(Builder *builder, OperationState &result,
1857                           Value valueToStore, Value memref,
1858                           ValueRange indices) {
1859   auto memrefType = memref.getType().cast<MemRefType>();
1860   auto rank = memrefType.getRank();
1861   // Create identity map for memrefs with at least one dimension or () -> ()
1862   // for zero-dimensional memrefs.
1863   auto map = rank ? builder->getMultiDimIdentityMap(rank)
1864                   : builder->getEmptyAffineMap();
1865   build(builder, result, valueToStore, memref, map, indices);
1866 }
1867 
1868 ParseResult AffineStoreOp::parse(OpAsmParser &parser, OperationState &result) {
1869   auto indexTy = parser.getBuilder().getIndexType();
1870 
1871   MemRefType type;
1872   OpAsmParser::OperandType storeValueInfo;
1873   OpAsmParser::OperandType memrefInfo;
1874   AffineMapAttr mapAttr;
1875   SmallVector<OpAsmParser::OperandType, 1> mapOperands;
1876   return failure(parser.parseOperand(storeValueInfo) || parser.parseComma() ||
1877                  parser.parseOperand(memrefInfo) ||
1878                  parser.parseAffineMapOfSSAIds(mapOperands, mapAttr,
1879                                                getMapAttrName(),
1880                                                result.attributes) ||
1881                  parser.parseOptionalAttrDict(result.attributes) ||
1882                  parser.parseColonType(type) ||
1883                  parser.resolveOperand(storeValueInfo, type.getElementType(),
1884                                        result.operands) ||
1885                  parser.resolveOperand(memrefInfo, type, result.operands) ||
1886                  parser.resolveOperands(mapOperands, indexTy, result.operands));
1887 }
1888 
1889 void AffineStoreOp::print(OpAsmPrinter &p) {
1890   p << "affine.store " << getValueToStore();
1891   p << ", " << getMemRef() << '[';
1892   if (AffineMapAttr mapAttr = getAttrOfType<AffineMapAttr>(getMapAttrName()))
1893     p.printAffineMapOfSSAIds(mapAttr, getMapOperands());
1894   p << ']';
1895   p.printOptionalAttrDict(getAttrs(), /*elidedAttrs=*/{getMapAttrName()});
1896   p << " : " << getMemRefType();
1897 }
1898 
1899 LogicalResult AffineStoreOp::verify() {
1900   // First operand must have same type as memref element type.
1901   if (getValueToStore().getType() != getMemRefType().getElementType())
1902     return emitOpError("first operand must have same type memref element type");
1903 
1904   auto mapAttr = getAttrOfType<AffineMapAttr>(getMapAttrName());
1905   if (mapAttr) {
1906     AffineMap map = mapAttr.getValue();
1907     if (map.getNumResults() != getMemRefType().getRank())
1908       return emitOpError("affine.store affine map num results must equal"
1909                          " memref rank");
1910     if (map.getNumInputs() != getNumOperands() - 2)
1911       return emitOpError("expects as many subscripts as affine map inputs");
1912   } else {
1913     if (getMemRefType().getRank() != getNumOperands() - 2)
1914       return emitOpError(
1915           "expects the number of subscripts to be equal to memref rank");
1916   }
1917 
1918   for (auto idx : getMapOperands()) {
1919     if (!idx.getType().isIndex())
1920       return emitOpError("index to store must have 'index' type");
1921     if (!isValidAffineIndexOperand(idx))
1922       return emitOpError("index must be a dimension or symbol identifier");
1923   }
1924   return success();
1925 }
1926 
1927 void AffineStoreOp::getCanonicalizationPatterns(
1928     OwningRewritePatternList &results, MLIRContext *context) {
1929   results.insert<SimplifyAffineOp<AffineStoreOp>>(context);
1930 }
1931 
1932 LogicalResult AffineStoreOp::fold(ArrayRef<Attribute> cstOperands,
1933                                   SmallVectorImpl<OpFoldResult> &results) {
1934   /// store(memrefcast) -> store
1935   return foldMemRefCast(*this);
1936 }
1937 
1938 //===----------------------------------------------------------------------===//
1939 // AffineMinMaxOpBase
1940 //===----------------------------------------------------------------------===//
1941 
1942 template <typename T>
1943 static LogicalResult verifyAffineMinMaxOp(T op) {
1944   // Verify that operand count matches affine map dimension and symbol count.
1945   if (op.getNumOperands() != op.map().getNumDims() + op.map().getNumSymbols())
1946     return op.emitOpError(
1947         "operand count and affine map dimension and symbol count must match");
1948   return success();
1949 }
1950 
1951 template <typename T>
1952 static void printAffineMinMaxOp(OpAsmPrinter &p, T op) {
1953   p << op.getOperationName() << ' ' << op.getAttr(T::getMapAttrName());
1954   auto operands = op.getOperands();
1955   unsigned numDims = op.map().getNumDims();
1956   p << '(' << operands.take_front(numDims) << ')';
1957 
1958   if (operands.size() != numDims)
1959     p << '[' << operands.drop_front(numDims) << ']';
1960   p.printOptionalAttrDict(op.getAttrs(),
1961                           /*elidedAttrs=*/{T::getMapAttrName()});
1962 }
1963 
1964 template <typename T>
1965 static ParseResult parseAffineMinMaxOp(OpAsmParser &parser,
1966                                        OperationState &result) {
1967   auto &builder = parser.getBuilder();
1968   auto indexType = builder.getIndexType();
1969   SmallVector<OpAsmParser::OperandType, 8> dim_infos;
1970   SmallVector<OpAsmParser::OperandType, 8> sym_infos;
1971   AffineMapAttr mapAttr;
1972   return failure(
1973       parser.parseAttribute(mapAttr, T::getMapAttrName(), result.attributes) ||
1974       parser.parseOperandList(dim_infos, OpAsmParser::Delimiter::Paren) ||
1975       parser.parseOperandList(sym_infos,
1976                               OpAsmParser::Delimiter::OptionalSquare) ||
1977       parser.parseOptionalAttrDict(result.attributes) ||
1978       parser.resolveOperands(dim_infos, indexType, result.operands) ||
1979       parser.resolveOperands(sym_infos, indexType, result.operands) ||
1980       parser.addTypeToList(indexType, result.types));
1981 }
1982 
1983 //===----------------------------------------------------------------------===//
1984 // AffineMinOp
1985 //===----------------------------------------------------------------------===//
1986 //
1987 //   %0 = affine.min (d0) -> (1000, d0 + 512) (%i0)
1988 //
1989 
1990 OpFoldResult AffineMinOp::fold(ArrayRef<Attribute> operands) {
1991   // Fold the affine map.
1992   // TODO(andydavis, ntv) Fold more cases: partial static information,
1993   // min(some_affine, some_affine + constant, ...).
1994   SmallVector<Attribute, 2> results;
1995   if (failed(map().constantFold(operands, results)))
1996     return {};
1997 
1998   // Compute and return min of folded map results.
1999   int64_t min = std::numeric_limits<int64_t>::max();
2000   int minIndex = -1;
2001   for (unsigned i = 0, e = results.size(); i < e; ++i) {
2002     auto intAttr = results[i].cast<IntegerAttr>();
2003     if (intAttr.getInt() < min) {
2004       min = intAttr.getInt();
2005       minIndex = i;
2006     }
2007   }
2008   if (minIndex < 0)
2009     return {};
2010   return results[minIndex];
2011 }
2012 
2013 void AffineMinOp::getCanonicalizationPatterns(
2014     OwningRewritePatternList &patterns, MLIRContext *context) {
2015   patterns.insert<SimplifyAffineOp<AffineMinOp>>(context);
2016 }
2017 
2018 //===----------------------------------------------------------------------===//
2019 // AffineMaxOp
2020 //===----------------------------------------------------------------------===//
2021 //
2022 //   %0 = affine.max (d0) -> (1000, d0 + 512) (%i0)
2023 //
2024 
2025 OpFoldResult AffineMaxOp::fold(ArrayRef<Attribute> operands) {
2026   // Fold the affine map.
2027   // TODO(andydavis, ntv, ouhang) Fold more cases: partial static information,
2028   // max(some_affine, some_affine + constant, ...).
2029   SmallVector<Attribute, 2> results;
2030   if (failed(map().constantFold(operands, results)))
2031     return {};
2032 
2033   // Compute and return max of folded map results.
2034   int64_t max = std::numeric_limits<int64_t>::min();
2035   int maxIndex = -1;
2036   for (unsigned i = 0, e = results.size(); i < e; ++i) {
2037     auto intAttr = results[i].cast<IntegerAttr>();
2038     if (intAttr.getInt() > max) {
2039       max = intAttr.getInt();
2040       maxIndex = i;
2041     }
2042   }
2043   if (maxIndex < 0)
2044     return {};
2045   return results[maxIndex];
2046 }
2047 
2048 void AffineMaxOp::getCanonicalizationPatterns(
2049     OwningRewritePatternList &patterns, MLIRContext *context) {
2050   patterns.insert<SimplifyAffineOp<AffineMaxOp>>(context);
2051 }
2052 
2053 //===----------------------------------------------------------------------===//
2054 // AffinePrefetchOp
2055 //===----------------------------------------------------------------------===//
2056 
2057 //
2058 // affine.prefetch %0[%i, %j + 5], read, locality<3>, data : memref<400x400xi32>
2059 //
2060 static ParseResult parseAffinePrefetchOp(OpAsmParser &parser,
2061                                          OperationState &result) {
2062   auto &builder = parser.getBuilder();
2063   auto indexTy = builder.getIndexType();
2064 
2065   MemRefType type;
2066   OpAsmParser::OperandType memrefInfo;
2067   IntegerAttr hintInfo;
2068   auto i32Type = parser.getBuilder().getIntegerType(32);
2069   StringRef readOrWrite, cacheType;
2070 
2071   AffineMapAttr mapAttr;
2072   SmallVector<OpAsmParser::OperandType, 1> mapOperands;
2073   if (parser.parseOperand(memrefInfo) ||
2074       parser.parseAffineMapOfSSAIds(mapOperands, mapAttr,
2075                                     AffinePrefetchOp::getMapAttrName(),
2076                                     result.attributes) ||
2077       parser.parseComma() || parser.parseKeyword(&readOrWrite) ||
2078       parser.parseComma() || parser.parseKeyword("locality") ||
2079       parser.parseLess() ||
2080       parser.parseAttribute(hintInfo, i32Type,
2081                             AffinePrefetchOp::getLocalityHintAttrName(),
2082                             result.attributes) ||
2083       parser.parseGreater() || parser.parseComma() ||
2084       parser.parseKeyword(&cacheType) ||
2085       parser.parseOptionalAttrDict(result.attributes) ||
2086       parser.parseColonType(type) ||
2087       parser.resolveOperand(memrefInfo, type, result.operands) ||
2088       parser.resolveOperands(mapOperands, indexTy, result.operands))
2089     return failure();
2090 
2091   if (!readOrWrite.equals("read") && !readOrWrite.equals("write"))
2092     return parser.emitError(parser.getNameLoc(),
2093                             "rw specifier has to be 'read' or 'write'");
2094   result.addAttribute(
2095       AffinePrefetchOp::getIsWriteAttrName(),
2096       parser.getBuilder().getBoolAttr(readOrWrite.equals("write")));
2097 
2098   if (!cacheType.equals("data") && !cacheType.equals("instr"))
2099     return parser.emitError(parser.getNameLoc(),
2100                             "cache type has to be 'data' or 'instr'");
2101 
2102   result.addAttribute(
2103       AffinePrefetchOp::getIsDataCacheAttrName(),
2104       parser.getBuilder().getBoolAttr(cacheType.equals("data")));
2105 
2106   return success();
2107 }
2108 
2109 static void print(OpAsmPrinter &p, AffinePrefetchOp op) {
2110   p << AffinePrefetchOp::getOperationName() << " " << op.memref() << '[';
2111   AffineMapAttr mapAttr = op.getAttrOfType<AffineMapAttr>(op.getMapAttrName());
2112   if (mapAttr) {
2113     SmallVector<Value, 2> operands(op.getMapOperands());
2114     p.printAffineMapOfSSAIds(mapAttr, operands);
2115   }
2116   p << ']' << ", " << (op.isWrite() ? "write" : "read") << ", "
2117     << "locality<" << op.localityHint() << ">, "
2118     << (op.isDataCache() ? "data" : "instr");
2119   p.printOptionalAttrDict(
2120       op.getAttrs(),
2121       /*elidedAttrs=*/{op.getMapAttrName(), op.getLocalityHintAttrName(),
2122                        op.getIsDataCacheAttrName(), op.getIsWriteAttrName()});
2123   p << " : " << op.getMemRefType();
2124 }
2125 
2126 static LogicalResult verify(AffinePrefetchOp op) {
2127   auto mapAttr = op.getAttrOfType<AffineMapAttr>(op.getMapAttrName());
2128   if (mapAttr) {
2129     AffineMap map = mapAttr.getValue();
2130     if (map.getNumResults() != op.getMemRefType().getRank())
2131       return op.emitOpError("affine.prefetch affine map num results must equal"
2132                             " memref rank");
2133     if (map.getNumInputs() + 1 != op.getNumOperands())
2134       return op.emitOpError("too few operands");
2135   } else {
2136     if (op.getNumOperands() != 1)
2137       return op.emitOpError("too few operands");
2138   }
2139 
2140   for (auto idx : op.getMapOperands()) {
2141     if (!isValidAffineIndexOperand(idx))
2142       return op.emitOpError("index must be a dimension or symbol identifier");
2143   }
2144   return success();
2145 }
2146 
2147 void AffinePrefetchOp::getCanonicalizationPatterns(
2148     OwningRewritePatternList &results, MLIRContext *context) {
2149   // prefetch(memrefcast) -> prefetch
2150   results.insert<SimplifyAffineOp<AffinePrefetchOp>>(context);
2151 }
2152 
2153 LogicalResult AffinePrefetchOp::fold(ArrayRef<Attribute> cstOperands,
2154                                      SmallVectorImpl<OpFoldResult> &results) {
2155   /// prefetch(memrefcast) -> prefetch
2156   return foldMemRefCast(*this);
2157 }
2158 
2159 //===----------------------------------------------------------------------===//
2160 // AffineParallelOp
2161 //===----------------------------------------------------------------------===//
2162 
2163 void AffineParallelOp::build(Builder *builder, OperationState &result,
2164                              ArrayRef<int64_t> ranges) {
2165   // Default initialize empty maps.
2166   auto lbMap = AffineMap::get(builder->getContext());
2167   auto ubMap = AffineMap::get(builder->getContext());
2168   // If there are ranges, set each to [0, N).
2169   if (ranges.size()) {
2170     SmallVector<AffineExpr, 8> lbExprs(ranges.size(),
2171                                        builder->getAffineConstantExpr(0));
2172     lbMap = AffineMap::get(0, 0, lbExprs);
2173     SmallVector<AffineExpr, 8> ubExprs;
2174     for (int64_t range : ranges)
2175       ubExprs.push_back(builder->getAffineConstantExpr(range));
2176     ubMap = AffineMap::get(0, 0, ubExprs);
2177   }
2178   build(builder, result, lbMap, {}, ubMap, {});
2179 }
2180 
2181 void AffineParallelOp::build(Builder *builder, OperationState &result,
2182                              AffineMap lbMap, ValueRange lbArgs,
2183                              AffineMap ubMap, ValueRange ubArgs) {
2184   auto numDims = lbMap.getNumResults();
2185   // Verify that the dimensionality of both maps are the same.
2186   assert(numDims == ubMap.getNumResults() &&
2187          "num dims and num results mismatch");
2188   // Make default step sizes of 1.
2189   SmallVector<int64_t, 8> steps(numDims, 1);
2190   build(builder, result, lbMap, lbArgs, ubMap, ubArgs, steps);
2191 }
2192 
2193 void AffineParallelOp::build(Builder *builder, OperationState &result,
2194                              AffineMap lbMap, ValueRange lbArgs,
2195                              AffineMap ubMap, ValueRange ubArgs,
2196                              ArrayRef<int64_t> steps) {
2197   auto numDims = lbMap.getNumResults();
2198   // Verify that the dimensionality of the maps matches the number of steps.
2199   assert(numDims == ubMap.getNumResults() &&
2200          "num dims and num results mismatch");
2201   assert(numDims == steps.size() && "num dims and num steps mismatch");
2202   result.addAttribute(getLowerBoundsMapAttrName(), AffineMapAttr::get(lbMap));
2203   result.addAttribute(getUpperBoundsMapAttrName(), AffineMapAttr::get(ubMap));
2204   result.addAttribute(getStepsAttrName(), builder->getI64ArrayAttr(steps));
2205   result.addOperands(lbArgs);
2206   result.addOperands(ubArgs);
2207   // Create a region and a block for the body.
2208   auto bodyRegion = result.addRegion();
2209   auto body = new Block();
2210   // Add all the block arguments.
2211   for (unsigned i = 0; i < numDims; ++i)
2212     body->addArgument(IndexType::get(builder->getContext()));
2213   bodyRegion->push_back(body);
2214   ensureTerminator(*bodyRegion, *builder, result.location);
2215 }
2216 
2217 unsigned AffineParallelOp::getNumDims() { return steps().size(); }
2218 
2219 AffineParallelOp::operand_range AffineParallelOp::getLowerBoundsOperands() {
2220   return getOperands().take_front(lowerBoundsMap().getNumInputs());
2221 }
2222 
2223 AffineParallelOp::operand_range AffineParallelOp::getUpperBoundsOperands() {
2224   return getOperands().drop_front(lowerBoundsMap().getNumInputs());
2225 }
2226 
2227 AffineValueMap AffineParallelOp::getLowerBoundsValueMap() {
2228   return AffineValueMap(lowerBoundsMap(), getLowerBoundsOperands());
2229 }
2230 
2231 AffineValueMap AffineParallelOp::getUpperBoundsValueMap() {
2232   return AffineValueMap(upperBoundsMap(), getUpperBoundsOperands());
2233 }
2234 
2235 AffineValueMap AffineParallelOp::getRangesValueMap() {
2236   AffineValueMap out;
2237   AffineValueMap::difference(getUpperBoundsValueMap(), getLowerBoundsValueMap(),
2238                              &out);
2239   return out;
2240 }
2241 
2242 Optional<SmallVector<int64_t, 8>> AffineParallelOp::getConstantRanges() {
2243   // Try to convert all the ranges to constant expressions.
2244   SmallVector<int64_t, 8> out;
2245   AffineValueMap rangesValueMap = getRangesValueMap();
2246   out.reserve(rangesValueMap.getNumResults());
2247   for (unsigned i = 0, e = rangesValueMap.getNumResults(); i < e; ++i) {
2248     auto expr = rangesValueMap.getResult(i);
2249     auto cst = expr.dyn_cast<AffineConstantExpr>();
2250     if (!cst)
2251       return llvm::None;
2252     out.push_back(cst.getValue());
2253   }
2254   return out;
2255 }
2256 
2257 Block *AffineParallelOp::getBody() { return &region().front(); }
2258 
2259 OpBuilder AffineParallelOp::getBodyBuilder() {
2260   return OpBuilder(getBody(), std::prev(getBody()->end()));
2261 }
2262 
2263 void AffineParallelOp::setSteps(ArrayRef<int64_t> newSteps) {
2264   assert(newSteps.size() == getNumDims() && "steps & num dims mismatch");
2265   setAttr(getStepsAttrName(), getBodyBuilder().getI64ArrayAttr(newSteps));
2266 }
2267 
2268 static LogicalResult verify(AffineParallelOp op) {
2269   auto numDims = op.getNumDims();
2270   if (op.lowerBoundsMap().getNumResults() != numDims ||
2271       op.upperBoundsMap().getNumResults() != numDims ||
2272       op.steps().size() != numDims ||
2273       op.getBody()->getNumArguments() != numDims) {
2274     return op.emitOpError("region argument count and num results of upper "
2275                           "bounds, lower bounds, and steps must all match");
2276   }
2277   // Verify that the bound operands are valid dimension/symbols.
2278   /// Lower bounds.
2279   if (failed(verifyDimAndSymbolIdentifiers(op, op.getLowerBoundsOperands(),
2280                                            op.lowerBoundsMap().getNumDims())))
2281     return failure();
2282   /// Upper bounds.
2283   if (failed(verifyDimAndSymbolIdentifiers(op, op.getUpperBoundsOperands(),
2284                                            op.upperBoundsMap().getNumDims())))
2285     return failure();
2286   return success();
2287 }
2288 
2289 static void print(OpAsmPrinter &p, AffineParallelOp op) {
2290   p << op.getOperationName() << " (" << op.getBody()->getArguments() << ") = (";
2291   p.printAffineMapOfSSAIds(op.lowerBoundsMapAttr(),
2292                            op.getLowerBoundsOperands());
2293   p << ") to (";
2294   p.printAffineMapOfSSAIds(op.upperBoundsMapAttr(),
2295                            op.getUpperBoundsOperands());
2296   p << ')';
2297   SmallVector<int64_t, 4> steps;
2298   bool elideSteps = true;
2299   for (auto attr : op.steps()) {
2300     auto step = attr.cast<IntegerAttr>().getInt();
2301     elideSteps &= (step == 1);
2302     steps.push_back(step);
2303   }
2304   if (!elideSteps) {
2305     p << " step (";
2306     interleaveComma(steps, p);
2307     p << ')';
2308   }
2309   p.printRegion(op.region(), /*printEntryBlockArgs=*/false,
2310                 /*printBlockTerminators=*/false);
2311   p.printOptionalAttrDict(
2312       op.getAttrs(),
2313       /*elidedAttrs=*/{AffineParallelOp::getLowerBoundsMapAttrName(),
2314                        AffineParallelOp::getUpperBoundsMapAttrName(),
2315                        AffineParallelOp::getStepsAttrName()});
2316 }
2317 
2318 //
2319 // operation ::= `affine.parallel` `(` ssa-ids `)` `=` `(` map-of-ssa-ids `)`
2320 //               `to` `(` map-of-ssa-ids `)` steps? region attr-dict?
2321 // steps     ::= `steps` `(` integer-literals `)`
2322 //
2323 static ParseResult parseAffineParallelOp(OpAsmParser &parser,
2324                                          OperationState &result) {
2325   auto &builder = parser.getBuilder();
2326   auto indexType = builder.getIndexType();
2327   AffineMapAttr lowerBoundsAttr, upperBoundsAttr;
2328   SmallVector<OpAsmParser::OperandType, 4> ivs;
2329   SmallVector<OpAsmParser::OperandType, 4> lowerBoundsMapOperands;
2330   SmallVector<OpAsmParser::OperandType, 4> upperBoundsMapOperands;
2331   if (parser.parseRegionArgumentList(ivs, /*requiredOperandCount=*/-1,
2332                                      OpAsmParser::Delimiter::Paren) ||
2333       parser.parseEqual() ||
2334       parser.parseAffineMapOfSSAIds(
2335           lowerBoundsMapOperands, lowerBoundsAttr,
2336           AffineParallelOp::getLowerBoundsMapAttrName(), result.attributes,
2337           OpAsmParser::Delimiter::Paren) ||
2338       parser.resolveOperands(lowerBoundsMapOperands, indexType,
2339                              result.operands) ||
2340       parser.parseKeyword("to") ||
2341       parser.parseAffineMapOfSSAIds(
2342           upperBoundsMapOperands, upperBoundsAttr,
2343           AffineParallelOp::getUpperBoundsMapAttrName(), result.attributes,
2344           OpAsmParser::Delimiter::Paren) ||
2345       parser.resolveOperands(upperBoundsMapOperands, indexType,
2346                              result.operands))
2347     return failure();
2348 
2349   AffineMapAttr stepsMapAttr;
2350   SmallVector<NamedAttribute, 1> stepsAttrs;
2351   SmallVector<OpAsmParser::OperandType, 4> stepsMapOperands;
2352   if (failed(parser.parseOptionalKeyword("step"))) {
2353     SmallVector<int64_t, 4> steps(ivs.size(), 1);
2354     result.addAttribute(AffineParallelOp::getStepsAttrName(),
2355                         builder.getI64ArrayAttr(steps));
2356   } else {
2357     if (parser.parseAffineMapOfSSAIds(stepsMapOperands, stepsMapAttr,
2358                                       AffineParallelOp::getStepsAttrName(),
2359                                       stepsAttrs,
2360                                       OpAsmParser::Delimiter::Paren))
2361       return failure();
2362 
2363     // Convert steps from an AffineMap into an I64ArrayAttr.
2364     SmallVector<int64_t, 4> steps;
2365     auto stepsMap = stepsMapAttr.getValue();
2366     for (const auto &result : stepsMap.getResults()) {
2367       auto constExpr = result.dyn_cast<AffineConstantExpr>();
2368       if (!constExpr)
2369         return parser.emitError(parser.getNameLoc(),
2370                                 "steps must be constant integers");
2371       steps.push_back(constExpr.getValue());
2372     }
2373     result.addAttribute(AffineParallelOp::getStepsAttrName(),
2374                         builder.getI64ArrayAttr(steps));
2375   }
2376 
2377   // Now parse the body.
2378   Region *body = result.addRegion();
2379   SmallVector<Type, 4> types(ivs.size(), indexType);
2380   if (parser.parseRegion(*body, ivs, types) ||
2381       parser.parseOptionalAttrDict(result.attributes))
2382     return failure();
2383 
2384   // Add a terminator if none was parsed.
2385   AffineParallelOp::ensureTerminator(*body, builder, result.location);
2386   return success();
2387 }
2388 
2389 //===----------------------------------------------------------------------===//
2390 // TableGen'd op method definitions
2391 //===----------------------------------------------------------------------===//
2392 
2393 #define GET_OP_CLASSES
2394 #include "mlir/Dialect/Affine/IR/AffineOps.cpp.inc"
2395