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