brintos

brintos / llvm-project-archived public Read only

0
0
Text · 227.3 KiB · c6addfb Raw
5701 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8 9#include "mlir/Dialect/Affine/IR/AffineOps.h"10#include "mlir/Dialect/Affine/IR/AffineValueMap.h"11#include "mlir/Dialect/MemRef/IR/MemRef.h"12#include "mlir/Dialect/UB/IR/UBOps.h"13#include "mlir/Dialect/Utils/StaticValueUtils.h"14#include "mlir/IR/AffineExpr.h"15#include "mlir/IR/AffineExprVisitor.h"16#include "mlir/IR/IRMapping.h"17#include "mlir/IR/IntegerSet.h"18#include "mlir/IR/Matchers.h"19#include "mlir/IR/OpDefinition.h"20#include "mlir/IR/PatternMatch.h"21#include "mlir/IR/Value.h"22#include "mlir/Interfaces/ShapedOpInterfaces.h"23#include "mlir/Interfaces/ValueBoundsOpInterface.h"24#include "mlir/Transforms/InliningUtils.h"25#include "llvm/ADT/STLExtras.h"26#include "llvm/ADT/SmallBitVector.h"27#include "llvm/ADT/SmallVectorExtras.h"28#include "llvm/ADT/TypeSwitch.h"29#include "llvm/Support/DebugLog.h"30#include "llvm/Support/LogicalResult.h"31#include "llvm/Support/MathExtras.h"32#include <numeric>33#include <optional>34 35using namespace mlir;36using namespace mlir::affine;37 38using llvm::divideCeilSigned;39using llvm::divideFloorSigned;40using llvm::mod;41 42#define DEBUG_TYPE "affine-ops"43 44#include "mlir/Dialect/Affine/IR/AffineOpsDialect.cpp.inc"45 46/// A utility function to check if a value is defined at the top level of47/// `region` or is an argument of `region`. A value of index type defined at the48/// top level of a `AffineScope` region is always a valid symbol for all49/// uses in that region.50bool mlir::affine::isTopLevelValue(Value value, Region *region) {51  if (auto arg = dyn_cast<BlockArgument>(value))52    return arg.getParentRegion() == region;53  return value.getDefiningOp()->getParentRegion() == region;54}55 56/// Checks if `value` known to be a legal affine dimension or symbol in `src`57/// region remains legal if the operation that uses it is inlined into `dest`58/// with the given value mapping. `legalityCheck` is either `isValidDim` or59/// `isValidSymbol`, depending on the value being required to remain a valid60/// dimension or symbol.61static bool62remainsLegalAfterInline(Value value, Region *src, Region *dest,63                        const IRMapping &mapping,64                        function_ref<bool(Value, Region *)> legalityCheck) {65  // If the value is a valid dimension for any other reason than being66  // a top-level value, it will remain valid: constants get inlined67  // with the function, transitive affine applies also get inlined and68  // will be checked themselves, etc.69  if (!isTopLevelValue(value, src))70    return true;71 72  // If it's a top-level value because it's a block operand, i.e. a73  // function argument, check whether the value replacing it after74  // inlining is a valid dimension in the new region.75  if (llvm::isa<BlockArgument>(value))76    return legalityCheck(mapping.lookup(value), dest);77 78  // If it's a top-level value because it's defined in the region,79  // it can only be inlined if the defining op is a constant or a80  // `dim`, which can appear anywhere and be valid, since the defining81  // op won't be top-level anymore after inlining.82  Attribute operandCst;83  bool isDimLikeOp = isa<ShapedDimOpInterface>(value.getDefiningOp());84  return matchPattern(value.getDefiningOp(), m_Constant(&operandCst)) ||85         isDimLikeOp;86}87 88/// Checks if all values known to be legal affine dimensions or symbols in `src`89/// remain so if their respective users are inlined into `dest`.90static bool91remainsLegalAfterInline(ValueRange values, Region *src, Region *dest,92                        const IRMapping &mapping,93                        function_ref<bool(Value, Region *)> legalityCheck) {94  return llvm::all_of(values, [&](Value v) {95    return remainsLegalAfterInline(v, src, dest, mapping, legalityCheck);96  });97}98 99/// Checks if an affine read or write operation remains legal after inlining100/// from `src` to `dest`.101template <typename OpTy>102static bool remainsLegalAfterInline(OpTy op, Region *src, Region *dest,103                                    const IRMapping &mapping) {104  static_assert(llvm::is_one_of<OpTy, AffineReadOpInterface,105                                AffineWriteOpInterface>::value,106                "only ops with affine read/write interface are supported");107 108  AffineMap map = op.getAffineMap();109  ValueRange dimOperands = op.getMapOperands().take_front(map.getNumDims());110  ValueRange symbolOperands =111      op.getMapOperands().take_back(map.getNumSymbols());112  if (!remainsLegalAfterInline(113          dimOperands, src, dest, mapping,114          static_cast<bool (*)(Value, Region *)>(isValidDim)))115    return false;116  if (!remainsLegalAfterInline(117          symbolOperands, src, dest, mapping,118          static_cast<bool (*)(Value, Region *)>(isValidSymbol)))119    return false;120  return true;121}122 123/// Checks if an affine apply operation remains legal after inlining from `src`124/// to `dest`.125//  Use "unused attribute" marker to silence clang-tidy warning stemming from126//  the inability to see through "llvm::TypeSwitch".127template <>128[[maybe_unused]] bool remainsLegalAfterInline(AffineApplyOp op, Region *src,129                                              Region *dest,130                                              const IRMapping &mapping) {131  // If it's a valid dimension, we need to check that it remains so.132  if (isValidDim(op.getResult(), src))133    return remainsLegalAfterInline(134        op.getMapOperands(), src, dest, mapping,135        static_cast<bool (*)(Value, Region *)>(isValidDim));136 137  // Otherwise it must be a valid symbol, check that it remains so.138  return remainsLegalAfterInline(139      op.getMapOperands(), src, dest, mapping,140      static_cast<bool (*)(Value, Region *)>(isValidSymbol));141}142 143//===----------------------------------------------------------------------===//144// AffineDialect Interfaces145//===----------------------------------------------------------------------===//146 147namespace {148/// This class defines the interface for handling inlining with affine149/// operations.150struct AffineInlinerInterface : public DialectInlinerInterface {151  using DialectInlinerInterface::DialectInlinerInterface;152 153  //===--------------------------------------------------------------------===//154  // Analysis Hooks155  //===--------------------------------------------------------------------===//156 157  /// Returns true if the given region 'src' can be inlined into the region158  /// 'dest' that is attached to an operation registered to the current dialect.159  /// 'wouldBeCloned' is set if the region is cloned into its new location160  /// rather than moved, indicating there may be other users.161  bool isLegalToInline(Region *dest, Region *src, bool wouldBeCloned,162                       IRMapping &valueMapping) const final {163    // We can inline into affine loops and conditionals if this doesn't break164    // affine value categorization rules.165    Operation *destOp = dest->getParentOp();166    if (!isa<AffineParallelOp, AffineForOp, AffineIfOp>(destOp))167      return false;168 169    // Multi-block regions cannot be inlined into affine constructs, all of170    // which require single-block regions.171    if (!src->hasOneBlock())172      return false;173 174    // Side-effecting operations that the affine dialect cannot understand175    // should not be inlined.176    Block &srcBlock = src->front();177    for (Operation &op : srcBlock) {178      // Ops with no side effects are fine,179      if (auto iface = dyn_cast<MemoryEffectOpInterface>(op)) {180        if (iface.hasNoEffect())181          continue;182      }183 184      // Assuming the inlined region is valid, we only need to check if the185      // inlining would change it.186      bool remainsValid =187          llvm::TypeSwitch<Operation *, bool>(&op)188              .Case<AffineApplyOp, AffineReadOpInterface,189                    AffineWriteOpInterface>([&](auto op) {190                return remainsLegalAfterInline(op, src, dest, valueMapping);191              })192              .Default([](Operation *) {193                // Conservatively disallow inlining ops we cannot reason about.194                return false;195              });196 197      if (!remainsValid)198        return false;199    }200 201    return true;202  }203 204  /// Returns true if the given operation 'op', that is registered to this205  /// dialect, can be inlined into the given region, false otherwise.206  bool isLegalToInline(Operation *op, Region *region, bool wouldBeCloned,207                       IRMapping &valueMapping) const final {208    // Always allow inlining affine operations into a region that is marked as209    // affine scope, or into affine loops and conditionals. There are some edge210    // cases when inlining *into* affine structures, but that is handled in the211    // other 'isLegalToInline' hook above.212    Operation *parentOp = region->getParentOp();213    return parentOp->hasTrait<OpTrait::AffineScope>() ||214           isa<AffineForOp, AffineParallelOp, AffineIfOp>(parentOp);215  }216 217  /// Affine regions should be analyzed recursively.218  bool shouldAnalyzeRecursively(Operation *op) const final { return true; }219};220} // namespace221 222//===----------------------------------------------------------------------===//223// AffineDialect224//===----------------------------------------------------------------------===//225 226void AffineDialect::initialize() {227  addOperations<AffineDmaStartOp, AffineDmaWaitOp,228#define GET_OP_LIST229#include "mlir/Dialect/Affine/IR/AffineOps.cpp.inc"230                >();231  addInterfaces<AffineInlinerInterface>();232  declarePromisedInterfaces<ValueBoundsOpInterface, AffineApplyOp, AffineMaxOp,233                            AffineMinOp>();234}235 236/// Materialize a single constant operation from a given attribute value with237/// the desired resultant type.238Operation *AffineDialect::materializeConstant(OpBuilder &builder,239                                              Attribute value, Type type,240                                              Location loc) {241  if (auto poison = dyn_cast<ub::PoisonAttr>(value))242    return ub::PoisonOp::create(builder, loc, type, poison);243  return arith::ConstantOp::materialize(builder, value, type, loc);244}245 246/// A utility function to check if a value is defined at the top level of an247/// op with trait `AffineScope`. If the value is defined in an unlinked region,248/// conservatively assume it is not top-level. A value of index type defined at249/// the top level is always a valid symbol.250bool mlir::affine::isTopLevelValue(Value value) {251  if (auto arg = dyn_cast<BlockArgument>(value)) {252    // The block owning the argument may be unlinked, e.g. when the surrounding253    // region has not yet been attached to an Op, at which point the parent Op254    // is null.255    Operation *parentOp = arg.getOwner()->getParentOp();256    return parentOp && parentOp->hasTrait<OpTrait::AffineScope>();257  }258  // The defining Op may live in an unlinked block so its parent Op may be null.259  Operation *parentOp = value.getDefiningOp()->getParentOp();260  return parentOp && parentOp->hasTrait<OpTrait::AffineScope>();261}262 263/// Returns the closest region enclosing `op` that is held by an operation with264/// trait `AffineScope`; `nullptr` if there is no such region.265Region *mlir::affine::getAffineScope(Operation *op) {266  auto *curOp = op;267  while (auto *parentOp = curOp->getParentOp()) {268    if (parentOp->hasTrait<OpTrait::AffineScope>())269      return curOp->getParentRegion();270    curOp = parentOp;271  }272  return nullptr;273}274 275Region *mlir::affine::getAffineAnalysisScope(Operation *op) {276  Operation *curOp = op;277  while (auto *parentOp = curOp->getParentOp()) {278    if (!isa<AffineForOp, AffineIfOp, AffineParallelOp>(parentOp))279      return curOp->getParentRegion();280    curOp = parentOp;281  }282  return nullptr;283}284 285// A Value can be used as a dimension id iff it meets one of the following286// conditions:287// *) It is valid as a symbol.288// *) It is an induction variable.289// *) It is the result of affine apply operation with dimension id arguments.290bool mlir::affine::isValidDim(Value value) {291  // The value must be an index type.292  if (!value.getType().isIndex())293    return false;294 295  if (auto *defOp = value.getDefiningOp())296    return isValidDim(value, getAffineScope(defOp));297 298  // This value has to be a block argument for an op that has the299  // `AffineScope` trait or an induction var of an affine.for or300  // affine.parallel.301  if (isAffineInductionVar(value))302    return true;303  auto *parentOp = llvm::cast<BlockArgument>(value).getOwner()->getParentOp();304  return parentOp && parentOp->hasTrait<OpTrait::AffineScope>();305}306 307// Value can be used as a dimension id iff it meets one of the following308// conditions:309// *) It is valid as a symbol.310// *) It is an induction variable.311// *) It is the result of an affine apply operation with dimension id operands.312// *) It is the result of a more specialized index transformation (ex.313// delinearize_index or linearize_index) with dimension id operands.314bool mlir::affine::isValidDim(Value value, Region *region) {315  // The value must be an index type.316  if (!value.getType().isIndex())317    return false;318 319  // All valid symbols are okay.320  if (isValidSymbol(value, region))321    return true;322 323  auto *op = value.getDefiningOp();324  if (!op) {325    // This value has to be an induction var for an affine.for or an326    // affine.parallel.327    return isAffineInductionVar(value);328  }329 330  // Affine apply operation is ok if all of its operands are ok.331  if (auto applyOp = dyn_cast<AffineApplyOp>(op))332    return applyOp.isValidDim(region);333  // delinearize_index and linearize_index are special forms of apply334  // and so are valid dimensions if all their arguments are valid dimensions.335  if (isa<AffineDelinearizeIndexOp, AffineLinearizeIndexOp>(op))336    return llvm::all_of(op->getOperands(),337                        [&](Value arg) { return ::isValidDim(arg, region); });338  // The dim op is okay if its operand memref/tensor is defined at the top339  // level.340  if (auto dimOp = dyn_cast<ShapedDimOpInterface>(op))341    return isTopLevelValue(dimOp.getShapedValue());342  return false;343}344 345/// Returns true if the 'index' dimension of the `memref` defined by346/// `memrefDefOp` is a statically  shaped one or defined using a valid symbol347/// for `region`.348template <typename AnyMemRefDefOp>349static bool isMemRefSizeValidSymbol(AnyMemRefDefOp memrefDefOp, unsigned index,350                                    Region *region) {351  MemRefType memRefType = memrefDefOp.getType();352 353  // Dimension index is out of bounds.354  if (index >= memRefType.getRank()) {355    return false;356  }357 358  // Statically shaped.359  if (!memRefType.isDynamicDim(index))360    return true;361  // Get the position of the dimension among dynamic dimensions;362  unsigned dynamicDimPos = memRefType.getDynamicDimIndex(index);363  return isValidSymbol(*(memrefDefOp.getDynamicSizes().begin() + dynamicDimPos),364                       region);365}366 367/// Returns true if the result of the dim op is a valid symbol for `region`.368static bool isDimOpValidSymbol(ShapedDimOpInterface dimOp, Region *region) {369  // The dim op is okay if its source is defined at the top level.370  if (isTopLevelValue(dimOp.getShapedValue()))371    return true;372 373  // Conservatively handle remaining BlockArguments as non-valid symbols.374  // E.g. scf.for iterArgs.375  if (llvm::isa<BlockArgument>(dimOp.getShapedValue()))376    return false;377 378  // The dim op is also okay if its operand memref is a view/subview whose379  // corresponding size is a valid symbol.380  std::optional<int64_t> index = getConstantIntValue(dimOp.getDimension());381 382  // Be conservative if we can't understand the dimension.383  if (!index.has_value())384    return false;385 386  // Skip over all memref.cast ops (if any).387  Operation *op = dimOp.getShapedValue().getDefiningOp();388  while (auto castOp = dyn_cast<memref::CastOp>(op)) {389    // Bail on unranked memrefs.390    if (isa<UnrankedMemRefType>(castOp.getSource().getType()))391      return false;392    op = castOp.getSource().getDefiningOp();393    if (!op)394      return false;395  }396 397  int64_t i = index.value();398  return TypeSwitch<Operation *, bool>(op)399      .Case<memref::ViewOp, memref::SubViewOp, memref::AllocOp>(400          [&](auto op) { return isMemRefSizeValidSymbol(op, i, region); })401      .Default([](Operation *) { return false; });402}403 404// A value can be used as a symbol (at all its use sites) iff it meets one of405// the following conditions:406// *) It is a constant.407// *) Its defining op or block arg appearance is immediately enclosed by an op408//    with `AffineScope` trait.409// *) It is the result of an affine.apply operation with symbol operands.410// *) It is a result of the dim op on a memref whose corresponding size is a411//    valid symbol.412bool mlir::affine::isValidSymbol(Value value) {413  if (!value)414    return false;415 416  // The value must be an index type.417  if (!value.getType().isIndex())418    return false;419 420  // Check that the value is a top level value.421  if (isTopLevelValue(value))422    return true;423 424  if (auto *defOp = value.getDefiningOp())425    return isValidSymbol(value, getAffineScope(defOp));426 427  return false;428}429 430/// A utility function to check if a value is defined at the top level of431/// `region` or is an argument of `region` or is defined above the region.432static bool isTopLevelValueOrAbove(Value value, Region *region) {433  Region *parentRegion = value.getParentRegion();434  do {435    if (parentRegion == region)436      return true;437    Operation *regionOp = region->getParentOp();438    if (regionOp->hasTrait<OpTrait::IsIsolatedFromAbove>())439      break;440    region = region->getParentOp()->getParentRegion();441  } while (region);442  return false;443}444 445/// A value can be used as a symbol for `region` iff it meets one of the446/// following conditions:447/// *) It is a constant.448/// *) It is a result of a `Pure` operation whose operands are valid symbolic449/// *) identifiers.450/// *) It is a result of the dim op on a memref whose corresponding size is451///    a valid symbol.452/// *) It is defined at the top level of 'region' or is its argument.453/// *) It dominates `region`'s parent op.454/// If `region` is null, conservatively assume the symbol definition scope does455/// not exist and only accept the values that would be symbols regardless of456/// the surrounding region structure, i.e. the first three cases above.457bool mlir::affine::isValidSymbol(Value value, Region *region) {458  // The value must be an index type.459  if (!value.getType().isIndex())460    return false;461 462  // A top-level value is a valid symbol.463  if (region && isTopLevelValueOrAbove(value, region))464    return true;465 466  auto *defOp = value.getDefiningOp();467  if (!defOp)468    return false;469 470  // Constant operation is ok.471  Attribute operandCst;472  if (matchPattern(defOp, m_Constant(&operandCst)))473    return true;474 475  // `Pure` operation that whose operands are valid symbolic identifiers.476  if (isPure(defOp) && llvm::all_of(defOp->getOperands(), [&](Value operand) {477        return affine::isValidSymbol(operand, region);478      })) {479    return true;480  }481 482  // Dim op results could be valid symbols at any level.483  if (auto dimOp = dyn_cast<ShapedDimOpInterface>(defOp))484    return isDimOpValidSymbol(dimOp, region);485 486  return false;487}488 489// Returns true if 'value' is a valid index to an affine operation (e.g.490// affine.load, affine.store, affine.dma_start, affine.dma_wait) where491// `region` provides the polyhedral symbol scope. Returns false otherwise.492static bool isValidAffineIndexOperand(Value value, Region *region) {493  return isValidDim(value, region) || isValidSymbol(value, region);494}495 496/// Prints dimension and symbol list.497static void printDimAndSymbolList(Operation::operand_iterator begin,498                                  Operation::operand_iterator end,499                                  unsigned numDims, OpAsmPrinter &printer) {500  OperandRange operands(begin, end);501  printer << '(' << operands.take_front(numDims) << ')';502  if (operands.size() > numDims)503    printer << '[' << operands.drop_front(numDims) << ']';504}505 506/// Parses dimension and symbol list and returns true if parsing failed.507ParseResult mlir::affine::parseDimAndSymbolList(508    OpAsmParser &parser, SmallVectorImpl<Value> &operands, unsigned &numDims) {509  SmallVector<OpAsmParser::UnresolvedOperand, 8> opInfos;510  if (parser.parseOperandList(opInfos, OpAsmParser::Delimiter::Paren))511    return failure();512  // Store number of dimensions for validation by caller.513  numDims = opInfos.size();514 515  // Parse the optional symbol operands.516  auto indexTy = parser.getBuilder().getIndexType();517  return failure(parser.parseOperandList(518                     opInfos, OpAsmParser::Delimiter::OptionalSquare) ||519                 parser.resolveOperands(opInfos, indexTy, operands));520}521 522/// Utility function to verify that a set of operands are valid dimension and523/// symbol identifiers. The operands should be laid out such that the dimension524/// operands are before the symbol operands. This function returns failure if525/// there was an invalid operand. An operation is provided to emit any necessary526/// errors.527template <typename OpTy>528static LogicalResult529verifyDimAndSymbolIdentifiers(OpTy &op, Operation::operand_range operands,530                              unsigned numDims) {531  unsigned opIt = 0;532  for (auto operand : operands) {533    if (opIt++ < numDims) {534      if (!isValidDim(operand, getAffineScope(op)))535        return op.emitOpError("operand cannot be used as a dimension id");536    } else if (!isValidSymbol(operand, getAffineScope(op))) {537      return op.emitOpError("operand cannot be used as a symbol");538    }539  }540  return success();541}542 543//===----------------------------------------------------------------------===//544// AffineApplyOp545//===----------------------------------------------------------------------===//546 547AffineValueMap AffineApplyOp::getAffineValueMap() {548  return AffineValueMap(getAffineMap(), getOperands(), getResult());549}550 551ParseResult AffineApplyOp::parse(OpAsmParser &parser, OperationState &result) {552  auto &builder = parser.getBuilder();553  auto indexTy = builder.getIndexType();554 555  AffineMapAttr mapAttr;556  unsigned numDims;557  if (parser.parseAttribute(mapAttr, "map", result.attributes) ||558      parseDimAndSymbolList(parser, result.operands, numDims) ||559      parser.parseOptionalAttrDict(result.attributes))560    return failure();561  auto map = mapAttr.getValue();562 563  if (map.getNumDims() != numDims ||564      numDims + map.getNumSymbols() != result.operands.size()) {565    return parser.emitError(parser.getNameLoc(),566                            "dimension or symbol index mismatch");567  }568 569  result.types.append(map.getNumResults(), indexTy);570  return success();571}572 573void AffineApplyOp::print(OpAsmPrinter &p) {574  p << " " << getMapAttr();575  printDimAndSymbolList(operand_begin(), operand_end(),576                        getAffineMap().getNumDims(), p);577  p.printOptionalAttrDict((*this)->getAttrs(), /*elidedAttrs=*/{"map"});578}579 580LogicalResult AffineApplyOp::verify() {581  // Check input and output dimensions match.582  AffineMap affineMap = getMap();583 584  // Verify that operand count matches affine map dimension and symbol count.585  if (getNumOperands() != affineMap.getNumDims() + affineMap.getNumSymbols())586    return emitOpError(587        "operand count and affine map dimension and symbol count must match");588 589  // Verify that the map only produces one result.590  if (affineMap.getNumResults() != 1)591    return emitOpError("mapping must produce one value");592 593  // Do not allow valid dims to be used in symbol positions. We do allow594  // affine.apply to use operands for values that may neither qualify as affine595  // dims or affine symbols due to usage outside of affine ops, analyses, etc.596  Region *region = getAffineScope(*this);597  for (Value operand : getMapOperands().drop_front(affineMap.getNumDims())) {598    if (::isValidDim(operand, region) && !::isValidSymbol(operand, region))599      return emitError("dimensional operand cannot be used as a symbol");600  }601 602  return success();603}604 605// The result of the affine apply operation can be used as a dimension id if all606// its operands are valid dimension ids.607bool AffineApplyOp::isValidDim() {608  return llvm::all_of(getOperands(),609                      [](Value op) { return affine::isValidDim(op); });610}611 612// The result of the affine apply operation can be used as a dimension id if all613// its operands are valid dimension ids with the parent operation of `region`614// defining the polyhedral scope for symbols.615bool AffineApplyOp::isValidDim(Region *region) {616  return llvm::all_of(getOperands(),617                      [&](Value op) { return ::isValidDim(op, region); });618}619 620// The result of the affine apply operation can be used as a symbol if all its621// operands are symbols.622bool AffineApplyOp::isValidSymbol() {623  return llvm::all_of(getOperands(),624                      [](Value op) { return affine::isValidSymbol(op); });625}626 627// The result of the affine apply operation can be used as a symbol in `region`628// if all its operands are symbols in `region`.629bool AffineApplyOp::isValidSymbol(Region *region) {630  return llvm::all_of(getOperands(), [&](Value operand) {631    return affine::isValidSymbol(operand, region);632  });633}634 635OpFoldResult AffineApplyOp::fold(FoldAdaptor adaptor) {636  auto map = getAffineMap();637 638  // Fold dims and symbols to existing values.639  auto expr = map.getResult(0);640  if (auto dim = dyn_cast<AffineDimExpr>(expr))641    return getOperand(dim.getPosition());642  if (auto sym = dyn_cast<AffineSymbolExpr>(expr))643    return getOperand(map.getNumDims() + sym.getPosition());644 645  // Otherwise, default to folding the map.646  SmallVector<Attribute, 1> result;647  bool hasPoison = false;648  auto foldResult =649      map.constantFold(adaptor.getMapOperands(), result, &hasPoison);650  if (hasPoison)651    return ub::PoisonAttr::get(getContext());652  if (failed(foldResult))653    return {};654  return result[0];655}656 657/// Returns the largest known divisor of `e`. Exploits information from the658/// values in `operands`.659static int64_t getLargestKnownDivisor(AffineExpr e, ArrayRef<Value> operands) {660  // This method isn't aware of `operands`.661  int64_t div = e.getLargestKnownDivisor();662 663  // We now make use of operands for the case `e` is a dim expression.664  // TODO: More powerful simplification would have to modify665  // getLargestKnownDivisor to take `operands` and exploit that information as666  // well for dim/sym expressions, but in that case, getLargestKnownDivisor667  // can't be part of the IR library but of the `Analysis` library. The IR668  // library can only really depend on simple O(1) checks.669  auto dimExpr = dyn_cast<AffineDimExpr>(e);670  // If it's not a dim expr, `div` is the best we have.671  if (!dimExpr)672    return div;673 674  // We simply exploit information from loop IVs.675  // We don't need to use mlir::getLargestKnownDivisorOfValue since the other676  // desired simplifications are expected to be part of other677  // canonicalizations. Also, mlir::getLargestKnownDivisorOfValue is part of the678  // LoopAnalysis library.679  Value operand = operands[dimExpr.getPosition()];680  int64_t operandDivisor = 1;681  // TODO: With the right accessors, this can be extended to682  // LoopLikeOpInterface.683  if (AffineForOp forOp = getForInductionVarOwner(operand)) {684    if (forOp.hasConstantLowerBound() && forOp.getConstantLowerBound() == 0) {685      operandDivisor = forOp.getStepAsInt();686    } else {687      uint64_t lbLargestKnownDivisor =688          forOp.getLowerBoundMap().getLargestKnownDivisorOfMapExprs();689      operandDivisor = std::gcd(lbLargestKnownDivisor, forOp.getStepAsInt());690    }691  }692  return operandDivisor;693}694 695/// Check if `e` is known to be: 0 <= `e` < `k`. Handles the simple cases of `e`696/// being an affine dim expression or a constant.697static bool isNonNegativeBoundedBy(AffineExpr e, ArrayRef<Value> operands,698                                   int64_t k) {699  if (auto constExpr = dyn_cast<AffineConstantExpr>(e)) {700    int64_t constVal = constExpr.getValue();701    return constVal >= 0 && constVal < k;702  }703  auto dimExpr = dyn_cast<AffineDimExpr>(e);704  if (!dimExpr)705    return false;706  Value operand = operands[dimExpr.getPosition()];707  // TODO: With the right accessors, this can be extended to708  // LoopLikeOpInterface.709  if (AffineForOp forOp = getForInductionVarOwner(operand)) {710    if (forOp.hasConstantLowerBound() && forOp.getConstantLowerBound() >= 0 &&711        forOp.hasConstantUpperBound() && forOp.getConstantUpperBound() <= k) {712      return true;713    }714  }715 716  // We don't consider other cases like `operand` being defined by a constant or717  // an affine.apply op since such cases will already be handled by other718  // patterns and propagation of loop IVs or constant would happen.719  return false;720}721 722/// Check if expression `e` is of the form d*e_1 + e_2 where 0 <= e_2 < d.723/// Set `div` to `d`, `quotientTimesDiv` to e_1 and `rem` to e_2 if the724/// expression is in that form.725static bool isQTimesDPlusR(AffineExpr e, ArrayRef<Value> operands, int64_t &div,726                           AffineExpr &quotientTimesDiv, AffineExpr &rem) {727  auto bin = dyn_cast<AffineBinaryOpExpr>(e);728  if (!bin || bin.getKind() != AffineExprKind::Add)729    return false;730 731  AffineExpr llhs = bin.getLHS();732  AffineExpr rlhs = bin.getRHS();733  div = getLargestKnownDivisor(llhs, operands);734  if (isNonNegativeBoundedBy(rlhs, operands, div)) {735    quotientTimesDiv = llhs;736    rem = rlhs;737    return true;738  }739  div = getLargestKnownDivisor(rlhs, operands);740  if (isNonNegativeBoundedBy(llhs, operands, div)) {741    quotientTimesDiv = rlhs;742    rem = llhs;743    return true;744  }745  return false;746}747 748/// Gets the constant lower bound on an `iv`.749static std::optional<int64_t> getLowerBound(Value iv) {750  AffineForOp forOp = getForInductionVarOwner(iv);751  if (forOp && forOp.hasConstantLowerBound())752    return forOp.getConstantLowerBound();753  return std::nullopt;754}755 756/// Gets the constant upper bound on an affine.for `iv`.757static std::optional<int64_t> getUpperBound(Value iv) {758  AffineForOp forOp = getForInductionVarOwner(iv);759  if (!forOp || !forOp.hasConstantUpperBound())760    return std::nullopt;761 762  // If its lower bound is also known, we can get a more precise bound763  // whenever the step is not one.764  if (forOp.hasConstantLowerBound()) {765    return forOp.getConstantUpperBound() - 1 -766           (forOp.getConstantUpperBound() - forOp.getConstantLowerBound() - 1) %767               forOp.getStepAsInt();768  }769  return forOp.getConstantUpperBound() - 1;770}771 772/// Determine a constant upper bound for `expr` if one exists while exploiting773/// values in `operands`. Note that the upper bound is an inclusive one. `expr`774/// is guaranteed to be less than or equal to it.775static std::optional<int64_t> getUpperBound(AffineExpr expr, unsigned numDims,776                                            unsigned numSymbols,777                                            ArrayRef<Value> operands) {778  // Get the constant lower or upper bounds on the operands.779  SmallVector<std::optional<int64_t>> constLowerBounds, constUpperBounds;780  constLowerBounds.reserve(operands.size());781  constUpperBounds.reserve(operands.size());782  for (Value operand : operands) {783    constLowerBounds.push_back(getLowerBound(operand));784    constUpperBounds.push_back(getUpperBound(operand));785  }786 787  if (auto constExpr = dyn_cast<AffineConstantExpr>(expr))788    return constExpr.getValue();789 790  return getBoundForAffineExpr(expr, numDims, numSymbols, constLowerBounds,791                               constUpperBounds,792                               /*isUpper=*/true);793}794 795/// Determine a constant lower bound for `expr` if one exists while exploiting796/// values in `operands`. Note that the upper bound is an inclusive one. `expr`797/// is guaranteed to be less than or equal to it.798static std::optional<int64_t> getLowerBound(AffineExpr expr, unsigned numDims,799                                            unsigned numSymbols,800                                            ArrayRef<Value> operands) {801  // Get the constant lower or upper bounds on the operands.802  SmallVector<std::optional<int64_t>> constLowerBounds, constUpperBounds;803  constLowerBounds.reserve(operands.size());804  constUpperBounds.reserve(operands.size());805  for (Value operand : operands) {806    constLowerBounds.push_back(getLowerBound(operand));807    constUpperBounds.push_back(getUpperBound(operand));808  }809 810  std::optional<int64_t> lowerBound;811  if (auto constExpr = dyn_cast<AffineConstantExpr>(expr)) {812    lowerBound = constExpr.getValue();813  } else {814    lowerBound = getBoundForAffineExpr(expr, numDims, numSymbols,815                                       constLowerBounds, constUpperBounds,816                                       /*isUpper=*/false);817  }818  return lowerBound;819}820 821/// Simplify `expr` while exploiting information from the values in `operands`.822static void simplifyExprAndOperands(AffineExpr &expr, unsigned numDims,823                                    unsigned numSymbols,824                                    ArrayRef<Value> operands) {825  // We do this only for certain floordiv/mod expressions.826  auto binExpr = dyn_cast<AffineBinaryOpExpr>(expr);827  if (!binExpr)828    return;829 830  // Simplify the child expressions first.831  AffineExpr lhs = binExpr.getLHS();832  AffineExpr rhs = binExpr.getRHS();833  simplifyExprAndOperands(lhs, numDims, numSymbols, operands);834  simplifyExprAndOperands(rhs, numDims, numSymbols, operands);835  expr = getAffineBinaryOpExpr(binExpr.getKind(), lhs, rhs);836 837  binExpr = dyn_cast<AffineBinaryOpExpr>(expr);838  if (!binExpr || (expr.getKind() != AffineExprKind::FloorDiv &&839                   expr.getKind() != AffineExprKind::CeilDiv &&840                   expr.getKind() != AffineExprKind::Mod)) {841    return;842  }843 844  // The `lhs` and `rhs` may be different post construction of simplified expr.845  lhs = binExpr.getLHS();846  rhs = binExpr.getRHS();847  auto rhsConst = dyn_cast<AffineConstantExpr>(rhs);848  if (!rhsConst)849    return;850 851  int64_t rhsConstVal = rhsConst.getValue();852  // Undefined exprsessions aren't touched; IR can still be valid with them.853  if (rhsConstVal <= 0)854    return;855 856  // Exploit constant lower/upper bounds to simplify a floordiv or mod.857  MLIRContext *context = expr.getContext();858  std::optional<int64_t> lhsLbConst =859      getLowerBound(lhs, numDims, numSymbols, operands);860  std::optional<int64_t> lhsUbConst =861      getUpperBound(lhs, numDims, numSymbols, operands);862  if (lhsLbConst && lhsUbConst) {863    int64_t lhsLbConstVal = *lhsLbConst;864    int64_t lhsUbConstVal = *lhsUbConst;865    // lhs floordiv c is a single value lhs is bounded in a range `c` that has866    // the same quotient.867    if (binExpr.getKind() == AffineExprKind::FloorDiv &&868        divideFloorSigned(lhsLbConstVal, rhsConstVal) ==869            divideFloorSigned(lhsUbConstVal, rhsConstVal)) {870      expr = getAffineConstantExpr(871          divideFloorSigned(lhsLbConstVal, rhsConstVal), context);872      return;873    }874    // lhs ceildiv c is a single value if the entire range has the same ceil875    // quotient.876    if (binExpr.getKind() == AffineExprKind::CeilDiv &&877        divideCeilSigned(lhsLbConstVal, rhsConstVal) ==878            divideCeilSigned(lhsUbConstVal, rhsConstVal)) {879      expr = getAffineConstantExpr(divideCeilSigned(lhsLbConstVal, rhsConstVal),880                                   context);881      return;882    }883    // lhs mod c is lhs if the entire range has quotient 0 w.r.t the rhs.884    if (binExpr.getKind() == AffineExprKind::Mod && lhsLbConstVal >= 0 &&885        lhsLbConstVal < rhsConstVal && lhsUbConstVal < rhsConstVal) {886      expr = lhs;887      return;888    }889  }890 891  // Simplify expressions of the form e = (e_1 + e_2) floordiv c or (e_1 + e_2)892  // mod c, where e_1 is a multiple of `k` and 0 <= e_2 < k. In such cases, if893  // `c` % `k` == 0, (e_1 + e_2) floordiv c can be simplified to e_1 floordiv c.894  // And when k % c == 0, (e_1 + e_2) mod c can be simplified to e_2 mod c.895  AffineExpr quotientTimesDiv, rem;896  int64_t divisor;897  if (isQTimesDPlusR(lhs, operands, divisor, quotientTimesDiv, rem)) {898    if (rhsConstVal % divisor == 0 &&899        binExpr.getKind() == AffineExprKind::FloorDiv) {900      expr = quotientTimesDiv.floorDiv(rhsConst);901    } else if (divisor % rhsConstVal == 0 &&902               binExpr.getKind() == AffineExprKind::Mod) {903      expr = rem % rhsConst;904    }905    return;906  }907 908  // Handle the simple case when the LHS expression can be either upper909  // bounded or is a known multiple of RHS constant.910  // lhs floordiv c -> 0 if 0 <= lhs < c,911  // lhs mod c -> 0 if lhs % c = 0.912  if ((isNonNegativeBoundedBy(lhs, operands, rhsConstVal) &&913       binExpr.getKind() == AffineExprKind::FloorDiv) ||914      (getLargestKnownDivisor(lhs, operands) % rhsConstVal == 0 &&915       binExpr.getKind() == AffineExprKind::Mod)) {916    expr = getAffineConstantExpr(0, expr.getContext());917  }918}919 920/// Simplify the expressions in `map` while making use of lower or upper bounds921/// of its operands. If `isMax` is true, the map is to be treated as a max of922/// its result expressions, and min otherwise. Eg: min (d0, d1) -> (8, 4 * d0 +923/// d1) can be simplified to (8) if the operands are respectively lower bounded924/// by 2 and 0 (the second expression can't be lower than 8).925static void simplifyMinOrMaxExprWithOperands(AffineMap &map,926                                             ArrayRef<Value> operands,927                                             bool isMax) {928  // Can't simplify.929  if (operands.empty())930    return;931 932  // Get the upper or lower bound on an affine.for op IV using its range.933  // Get the constant lower or upper bounds on the operands.934  SmallVector<std::optional<int64_t>> constLowerBounds, constUpperBounds;935  constLowerBounds.reserve(operands.size());936  constUpperBounds.reserve(operands.size());937  for (Value operand : operands) {938    constLowerBounds.push_back(getLowerBound(operand));939    constUpperBounds.push_back(getUpperBound(operand));940  }941 942  // We will compute the lower and upper bounds on each of the expressions943  // Then, we will check (depending on max or min) as to whether a specific944  // bound is redundant by checking if its highest (in case of max) and its945  // lowest (in the case of min) value is already lower than (or higher than)946  // the lower bound (or upper bound in the case of min) of another bound.947  SmallVector<std::optional<int64_t>, 4> lowerBounds, upperBounds;948  lowerBounds.reserve(map.getNumResults());949  upperBounds.reserve(map.getNumResults());950  for (AffineExpr e : map.getResults()) {951    if (auto constExpr = dyn_cast<AffineConstantExpr>(e)) {952      lowerBounds.push_back(constExpr.getValue());953      upperBounds.push_back(constExpr.getValue());954    } else {955      lowerBounds.push_back(956          getBoundForAffineExpr(e, map.getNumDims(), map.getNumSymbols(),957                                constLowerBounds, constUpperBounds,958                                /*isUpper=*/false));959      upperBounds.push_back(960          getBoundForAffineExpr(e, map.getNumDims(), map.getNumSymbols(),961                                constLowerBounds, constUpperBounds,962                                /*isUpper=*/true));963    }964  }965 966  // Collect expressions that are not redundant.967  SmallVector<AffineExpr, 4> irredundantExprs;968  for (auto exprEn : llvm::enumerate(map.getResults())) {969    AffineExpr e = exprEn.value();970    unsigned i = exprEn.index();971    // Some expressions can be turned into constants.972    if (lowerBounds[i] && upperBounds[i] && *lowerBounds[i] == *upperBounds[i])973      e = getAffineConstantExpr(*lowerBounds[i], e.getContext());974 975    // Check if the expression is redundant.976    if (isMax) {977      if (!upperBounds[i]) {978        irredundantExprs.push_back(e);979        continue;980      }981      // If there exists another expression such that its lower bound is greater982      // than this expression's upper bound, it's redundant.983      if (!llvm::any_of(llvm::enumerate(lowerBounds), [&](const auto &en) {984            auto otherLowerBound = en.value();985            unsigned pos = en.index();986            if (pos == i || !otherLowerBound)987              return false;988            if (*otherLowerBound > *upperBounds[i])989              return true;990            if (*otherLowerBound < *upperBounds[i])991              return false;992            // Equality case. When both expressions are considered redundant, we993            // don't want to get both of them. We keep the one that appears994            // first.995            if (upperBounds[pos] && lowerBounds[i] &&996                lowerBounds[i] == upperBounds[i] &&997                otherLowerBound == *upperBounds[pos] && i < pos)998              return false;999            return true;1000          }))1001        irredundantExprs.push_back(e);1002    } else {1003      if (!lowerBounds[i]) {1004        irredundantExprs.push_back(e);1005        continue;1006      }1007      // Likewise for the `min` case. Use the complement of the condition above.1008      if (!llvm::any_of(llvm::enumerate(upperBounds), [&](const auto &en) {1009            auto otherUpperBound = en.value();1010            unsigned pos = en.index();1011            if (pos == i || !otherUpperBound)1012              return false;1013            if (*otherUpperBound < *lowerBounds[i])1014              return true;1015            if (*otherUpperBound > *lowerBounds[i])1016              return false;1017            if (lowerBounds[pos] && upperBounds[i] &&1018                lowerBounds[i] == upperBounds[i] &&1019                otherUpperBound == lowerBounds[pos] && i < pos)1020              return false;1021            return true;1022          }))1023        irredundantExprs.push_back(e);1024    }1025  }1026 1027  // Create the map without the redundant expressions.1028  map = AffineMap::get(map.getNumDims(), map.getNumSymbols(), irredundantExprs,1029                       map.getContext());1030}1031 1032/// Simplify the map while exploiting information on the values in `operands`.1033//  Use "unused attribute" marker to silence warning stemming from the inability1034//  to see through the template expansion.1035[[maybe_unused]] static void simplifyMapWithOperands(AffineMap &map,1036                                                     ArrayRef<Value> operands) {1037  assert(map.getNumInputs() == operands.size() && "invalid operands for map");1038  SmallVector<AffineExpr> newResults;1039  newResults.reserve(map.getNumResults());1040  for (AffineExpr expr : map.getResults()) {1041    simplifyExprAndOperands(expr, map.getNumDims(), map.getNumSymbols(),1042                            operands);1043    newResults.push_back(expr);1044  }1045  map = AffineMap::get(map.getNumDims(), map.getNumSymbols(), newResults,1046                       map.getContext());1047}1048 1049/// Assuming `dimOrSym` is a quantity in the apply op map `map` and defined by1050/// `minOp = affine_min(x_1, ..., x_n)`. This function checks that:1051/// `0 < affine_min(x_1, ..., x_n)` and proceeds with replacing the patterns:1052/// ```1053///   dimOrSym.ceildiv(x_k)1054///   (dimOrSym + x_k - 1).floordiv(x_k)1055/// ```1056/// by `1` for all `k` in `1, ..., n`. This is possible because `x / x_k <= 1`.1057///1058///1059/// Warning: ValueBoundsConstraintSet::computeConstantBound is needed to check1060/// `minOp` is positive.1061static LogicalResult replaceAffineMinBoundingBoxExpression(AffineMinOp minOp,1062                                                           AffineExpr dimOrSym,1063                                                           AffineMap *map,1064                                                           ValueRange dims,1065                                                           ValueRange syms) {1066  LDBG() << "replaceAffineMinBoundingBoxExpression: `" << minOp << "`";1067  AffineMap affineMinMap = minOp.getAffineMap();1068 1069  // Check the value is positive.1070  for (unsigned i = 0, e = affineMinMap.getNumResults(); i < e; ++i) {1071    // Compare each expression in the minimum against 0.1072    if (!ValueBoundsConstraintSet::compare(1073            getAsIndexOpFoldResult(minOp.getContext(), 0),1074            ValueBoundsConstraintSet::ComparisonOperator::LT,1075            ValueBoundsConstraintSet::Variable(affineMinMap.getSliceMap(i, 1),1076                                               minOp.getOperands())))1077      return failure();1078  }1079 1080  /// Convert affine symbols and dimensions in minOp to symbols or dimensions in1081  /// the apply op affine map.1082  DenseMap<AffineExpr, AffineExpr> dimSymConversionTable;1083  SmallVector<unsigned> unmappedDims, unmappedSyms;1084  for (auto [i, dim] : llvm::enumerate(minOp.getDimOperands())) {1085    auto it = llvm::find(dims, dim);1086    if (it == dims.end()) {1087      unmappedDims.push_back(i);1088      continue;1089    }1090    dimSymConversionTable[getAffineDimExpr(i, minOp.getContext())] =1091        getAffineDimExpr(it.getIndex(), minOp.getContext());1092  }1093  for (auto [i, sym] : llvm::enumerate(minOp.getSymbolOperands())) {1094    auto it = llvm::find(syms, sym);1095    if (it == syms.end()) {1096      unmappedSyms.push_back(i);1097      continue;1098    }1099    dimSymConversionTable[getAffineSymbolExpr(i, minOp.getContext())] =1100        getAffineSymbolExpr(it.getIndex(), minOp.getContext());1101  }1102 1103  // Create the replacement map.1104  DenseMap<AffineExpr, AffineExpr> repl;1105  AffineExpr c1 = getAffineConstantExpr(1, minOp.getContext());1106  for (AffineExpr expr : affineMinMap.getResults()) {1107    // If we cannot express the result in terms of the apply map symbols and1108    // sims then continue.1109    if (llvm::any_of(unmappedDims,1110                     [&](unsigned i) { return expr.isFunctionOfDim(i); }) ||1111        llvm::any_of(unmappedSyms,1112                     [&](unsigned i) { return expr.isFunctionOfSymbol(i); }))1113      continue;1114 1115    AffineExpr convertedExpr = expr.replace(dimSymConversionTable);1116 1117    // dimOrSym.ceilDiv(expr) -> 11118    repl[dimOrSym.ceilDiv(convertedExpr)] = c1;1119    // (dimOrSym + expr - 1).floorDiv(expr) -> 11120    repl[(dimOrSym + convertedExpr - 1).floorDiv(convertedExpr)] = c1;1121  }1122  AffineMap initialMap = *map;1123  *map = initialMap.replace(repl, initialMap.getNumDims(),1124                            initialMap.getNumSymbols());1125  return success(*map != initialMap);1126}1127 1128/// Recursively traverse `e`. If `e` or one of its sub-expressions has the form1129/// e1 + e2 + ... + eK, where the e_i are a super(multi)set of `exprsToRemove`,1130/// place a map between e and `newVal` + sum({e1, e2, .. eK} - exprsToRemove)1131/// into `replacementsMap`. If no entries were added to `replacementsMap`,1132/// nothing was found.1133static void shortenAddChainsContainingAll(1134    AffineExpr e, const llvm::SmallDenseSet<AffineExpr, 4> &exprsToRemove,1135    AffineExpr newVal, DenseMap<AffineExpr, AffineExpr> &replacementsMap) {1136  auto binOp = dyn_cast<AffineBinaryOpExpr>(e);1137  if (!binOp)1138    return;1139  AffineExpr lhs = binOp.getLHS();1140  AffineExpr rhs = binOp.getRHS();1141  if (binOp.getKind() != AffineExprKind::Add) {1142    shortenAddChainsContainingAll(lhs, exprsToRemove, newVal, replacementsMap);1143    shortenAddChainsContainingAll(rhs, exprsToRemove, newVal, replacementsMap);1144    return;1145  }1146  SmallVector<AffineExpr> toPreserve;1147  llvm::SmallDenseSet<AffineExpr, 4> ourTracker(exprsToRemove);1148  AffineExpr thisTerm = rhs;1149  AffineExpr nextTerm = lhs;1150 1151  while (thisTerm) {1152    if (!ourTracker.erase(thisTerm)) {1153      toPreserve.push_back(thisTerm);1154      shortenAddChainsContainingAll(thisTerm, exprsToRemove, newVal,1155                                    replacementsMap);1156    }1157    auto nextBinOp = dyn_cast_if_present<AffineBinaryOpExpr>(nextTerm);1158    if (!nextBinOp || nextBinOp.getKind() != AffineExprKind::Add) {1159      thisTerm = nextTerm;1160      nextTerm = AffineExpr();1161    } else {1162      thisTerm = nextBinOp.getRHS();1163      nextTerm = nextBinOp.getLHS();1164    }1165  }1166  if (!ourTracker.empty())1167    return;1168  // We reverse the terms to be preserved here in order to preserve1169  // associativity between them.1170  AffineExpr newExpr = newVal;1171  for (AffineExpr preserved : llvm::reverse(toPreserve))1172    newExpr = newExpr + preserved;1173  replacementsMap.insert({e, newExpr});1174}1175 1176/// If this map contains of the expression `x_1 + x_1 * C_1 + ... x_n * C_N +1177/// ...` (not necessarily in order) where the set of the `x_i` is the set of1178/// outputs of an `affine.delinearize_index` whos inverse is that expression,1179/// replace that expression with the input of that delinearize_index op.1180///1181/// `unitDimInput` is the input that was detected as the potential start to this1182/// replacement chain - if it isn't the rightmost result of the delinearization,1183/// this method fails. (This is intended to ensure we don't have redundant scans1184/// over the same expression).1185///1186/// While this currently only handles delinearizations with a constant basis,1187/// that isn't a fundamental limitation.1188///1189/// This is a utility function for `replaceDimOrSym` below.1190static LogicalResult replaceAffineDelinearizeIndexInverseExpression(1191    AffineDelinearizeIndexOp delinOp, Value resultToReplace, AffineMap *map,1192    SmallVectorImpl<Value> &dims, SmallVectorImpl<Value> &syms) {1193  if (!delinOp.getDynamicBasis().empty())1194    return failure();1195  if (resultToReplace != delinOp.getMultiIndex().back())1196    return failure();1197 1198  MLIRContext *ctx = delinOp.getContext();1199  SmallVector<AffineExpr> resToExpr(delinOp.getNumResults(), AffineExpr());1200  for (auto [pos, dim] : llvm::enumerate(dims)) {1201    auto asResult = dyn_cast_if_present<OpResult>(dim);1202    if (!asResult)1203      continue;1204    if (asResult.getOwner() == delinOp.getOperation())1205      resToExpr[asResult.getResultNumber()] = getAffineDimExpr(pos, ctx);1206  }1207  for (auto [pos, sym] : llvm::enumerate(syms)) {1208    auto asResult = dyn_cast_if_present<OpResult>(sym);1209    if (!asResult)1210      continue;1211    if (asResult.getOwner() == delinOp.getOperation())1212      resToExpr[asResult.getResultNumber()] = getAffineSymbolExpr(pos, ctx);1213  }1214  if (llvm::is_contained(resToExpr, AffineExpr()))1215    return failure();1216 1217  bool isDimReplacement = llvm::all_of(resToExpr, llvm::IsaPred<AffineDimExpr>);1218  int64_t stride = 1;1219  llvm::SmallDenseSet<AffineExpr, 4> expectedExprs;1220  // This isn't zip_equal since sometimes the delinearize basis is missing a1221  // size for the first result.1222  for (auto [binding, size] : llvm::zip(1223           llvm::reverse(resToExpr), llvm::reverse(delinOp.getStaticBasis()))) {1224    expectedExprs.insert(binding * getAffineConstantExpr(stride, ctx));1225    stride *= size;1226  }1227  if (resToExpr.size() != delinOp.getStaticBasis().size())1228    expectedExprs.insert(resToExpr[0] * stride);1229 1230  DenseMap<AffineExpr, AffineExpr> replacements;1231  AffineExpr delinInExpr = isDimReplacement1232                               ? getAffineDimExpr(dims.size(), ctx)1233                               : getAffineSymbolExpr(syms.size(), ctx);1234 1235  for (AffineExpr e : map->getResults())1236    shortenAddChainsContainingAll(e, expectedExprs, delinInExpr, replacements);1237  if (replacements.empty())1238    return failure();1239 1240  AffineMap origMap = *map;1241  if (isDimReplacement)1242    dims.push_back(delinOp.getLinearIndex());1243  else1244    syms.push_back(delinOp.getLinearIndex());1245  *map = origMap.replace(replacements, dims.size(), syms.size());1246 1247  // Blank out dead dimensions and symbols1248  for (AffineExpr e : resToExpr) {1249    if (auto d = dyn_cast<AffineDimExpr>(e)) {1250      unsigned pos = d.getPosition();1251      if (!map->isFunctionOfDim(pos))1252        dims[pos] = nullptr;1253    }1254    if (auto s = dyn_cast<AffineSymbolExpr>(e)) {1255      unsigned pos = s.getPosition();1256      if (!map->isFunctionOfSymbol(pos))1257        syms[pos] = nullptr;1258    }1259  }1260  return success();1261}1262 1263/// Replace all occurrences of AffineExpr at position `pos` in `map` by the1264/// defining AffineApplyOp expression and operands.1265/// When `dimOrSymbolPosition < dims.size()`, AffineDimExpr@[pos] is replaced.1266/// When `dimOrSymbolPosition >= dims.size()`,1267/// AffineSymbolExpr@[pos - dims.size()] is replaced.1268/// Mutate `map`,`dims` and `syms` in place as follows:1269///   1. `dims` and `syms` are only appended to.1270///   2. `map` dim and symbols are gradually shifted to higher positions.1271///   3. Old `dim` and `sym` entries are replaced by nullptr1272/// This avoids the need for any bookkeeping.1273/// If `replaceAffineMin` is set to true, additionally triggers more expensive1274/// replacements involving affine_min operations.1275static LogicalResult replaceDimOrSym(AffineMap *map,1276                                     unsigned dimOrSymbolPosition,1277                                     SmallVectorImpl<Value> &dims,1278                                     SmallVectorImpl<Value> &syms,1279                                     bool replaceAffineMin) {1280  MLIRContext *ctx = map->getContext();1281  bool isDimReplacement = (dimOrSymbolPosition < dims.size());1282  unsigned pos = isDimReplacement ? dimOrSymbolPosition1283                                  : dimOrSymbolPosition - dims.size();1284  Value &v = isDimReplacement ? dims[pos] : syms[pos];1285  if (!v)1286    return failure();1287 1288  if (auto minOp = v.getDefiningOp<AffineMinOp>(); minOp && replaceAffineMin) {1289    AffineExpr dimOrSym = isDimReplacement ? getAffineDimExpr(pos, ctx)1290                                           : getAffineSymbolExpr(pos, ctx);1291    return replaceAffineMinBoundingBoxExpression(minOp, dimOrSym, map, dims,1292                                                 syms);1293  }1294 1295  if (auto delinOp = v.getDefiningOp<affine::AffineDelinearizeIndexOp>()) {1296    return replaceAffineDelinearizeIndexInverseExpression(delinOp, v, map, dims,1297                                                          syms);1298  }1299 1300  auto affineApply = v.getDefiningOp<AffineApplyOp>();1301  if (!affineApply)1302    return failure();1303 1304  // At this point we will perform a replacement of `v`, set the entry in `dim`1305  // or `sym` to nullptr immediately.1306  v = nullptr;1307 1308  // Compute the map, dims and symbols coming from the AffineApplyOp.1309  AffineMap composeMap = affineApply.getAffineMap();1310  assert(composeMap.getNumResults() == 1 && "affine.apply with >1 results");1311  SmallVector<Value> composeOperands(affineApply.getMapOperands().begin(),1312                                     affineApply.getMapOperands().end());1313  // Canonicalize the map to promote dims to symbols when possible. This is to1314  // avoid generating invalid maps.1315  canonicalizeMapAndOperands(&composeMap, &composeOperands);1316  AffineExpr replacementExpr =1317      composeMap.shiftDims(dims.size()).shiftSymbols(syms.size()).getResult(0);1318  ValueRange composeDims =1319      ArrayRef<Value>(composeOperands).take_front(composeMap.getNumDims());1320  ValueRange composeSyms =1321      ArrayRef<Value>(composeOperands).take_back(composeMap.getNumSymbols());1322  AffineExpr toReplace = isDimReplacement ? getAffineDimExpr(pos, ctx)1323                                          : getAffineSymbolExpr(pos, ctx);1324 1325  // Append the dims and symbols where relevant and perform the replacement.1326  dims.append(composeDims.begin(), composeDims.end());1327  syms.append(composeSyms.begin(), composeSyms.end());1328  *map = map->replace(toReplace, replacementExpr, dims.size(), syms.size());1329 1330  return success();1331}1332 1333/// Iterate over `operands` and fold away all those produced by an AffineApplyOp1334/// iteratively. Perform canonicalization of map and operands as well as1335/// AffineMap simplification. `map` and `operands` are mutated in place.1336static void composeAffineMapAndOperands(AffineMap *map,1337                                        SmallVectorImpl<Value> *operands,1338                                        bool composeAffineMin = false) {1339  if (map->getNumResults() == 0) {1340    canonicalizeMapAndOperands(map, operands);1341    *map = simplifyAffineMap(*map);1342    return;1343  }1344 1345  MLIRContext *ctx = map->getContext();1346  SmallVector<Value, 4> dims(operands->begin(),1347                             operands->begin() + map->getNumDims());1348  SmallVector<Value, 4> syms(operands->begin() + map->getNumDims(),1349                             operands->end());1350 1351  // Iterate over dims and symbols coming from AffineApplyOp and replace until1352  // exhaustion. This iteratively mutates `map`, `dims` and `syms`. Both `dims`1353  // and `syms` can only increase by construction.1354  // The implementation uses a `while` loop to support the case of symbols1355  // that may be constructed from dims ;this may be overkill.1356  while (true) {1357    bool changed = false;1358    for (unsigned pos = 0; pos != dims.size() + syms.size(); ++pos)1359      if ((changed |=1360           succeeded(replaceDimOrSym(map, pos, dims, syms, composeAffineMin))))1361        break;1362    if (!changed)1363      break;1364  }1365 1366  // Clear operands so we can fill them anew.1367  operands->clear();1368 1369  // At this point we may have introduced null operands, prune them out before1370  // canonicalizing map and operands.1371  unsigned nDims = 0, nSyms = 0;1372  SmallVector<AffineExpr, 4> dimReplacements, symReplacements;1373  dimReplacements.reserve(dims.size());1374  symReplacements.reserve(syms.size());1375  for (auto *container : {&dims, &syms}) {1376    bool isDim = (container == &dims);1377    auto &repls = isDim ? dimReplacements : symReplacements;1378    for (const auto &en : llvm::enumerate(*container)) {1379      Value v = en.value();1380      if (!v) {1381        assert(isDim ? !map->isFunctionOfDim(en.index())1382                     : !map->isFunctionOfSymbol(en.index()) &&1383                           "map is function of unexpected expr@pos");1384        repls.push_back(getAffineConstantExpr(0, ctx));1385        continue;1386      }1387      repls.push_back(isDim ? getAffineDimExpr(nDims++, ctx)1388                            : getAffineSymbolExpr(nSyms++, ctx));1389      operands->push_back(v);1390    }1391  }1392  *map = map->replaceDimsAndSymbols(dimReplacements, symReplacements, nDims,1393                                    nSyms);1394 1395  // Canonicalize and simplify before returning.1396  canonicalizeMapAndOperands(map, operands);1397  *map = simplifyAffineMap(*map);1398}1399 1400void mlir::affine::fullyComposeAffineMapAndOperands(1401    AffineMap *map, SmallVectorImpl<Value> *operands, bool composeAffineMin) {1402  while (llvm::any_of(*operands, [](Value v) {1403    return isa_and_nonnull<AffineApplyOp>(v.getDefiningOp());1404  })) {1405    composeAffineMapAndOperands(map, operands, composeAffineMin);1406  }1407  // Additional trailing step for AffineMinOps in case no chains of AffineApply.1408  if (composeAffineMin && llvm::any_of(*operands, [](Value v) {1409        return isa_and_nonnull<AffineMinOp>(v.getDefiningOp());1410      })) {1411    composeAffineMapAndOperands(map, operands, composeAffineMin);1412  }1413}1414 1415AffineApplyOp1416mlir::affine::makeComposedAffineApply(OpBuilder &b, Location loc, AffineMap map,1417                                      ArrayRef<OpFoldResult> operands,1418                                      bool composeAffineMin) {1419  SmallVector<Value> valueOperands;1420  map = foldAttributesIntoMap(b, map, operands, valueOperands);1421  composeAffineMapAndOperands(&map, &valueOperands, composeAffineMin);1422  assert(map);1423  return AffineApplyOp::create(b, loc, map, valueOperands);1424}1425 1426AffineApplyOp1427mlir::affine::makeComposedAffineApply(OpBuilder &b, Location loc, AffineExpr e,1428                                      ArrayRef<OpFoldResult> operands,1429                                      bool composeAffineMin) {1430  return makeComposedAffineApply(1431      b, loc,1432      AffineMap::inferFromExprList(ArrayRef<AffineExpr>{e}, b.getContext())1433          .front(),1434      operands, composeAffineMin);1435}1436 1437/// Composes the given affine map with the given list of operands, pulling in1438/// the maps from any affine.apply operations that supply the operands.1439static void composeMultiResultAffineMap(AffineMap &map,1440                                        SmallVectorImpl<Value> &operands,1441                                        bool composeAffineMin = false) {1442  // Compose and canonicalize each expression in the map individually because1443  // composition only applies to single-result maps, collecting potentially1444  // duplicate operands in a single list with shifted dimensions and symbols.1445  SmallVector<Value> dims, symbols;1446  SmallVector<AffineExpr> exprs;1447  for (unsigned i : llvm::seq<unsigned>(0, map.getNumResults())) {1448    SmallVector<Value> submapOperands(operands.begin(), operands.end());1449    AffineMap submap = map.getSubMap({i});1450    fullyComposeAffineMapAndOperands(&submap, &submapOperands,1451                                     composeAffineMin);1452    canonicalizeMapAndOperands(&submap, &submapOperands);1453    unsigned numNewDims = submap.getNumDims();1454    submap = submap.shiftDims(dims.size()).shiftSymbols(symbols.size());1455    llvm::append_range(dims,1456                       ArrayRef<Value>(submapOperands).take_front(numNewDims));1457    llvm::append_range(symbols,1458                       ArrayRef<Value>(submapOperands).drop_front(numNewDims));1459    exprs.push_back(submap.getResult(0));1460  }1461 1462  // Canonicalize the map created from composed expressions to deduplicate the1463  // dimension and symbol operands.1464  operands = llvm::to_vector(llvm::concat<Value>(dims, symbols));1465  map = AffineMap::get(dims.size(), symbols.size(), exprs, map.getContext());1466  canonicalizeMapAndOperands(&map, &operands);1467}1468 1469OpFoldResult mlir::affine::makeComposedFoldedAffineApply(1470    OpBuilder &b, Location loc, AffineMap map, ArrayRef<OpFoldResult> operands,1471    bool composeAffineMin) {1472  assert(map.getNumResults() == 1 && "building affine.apply with !=1 result");1473 1474  // Create new builder without a listener, so that no notification is1475  // triggered if the op is folded.1476  // TODO: OpBuilder::createOrFold should return OpFoldResults, then this1477  // workaround is no longer needed.1478  OpBuilder newBuilder(b.getContext());1479  newBuilder.setInsertionPoint(b.getInsertionBlock(), b.getInsertionPoint());1480 1481  // Create op.1482  AffineApplyOp applyOp =1483      makeComposedAffineApply(newBuilder, loc, map, operands, composeAffineMin);1484 1485  // Get constant operands.1486  SmallVector<Attribute> constOperands(applyOp->getNumOperands());1487  for (unsigned i = 0, e = constOperands.size(); i != e; ++i)1488    matchPattern(applyOp->getOperand(i), m_Constant(&constOperands[i]));1489 1490  // Try to fold the operation.1491  SmallVector<OpFoldResult> foldResults;1492  if (failed(applyOp->fold(constOperands, foldResults)) ||1493      foldResults.empty()) {1494    if (OpBuilder::Listener *listener = b.getListener())1495      listener->notifyOperationInserted(applyOp, /*previous=*/{});1496    return applyOp.getResult();1497  }1498 1499  applyOp->erase();1500  return llvm::getSingleElement(foldResults);1501}1502 1503OpFoldResult mlir::affine::makeComposedFoldedAffineApply(1504    OpBuilder &b, Location loc, AffineExpr expr,1505    ArrayRef<OpFoldResult> operands, bool composeAffineMin) {1506  return makeComposedFoldedAffineApply(1507      b, loc,1508      AffineMap::inferFromExprList(ArrayRef<AffineExpr>{expr}, b.getContext())1509          .front(),1510      operands, composeAffineMin);1511}1512 1513SmallVector<OpFoldResult>1514mlir::affine::makeComposedFoldedMultiResultAffineApply(1515    OpBuilder &b, Location loc, AffineMap map, ArrayRef<OpFoldResult> operands,1516    bool composeAffineMin) {1517  return llvm::map_to_vector(1518      llvm::seq<unsigned>(0, map.getNumResults()), [&](unsigned i) {1519        return makeComposedFoldedAffineApply(b, loc, map.getSubMap({i}),1520                                             operands, composeAffineMin);1521      });1522}1523 1524template <typename OpTy>1525static OpTy makeComposedMinMax(OpBuilder &b, Location loc, AffineMap map,1526                               ArrayRef<OpFoldResult> operands) {1527  SmallVector<Value> valueOperands;1528  map = foldAttributesIntoMap(b, map, operands, valueOperands);1529  composeMultiResultAffineMap(map, valueOperands);1530  return OpTy::create(b, loc, b.getIndexType(), map, valueOperands);1531}1532 1533AffineMinOp1534mlir::affine::makeComposedAffineMin(OpBuilder &b, Location loc, AffineMap map,1535                                    ArrayRef<OpFoldResult> operands) {1536  return makeComposedMinMax<AffineMinOp>(b, loc, map, operands);1537}1538 1539template <typename OpTy>1540static OpFoldResult makeComposedFoldedMinMax(OpBuilder &b, Location loc,1541                                             AffineMap map,1542                                             ArrayRef<OpFoldResult> operands) {1543  // Create new builder without a listener, so that no notification is1544  // triggered if the op is folded.1545  // TODO: OpBuilder::createOrFold should return OpFoldResults, then this1546  // workaround is no longer needed.1547  OpBuilder newBuilder(b.getContext());1548  newBuilder.setInsertionPoint(b.getInsertionBlock(), b.getInsertionPoint());1549 1550  // Create op.1551  auto minMaxOp = makeComposedMinMax<OpTy>(newBuilder, loc, map, operands);1552 1553  // Get constant operands.1554  SmallVector<Attribute> constOperands(minMaxOp->getNumOperands());1555  for (unsigned i = 0, e = constOperands.size(); i != e; ++i)1556    matchPattern(minMaxOp->getOperand(i), m_Constant(&constOperands[i]));1557 1558  // Try to fold the operation.1559  SmallVector<OpFoldResult> foldResults;1560  if (failed(minMaxOp->fold(constOperands, foldResults)) ||1561      foldResults.empty()) {1562    if (OpBuilder::Listener *listener = b.getListener())1563      listener->notifyOperationInserted(minMaxOp, /*previous=*/{});1564    return minMaxOp.getResult();1565  }1566 1567  minMaxOp->erase();1568  return llvm::getSingleElement(foldResults);1569}1570 1571OpFoldResult1572mlir::affine::makeComposedFoldedAffineMin(OpBuilder &b, Location loc,1573                                          AffineMap map,1574                                          ArrayRef<OpFoldResult> operands) {1575  return makeComposedFoldedMinMax<AffineMinOp>(b, loc, map, operands);1576}1577 1578OpFoldResult1579mlir::affine::makeComposedFoldedAffineMax(OpBuilder &b, Location loc,1580                                          AffineMap map,1581                                          ArrayRef<OpFoldResult> operands) {1582  return makeComposedFoldedMinMax<AffineMaxOp>(b, loc, map, operands);1583}1584 1585// A symbol may appear as a dim in affine.apply operations. This function1586// canonicalizes dims that are valid symbols into actual symbols.1587template <class MapOrSet>1588static void canonicalizePromotedSymbols(MapOrSet *mapOrSet,1589                                        SmallVectorImpl<Value> *operands) {1590  if (!mapOrSet || operands->empty())1591    return;1592 1593  assert(mapOrSet->getNumInputs() == operands->size() &&1594         "map/set inputs must match number of operands");1595 1596  auto *context = mapOrSet->getContext();1597  SmallVector<Value, 8> resultOperands;1598  resultOperands.reserve(operands->size());1599  SmallVector<Value, 8> remappedSymbols;1600  remappedSymbols.reserve(operands->size());1601  unsigned nextDim = 0;1602  unsigned nextSym = 0;1603  unsigned oldNumSyms = mapOrSet->getNumSymbols();1604  SmallVector<AffineExpr, 8> dimRemapping(mapOrSet->getNumDims());1605  for (unsigned i = 0, e = mapOrSet->getNumInputs(); i != e; ++i) {1606    if (i < mapOrSet->getNumDims()) {1607      if (isValidSymbol((*operands)[i])) {1608        // This is a valid symbol that appears as a dim, canonicalize it.1609        dimRemapping[i] = getAffineSymbolExpr(oldNumSyms + nextSym++, context);1610        remappedSymbols.push_back((*operands)[i]);1611      } else {1612        dimRemapping[i] = getAffineDimExpr(nextDim++, context);1613        resultOperands.push_back((*operands)[i]);1614      }1615    } else {1616      resultOperands.push_back((*operands)[i]);1617    }1618  }1619 1620  resultOperands.append(remappedSymbols.begin(), remappedSymbols.end());1621  *operands = resultOperands;1622  *mapOrSet = mapOrSet->replaceDimsAndSymbols(1623      dimRemapping, /*symReplacements=*/{}, nextDim, oldNumSyms + nextSym);1624 1625  assert(mapOrSet->getNumInputs() == operands->size() &&1626         "map/set inputs must match number of operands");1627}1628 1629/// A valid affine dimension may appear as a symbol in affine.apply operations.1630/// Given an application of `operands` to an affine map or integer set1631/// `mapOrSet`, this function canonicalizes symbols of `mapOrSet` that are valid1632/// dims, but not valid symbols into actual dims. Without such a legalization,1633/// the affine.apply will be invalid. This method is the exact inverse of1634/// canonicalizePromotedSymbols.1635template <class MapOrSet>1636static void legalizeDemotedDims(MapOrSet &mapOrSet,1637                                SmallVectorImpl<Value> &operands) {1638  if (!mapOrSet || operands.empty())1639    return;1640 1641  unsigned numOperands = operands.size();1642 1643  assert(mapOrSet.getNumInputs() == numOperands &&1644         "map/set inputs must match number of operands");1645 1646  auto *context = mapOrSet.getContext();1647  SmallVector<Value, 8> resultOperands;1648  resultOperands.reserve(numOperands);1649  SmallVector<Value, 8> remappedDims;1650  remappedDims.reserve(numOperands);1651  SmallVector<Value, 8> symOperands;1652  symOperands.reserve(mapOrSet.getNumSymbols());1653  unsigned nextSym = 0;1654  unsigned nextDim = 0;1655  unsigned oldNumDims = mapOrSet.getNumDims();1656  SmallVector<AffineExpr, 8> symRemapping(mapOrSet.getNumSymbols());1657  resultOperands.assign(operands.begin(), operands.begin() + oldNumDims);1658  for (unsigned i = oldNumDims, e = mapOrSet.getNumInputs(); i != e; ++i) {1659    if (operands[i] && isValidDim(operands[i]) && !isValidSymbol(operands[i])) {1660      // This is a valid dim that appears as a symbol, legalize it.1661      symRemapping[i - oldNumDims] =1662          getAffineDimExpr(oldNumDims + nextDim++, context);1663      remappedDims.push_back(operands[i]);1664    } else {1665      symRemapping[i - oldNumDims] = getAffineSymbolExpr(nextSym++, context);1666      symOperands.push_back(operands[i]);1667    }1668  }1669 1670  append_range(resultOperands, remappedDims);1671  append_range(resultOperands, symOperands);1672  operands = resultOperands;1673  mapOrSet = mapOrSet.replaceDimsAndSymbols(1674      /*dimReplacements=*/{}, symRemapping, oldNumDims + nextDim, nextSym);1675 1676  assert(mapOrSet.getNumInputs() == operands.size() &&1677         "map/set inputs must match number of operands");1678}1679 1680// Works for either an affine map or an integer set.1681template <class MapOrSet>1682static void canonicalizeMapOrSetAndOperands(MapOrSet *mapOrSet,1683                                            SmallVectorImpl<Value> *operands) {1684  static_assert(llvm::is_one_of<MapOrSet, AffineMap, IntegerSet>::value,1685                "Argument must be either of AffineMap or IntegerSet type");1686 1687  if (!mapOrSet || operands->empty())1688    return;1689 1690  assert(mapOrSet->getNumInputs() == operands->size() &&1691         "map/set inputs must match number of operands");1692 1693  canonicalizePromotedSymbols<MapOrSet>(mapOrSet, operands);1694  legalizeDemotedDims<MapOrSet>(*mapOrSet, *operands);1695 1696  // Check to see what dims are used.1697  llvm::SmallBitVector usedDims(mapOrSet->getNumDims());1698  llvm::SmallBitVector usedSyms(mapOrSet->getNumSymbols());1699  mapOrSet->walkExprs([&](AffineExpr expr) {1700    if (auto dimExpr = dyn_cast<AffineDimExpr>(expr))1701      usedDims[dimExpr.getPosition()] = true;1702    else if (auto symExpr = dyn_cast<AffineSymbolExpr>(expr))1703      usedSyms[symExpr.getPosition()] = true;1704  });1705 1706  auto *context = mapOrSet->getContext();1707 1708  SmallVector<Value, 8> resultOperands;1709  resultOperands.reserve(operands->size());1710 1711  llvm::SmallDenseMap<Value, AffineExpr, 8> seenDims;1712  SmallVector<AffineExpr, 8> dimRemapping(mapOrSet->getNumDims());1713  unsigned nextDim = 0;1714  for (unsigned i = 0, e = mapOrSet->getNumDims(); i != e; ++i) {1715    if (usedDims[i]) {1716      // Remap dim positions for duplicate operands.1717      auto it = seenDims.find((*operands)[i]);1718      if (it == seenDims.end()) {1719        dimRemapping[i] = getAffineDimExpr(nextDim++, context);1720        resultOperands.push_back((*operands)[i]);1721        seenDims.insert(std::make_pair((*operands)[i], dimRemapping[i]));1722      } else {1723        dimRemapping[i] = it->second;1724      }1725    }1726  }1727  llvm::SmallDenseMap<Value, AffineExpr, 8> seenSymbols;1728  SmallVector<AffineExpr, 8> symRemapping(mapOrSet->getNumSymbols());1729  unsigned nextSym = 0;1730  for (unsigned i = 0, e = mapOrSet->getNumSymbols(); i != e; ++i) {1731    if (!usedSyms[i])1732      continue;1733    // Handle constant operands (only needed for symbolic operands since1734    // constant operands in dimensional positions would have already been1735    // promoted to symbolic positions above).1736    IntegerAttr operandCst;1737    if (matchPattern((*operands)[i + mapOrSet->getNumDims()],1738                     m_Constant(&operandCst))) {1739      symRemapping[i] =1740          getAffineConstantExpr(operandCst.getValue().getSExtValue(), context);1741      continue;1742    }1743    // Remap symbol positions for duplicate operands.1744    auto it = seenSymbols.find((*operands)[i + mapOrSet->getNumDims()]);1745    if (it == seenSymbols.end()) {1746      symRemapping[i] = getAffineSymbolExpr(nextSym++, context);1747      resultOperands.push_back((*operands)[i + mapOrSet->getNumDims()]);1748      seenSymbols.insert(std::make_pair((*operands)[i + mapOrSet->getNumDims()],1749                                        symRemapping[i]));1750    } else {1751      symRemapping[i] = it->second;1752    }1753  }1754  *mapOrSet = mapOrSet->replaceDimsAndSymbols(dimRemapping, symRemapping,1755                                              nextDim, nextSym);1756  *operands = resultOperands;1757}1758 1759void mlir::affine::canonicalizeMapAndOperands(1760    AffineMap *map, SmallVectorImpl<Value> *operands) {1761  canonicalizeMapOrSetAndOperands<AffineMap>(map, operands);1762}1763 1764void mlir::affine::canonicalizeSetAndOperands(1765    IntegerSet *set, SmallVectorImpl<Value> *operands) {1766  canonicalizeMapOrSetAndOperands<IntegerSet>(set, operands);1767}1768 1769namespace {1770/// Simplify AffineApply, AffineLoad, and AffineStore operations by composing1771/// maps that supply results into them.1772///1773template <typename AffineOpTy>1774struct SimplifyAffineOp : public OpRewritePattern<AffineOpTy> {1775  using OpRewritePattern<AffineOpTy>::OpRewritePattern;1776 1777  /// Replace the affine op with another instance of it with the supplied1778  /// map and mapOperands.1779  void replaceAffineOp(PatternRewriter &rewriter, AffineOpTy affineOp,1780                       AffineMap map, ArrayRef<Value> mapOperands) const;1781 1782  LogicalResult matchAndRewrite(AffineOpTy affineOp,1783                                PatternRewriter &rewriter) const override {1784    static_assert(1785        llvm::is_one_of<AffineOpTy, AffineLoadOp, AffinePrefetchOp,1786                        AffineStoreOp, AffineApplyOp, AffineMinOp, AffineMaxOp,1787                        AffineVectorStoreOp, AffineVectorLoadOp>::value,1788        "affine load/store/vectorstore/vectorload/apply/prefetch/min/max op "1789        "expected");1790    auto map = affineOp.getAffineMap();1791    AffineMap oldMap = map;1792    auto oldOperands = affineOp.getMapOperands();1793    SmallVector<Value, 8> resultOperands(oldOperands);1794    composeAffineMapAndOperands(&map, &resultOperands);1795    canonicalizeMapAndOperands(&map, &resultOperands);1796    simplifyMapWithOperands(map, resultOperands);1797    if (map == oldMap && std::equal(oldOperands.begin(), oldOperands.end(),1798                                    resultOperands.begin()))1799      return failure();1800 1801    replaceAffineOp(rewriter, affineOp, map, resultOperands);1802    return success();1803  }1804};1805 1806// Specialize the template to account for the different build signatures for1807// affine load, store, and apply ops.1808template <>1809void SimplifyAffineOp<AffineLoadOp>::replaceAffineOp(1810    PatternRewriter &rewriter, AffineLoadOp load, AffineMap map,1811    ArrayRef<Value> mapOperands) const {1812  rewriter.replaceOpWithNewOp<AffineLoadOp>(load, load.getMemRef(), map,1813                                            mapOperands);1814}1815template <>1816void SimplifyAffineOp<AffinePrefetchOp>::replaceAffineOp(1817    PatternRewriter &rewriter, AffinePrefetchOp prefetch, AffineMap map,1818    ArrayRef<Value> mapOperands) const {1819  rewriter.replaceOpWithNewOp<AffinePrefetchOp>(1820      prefetch, prefetch.getMemref(), map, mapOperands, prefetch.getIsWrite(),1821      prefetch.getLocalityHint(), prefetch.getIsDataCache());1822}1823template <>1824void SimplifyAffineOp<AffineStoreOp>::replaceAffineOp(1825    PatternRewriter &rewriter, AffineStoreOp store, AffineMap map,1826    ArrayRef<Value> mapOperands) const {1827  rewriter.replaceOpWithNewOp<AffineStoreOp>(1828      store, store.getValueToStore(), store.getMemRef(), map, mapOperands);1829}1830template <>1831void SimplifyAffineOp<AffineVectorLoadOp>::replaceAffineOp(1832    PatternRewriter &rewriter, AffineVectorLoadOp vectorload, AffineMap map,1833    ArrayRef<Value> mapOperands) const {1834  rewriter.replaceOpWithNewOp<AffineVectorLoadOp>(1835      vectorload, vectorload.getVectorType(), vectorload.getMemRef(), map,1836      mapOperands);1837}1838template <>1839void SimplifyAffineOp<AffineVectorStoreOp>::replaceAffineOp(1840    PatternRewriter &rewriter, AffineVectorStoreOp vectorstore, AffineMap map,1841    ArrayRef<Value> mapOperands) const {1842  rewriter.replaceOpWithNewOp<AffineVectorStoreOp>(1843      vectorstore, vectorstore.getValueToStore(), vectorstore.getMemRef(), map,1844      mapOperands);1845}1846 1847// Generic version for ops that don't have extra operands.1848template <typename AffineOpTy>1849void SimplifyAffineOp<AffineOpTy>::replaceAffineOp(1850    PatternRewriter &rewriter, AffineOpTy op, AffineMap map,1851    ArrayRef<Value> mapOperands) const {1852  rewriter.replaceOpWithNewOp<AffineOpTy>(op, map, mapOperands);1853}1854} // namespace1855 1856void AffineApplyOp::getCanonicalizationPatterns(RewritePatternSet &results,1857                                                MLIRContext *context) {1858  results.add<SimplifyAffineOp<AffineApplyOp>>(context);1859}1860 1861//===----------------------------------------------------------------------===//1862// AffineDmaStartOp1863//===----------------------------------------------------------------------===//1864 1865// TODO: Check that map operands are loop IVs or symbols.1866void AffineDmaStartOp::build(OpBuilder &builder, OperationState &result,1867                             Value srcMemRef, AffineMap srcMap,1868                             ValueRange srcIndices, Value destMemRef,1869                             AffineMap dstMap, ValueRange destIndices,1870                             Value tagMemRef, AffineMap tagMap,1871                             ValueRange tagIndices, Value numElements,1872                             Value stride, Value elementsPerStride) {1873  result.addOperands(srcMemRef);1874  result.addAttribute(getSrcMapAttrStrName(), AffineMapAttr::get(srcMap));1875  result.addOperands(srcIndices);1876  result.addOperands(destMemRef);1877  result.addAttribute(getDstMapAttrStrName(), AffineMapAttr::get(dstMap));1878  result.addOperands(destIndices);1879  result.addOperands(tagMemRef);1880  result.addAttribute(getTagMapAttrStrName(), AffineMapAttr::get(tagMap));1881  result.addOperands(tagIndices);1882  result.addOperands(numElements);1883  if (stride) {1884    result.addOperands({stride, elementsPerStride});1885  }1886}1887 1888AffineDmaStartOp AffineDmaStartOp::create(1889    OpBuilder &builder, Location location, Value srcMemRef, AffineMap srcMap,1890    ValueRange srcIndices, Value destMemRef, AffineMap dstMap,1891    ValueRange destIndices, Value tagMemRef, AffineMap tagMap,1892    ValueRange tagIndices, Value numElements, Value stride,1893    Value elementsPerStride) {1894  mlir::OperationState state(location, getOperationName());1895  build(builder, state, srcMemRef, srcMap, srcIndices, destMemRef, dstMap,1896        destIndices, tagMemRef, tagMap, tagIndices, numElements, stride,1897        elementsPerStride);1898  auto result = dyn_cast<AffineDmaStartOp>(builder.create(state));1899  assert(result && "builder didn't return the right type");1900  return result;1901}1902 1903AffineDmaStartOp AffineDmaStartOp::create(1904    ImplicitLocOpBuilder &builder, Value srcMemRef, AffineMap srcMap,1905    ValueRange srcIndices, Value destMemRef, AffineMap dstMap,1906    ValueRange destIndices, Value tagMemRef, AffineMap tagMap,1907    ValueRange tagIndices, Value numElements, Value stride,1908    Value elementsPerStride) {1909  return create(builder, builder.getLoc(), srcMemRef, srcMap, srcIndices,1910                destMemRef, dstMap, destIndices, tagMemRef, tagMap, tagIndices,1911                numElements, stride, elementsPerStride);1912}1913 1914void AffineDmaStartOp::print(OpAsmPrinter &p) {1915  p << " " << getSrcMemRef() << '[';1916  p.printAffineMapOfSSAIds(getSrcMapAttr(), getSrcIndices());1917  p << "], " << getDstMemRef() << '[';1918  p.printAffineMapOfSSAIds(getDstMapAttr(), getDstIndices());1919  p << "], " << getTagMemRef() << '[';1920  p.printAffineMapOfSSAIds(getTagMapAttr(), getTagIndices());1921  p << "], " << getNumElements();1922  if (isStrided()) {1923    p << ", " << getStride();1924    p << ", " << getNumElementsPerStride();1925  }1926  p << " : " << getSrcMemRefType() << ", " << getDstMemRefType() << ", "1927    << getTagMemRefType();1928}1929 1930// Parse AffineDmaStartOp.1931// Ex:1932//   affine.dma_start %src[%i, %j], %dst[%k, %l], %tag[%index], %size,1933//     %stride, %num_elt_per_stride1934//       : memref<3076 x f32, 0>, memref<1024 x f32, 2>, memref<1 x i32>1935//1936ParseResult AffineDmaStartOp::parse(OpAsmParser &parser,1937                                    OperationState &result) {1938  OpAsmParser::UnresolvedOperand srcMemRefInfo;1939  AffineMapAttr srcMapAttr;1940  SmallVector<OpAsmParser::UnresolvedOperand, 4> srcMapOperands;1941  OpAsmParser::UnresolvedOperand dstMemRefInfo;1942  AffineMapAttr dstMapAttr;1943  SmallVector<OpAsmParser::UnresolvedOperand, 4> dstMapOperands;1944  OpAsmParser::UnresolvedOperand tagMemRefInfo;1945  AffineMapAttr tagMapAttr;1946  SmallVector<OpAsmParser::UnresolvedOperand, 4> tagMapOperands;1947  OpAsmParser::UnresolvedOperand numElementsInfo;1948  SmallVector<OpAsmParser::UnresolvedOperand, 2> strideInfo;1949 1950  SmallVector<Type, 3> types;1951  auto indexType = parser.getBuilder().getIndexType();1952 1953  // Parse and resolve the following list of operands:1954  // *) dst memref followed by its affine maps operands (in square brackets).1955  // *) src memref followed by its affine map operands (in square brackets).1956  // *) tag memref followed by its affine map operands (in square brackets).1957  // *) number of elements transferred by DMA operation.1958  if (parser.parseOperand(srcMemRefInfo) ||1959      parser.parseAffineMapOfSSAIds(srcMapOperands, srcMapAttr,1960                                    getSrcMapAttrStrName(),1961                                    result.attributes) ||1962      parser.parseComma() || parser.parseOperand(dstMemRefInfo) ||1963      parser.parseAffineMapOfSSAIds(dstMapOperands, dstMapAttr,1964                                    getDstMapAttrStrName(),1965                                    result.attributes) ||1966      parser.parseComma() || parser.parseOperand(tagMemRefInfo) ||1967      parser.parseAffineMapOfSSAIds(tagMapOperands, tagMapAttr,1968                                    getTagMapAttrStrName(),1969                                    result.attributes) ||1970      parser.parseComma() || parser.parseOperand(numElementsInfo))1971    return failure();1972 1973  // Parse optional stride and elements per stride.1974  if (parser.parseTrailingOperandList(strideInfo))1975    return failure();1976 1977  if (!strideInfo.empty() && strideInfo.size() != 2) {1978    return parser.emitError(parser.getNameLoc(),1979                            "expected two stride related operands");1980  }1981  bool isStrided = strideInfo.size() == 2;1982 1983  if (parser.parseColonTypeList(types))1984    return failure();1985 1986  if (types.size() != 3)1987    return parser.emitError(parser.getNameLoc(), "expected three types");1988 1989  if (parser.resolveOperand(srcMemRefInfo, types[0], result.operands) ||1990      parser.resolveOperands(srcMapOperands, indexType, result.operands) ||1991      parser.resolveOperand(dstMemRefInfo, types[1], result.operands) ||1992      parser.resolveOperands(dstMapOperands, indexType, result.operands) ||1993      parser.resolveOperand(tagMemRefInfo, types[2], result.operands) ||1994      parser.resolveOperands(tagMapOperands, indexType, result.operands) ||1995      parser.resolveOperand(numElementsInfo, indexType, result.operands))1996    return failure();1997 1998  if (isStrided) {1999    if (parser.resolveOperands(strideInfo, indexType, result.operands))2000      return failure();2001  }2002 2003  // Check that src/dst/tag operand counts match their map.numInputs.2004  if (srcMapOperands.size() != srcMapAttr.getValue().getNumInputs() ||2005      dstMapOperands.size() != dstMapAttr.getValue().getNumInputs() ||2006      tagMapOperands.size() != tagMapAttr.getValue().getNumInputs())2007    return parser.emitError(parser.getNameLoc(),2008                            "memref operand count not equal to map.numInputs");2009  return success();2010}2011 2012LogicalResult AffineDmaStartOp::verifyInvariantsImpl() {2013  if (!llvm::isa<MemRefType>(getOperand(getSrcMemRefOperandIndex()).getType()))2014    return emitOpError("expected DMA source to be of memref type");2015  if (!llvm::isa<MemRefType>(getOperand(getDstMemRefOperandIndex()).getType()))2016    return emitOpError("expected DMA destination to be of memref type");2017  if (!llvm::isa<MemRefType>(getOperand(getTagMemRefOperandIndex()).getType()))2018    return emitOpError("expected DMA tag to be of memref type");2019 2020  unsigned numInputsAllMaps = getSrcMap().getNumInputs() +2021                              getDstMap().getNumInputs() +2022                              getTagMap().getNumInputs();2023  if (getNumOperands() != numInputsAllMaps + 3 + 1 &&2024      getNumOperands() != numInputsAllMaps + 3 + 1 + 2) {2025    return emitOpError("incorrect number of operands");2026  }2027 2028  Region *scope = getAffineScope(*this);2029  for (auto idx : getSrcIndices()) {2030    if (!idx.getType().isIndex())2031      return emitOpError("src index to dma_start must have 'index' type");2032    if (!isValidAffineIndexOperand(idx, scope))2033      return emitOpError(2034          "src index must be a valid dimension or symbol identifier");2035  }2036  for (auto idx : getDstIndices()) {2037    if (!idx.getType().isIndex())2038      return emitOpError("dst index to dma_start must have 'index' type");2039    if (!isValidAffineIndexOperand(idx, scope))2040      return emitOpError(2041          "dst index must be a valid dimension or symbol identifier");2042  }2043  for (auto idx : getTagIndices()) {2044    if (!idx.getType().isIndex())2045      return emitOpError("tag index to dma_start must have 'index' type");2046    if (!isValidAffineIndexOperand(idx, scope))2047      return emitOpError(2048          "tag index must be a valid dimension or symbol identifier");2049  }2050  return success();2051}2052 2053LogicalResult AffineDmaStartOp::fold(ArrayRef<Attribute> cstOperands,2054                                     SmallVectorImpl<OpFoldResult> &results) {2055  /// dma_start(memrefcast) -> dma_start2056  return memref::foldMemRefCast(*this);2057}2058 2059void AffineDmaStartOp::getEffects(2060    SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>2061        &effects) {2062  effects.emplace_back(MemoryEffects::Read::get(), &getSrcMemRefMutable(),2063                       SideEffects::DefaultResource::get());2064  effects.emplace_back(MemoryEffects::Write::get(), &getDstMemRefMutable(),2065                       SideEffects::DefaultResource::get());2066  effects.emplace_back(MemoryEffects::Read::get(), &getTagMemRefMutable(),2067                       SideEffects::DefaultResource::get());2068}2069 2070//===----------------------------------------------------------------------===//2071// AffineDmaWaitOp2072//===----------------------------------------------------------------------===//2073 2074// TODO: Check that map operands are loop IVs or symbols.2075void AffineDmaWaitOp::build(OpBuilder &builder, OperationState &result,2076                            Value tagMemRef, AffineMap tagMap,2077                            ValueRange tagIndices, Value numElements) {2078  result.addOperands(tagMemRef);2079  result.addAttribute(getTagMapAttrStrName(), AffineMapAttr::get(tagMap));2080  result.addOperands(tagIndices);2081  result.addOperands(numElements);2082}2083 2084AffineDmaWaitOp AffineDmaWaitOp::create(OpBuilder &builder, Location location,2085                                        Value tagMemRef, AffineMap tagMap,2086                                        ValueRange tagIndices,2087                                        Value numElements) {2088  mlir::OperationState state(location, getOperationName());2089  build(builder, state, tagMemRef, tagMap, tagIndices, numElements);2090  auto result = dyn_cast<AffineDmaWaitOp>(builder.create(state));2091  assert(result && "builder didn't return the right type");2092  return result;2093}2094 2095AffineDmaWaitOp AffineDmaWaitOp::create(ImplicitLocOpBuilder &builder,2096                                        Value tagMemRef, AffineMap tagMap,2097                                        ValueRange tagIndices,2098                                        Value numElements) {2099  return create(builder, builder.getLoc(), tagMemRef, tagMap, tagIndices,2100                numElements);2101}2102 2103void AffineDmaWaitOp::print(OpAsmPrinter &p) {2104  p << " " << getTagMemRef() << '[';2105  SmallVector<Value, 2> operands(getTagIndices());2106  p.printAffineMapOfSSAIds(getTagMapAttr(), operands);2107  p << "], ";2108  p.printOperand(getNumElements());2109  p << " : " << getTagMemRef().getType();2110}2111 2112// Parse AffineDmaWaitOp.2113// Eg:2114//   affine.dma_wait %tag[%index], %num_elements2115//     : memref<1 x i32, (d0) -> (d0), 4>2116//2117ParseResult AffineDmaWaitOp::parse(OpAsmParser &parser,2118                                   OperationState &result) {2119  OpAsmParser::UnresolvedOperand tagMemRefInfo;2120  AffineMapAttr tagMapAttr;2121  SmallVector<OpAsmParser::UnresolvedOperand, 2> tagMapOperands;2122  Type type;2123  auto indexType = parser.getBuilder().getIndexType();2124  OpAsmParser::UnresolvedOperand numElementsInfo;2125 2126  // Parse tag memref, its map operands, and dma size.2127  if (parser.parseOperand(tagMemRefInfo) ||2128      parser.parseAffineMapOfSSAIds(tagMapOperands, tagMapAttr,2129                                    getTagMapAttrStrName(),2130                                    result.attributes) ||2131      parser.parseComma() || parser.parseOperand(numElementsInfo) ||2132      parser.parseColonType(type) ||2133      parser.resolveOperand(tagMemRefInfo, type, result.operands) ||2134      parser.resolveOperands(tagMapOperands, indexType, result.operands) ||2135      parser.resolveOperand(numElementsInfo, indexType, result.operands))2136    return failure();2137 2138  if (!llvm::isa<MemRefType>(type))2139    return parser.emitError(parser.getNameLoc(),2140                            "expected tag to be of memref type");2141 2142  if (tagMapOperands.size() != tagMapAttr.getValue().getNumInputs())2143    return parser.emitError(parser.getNameLoc(),2144                            "tag memref operand count != to map.numInputs");2145  return success();2146}2147 2148LogicalResult AffineDmaWaitOp::verifyInvariantsImpl() {2149  if (!llvm::isa<MemRefType>(getOperand(0).getType()))2150    return emitOpError("expected DMA tag to be of memref type");2151  Region *scope = getAffineScope(*this);2152  for (auto idx : getTagIndices()) {2153    if (!idx.getType().isIndex())2154      return emitOpError("index to dma_wait must have 'index' type");2155    if (!isValidAffineIndexOperand(idx, scope))2156      return emitOpError(2157          "index must be a valid dimension or symbol identifier");2158  }2159  return success();2160}2161 2162LogicalResult AffineDmaWaitOp::fold(ArrayRef<Attribute> cstOperands,2163                                    SmallVectorImpl<OpFoldResult> &results) {2164  /// dma_wait(memrefcast) -> dma_wait2165  return memref::foldMemRefCast(*this);2166}2167 2168void AffineDmaWaitOp::getEffects(2169    SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>2170        &effects) {2171  effects.emplace_back(MemoryEffects::Read::get(), &getTagMemRefMutable(),2172                       SideEffects::DefaultResource::get());2173}2174 2175//===----------------------------------------------------------------------===//2176// AffineForOp2177//===----------------------------------------------------------------------===//2178 2179/// 'bodyBuilder' is used to build the body of affine.for. If iterArgs and2180/// bodyBuilder are empty/null, we include default terminator op.2181void AffineForOp::build(OpBuilder &builder, OperationState &result,2182                        ValueRange lbOperands, AffineMap lbMap,2183                        ValueRange ubOperands, AffineMap ubMap, int64_t step,2184                        ValueRange iterArgs, BodyBuilderFn bodyBuilder) {2185  assert(((!lbMap && lbOperands.empty()) ||2186          lbOperands.size() == lbMap.getNumInputs()) &&2187         "lower bound operand count does not match the affine map");2188  assert(((!ubMap && ubOperands.empty()) ||2189          ubOperands.size() == ubMap.getNumInputs()) &&2190         "upper bound operand count does not match the affine map");2191  assert(step > 0 && "step has to be a positive integer constant");2192 2193  OpBuilder::InsertionGuard guard(builder);2194 2195  // Set variadic segment sizes.2196  result.addAttribute(2197      getOperandSegmentSizeAttr(),2198      builder.getDenseI32ArrayAttr({static_cast<int32_t>(lbOperands.size()),2199                                    static_cast<int32_t>(ubOperands.size()),2200                                    static_cast<int32_t>(iterArgs.size())}));2201 2202  for (Value val : iterArgs)2203    result.addTypes(val.getType());2204 2205  // Add an attribute for the step.2206  result.addAttribute(getStepAttrName(result.name),2207                      builder.getIntegerAttr(builder.getIndexType(), step));2208 2209  // Add the lower bound.2210  result.addAttribute(getLowerBoundMapAttrName(result.name),2211                      AffineMapAttr::get(lbMap));2212  result.addOperands(lbOperands);2213 2214  // Add the upper bound.2215  result.addAttribute(getUpperBoundMapAttrName(result.name),2216                      AffineMapAttr::get(ubMap));2217  result.addOperands(ubOperands);2218 2219  result.addOperands(iterArgs);2220  // Create a region and a block for the body.  The argument of the region is2221  // the loop induction variable.2222  Region *bodyRegion = result.addRegion();2223  Block *bodyBlock = builder.createBlock(bodyRegion);2224  Value inductionVar =2225      bodyBlock->addArgument(builder.getIndexType(), result.location);2226  for (Value val : iterArgs)2227    bodyBlock->addArgument(val.getType(), val.getLoc());2228 2229  // Create the default terminator if the builder is not provided and if the2230  // iteration arguments are not provided. Otherwise, leave this to the caller2231  // because we don't know which values to return from the loop.2232  if (iterArgs.empty() && !bodyBuilder) {2233    ensureTerminator(*bodyRegion, builder, result.location);2234  } else if (bodyBuilder) {2235    OpBuilder::InsertionGuard guard(builder);2236    builder.setInsertionPointToStart(bodyBlock);2237    bodyBuilder(builder, result.location, inductionVar,2238                bodyBlock->getArguments().drop_front());2239  }2240}2241 2242void AffineForOp::build(OpBuilder &builder, OperationState &result, int64_t lb,2243                        int64_t ub, int64_t step, ValueRange iterArgs,2244                        BodyBuilderFn bodyBuilder) {2245  auto lbMap = AffineMap::getConstantMap(lb, builder.getContext());2246  auto ubMap = AffineMap::getConstantMap(ub, builder.getContext());2247  return build(builder, result, {}, lbMap, {}, ubMap, step, iterArgs,2248               bodyBuilder);2249}2250 2251LogicalResult AffineForOp::verifyRegions() {2252  // Check that the body defines as single block argument for the induction2253  // variable.2254  auto *body = getBody();2255  if (body->getNumArguments() == 0 || !body->getArgument(0).getType().isIndex())2256    return emitOpError("expected body to have a single index argument for the "2257                       "induction variable");2258 2259  // Verify that the bound operands are valid dimension/symbols.2260  /// Lower bound.2261  if (getLowerBoundMap().getNumInputs() > 0)2262    if (failed(verifyDimAndSymbolIdentifiers(*this, getLowerBoundOperands(),2263                                             getLowerBoundMap().getNumDims())))2264      return failure();2265  /// Upper bound.2266  if (getUpperBoundMap().getNumInputs() > 0)2267    if (failed(verifyDimAndSymbolIdentifiers(*this, getUpperBoundOperands(),2268                                             getUpperBoundMap().getNumDims())))2269      return failure();2270  if (getLowerBoundMap().getNumResults() < 1)2271    return emitOpError("expected lower bound map to have at least one result");2272  if (getUpperBoundMap().getNumResults() < 1)2273    return emitOpError("expected upper bound map to have at least one result");2274 2275  unsigned opNumResults = getNumResults();2276  if (opNumResults == 0)2277    return success();2278 2279  // If ForOp defines values, check that the number and types of the defined2280  // values match ForOp initial iter operands and backedge basic block2281  // arguments.2282  if (getNumIterOperands() != opNumResults)2283    return emitOpError(2284        "mismatch between the number of loop-carried values and results");2285  if (getNumRegionIterArgs() != opNumResults)2286    return emitOpError(2287        "mismatch between the number of basic block args and results");2288 2289  return success();2290}2291 2292/// Parse a for operation loop bounds.2293static ParseResult parseBound(bool isLower, OperationState &result,2294                              OpAsmParser &p) {2295  // 'min' / 'max' prefixes are generally syntactic sugar, but are required if2296  // the map has multiple results.2297  bool failedToParsedMinMax =2298      failed(p.parseOptionalKeyword(isLower ? "max" : "min"));2299 2300  auto &builder = p.getBuilder();2301  auto boundAttrStrName =2302      isLower ? AffineForOp::getLowerBoundMapAttrName(result.name)2303              : AffineForOp::getUpperBoundMapAttrName(result.name);2304 2305  // Parse ssa-id as identity map.2306  SmallVector<OpAsmParser::UnresolvedOperand, 1> boundOpInfos;2307  if (p.parseOperandList(boundOpInfos))2308    return failure();2309 2310  if (!boundOpInfos.empty()) {2311    // Check that only one operand was parsed.2312    if (boundOpInfos.size() > 1)2313      return p.emitError(p.getNameLoc(),2314                         "expected only one loop bound operand");2315 2316    // TODO: improve error message when SSA value is not of index type.2317    // Currently it is 'use of value ... expects different type than prior uses'2318    if (p.resolveOperand(boundOpInfos.front(), builder.getIndexType(),2319                         result.operands))2320      return failure();2321 2322    // Create an identity map using symbol id. This representation is optimized2323    // for storage. Analysis passes may expand it into a multi-dimensional map2324    // if desired.2325    AffineMap map = builder.getSymbolIdentityMap();2326    result.addAttribute(boundAttrStrName, AffineMapAttr::get(map));2327    return success();2328  }2329 2330  // Get the attribute location.2331  SMLoc attrLoc = p.getCurrentLocation();2332 2333  Attribute boundAttr;2334  if (p.parseAttribute(boundAttr, builder.getIndexType(), boundAttrStrName,2335                       result.attributes))2336    return failure();2337 2338  // Parse full form - affine map followed by dim and symbol list.2339  if (auto affineMapAttr = dyn_cast<AffineMapAttr>(boundAttr)) {2340    unsigned currentNumOperands = result.operands.size();2341    unsigned numDims;2342    if (parseDimAndSymbolList(p, result.operands, numDims))2343      return failure();2344 2345    auto map = affineMapAttr.getValue();2346    if (map.getNumDims() != numDims)2347      return p.emitError(2348          p.getNameLoc(),2349          "dim operand count and affine map dim count must match");2350 2351    unsigned numDimAndSymbolOperands =2352        result.operands.size() - currentNumOperands;2353    if (numDims + map.getNumSymbols() != numDimAndSymbolOperands)2354      return p.emitError(2355          p.getNameLoc(),2356          "symbol operand count and affine map symbol count must match");2357 2358    // If the map has multiple results, make sure that we parsed the min/max2359    // prefix.2360    if (map.getNumResults() > 1 && failedToParsedMinMax) {2361      if (isLower) {2362        return p.emitError(attrLoc, "lower loop bound affine map with "2363                                    "multiple results requires 'max' prefix");2364      }2365      return p.emitError(attrLoc, "upper loop bound affine map with multiple "2366                                  "results requires 'min' prefix");2367    }2368    return success();2369  }2370 2371  // Parse custom assembly form.2372  if (auto integerAttr = dyn_cast<IntegerAttr>(boundAttr)) {2373    result.attributes.pop_back();2374    result.addAttribute(2375        boundAttrStrName,2376        AffineMapAttr::get(builder.getConstantAffineMap(integerAttr.getInt())));2377    return success();2378  }2379 2380  return p.emitError(2381      p.getNameLoc(),2382      "expected valid affine map representation for loop bounds");2383}2384 2385ParseResult AffineForOp::parse(OpAsmParser &parser, OperationState &result) {2386  auto &builder = parser.getBuilder();2387  OpAsmParser::Argument inductionVariable;2388  inductionVariable.type = builder.getIndexType();2389  // Parse the induction variable followed by '='.2390  if (parser.parseArgument(inductionVariable) || parser.parseEqual())2391    return failure();2392 2393  // Parse loop bounds.2394  int64_t numOperands = result.operands.size();2395  if (parseBound(/*isLower=*/true, result, parser))2396    return failure();2397  int64_t numLbOperands = result.operands.size() - numOperands;2398  if (parser.parseKeyword("to", " between bounds"))2399    return failure();2400  numOperands = result.operands.size();2401  if (parseBound(/*isLower=*/false, result, parser))2402    return failure();2403  int64_t numUbOperands = result.operands.size() - numOperands;2404 2405  // Parse the optional loop step, we default to 1 if one is not present.2406  if (parser.parseOptionalKeyword("step")) {2407    result.addAttribute(2408        getStepAttrName(result.name),2409        builder.getIntegerAttr(builder.getIndexType(), /*value=*/1));2410  } else {2411    SMLoc stepLoc = parser.getCurrentLocation();2412    IntegerAttr stepAttr;2413    if (parser.parseAttribute(stepAttr, builder.getIndexType(),2414                              getStepAttrName(result.name).data(),2415                              result.attributes))2416      return failure();2417 2418    if (stepAttr.getValue().isNegative())2419      return parser.emitError(2420          stepLoc,2421          "expected step to be representable as a positive signed integer");2422  }2423 2424  // Parse the optional initial iteration arguments.2425  SmallVector<OpAsmParser::Argument, 4> regionArgs;2426  SmallVector<OpAsmParser::UnresolvedOperand, 4> operands;2427 2428  // Induction variable.2429  regionArgs.push_back(inductionVariable);2430 2431  if (succeeded(parser.parseOptionalKeyword("iter_args"))) {2432    // Parse assignment list and results type list.2433    if (parser.parseAssignmentList(regionArgs, operands) ||2434        parser.parseArrowTypeList(result.types))2435      return failure();2436    // Resolve input operands.2437    for (auto argOperandType :2438         llvm::zip(llvm::drop_begin(regionArgs), operands, result.types)) {2439      Type type = std::get<2>(argOperandType);2440      std::get<0>(argOperandType).type = type;2441      if (parser.resolveOperand(std::get<1>(argOperandType), type,2442                                result.operands))2443        return failure();2444    }2445  }2446 2447  result.addAttribute(2448      getOperandSegmentSizeAttr(),2449      builder.getDenseI32ArrayAttr({static_cast<int32_t>(numLbOperands),2450                                    static_cast<int32_t>(numUbOperands),2451                                    static_cast<int32_t>(operands.size())}));2452 2453  // Parse the body region.2454  Region *body = result.addRegion();2455  if (regionArgs.size() != result.types.size() + 1)2456    return parser.emitError(2457        parser.getNameLoc(),2458        "mismatch between the number of loop-carried values and results");2459  if (parser.parseRegion(*body, regionArgs))2460    return failure();2461 2462  AffineForOp::ensureTerminator(*body, builder, result.location);2463 2464  // Parse the optional attribute list.2465  return parser.parseOptionalAttrDict(result.attributes);2466}2467 2468static void printBound(AffineMapAttr boundMap,2469                       Operation::operand_range boundOperands,2470                       const char *prefix, OpAsmPrinter &p) {2471  AffineMap map = boundMap.getValue();2472 2473  // Check if this bound should be printed using custom assembly form.2474  // The decision to restrict printing custom assembly form to trivial cases2475  // comes from the will to roundtrip MLIR binary -> text -> binary in a2476  // lossless way.2477  // Therefore, custom assembly form parsing and printing is only supported for2478  // zero-operand constant maps and single symbol operand identity maps.2479  if (map.getNumResults() == 1) {2480    AffineExpr expr = map.getResult(0);2481 2482    // Print constant bound.2483    if (map.getNumDims() == 0 && map.getNumSymbols() == 0) {2484      if (auto constExpr = dyn_cast<AffineConstantExpr>(expr)) {2485        p << constExpr.getValue();2486        return;2487      }2488    }2489 2490    // Print bound that consists of a single SSA symbol if the map is over a2491    // single symbol.2492    if (map.getNumDims() == 0 && map.getNumSymbols() == 1) {2493      if (isa<AffineSymbolExpr>(expr)) {2494        p.printOperand(*boundOperands.begin());2495        return;2496      }2497    }2498  } else {2499    // Map has multiple results. Print 'min' or 'max' prefix.2500    p << prefix << ' ';2501  }2502 2503  // Print the map and its operands.2504  p << boundMap;2505  printDimAndSymbolList(boundOperands.begin(), boundOperands.end(),2506                        map.getNumDims(), p);2507}2508 2509unsigned AffineForOp::getNumIterOperands() {2510  AffineMap lbMap = getLowerBoundMapAttr().getValue();2511  AffineMap ubMap = getUpperBoundMapAttr().getValue();2512 2513  return getNumOperands() - lbMap.getNumInputs() - ubMap.getNumInputs();2514}2515 2516std::optional<MutableArrayRef<OpOperand>>2517AffineForOp::getYieldedValuesMutable() {2518  return cast<AffineYieldOp>(getBody()->getTerminator()).getOperandsMutable();2519}2520 2521void AffineForOp::print(OpAsmPrinter &p) {2522  p << ' ';2523  p.printRegionArgument(getBody()->getArgument(0), /*argAttrs=*/{},2524                        /*omitType=*/true);2525  p << " = ";2526  printBound(getLowerBoundMapAttr(), getLowerBoundOperands(), "max", p);2527  p << " to ";2528  printBound(getUpperBoundMapAttr(), getUpperBoundOperands(), "min", p);2529 2530  if (getStepAsInt() != 1)2531    p << " step " << getStepAsInt();2532 2533  bool printBlockTerminators = false;2534  if (getNumIterOperands() > 0) {2535    p << " iter_args(";2536    auto regionArgs = getRegionIterArgs();2537    auto operands = getInits();2538 2539    llvm::interleaveComma(llvm::zip(regionArgs, operands), p, [&](auto it) {2540      p << std::get<0>(it) << " = " << std::get<1>(it);2541    });2542    p << ") -> (" << getResultTypes() << ")";2543    printBlockTerminators = true;2544  }2545 2546  p << ' ';2547  p.printRegion(getRegion(), /*printEntryBlockArgs=*/false,2548                printBlockTerminators);2549  p.printOptionalAttrDict(2550      (*this)->getAttrs(),2551      /*elidedAttrs=*/{getLowerBoundMapAttrName(getOperation()->getName()),2552                       getUpperBoundMapAttrName(getOperation()->getName()),2553                       getStepAttrName(getOperation()->getName()),2554                       getOperandSegmentSizeAttr()});2555}2556 2557/// Fold the constant bounds of a loop.2558static LogicalResult foldLoopBounds(AffineForOp forOp) {2559  auto foldLowerOrUpperBound = [&forOp](bool lower) {2560    // Check to see if each of the operands is the result of a constant.  If2561    // so, get the value.  If not, ignore it.2562    SmallVector<Attribute, 8> operandConstants;2563    auto boundOperands =2564        lower ? forOp.getLowerBoundOperands() : forOp.getUpperBoundOperands();2565    for (auto operand : boundOperands) {2566      Attribute operandCst;2567      matchPattern(operand, m_Constant(&operandCst));2568      operandConstants.push_back(operandCst);2569    }2570 2571    AffineMap boundMap =2572        lower ? forOp.getLowerBoundMap() : forOp.getUpperBoundMap();2573    assert(boundMap.getNumResults() >= 1 &&2574           "bound maps should have at least one result");2575    SmallVector<Attribute, 4> foldedResults;2576    if (failed(boundMap.constantFold(operandConstants, foldedResults)))2577      return failure();2578 2579    // Compute the max or min as applicable over the results.2580    assert(!foldedResults.empty() && "bounds should have at least one result");2581    auto maxOrMin = llvm::cast<IntegerAttr>(foldedResults[0]).getValue();2582    for (unsigned i = 1, e = foldedResults.size(); i < e; i++) {2583      auto foldedResult = llvm::cast<IntegerAttr>(foldedResults[i]).getValue();2584      maxOrMin = lower ? llvm::APIntOps::smax(maxOrMin, foldedResult)2585                       : llvm::APIntOps::smin(maxOrMin, foldedResult);2586    }2587    lower ? forOp.setConstantLowerBound(maxOrMin.getSExtValue())2588          : forOp.setConstantUpperBound(maxOrMin.getSExtValue());2589    return success();2590  };2591 2592  // Try to fold the lower bound.2593  bool folded = false;2594  if (!forOp.hasConstantLowerBound())2595    folded |= succeeded(foldLowerOrUpperBound(/*lower=*/true));2596 2597  // Try to fold the upper bound.2598  if (!forOp.hasConstantUpperBound())2599    folded |= succeeded(foldLowerOrUpperBound(/*lower=*/false));2600  return success(folded);2601}2602 2603/// Returns constant trip count in trivial cases.2604static std::optional<uint64_t> getTrivialConstantTripCount(AffineForOp forOp) {2605  int64_t step = forOp.getStepAsInt();2606  if (!forOp.hasConstantBounds() || step <= 0)2607    return std::nullopt;2608  int64_t lb = forOp.getConstantLowerBound();2609  int64_t ub = forOp.getConstantUpperBound();2610  return ub - lb <= 0 ? 0 : (ub - lb + step - 1) / step;2611}2612 2613/// Fold the empty loop.2614static SmallVector<OpFoldResult> AffineForEmptyLoopFolder(AffineForOp forOp) {2615  if (!llvm::hasSingleElement(*forOp.getBody()))2616    return {};2617  if (forOp.getNumResults() == 0)2618    return {};2619  std::optional<uint64_t> tripCount = getTrivialConstantTripCount(forOp);2620  if (tripCount == 0) {2621    // The initial values of the iteration arguments would be the op's2622    // results.2623    return forOp.getInits();2624  }2625  SmallVector<Value, 4> replacements;2626  auto yieldOp = cast<AffineYieldOp>(forOp.getBody()->getTerminator());2627  auto iterArgs = forOp.getRegionIterArgs();2628  bool hasValDefinedOutsideLoop = false;2629  bool iterArgsNotInOrder = false;2630  for (unsigned i = 0, e = yieldOp->getNumOperands(); i < e; ++i) {2631    Value val = yieldOp.getOperand(i);2632    BlockArgument *iterArgIt = llvm::find(iterArgs, val);2633    // TODO: It should be possible to perform a replacement by computing the2634    // last value of the IV based on the bounds and the step.2635    if (val == forOp.getInductionVar())2636      return {};2637    if (iterArgIt == iterArgs.end()) {2638      // `val` is defined outside of the loop.2639      assert(forOp.isDefinedOutsideOfLoop(val) &&2640             "must be defined outside of the loop");2641      hasValDefinedOutsideLoop = true;2642      replacements.push_back(val);2643    } else {2644      unsigned pos = std::distance(iterArgs.begin(), iterArgIt);2645      if (pos != i)2646        iterArgsNotInOrder = true;2647      replacements.push_back(forOp.getInits()[pos]);2648    }2649  }2650  // Bail out when the trip count is unknown and the loop returns any value2651  // defined outside of the loop or any iterArg out of order.2652  if (!tripCount.has_value() &&2653      (hasValDefinedOutsideLoop || iterArgsNotInOrder))2654    return {};2655  // Bail out when the loop iterates more than once and it returns any iterArg2656  // out of order.2657  if (tripCount.has_value() && tripCount.value() >= 2 && iterArgsNotInOrder)2658    return {};2659  return llvm::to_vector_of<OpFoldResult>(replacements);2660}2661 2662/// Canonicalize the bounds of the given loop.2663static LogicalResult canonicalizeLoopBounds(AffineForOp forOp) {2664  SmallVector<Value, 4> lbOperands(forOp.getLowerBoundOperands());2665  SmallVector<Value, 4> ubOperands(forOp.getUpperBoundOperands());2666 2667  auto lbMap = forOp.getLowerBoundMap();2668  auto ubMap = forOp.getUpperBoundMap();2669  auto prevLbMap = lbMap;2670  auto prevUbMap = ubMap;2671 2672  composeAffineMapAndOperands(&lbMap, &lbOperands);2673  canonicalizeMapAndOperands(&lbMap, &lbOperands);2674  simplifyMinOrMaxExprWithOperands(lbMap, lbOperands, /*isMax=*/true);2675  simplifyMinOrMaxExprWithOperands(ubMap, ubOperands, /*isMax=*/false);2676  lbMap = removeDuplicateExprs(lbMap);2677 2678  composeAffineMapAndOperands(&ubMap, &ubOperands);2679  canonicalizeMapAndOperands(&ubMap, &ubOperands);2680  ubMap = removeDuplicateExprs(ubMap);2681 2682  // Any canonicalization change always leads to updated map(s).2683  if (lbMap == prevLbMap && ubMap == prevUbMap)2684    return failure();2685 2686  if (lbMap != prevLbMap)2687    forOp.setLowerBound(lbOperands, lbMap);2688  if (ubMap != prevUbMap)2689    forOp.setUpperBound(ubOperands, ubMap);2690  return success();2691}2692 2693/// Returns true if the affine.for has zero iterations in trivial cases.2694static bool hasTrivialZeroTripCount(AffineForOp op) {2695  return getTrivialConstantTripCount(op) == 0;2696}2697 2698LogicalResult AffineForOp::fold(FoldAdaptor adaptor,2699                                SmallVectorImpl<OpFoldResult> &results) {2700  bool folded = succeeded(foldLoopBounds(*this));2701  folded |= succeeded(canonicalizeLoopBounds(*this));2702  if (hasTrivialZeroTripCount(*this) && getNumResults() != 0) {2703    // The initial values of the loop-carried variables (iter_args) are the2704    // results of the op. But this must be avoided for an affine.for op that2705    // does not return any results. Since ops that do not return results cannot2706    // be folded away, we would enter an infinite loop of folds on the same2707    // affine.for op.2708    results.assign(getInits().begin(), getInits().end());2709    folded = true;2710  }2711  SmallVector<OpFoldResult> foldResults = AffineForEmptyLoopFolder(*this);2712  if (!foldResults.empty()) {2713    results.assign(foldResults);2714    folded = true;2715  }2716  return success(folded);2717}2718 2719OperandRange AffineForOp::getEntrySuccessorOperands(RegionSuccessor successor) {2720  assert((successor.isParent() || successor.getSuccessor() == &getRegion()) &&2721         "invalid region point");2722 2723  // The initial operands map to the loop arguments after the induction2724  // variable or are forwarded to the results when the trip count is zero.2725  return getInits();2726}2727 2728void AffineForOp::getSuccessorRegions(2729    RegionBranchPoint point, SmallVectorImpl<RegionSuccessor> &regions) {2730  assert((point.isParent() ||2731          point.getTerminatorPredecessorOrNull()->getParentRegion() ==2732              &getRegion()) &&2733         "expected loop region");2734  // The loop may typically branch back to its body or to the parent operation.2735  // If the predecessor is the parent op and the trip count is known to be at2736  // least one, branch into the body using the iterator arguments. And in cases2737  // we know the trip count is zero, it can only branch back to its parent.2738  std::optional<uint64_t> tripCount = getTrivialConstantTripCount(*this);2739  if (tripCount.has_value()) {2740    if (!point.isParent()) {2741      // From the loop body, if the trip count is one, we can only branch back2742      // to the parent.2743      if (tripCount == 1) {2744        regions.push_back(RegionSuccessor(getOperation(), getResults()));2745        return;2746      }2747      if (tripCount == 0)2748        return;2749    } else {2750      if (tripCount.value() > 0) {2751        regions.push_back(RegionSuccessor(&getRegion(), getRegionIterArgs()));2752        return;2753      }2754      if (tripCount.value() == 0) {2755        regions.push_back(RegionSuccessor(getOperation(), getResults()));2756        return;2757      }2758    }2759  }2760 2761  // In all other cases, the loop may branch back to itself or the parent2762  // operation.2763  regions.push_back(RegionSuccessor(&getRegion(), getRegionIterArgs()));2764  regions.push_back(RegionSuccessor(getOperation(), getResults()));2765}2766 2767AffineBound AffineForOp::getLowerBound() {2768  return AffineBound(*this, getLowerBoundOperands(), getLowerBoundMap());2769}2770 2771AffineBound AffineForOp::getUpperBound() {2772  return AffineBound(*this, getUpperBoundOperands(), getUpperBoundMap());2773}2774 2775void AffineForOp::setLowerBound(ValueRange lbOperands, AffineMap map) {2776  assert(lbOperands.size() == map.getNumInputs());2777  assert(map.getNumResults() >= 1 && "bound map has at least one result");2778  getLowerBoundOperandsMutable().assign(lbOperands);2779  setLowerBoundMap(map);2780}2781 2782void AffineForOp::setUpperBound(ValueRange ubOperands, AffineMap map) {2783  assert(ubOperands.size() == map.getNumInputs());2784  assert(map.getNumResults() >= 1 && "bound map has at least one result");2785  getUpperBoundOperandsMutable().assign(ubOperands);2786  setUpperBoundMap(map);2787}2788 2789bool AffineForOp::hasConstantLowerBound() {2790  return getLowerBoundMap().isSingleConstant();2791}2792 2793bool AffineForOp::hasConstantUpperBound() {2794  return getUpperBoundMap().isSingleConstant();2795}2796 2797int64_t AffineForOp::getConstantLowerBound() {2798  return getLowerBoundMap().getSingleConstantResult();2799}2800 2801int64_t AffineForOp::getConstantUpperBound() {2802  return getUpperBoundMap().getSingleConstantResult();2803}2804 2805void AffineForOp::setConstantLowerBound(int64_t value) {2806  setLowerBound({}, AffineMap::getConstantMap(value, getContext()));2807}2808 2809void AffineForOp::setConstantUpperBound(int64_t value) {2810  setUpperBound({}, AffineMap::getConstantMap(value, getContext()));2811}2812 2813AffineForOp::operand_range AffineForOp::getControlOperands() {2814  return {operand_begin(), operand_begin() + getLowerBoundOperands().size() +2815                               getUpperBoundOperands().size()};2816}2817 2818bool AffineForOp::matchingBoundOperandList() {2819  auto lbMap = getLowerBoundMap();2820  auto ubMap = getUpperBoundMap();2821  if (lbMap.getNumDims() != ubMap.getNumDims() ||2822      lbMap.getNumSymbols() != ubMap.getNumSymbols())2823    return false;2824 2825  unsigned numOperands = lbMap.getNumInputs();2826  for (unsigned i = 0, e = lbMap.getNumInputs(); i < e; i++) {2827    // Compare Value 's.2828    if (getOperand(i) != getOperand(numOperands + i))2829      return false;2830  }2831  return true;2832}2833 2834SmallVector<Region *> AffineForOp::getLoopRegions() { return {&getRegion()}; }2835 2836std::optional<SmallVector<Value>> AffineForOp::getLoopInductionVars() {2837  return SmallVector<Value>{getInductionVar()};2838}2839 2840std::optional<SmallVector<OpFoldResult>> AffineForOp::getLoopLowerBounds() {2841  if (!hasConstantLowerBound())2842    return std::nullopt;2843  OpBuilder b(getContext());2844  return SmallVector<OpFoldResult>{2845      OpFoldResult(b.getI64IntegerAttr(getConstantLowerBound()))};2846}2847 2848std::optional<SmallVector<OpFoldResult>> AffineForOp::getLoopSteps() {2849  OpBuilder b(getContext());2850  return SmallVector<OpFoldResult>{2851      OpFoldResult(b.getI64IntegerAttr(getStepAsInt()))};2852}2853 2854std::optional<SmallVector<OpFoldResult>> AffineForOp::getLoopUpperBounds() {2855  if (!hasConstantUpperBound())2856    return {};2857  OpBuilder b(getContext());2858  return SmallVector<OpFoldResult>{2859      OpFoldResult(b.getI64IntegerAttr(getConstantUpperBound()))};2860}2861 2862FailureOr<LoopLikeOpInterface> AffineForOp::replaceWithAdditionalYields(2863    RewriterBase &rewriter, ValueRange newInitOperands,2864    bool replaceInitOperandUsesInLoop,2865    const NewYieldValuesFn &newYieldValuesFn) {2866  // Create a new loop before the existing one, with the extra operands.2867  OpBuilder::InsertionGuard g(rewriter);2868  rewriter.setInsertionPoint(getOperation());2869  auto inits = llvm::to_vector(getInits());2870  inits.append(newInitOperands.begin(), newInitOperands.end());2871  AffineForOp newLoop = AffineForOp::create(2872      rewriter, getLoc(), getLowerBoundOperands(), getLowerBoundMap(),2873      getUpperBoundOperands(), getUpperBoundMap(), getStepAsInt(), inits);2874 2875  // Generate the new yield values and append them to the scf.yield operation.2876  auto yieldOp = cast<AffineYieldOp>(getBody()->getTerminator());2877  ArrayRef<BlockArgument> newIterArgs =2878      newLoop.getBody()->getArguments().take_back(newInitOperands.size());2879  {2880    OpBuilder::InsertionGuard g(rewriter);2881    rewriter.setInsertionPoint(yieldOp);2882    SmallVector<Value> newYieldedValues =2883        newYieldValuesFn(rewriter, getLoc(), newIterArgs);2884    assert(newInitOperands.size() == newYieldedValues.size() &&2885           "expected as many new yield values as new iter operands");2886    rewriter.modifyOpInPlace(yieldOp, [&]() {2887      yieldOp.getOperandsMutable().append(newYieldedValues);2888    });2889  }2890 2891  // Move the loop body to the new op.2892  rewriter.mergeBlocks(getBody(), newLoop.getBody(),2893                       newLoop.getBody()->getArguments().take_front(2894                           getBody()->getNumArguments()));2895 2896  if (replaceInitOperandUsesInLoop) {2897    // Replace all uses of `newInitOperands` with the corresponding basic block2898    // arguments.2899    for (auto it : llvm::zip(newInitOperands, newIterArgs)) {2900      rewriter.replaceUsesWithIf(std::get<0>(it), std::get<1>(it),2901                                 [&](OpOperand &use) {2902                                   Operation *user = use.getOwner();2903                                   return newLoop->isProperAncestor(user);2904                                 });2905    }2906  }2907 2908  // Replace the old loop.2909  rewriter.replaceOp(getOperation(),2910                     newLoop->getResults().take_front(getNumResults()));2911  return cast<LoopLikeOpInterface>(newLoop.getOperation());2912}2913 2914Speculation::Speculatability AffineForOp::getSpeculatability() {2915  // `affine.for (I = Start; I < End; I += 1)` terminates for all values of2916  // Start and End.2917  //2918  // For Step != 1, the loop may not terminate.  We can add more smarts here if2919  // needed.2920  return getStepAsInt() == 1 ? Speculation::RecursivelySpeculatable2921                             : Speculation::NotSpeculatable;2922}2923 2924/// Returns true if the provided value is the induction variable of a2925/// AffineForOp.2926bool mlir::affine::isAffineForInductionVar(Value val) {2927  return getForInductionVarOwner(val) != AffineForOp();2928}2929 2930bool mlir::affine::isAffineParallelInductionVar(Value val) {2931  return getAffineParallelInductionVarOwner(val) != nullptr;2932}2933 2934bool mlir::affine::isAffineInductionVar(Value val) {2935  return isAffineForInductionVar(val) || isAffineParallelInductionVar(val);2936}2937 2938AffineForOp mlir::affine::getForInductionVarOwner(Value val) {2939  auto ivArg = dyn_cast<BlockArgument>(val);2940  if (!ivArg || !ivArg.getOwner() || !ivArg.getOwner()->getParent())2941    return AffineForOp();2942  if (auto forOp =2943          ivArg.getOwner()->getParent()->getParentOfType<AffineForOp>())2944    // Check to make sure `val` is the induction variable, not an iter_arg.2945    return forOp.getInductionVar() == val ? forOp : AffineForOp();2946  return AffineForOp();2947}2948 2949AffineParallelOp mlir::affine::getAffineParallelInductionVarOwner(Value val) {2950  auto ivArg = dyn_cast<BlockArgument>(val);2951  if (!ivArg || !ivArg.getOwner())2952    return nullptr;2953  Operation *containingOp = ivArg.getOwner()->getParentOp();2954  auto parallelOp = dyn_cast_if_present<AffineParallelOp>(containingOp);2955  if (parallelOp && llvm::is_contained(parallelOp.getIVs(), val))2956    return parallelOp;2957  return nullptr;2958}2959 2960/// Extracts the induction variables from a list of AffineForOps and returns2961/// them.2962void mlir::affine::extractForInductionVars(ArrayRef<AffineForOp> forInsts,2963                                           SmallVectorImpl<Value> *ivs) {2964  ivs->reserve(forInsts.size());2965  for (auto forInst : forInsts)2966    ivs->push_back(forInst.getInductionVar());2967}2968 2969void mlir::affine::extractInductionVars(ArrayRef<mlir::Operation *> affineOps,2970                                        SmallVectorImpl<mlir::Value> &ivs) {2971  ivs.reserve(affineOps.size());2972  for (Operation *op : affineOps) {2973    // Add constraints from forOp's bounds.2974    if (auto forOp = dyn_cast<AffineForOp>(op))2975      ivs.push_back(forOp.getInductionVar());2976    else if (auto parallelOp = dyn_cast<AffineParallelOp>(op))2977      for (size_t i = 0; i < parallelOp.getBody()->getNumArguments(); i++)2978        ivs.push_back(parallelOp.getBody()->getArgument(i));2979  }2980}2981 2982/// Builds an affine loop nest, using "loopCreatorFn" to create individual loop2983/// operations.2984template <typename BoundListTy, typename LoopCreatorTy>2985static void buildAffineLoopNestImpl(2986    OpBuilder &builder, Location loc, BoundListTy lbs, BoundListTy ubs,2987    ArrayRef<int64_t> steps,2988    function_ref<void(OpBuilder &, Location, ValueRange)> bodyBuilderFn,2989    LoopCreatorTy &&loopCreatorFn) {2990  assert(lbs.size() == ubs.size() && "Mismatch in number of arguments");2991  assert(lbs.size() == steps.size() && "Mismatch in number of arguments");2992 2993  // If there are no loops to be constructed, construct the body anyway.2994  OpBuilder::InsertionGuard guard(builder);2995  if (lbs.empty()) {2996    if (bodyBuilderFn)2997      bodyBuilderFn(builder, loc, ValueRange());2998    return;2999  }3000 3001  // Create the loops iteratively and store the induction variables.3002  SmallVector<Value, 4> ivs;3003  ivs.reserve(lbs.size());3004  for (unsigned i = 0, e = lbs.size(); i < e; ++i) {3005    // Callback for creating the loop body, always creates the terminator.3006    auto loopBody = [&](OpBuilder &nestedBuilder, Location nestedLoc, Value iv,3007                        ValueRange iterArgs) {3008      ivs.push_back(iv);3009      // In the innermost loop, call the body builder.3010      if (i == e - 1 && bodyBuilderFn) {3011        OpBuilder::InsertionGuard nestedGuard(nestedBuilder);3012        bodyBuilderFn(nestedBuilder, nestedLoc, ivs);3013      }3014      AffineYieldOp::create(nestedBuilder, nestedLoc);3015    };3016 3017    // Delegate actual loop creation to the callback in order to dispatch3018    // between constant- and variable-bound loops.3019    auto loop = loopCreatorFn(builder, loc, lbs[i], ubs[i], steps[i], loopBody);3020    builder.setInsertionPointToStart(loop.getBody());3021  }3022}3023 3024/// Creates an affine loop from the bounds known to be constants.3025static AffineForOp3026buildAffineLoopFromConstants(OpBuilder &builder, Location loc, int64_t lb,3027                             int64_t ub, int64_t step,3028                             AffineForOp::BodyBuilderFn bodyBuilderFn) {3029  return AffineForOp::create(builder, loc, lb, ub, step,3030                             /*iterArgs=*/ValueRange(), bodyBuilderFn);3031}3032 3033/// Creates an affine loop from the bounds that may or may not be constants.3034static AffineForOp3035buildAffineLoopFromValues(OpBuilder &builder, Location loc, Value lb, Value ub,3036                          int64_t step,3037                          AffineForOp::BodyBuilderFn bodyBuilderFn) {3038  std::optional<int64_t> lbConst = getConstantIntValue(lb);3039  std::optional<int64_t> ubConst = getConstantIntValue(ub);3040  if (lbConst && ubConst)3041    return buildAffineLoopFromConstants(builder, loc, lbConst.value(),3042                                        ubConst.value(), step, bodyBuilderFn);3043  return AffineForOp::create(builder, loc, lb, builder.getDimIdentityMap(), ub,3044                             builder.getDimIdentityMap(), step,3045                             /*iterArgs=*/ValueRange(), bodyBuilderFn);3046}3047 3048void mlir::affine::buildAffineLoopNest(3049    OpBuilder &builder, Location loc, ArrayRef<int64_t> lbs,3050    ArrayRef<int64_t> ubs, ArrayRef<int64_t> steps,3051    function_ref<void(OpBuilder &, Location, ValueRange)> bodyBuilderFn) {3052  buildAffineLoopNestImpl(builder, loc, lbs, ubs, steps, bodyBuilderFn,3053                          buildAffineLoopFromConstants);3054}3055 3056void mlir::affine::buildAffineLoopNest(3057    OpBuilder &builder, Location loc, ValueRange lbs, ValueRange ubs,3058    ArrayRef<int64_t> steps,3059    function_ref<void(OpBuilder &, Location, ValueRange)> bodyBuilderFn) {3060  buildAffineLoopNestImpl(builder, loc, lbs, ubs, steps, bodyBuilderFn,3061                          buildAffineLoopFromValues);3062}3063 3064//===----------------------------------------------------------------------===//3065// AffineIfOp3066//===----------------------------------------------------------------------===//3067 3068namespace {3069/// Remove else blocks that have nothing other than a zero value yield.3070struct SimplifyDeadElse : public OpRewritePattern<AffineIfOp> {3071  using OpRewritePattern<AffineIfOp>::OpRewritePattern;3072 3073  LogicalResult matchAndRewrite(AffineIfOp ifOp,3074                                PatternRewriter &rewriter) const override {3075    if (ifOp.getElseRegion().empty() ||3076        !llvm::hasSingleElement(*ifOp.getElseBlock()) || ifOp.getNumResults())3077      return failure();3078 3079    rewriter.startOpModification(ifOp);3080    rewriter.eraseBlock(ifOp.getElseBlock());3081    rewriter.finalizeOpModification(ifOp);3082    return success();3083  }3084};3085 3086/// Removes affine.if cond if the condition is always true or false in certain3087/// trivial cases. Promotes the then/else block in the parent operation block.3088struct AlwaysTrueOrFalseIf : public OpRewritePattern<AffineIfOp> {3089  using OpRewritePattern<AffineIfOp>::OpRewritePattern;3090 3091  LogicalResult matchAndRewrite(AffineIfOp op,3092                                PatternRewriter &rewriter) const override {3093 3094    auto isTriviallyFalse = [](IntegerSet iSet) {3095      return iSet.isEmptyIntegerSet();3096    };3097 3098    auto isTriviallyTrue = [](IntegerSet iSet) {3099      return (iSet.getNumEqualities() == 1 && iSet.getNumInequalities() == 0 &&3100              iSet.getConstraint(0) == 0);3101    };3102 3103    IntegerSet affineIfConditions = op.getIntegerSet();3104    Block *blockToMove;3105    if (isTriviallyFalse(affineIfConditions)) {3106      // The absence, or equivalently, the emptiness of the else region need not3107      // be checked when affine.if is returning results because if an affine.if3108      // operation is returning results, it always has a non-empty else region.3109      if (op.getNumResults() == 0 && !op.hasElse()) {3110        // If the else region is absent, or equivalently, empty, remove the3111        // affine.if operation (which is not returning any results).3112        rewriter.eraseOp(op);3113        return success();3114      }3115      blockToMove = op.getElseBlock();3116    } else if (isTriviallyTrue(affineIfConditions)) {3117      blockToMove = op.getThenBlock();3118    } else {3119      return failure();3120    }3121    Operation *blockToMoveTerminator = blockToMove->getTerminator();3122    // Promote the "blockToMove" block to the parent operation block between the3123    // prologue and epilogue of "op".3124    rewriter.inlineBlockBefore(blockToMove, op);3125    // Replace the "op" operation with the operands of the3126    // "blockToMoveTerminator" operation. Note that "blockToMoveTerminator" is3127    // the affine.yield operation present in the "blockToMove" block. It has no3128    // operands when affine.if is not returning results and therefore, in that3129    // case, replaceOp just erases "op". When affine.if is not returning3130    // results, the affine.yield operation can be omitted. It gets inserted3131    // implicitly.3132    rewriter.replaceOp(op, blockToMoveTerminator->getOperands());3133    // Erase the "blockToMoveTerminator" operation since it is now in the parent3134    // operation block, which already has its own terminator.3135    rewriter.eraseOp(blockToMoveTerminator);3136    return success();3137  }3138};3139} // namespace3140 3141/// AffineIfOp has two regions -- `then` and `else`. The flow of data should be3142/// as follows: AffineIfOp -> `then`/`else` -> AffineIfOp3143void AffineIfOp::getSuccessorRegions(3144    RegionBranchPoint point, SmallVectorImpl<RegionSuccessor> &regions) {3145  // If the predecessor is an AffineIfOp, then branching into both `then` and3146  // `else` region is valid.3147  if (point.isParent()) {3148    regions.reserve(2);3149    regions.push_back(3150        RegionSuccessor(&getThenRegion(), getThenRegion().getArguments()));3151    // If the "else" region is empty, branch bach into parent.3152    if (getElseRegion().empty()) {3153      regions.push_back(RegionSuccessor(getOperation(), getResults()));3154    } else {3155      regions.push_back(3156          RegionSuccessor(&getElseRegion(), getElseRegion().getArguments()));3157    }3158    return;3159  }3160 3161  // If the predecessor is the `else`/`then` region, then branching into parent3162  // op is valid.3163  regions.push_back(RegionSuccessor(getOperation(), getResults()));3164}3165 3166LogicalResult AffineIfOp::verify() {3167  // Verify that we have a condition attribute.3168  // FIXME: This should be specified in the arguments list in ODS.3169  auto conditionAttr =3170      (*this)->getAttrOfType<IntegerSetAttr>(getConditionAttrStrName());3171  if (!conditionAttr)3172    return emitOpError("requires an integer set attribute named 'condition'");3173 3174  // Verify that there are enough operands for the condition.3175  IntegerSet condition = conditionAttr.getValue();3176  if (getNumOperands() != condition.getNumInputs())3177    return emitOpError("operand count and condition integer set dimension and "3178                       "symbol count must match");3179 3180  // Verify that the operands are valid dimension/symbols.3181  if (failed(verifyDimAndSymbolIdentifiers(*this, getOperands(),3182                                           condition.getNumDims())))3183    return failure();3184 3185  return success();3186}3187 3188ParseResult AffineIfOp::parse(OpAsmParser &parser, OperationState &result) {3189  // Parse the condition attribute set.3190  IntegerSetAttr conditionAttr;3191  unsigned numDims;3192  if (parser.parseAttribute(conditionAttr,3193                            AffineIfOp::getConditionAttrStrName(),3194                            result.attributes) ||3195      parseDimAndSymbolList(parser, result.operands, numDims))3196    return failure();3197 3198  // Verify the condition operands.3199  auto set = conditionAttr.getValue();3200  if (set.getNumDims() != numDims)3201    return parser.emitError(3202        parser.getNameLoc(),3203        "dim operand count and integer set dim count must match");3204  if (numDims + set.getNumSymbols() != result.operands.size())3205    return parser.emitError(3206        parser.getNameLoc(),3207        "symbol operand count and integer set symbol count must match");3208 3209  if (parser.parseOptionalArrowTypeList(result.types))3210    return failure();3211 3212  // Create the regions for 'then' and 'else'.  The latter must be created even3213  // if it remains empty for the validity of the operation.3214  result.regions.reserve(2);3215  Region *thenRegion = result.addRegion();3216  Region *elseRegion = result.addRegion();3217 3218  // Parse the 'then' region.3219  if (parser.parseRegion(*thenRegion, {}, {}))3220    return failure();3221  AffineIfOp::ensureTerminator(*thenRegion, parser.getBuilder(),3222                               result.location);3223 3224  // If we find an 'else' keyword then parse the 'else' region.3225  if (!parser.parseOptionalKeyword("else")) {3226    if (parser.parseRegion(*elseRegion, {}, {}))3227      return failure();3228    AffineIfOp::ensureTerminator(*elseRegion, parser.getBuilder(),3229                                 result.location);3230  }3231 3232  // Parse the optional attribute list.3233  if (parser.parseOptionalAttrDict(result.attributes))3234    return failure();3235 3236  return success();3237}3238 3239void AffineIfOp::print(OpAsmPrinter &p) {3240  auto conditionAttr =3241      (*this)->getAttrOfType<IntegerSetAttr>(getConditionAttrStrName());3242  p << " " << conditionAttr;3243  printDimAndSymbolList(operand_begin(), operand_end(),3244                        conditionAttr.getValue().getNumDims(), p);3245  p.printOptionalArrowTypeList(getResultTypes());3246  p << ' ';3247  p.printRegion(getThenRegion(), /*printEntryBlockArgs=*/false,3248                /*printBlockTerminators=*/getNumResults());3249 3250  // Print the 'else' regions if it has any blocks.3251  auto &elseRegion = this->getElseRegion();3252  if (!elseRegion.empty()) {3253    p << " else ";3254    p.printRegion(elseRegion,3255                  /*printEntryBlockArgs=*/false,3256                  /*printBlockTerminators=*/getNumResults());3257  }3258 3259  // Print the attribute list.3260  p.printOptionalAttrDict((*this)->getAttrs(),3261                          /*elidedAttrs=*/getConditionAttrStrName());3262}3263 3264IntegerSet AffineIfOp::getIntegerSet() {3265  return (*this)3266      ->getAttrOfType<IntegerSetAttr>(getConditionAttrStrName())3267      .getValue();3268}3269 3270void AffineIfOp::setIntegerSet(IntegerSet newSet) {3271  (*this)->setAttr(getConditionAttrStrName(), IntegerSetAttr::get(newSet));3272}3273 3274void AffineIfOp::setConditional(IntegerSet set, ValueRange operands) {3275  setIntegerSet(set);3276  (*this)->setOperands(operands);3277}3278 3279void AffineIfOp::build(OpBuilder &builder, OperationState &result,3280                       TypeRange resultTypes, IntegerSet set, ValueRange args,3281                       bool withElseRegion) {3282  assert(resultTypes.empty() || withElseRegion);3283  OpBuilder::InsertionGuard guard(builder);3284 3285  result.addTypes(resultTypes);3286  result.addOperands(args);3287  result.addAttribute(getConditionAttrStrName(), IntegerSetAttr::get(set));3288 3289  Region *thenRegion = result.addRegion();3290  builder.createBlock(thenRegion);3291  if (resultTypes.empty())3292    AffineIfOp::ensureTerminator(*thenRegion, builder, result.location);3293 3294  Region *elseRegion = result.addRegion();3295  if (withElseRegion) {3296    builder.createBlock(elseRegion);3297    if (resultTypes.empty())3298      AffineIfOp::ensureTerminator(*elseRegion, builder, result.location);3299  }3300}3301 3302void AffineIfOp::build(OpBuilder &builder, OperationState &result,3303                       IntegerSet set, ValueRange args, bool withElseRegion) {3304  AffineIfOp::build(builder, result, /*resultTypes=*/{}, set, args,3305                    withElseRegion);3306}3307 3308/// Compose any affine.apply ops feeding into `operands` of the integer set3309/// `set` by composing the maps of such affine.apply ops with the integer3310/// set constraints.3311static void composeSetAndOperands(IntegerSet &set,3312                                  SmallVectorImpl<Value> &operands,3313                                  bool composeAffineMin = false) {3314  // We will simply reuse the API of the map composition by viewing the LHSs of3315  // the equalities and inequalities of `set` as the affine exprs of an affine3316  // map. Convert to equivalent map, compose, and convert back to set.3317  auto map = AffineMap::get(set.getNumDims(), set.getNumSymbols(),3318                            set.getConstraints(), set.getContext());3319  // Check if any composition is possible.3320  if (llvm::none_of(operands,3321                    [](Value v) { return v.getDefiningOp<AffineApplyOp>(); }))3322    return;3323 3324  composeAffineMapAndOperands(&map, &operands, composeAffineMin);3325  set = IntegerSet::get(map.getNumDims(), map.getNumSymbols(), map.getResults(),3326                        set.getEqFlags());3327}3328 3329/// Canonicalize an affine if op's conditional (integer set + operands).3330LogicalResult AffineIfOp::fold(FoldAdaptor, SmallVectorImpl<OpFoldResult> &) {3331  auto set = getIntegerSet();3332  SmallVector<Value, 4> operands(getOperands());3333  composeSetAndOperands(set, operands);3334  canonicalizeSetAndOperands(&set, &operands);3335 3336  // Check if the canonicalization or composition led to any change.3337  if (getIntegerSet() == set && llvm::equal(operands, getOperands()))3338    return failure();3339 3340  setConditional(set, operands);3341  return success();3342}3343 3344void AffineIfOp::getCanonicalizationPatterns(RewritePatternSet &results,3345                                             MLIRContext *context) {3346  results.add<SimplifyDeadElse, AlwaysTrueOrFalseIf>(context);3347}3348 3349//===----------------------------------------------------------------------===//3350// AffineLoadOp3351//===----------------------------------------------------------------------===//3352 3353void AffineLoadOp::build(OpBuilder &builder, OperationState &result,3354                         AffineMap map, ValueRange operands) {3355  assert(operands.size() == 1 + map.getNumInputs() && "inconsistent operands");3356  result.addOperands(operands);3357  if (map)3358    result.addAttribute(getMapAttrStrName(), AffineMapAttr::get(map));3359  auto memrefType = llvm::cast<MemRefType>(operands[0].getType());3360  result.types.push_back(memrefType.getElementType());3361}3362 3363void AffineLoadOp::build(OpBuilder &builder, OperationState &result,3364                         Value memref, AffineMap map, ValueRange mapOperands) {3365  assert(map.getNumInputs() == mapOperands.size() && "inconsistent index info");3366  result.addOperands(memref);3367  result.addOperands(mapOperands);3368  auto memrefType = llvm::cast<MemRefType>(memref.getType());3369  result.addAttribute(getMapAttrStrName(), AffineMapAttr::get(map));3370  result.types.push_back(memrefType.getElementType());3371}3372 3373void AffineLoadOp::build(OpBuilder &builder, OperationState &result,3374                         Value memref, ValueRange indices) {3375  auto memrefType = llvm::cast<MemRefType>(memref.getType());3376  int64_t rank = memrefType.getRank();3377  // Create identity map for memrefs with at least one dimension or () -> ()3378  // for zero-dimensional memrefs.3379  auto map =3380      rank ? builder.getMultiDimIdentityMap(rank) : builder.getEmptyAffineMap();3381  build(builder, result, memref, map, indices);3382}3383 3384ParseResult AffineLoadOp::parse(OpAsmParser &parser, OperationState &result) {3385  auto &builder = parser.getBuilder();3386  auto indexTy = builder.getIndexType();3387 3388  MemRefType type;3389  OpAsmParser::UnresolvedOperand memrefInfo;3390  AffineMapAttr mapAttr;3391  SmallVector<OpAsmParser::UnresolvedOperand, 1> mapOperands;3392  return failure(3393      parser.parseOperand(memrefInfo) ||3394      parser.parseAffineMapOfSSAIds(mapOperands, mapAttr,3395                                    AffineLoadOp::getMapAttrStrName(),3396                                    result.attributes) ||3397      parser.parseOptionalAttrDict(result.attributes) ||3398      parser.parseColonType(type) ||3399      parser.resolveOperand(memrefInfo, type, result.operands) ||3400      parser.resolveOperands(mapOperands, indexTy, result.operands) ||3401      parser.addTypeToList(type.getElementType(), result.types));3402}3403 3404void AffineLoadOp::print(OpAsmPrinter &p) {3405  p << " " << getMemRef() << '[';3406  if (AffineMapAttr mapAttr =3407          (*this)->getAttrOfType<AffineMapAttr>(getMapAttrStrName()))3408    p.printAffineMapOfSSAIds(mapAttr, getMapOperands());3409  p << ']';3410  p.printOptionalAttrDict((*this)->getAttrs(),3411                          /*elidedAttrs=*/{getMapAttrStrName()});3412  p << " : " << getMemRefType();3413}3414 3415/// Verify common indexing invariants of affine.load, affine.store,3416/// affine.vector_load and affine.vector_store.3417template <typename AffineMemOpTy>3418static LogicalResult3419verifyMemoryOpIndexing(AffineMemOpTy op, AffineMapAttr mapAttr,3420                       Operation::operand_range mapOperands,3421                       MemRefType memrefType, unsigned numIndexOperands) {3422  AffineMap map = mapAttr.getValue();3423  if (map.getNumResults() != memrefType.getRank())3424    return op->emitOpError("affine map num results must equal memref rank");3425  if (map.getNumInputs() != numIndexOperands)3426    return op->emitOpError("expects as many subscripts as affine map inputs");3427 3428  for (auto idx : mapOperands) {3429    if (!idx.getType().isIndex())3430      return op->emitOpError("index to load must have 'index' type");3431  }3432  if (failed(verifyDimAndSymbolIdentifiers(op, mapOperands, map.getNumDims())))3433    return failure();3434 3435  return success();3436}3437 3438LogicalResult AffineLoadOp::verify() {3439  auto memrefType = getMemRefType();3440  if (getType() != memrefType.getElementType())3441    return emitOpError("result type must match element type of memref");3442 3443  if (failed(verifyMemoryOpIndexing(3444          *this, (*this)->getAttrOfType<AffineMapAttr>(getMapAttrStrName()),3445          getMapOperands(), memrefType,3446          /*numIndexOperands=*/getNumOperands() - 1)))3447    return failure();3448 3449  return success();3450}3451 3452void AffineLoadOp::getCanonicalizationPatterns(RewritePatternSet &results,3453                                               MLIRContext *context) {3454  results.add<SimplifyAffineOp<AffineLoadOp>>(context);3455}3456 3457OpFoldResult AffineLoadOp::fold(FoldAdaptor adaptor) {3458  /// load(memrefcast) -> load3459  if (succeeded(memref::foldMemRefCast(*this)))3460    return getResult();3461 3462  // Fold load from a global constant memref.3463  auto getGlobalOp = getMemref().getDefiningOp<memref::GetGlobalOp>();3464  if (!getGlobalOp)3465    return {};3466  // Get to the memref.global defining the symbol.3467  auto *symbolTableOp = getGlobalOp->getParentWithTrait<OpTrait::SymbolTable>();3468  if (!symbolTableOp)3469    return {};3470  auto global = dyn_cast_or_null<memref::GlobalOp>(3471      SymbolTable::lookupSymbolIn(symbolTableOp, getGlobalOp.getNameAttr()));3472  if (!global)3473    return {};3474 3475  // Check if the global memref is a constant.3476  auto cstAttr =3477      dyn_cast_or_null<DenseElementsAttr>(global.getConstantInitValue());3478  if (!cstAttr)3479    return {};3480  // If it's a splat constant, we can fold irrespective of indices.3481  if (auto splatAttr = dyn_cast<SplatElementsAttr>(cstAttr))3482    return splatAttr.getSplatValue<Attribute>();3483  // Otherwise, we can fold only if we know the indices.3484  if (!getAffineMap().isConstant())3485    return {};3486  auto indices = llvm::to_vector<4>(3487      llvm::map_range(getAffineMap().getConstantResults(),3488                      [](int64_t v) -> uint64_t { return v; }));3489  return cstAttr.getValues<Attribute>()[indices];3490}3491 3492//===----------------------------------------------------------------------===//3493// AffineStoreOp3494//===----------------------------------------------------------------------===//3495 3496void AffineStoreOp::build(OpBuilder &builder, OperationState &result,3497                          Value valueToStore, Value memref, AffineMap map,3498                          ValueRange mapOperands) {3499  assert(map.getNumInputs() == mapOperands.size() && "inconsistent index info");3500  result.addOperands(valueToStore);3501  result.addOperands(memref);3502  result.addOperands(mapOperands);3503  result.getOrAddProperties<Properties>().map = AffineMapAttr::get(map);3504}3505 3506// Use identity map.3507void AffineStoreOp::build(OpBuilder &builder, OperationState &result,3508                          Value valueToStore, Value memref,3509                          ValueRange indices) {3510  auto memrefType = llvm::cast<MemRefType>(memref.getType());3511  int64_t rank = memrefType.getRank();3512  // Create identity map for memrefs with at least one dimension or () -> ()3513  // for zero-dimensional memrefs.3514  auto map =3515      rank ? builder.getMultiDimIdentityMap(rank) : builder.getEmptyAffineMap();3516  build(builder, result, valueToStore, memref, map, indices);3517}3518 3519ParseResult AffineStoreOp::parse(OpAsmParser &parser, OperationState &result) {3520  auto indexTy = parser.getBuilder().getIndexType();3521 3522  MemRefType type;3523  OpAsmParser::UnresolvedOperand storeValueInfo;3524  OpAsmParser::UnresolvedOperand memrefInfo;3525  AffineMapAttr mapAttr;3526  SmallVector<OpAsmParser::UnresolvedOperand, 1> mapOperands;3527  return failure(parser.parseOperand(storeValueInfo) || parser.parseComma() ||3528                 parser.parseOperand(memrefInfo) ||3529                 parser.parseAffineMapOfSSAIds(3530                     mapOperands, mapAttr, AffineStoreOp::getMapAttrStrName(),3531                     result.attributes) ||3532                 parser.parseOptionalAttrDict(result.attributes) ||3533                 parser.parseColonType(type) ||3534                 parser.resolveOperand(storeValueInfo, type.getElementType(),3535                                       result.operands) ||3536                 parser.resolveOperand(memrefInfo, type, result.operands) ||3537                 parser.resolveOperands(mapOperands, indexTy, result.operands));3538}3539 3540void AffineStoreOp::print(OpAsmPrinter &p) {3541  p << " " << getValueToStore();3542  p << ", " << getMemRef() << '[';3543  if (AffineMapAttr mapAttr =3544          (*this)->getAttrOfType<AffineMapAttr>(getMapAttrStrName()))3545    p.printAffineMapOfSSAIds(mapAttr, getMapOperands());3546  p << ']';3547  p.printOptionalAttrDict((*this)->getAttrs(),3548                          /*elidedAttrs=*/{getMapAttrStrName()});3549  p << " : " << getMemRefType();3550}3551 3552LogicalResult AffineStoreOp::verify() {3553  // The value to store must have the same type as memref element type.3554  auto memrefType = getMemRefType();3555  if (getValueToStore().getType() != memrefType.getElementType())3556    return emitOpError(3557        "value to store must have the same type as memref element type");3558 3559  if (failed(verifyMemoryOpIndexing(3560          *this, (*this)->getAttrOfType<AffineMapAttr>(getMapAttrStrName()),3561          getMapOperands(), memrefType,3562          /*numIndexOperands=*/getNumOperands() - 2)))3563    return failure();3564 3565  return success();3566}3567 3568void AffineStoreOp::getCanonicalizationPatterns(RewritePatternSet &results,3569                                                MLIRContext *context) {3570  results.add<SimplifyAffineOp<AffineStoreOp>>(context);3571}3572 3573LogicalResult AffineStoreOp::fold(FoldAdaptor adaptor,3574                                  SmallVectorImpl<OpFoldResult> &results) {3575  /// store(memrefcast) -> store3576  return memref::foldMemRefCast(*this, getValueToStore());3577}3578 3579//===----------------------------------------------------------------------===//3580// AffineMinMaxOpBase3581//===----------------------------------------------------------------------===//3582 3583template <typename T>3584static LogicalResult verifyAffineMinMaxOp(T op) {3585  // Verify that operand count matches affine map dimension and symbol count.3586  if (op.getNumOperands() !=3587      op.getMap().getNumDims() + op.getMap().getNumSymbols())3588    return op.emitOpError(3589        "operand count and affine map dimension and symbol count must match");3590 3591  if (op.getMap().getNumResults() == 0)3592    return op.emitOpError("affine map expect at least one result");3593  return success();3594}3595 3596template <typename T>3597static void printAffineMinMaxOp(OpAsmPrinter &p, T op) {3598  p << ' ' << op->getAttr(T::getMapAttrStrName());3599  auto operands = op.getOperands();3600  unsigned numDims = op.getMap().getNumDims();3601  p << '(' << operands.take_front(numDims) << ')';3602 3603  if (operands.size() != numDims)3604    p << '[' << operands.drop_front(numDims) << ']';3605  p.printOptionalAttrDict(op->getAttrs(),3606                          /*elidedAttrs=*/{T::getMapAttrStrName()});3607}3608 3609template <typename T>3610static ParseResult parseAffineMinMaxOp(OpAsmParser &parser,3611                                       OperationState &result) {3612  auto &builder = parser.getBuilder();3613  auto indexType = builder.getIndexType();3614  SmallVector<OpAsmParser::UnresolvedOperand, 8> dimInfos;3615  SmallVector<OpAsmParser::UnresolvedOperand, 8> symInfos;3616  AffineMapAttr mapAttr;3617  return failure(3618      parser.parseAttribute(mapAttr, T::getMapAttrStrName(),3619                            result.attributes) ||3620      parser.parseOperandList(dimInfos, OpAsmParser::Delimiter::Paren) ||3621      parser.parseOperandList(symInfos,3622                              OpAsmParser::Delimiter::OptionalSquare) ||3623      parser.parseOptionalAttrDict(result.attributes) ||3624      parser.resolveOperands(dimInfos, indexType, result.operands) ||3625      parser.resolveOperands(symInfos, indexType, result.operands) ||3626      parser.addTypeToList(indexType, result.types));3627}3628 3629/// Fold an affine min or max operation with the given operands. The operand3630/// list may contain nulls, which are interpreted as the operand not being a3631/// constant.3632template <typename T>3633static OpFoldResult foldMinMaxOp(T op, ArrayRef<Attribute> operands) {3634  static_assert(llvm::is_one_of<T, AffineMinOp, AffineMaxOp>::value,3635                "expected affine min or max op");3636 3637  // Fold the affine map.3638  // TODO: Fold more cases:3639  // min(some_affine, some_affine + constant, ...), etc.3640  SmallVector<int64_t, 2> results;3641  auto foldedMap = op.getMap().partialConstantFold(operands, &results);3642 3643  if (foldedMap.getNumSymbols() == 1 && foldedMap.isSymbolIdentity())3644    return op.getOperand(0);3645 3646  // If some of the map results are not constant, try changing the map in-place.3647  if (results.empty()) {3648    // If the map is the same, report that folding did not happen.3649    if (foldedMap == op.getMap())3650      return {};3651    op->setAttr("map", AffineMapAttr::get(foldedMap));3652    return op.getResult();3653  }3654 3655  // Otherwise, completely fold the op into a constant.3656  auto resultIt = std::is_same<T, AffineMinOp>::value3657                      ? llvm::min_element(results)3658                      : llvm::max_element(results);3659  if (resultIt == results.end())3660    return {};3661  return IntegerAttr::get(IndexType::get(op.getContext()), *resultIt);3662}3663 3664/// Remove duplicated expressions in affine min/max ops.3665template <typename T>3666struct DeduplicateAffineMinMaxExpressions : public OpRewritePattern<T> {3667  using OpRewritePattern<T>::OpRewritePattern;3668 3669  LogicalResult matchAndRewrite(T affineOp,3670                                PatternRewriter &rewriter) const override {3671    AffineMap oldMap = affineOp.getAffineMap();3672 3673    SmallVector<AffineExpr, 4> newExprs;3674    for (AffineExpr expr : oldMap.getResults()) {3675      // This is a linear scan over newExprs, but it should be fine given that3676      // we typically just have a few expressions per op.3677      if (!llvm::is_contained(newExprs, expr))3678        newExprs.push_back(expr);3679    }3680 3681    if (newExprs.size() == oldMap.getNumResults())3682      return failure();3683 3684    auto newMap = AffineMap::get(oldMap.getNumDims(), oldMap.getNumSymbols(),3685                                 newExprs, rewriter.getContext());3686    rewriter.replaceOpWithNewOp<T>(affineOp, newMap, affineOp.getMapOperands());3687 3688    return success();3689  }3690};3691 3692/// Merge an affine min/max op to its consumers if its consumer is also an3693/// affine min/max op.3694///3695/// This pattern requires the producer affine min/max op is bound to a3696/// dimension/symbol that is used as a standalone expression in the consumer3697/// affine op's map.3698///3699/// For example, a pattern like the following:3700///3701///   %0 = affine.min affine_map<()[s0] -> (s0 + 16, s0 * 8)> ()[%sym1]3702///   %1 = affine.min affine_map<(d0)[s0] -> (s0 + 4, d0)> (%0)[%sym2]3703///3704/// Can be turned into:3705///3706///   %1 = affine.min affine_map<3707///          ()[s0, s1] -> (s0 + 4, s1 + 16, s1 * 8)> ()[%sym2, %sym1]3708template <typename T>3709struct MergeAffineMinMaxOp : public OpRewritePattern<T> {3710  using OpRewritePattern<T>::OpRewritePattern;3711 3712  LogicalResult matchAndRewrite(T affineOp,3713                                PatternRewriter &rewriter) const override {3714    AffineMap oldMap = affineOp.getAffineMap();3715    ValueRange dimOperands =3716        affineOp.getMapOperands().take_front(oldMap.getNumDims());3717    ValueRange symOperands =3718        affineOp.getMapOperands().take_back(oldMap.getNumSymbols());3719 3720    auto newDimOperands = llvm::to_vector<8>(dimOperands);3721    auto newSymOperands = llvm::to_vector<8>(symOperands);3722    SmallVector<AffineExpr, 4> newExprs;3723    SmallVector<T, 4> producerOps;3724 3725    // Go over each expression to see whether it's a single dimension/symbol3726    // with the corresponding operand which is the result of another affine3727    // min/max op. If So it can be merged into this affine op.3728    for (AffineExpr expr : oldMap.getResults()) {3729      if (auto symExpr = dyn_cast<AffineSymbolExpr>(expr)) {3730        Value symValue = symOperands[symExpr.getPosition()];3731        if (auto producerOp = symValue.getDefiningOp<T>()) {3732          producerOps.push_back(producerOp);3733          continue;3734        }3735      } else if (auto dimExpr = dyn_cast<AffineDimExpr>(expr)) {3736        Value dimValue = dimOperands[dimExpr.getPosition()];3737        if (auto producerOp = dimValue.getDefiningOp<T>()) {3738          producerOps.push_back(producerOp);3739          continue;3740        }3741      }3742      // For the above cases we will remove the expression by merging the3743      // producer affine min/max's affine expressions. Otherwise we need to3744      // keep the existing expression.3745      newExprs.push_back(expr);3746    }3747 3748    if (producerOps.empty())3749      return failure();3750 3751    unsigned numUsedDims = oldMap.getNumDims();3752    unsigned numUsedSyms = oldMap.getNumSymbols();3753 3754    // Now go over all producer affine ops and merge their expressions.3755    for (T producerOp : producerOps) {3756      AffineMap producerMap = producerOp.getAffineMap();3757      unsigned numProducerDims = producerMap.getNumDims();3758      unsigned numProducerSyms = producerMap.getNumSymbols();3759 3760      // Collect all dimension/symbol values.3761      ValueRange dimValues =3762          producerOp.getMapOperands().take_front(numProducerDims);3763      ValueRange symValues =3764          producerOp.getMapOperands().take_back(numProducerSyms);3765      newDimOperands.append(dimValues.begin(), dimValues.end());3766      newSymOperands.append(symValues.begin(), symValues.end());3767 3768      // For expressions we need to shift to avoid overlap.3769      for (AffineExpr expr : producerMap.getResults()) {3770        newExprs.push_back(expr.shiftDims(numProducerDims, numUsedDims)3771                               .shiftSymbols(numProducerSyms, numUsedSyms));3772      }3773 3774      numUsedDims += numProducerDims;3775      numUsedSyms += numProducerSyms;3776    }3777 3778    auto newMap = AffineMap::get(numUsedDims, numUsedSyms, newExprs,3779                                 rewriter.getContext());3780    auto newOperands =3781        llvm::to_vector<8>(llvm::concat<Value>(newDimOperands, newSymOperands));3782    rewriter.replaceOpWithNewOp<T>(affineOp, newMap, newOperands);3783 3784    return success();3785  }3786};3787 3788/// Canonicalize the result expression order of an affine map and return success3789/// if the order changed.3790///3791/// The function flattens the map's affine expressions to coefficient arrays and3792/// sorts them in lexicographic order. A coefficient array contains a multiplier3793/// for every dimension/symbol and a constant term. The canonicalization fails3794/// if a result expression is not pure or if the flattening requires local3795/// variables that, unlike dimensions and symbols, have no global order.3796static LogicalResult canonicalizeMapExprAndTermOrder(AffineMap &map) {3797  SmallVector<SmallVector<int64_t>> flattenedExprs;3798  for (const AffineExpr &resultExpr : map.getResults()) {3799    // Fail if the expression is not pure.3800    if (!resultExpr.isPureAffine())3801      return failure();3802 3803    SimpleAffineExprFlattener flattener(map.getNumDims(), map.getNumSymbols());3804    auto flattenResult = flattener.walkPostOrder(resultExpr);3805    if (failed(flattenResult))3806      return failure();3807 3808    // Fail if the flattened expression has local variables.3809    if (flattener.operandExprStack.back().size() !=3810        map.getNumDims() + map.getNumSymbols() + 1)3811      return failure();3812 3813    flattenedExprs.emplace_back(flattener.operandExprStack.back().begin(),3814                                flattener.operandExprStack.back().end());3815  }3816 3817  // Fail if sorting is not necessary.3818  if (llvm::is_sorted(flattenedExprs))3819    return failure();3820 3821  // Reorder the result expressions according to their flattened form.3822  SmallVector<unsigned> resultPermutation =3823      llvm::to_vector(llvm::seq<unsigned>(0, map.getNumResults()));3824  llvm::sort(resultPermutation, [&](unsigned lhs, unsigned rhs) {3825    return flattenedExprs[lhs] < flattenedExprs[rhs];3826  });3827  SmallVector<AffineExpr> newExprs;3828  for (unsigned idx : resultPermutation)3829    newExprs.push_back(map.getResult(idx));3830 3831  map = AffineMap::get(map.getNumDims(), map.getNumSymbols(), newExprs,3832                       map.getContext());3833  return success();3834}3835 3836/// Canonicalize the affine map result expression order of an affine min/max3837/// operation.3838///3839/// The pattern calls `canonicalizeMapExprAndTermOrder` to order the result3840/// expressions and replaces the operation if the order changed.3841///3842/// For example, the following operation:3843///3844///   %0 = affine.min affine_map<(d0, d1) -> (d0 + d1, d1 + 16, 32)> (%i0, %i1)3845///3846/// Turns into:3847///3848///   %0 = affine.min affine_map<(d0, d1) -> (32, d1 + 16, d0 + d1)> (%i0, %i1)3849template <typename T>3850struct CanonicalizeAffineMinMaxOpExprAndTermOrder : public OpRewritePattern<T> {3851  using OpRewritePattern<T>::OpRewritePattern;3852 3853  LogicalResult matchAndRewrite(T affineOp,3854                                PatternRewriter &rewriter) const override {3855    AffineMap map = affineOp.getAffineMap();3856    if (failed(canonicalizeMapExprAndTermOrder(map)))3857      return failure();3858    rewriter.replaceOpWithNewOp<T>(affineOp, map, affineOp.getMapOperands());3859    return success();3860  }3861};3862 3863template <typename T>3864struct CanonicalizeSingleResultAffineMinMaxOp : public OpRewritePattern<T> {3865  using OpRewritePattern<T>::OpRewritePattern;3866 3867  LogicalResult matchAndRewrite(T affineOp,3868                                PatternRewriter &rewriter) const override {3869    if (affineOp.getMap().getNumResults() != 1)3870      return failure();3871    rewriter.replaceOpWithNewOp<AffineApplyOp>(affineOp, affineOp.getMap(),3872                                               affineOp.getOperands());3873    return success();3874  }3875};3876 3877//===----------------------------------------------------------------------===//3878// AffineMinOp3879//===----------------------------------------------------------------------===//3880//3881//   %0 = affine.min (d0) -> (1000, d0 + 512) (%i0)3882//3883 3884OpFoldResult AffineMinOp::fold(FoldAdaptor adaptor) {3885  return foldMinMaxOp(*this, adaptor.getOperands());3886}3887 3888void AffineMinOp::getCanonicalizationPatterns(RewritePatternSet &patterns,3889                                              MLIRContext *context) {3890  patterns.add<CanonicalizeSingleResultAffineMinMaxOp<AffineMinOp>,3891               DeduplicateAffineMinMaxExpressions<AffineMinOp>,3892               MergeAffineMinMaxOp<AffineMinOp>, SimplifyAffineOp<AffineMinOp>,3893               CanonicalizeAffineMinMaxOpExprAndTermOrder<AffineMinOp>>(3894      context);3895}3896 3897LogicalResult AffineMinOp::verify() { return verifyAffineMinMaxOp(*this); }3898 3899ParseResult AffineMinOp::parse(OpAsmParser &parser, OperationState &result) {3900  return parseAffineMinMaxOp<AffineMinOp>(parser, result);3901}3902 3903void AffineMinOp::print(OpAsmPrinter &p) { printAffineMinMaxOp(p, *this); }3904 3905//===----------------------------------------------------------------------===//3906// AffineMaxOp3907//===----------------------------------------------------------------------===//3908//3909//   %0 = affine.max (d0) -> (1000, d0 + 512) (%i0)3910//3911 3912OpFoldResult AffineMaxOp::fold(FoldAdaptor adaptor) {3913  return foldMinMaxOp(*this, adaptor.getOperands());3914}3915 3916void AffineMaxOp::getCanonicalizationPatterns(RewritePatternSet &patterns,3917                                              MLIRContext *context) {3918  patterns.add<CanonicalizeSingleResultAffineMinMaxOp<AffineMaxOp>,3919               DeduplicateAffineMinMaxExpressions<AffineMaxOp>,3920               MergeAffineMinMaxOp<AffineMaxOp>, SimplifyAffineOp<AffineMaxOp>,3921               CanonicalizeAffineMinMaxOpExprAndTermOrder<AffineMaxOp>>(3922      context);3923}3924 3925LogicalResult AffineMaxOp::verify() { return verifyAffineMinMaxOp(*this); }3926 3927ParseResult AffineMaxOp::parse(OpAsmParser &parser, OperationState &result) {3928  return parseAffineMinMaxOp<AffineMaxOp>(parser, result);3929}3930 3931void AffineMaxOp::print(OpAsmPrinter &p) { printAffineMinMaxOp(p, *this); }3932 3933//===----------------------------------------------------------------------===//3934// AffinePrefetchOp3935//===----------------------------------------------------------------------===//3936 3937//3938// affine.prefetch %0[%i, %j + 5], read, locality<3>, data : memref<400x400xi32>3939//3940ParseResult AffinePrefetchOp::parse(OpAsmParser &parser,3941                                    OperationState &result) {3942  auto &builder = parser.getBuilder();3943  auto indexTy = builder.getIndexType();3944 3945  MemRefType type;3946  OpAsmParser::UnresolvedOperand memrefInfo;3947  IntegerAttr hintInfo;3948  auto i32Type = parser.getBuilder().getIntegerType(32);3949  StringRef readOrWrite, cacheType;3950 3951  AffineMapAttr mapAttr;3952  SmallVector<OpAsmParser::UnresolvedOperand, 1> mapOperands;3953  if (parser.parseOperand(memrefInfo) ||3954      parser.parseAffineMapOfSSAIds(mapOperands, mapAttr,3955                                    AffinePrefetchOp::getMapAttrStrName(),3956                                    result.attributes) ||3957      parser.parseComma() || parser.parseKeyword(&readOrWrite) ||3958      parser.parseComma() || parser.parseKeyword("locality") ||3959      parser.parseLess() ||3960      parser.parseAttribute(hintInfo, i32Type,3961                            AffinePrefetchOp::getLocalityHintAttrStrName(),3962                            result.attributes) ||3963      parser.parseGreater() || parser.parseComma() ||3964      parser.parseKeyword(&cacheType) ||3965      parser.parseOptionalAttrDict(result.attributes) ||3966      parser.parseColonType(type) ||3967      parser.resolveOperand(memrefInfo, type, result.operands) ||3968      parser.resolveOperands(mapOperands, indexTy, result.operands))3969    return failure();3970 3971  if (readOrWrite != "read" && readOrWrite != "write")3972    return parser.emitError(parser.getNameLoc(),3973                            "rw specifier has to be 'read' or 'write'");3974  result.addAttribute(AffinePrefetchOp::getIsWriteAttrStrName(),3975                      parser.getBuilder().getBoolAttr(readOrWrite == "write"));3976 3977  if (cacheType != "data" && cacheType != "instr")3978    return parser.emitError(parser.getNameLoc(),3979                            "cache type has to be 'data' or 'instr'");3980 3981  result.addAttribute(AffinePrefetchOp::getIsDataCacheAttrStrName(),3982                      parser.getBuilder().getBoolAttr(cacheType == "data"));3983 3984  return success();3985}3986 3987void AffinePrefetchOp::print(OpAsmPrinter &p) {3988  p << " " << getMemref() << '[';3989  AffineMapAttr mapAttr =3990      (*this)->getAttrOfType<AffineMapAttr>(getMapAttrStrName());3991  if (mapAttr)3992    p.printAffineMapOfSSAIds(mapAttr, getMapOperands());3993  p << ']' << ", " << (getIsWrite() ? "write" : "read") << ", "3994    << "locality<" << getLocalityHint() << ">, "3995    << (getIsDataCache() ? "data" : "instr");3996  p.printOptionalAttrDict(3997      (*this)->getAttrs(),3998      /*elidedAttrs=*/{getMapAttrStrName(), getLocalityHintAttrStrName(),3999                       getIsDataCacheAttrStrName(), getIsWriteAttrStrName()});4000  p << " : " << getMemRefType();4001}4002 4003LogicalResult AffinePrefetchOp::verify() {4004  auto mapAttr = (*this)->getAttrOfType<AffineMapAttr>(getMapAttrStrName());4005  if (mapAttr) {4006    AffineMap map = mapAttr.getValue();4007    if (map.getNumResults() != getMemRefType().getRank())4008      return emitOpError("affine.prefetch affine map num results must equal"4009                         " memref rank");4010    if (map.getNumInputs() + 1 != getNumOperands())4011      return emitOpError("too few operands");4012  } else {4013    if (getNumOperands() != 1)4014      return emitOpError("too few operands");4015  }4016 4017  Region *scope = getAffineScope(*this);4018  for (auto idx : getMapOperands()) {4019    if (!isValidAffineIndexOperand(idx, scope))4020      return emitOpError(4021          "index must be a valid dimension or symbol identifier");4022  }4023  return success();4024}4025 4026void AffinePrefetchOp::getCanonicalizationPatterns(RewritePatternSet &results,4027                                                   MLIRContext *context) {4028  // prefetch(memrefcast) -> prefetch4029  results.add<SimplifyAffineOp<AffinePrefetchOp>>(context);4030}4031 4032LogicalResult AffinePrefetchOp::fold(FoldAdaptor adaptor,4033                                     SmallVectorImpl<OpFoldResult> &results) {4034  /// prefetch(memrefcast) -> prefetch4035  return memref::foldMemRefCast(*this);4036}4037 4038//===----------------------------------------------------------------------===//4039// AffineParallelOp4040//===----------------------------------------------------------------------===//4041 4042void AffineParallelOp::build(OpBuilder &builder, OperationState &result,4043                             TypeRange resultTypes,4044                             ArrayRef<arith::AtomicRMWKind> reductions,4045                             ArrayRef<int64_t> ranges) {4046  SmallVector<AffineMap> lbs(ranges.size(), builder.getConstantAffineMap(0));4047  auto ubs = llvm::to_vector<4>(llvm::map_range(ranges, [&](int64_t value) {4048    return builder.getConstantAffineMap(value);4049  }));4050  SmallVector<int64_t> steps(ranges.size(), 1);4051  build(builder, result, resultTypes, reductions, lbs, /*lbArgs=*/{}, ubs,4052        /*ubArgs=*/{}, steps);4053}4054 4055void AffineParallelOp::build(OpBuilder &builder, OperationState &result,4056                             TypeRange resultTypes,4057                             ArrayRef<arith::AtomicRMWKind> reductions,4058                             ArrayRef<AffineMap> lbMaps, ValueRange lbArgs,4059                             ArrayRef<AffineMap> ubMaps, ValueRange ubArgs,4060                             ArrayRef<int64_t> steps) {4061  assert(llvm::all_of(lbMaps,4062                      [lbMaps](AffineMap m) {4063                        return m.getNumDims() == lbMaps[0].getNumDims() &&4064                               m.getNumSymbols() == lbMaps[0].getNumSymbols();4065                      }) &&4066         "expected all lower bounds maps to have the same number of dimensions "4067         "and symbols");4068  assert(llvm::all_of(ubMaps,4069                      [ubMaps](AffineMap m) {4070                        return m.getNumDims() == ubMaps[0].getNumDims() &&4071                               m.getNumSymbols() == ubMaps[0].getNumSymbols();4072                      }) &&4073         "expected all upper bounds maps to have the same number of dimensions "4074         "and symbols");4075  assert((lbMaps.empty() || lbMaps[0].getNumInputs() == lbArgs.size()) &&4076         "expected lower bound maps to have as many inputs as lower bound "4077         "operands");4078  assert((ubMaps.empty() || ubMaps[0].getNumInputs() == ubArgs.size()) &&4079         "expected upper bound maps to have as many inputs as upper bound "4080         "operands");4081 4082  OpBuilder::InsertionGuard guard(builder);4083  result.addTypes(resultTypes);4084 4085  // Convert the reductions to integer attributes.4086  SmallVector<Attribute, 4> reductionAttrs;4087  for (arith::AtomicRMWKind reduction : reductions)4088    reductionAttrs.push_back(4089        builder.getI64IntegerAttr(static_cast<int64_t>(reduction)));4090  result.addAttribute(getReductionsAttrStrName(),4091                      builder.getArrayAttr(reductionAttrs));4092 4093  // Concatenates maps defined in the same input space (same dimensions and4094  // symbols), assumes there is at least one map.4095  auto concatMapsSameInput = [&builder](ArrayRef<AffineMap> maps,4096                                        SmallVectorImpl<int32_t> &groups) {4097    if (maps.empty())4098      return AffineMap::get(builder.getContext());4099    SmallVector<AffineExpr> exprs;4100    groups.reserve(groups.size() + maps.size());4101    exprs.reserve(maps.size());4102    for (AffineMap m : maps) {4103      llvm::append_range(exprs, m.getResults());4104      groups.push_back(m.getNumResults());4105    }4106    return AffineMap::get(maps[0].getNumDims(), maps[0].getNumSymbols(), exprs,4107                          maps[0].getContext());4108  };4109 4110  // Set up the bounds.4111  SmallVector<int32_t> lbGroups, ubGroups;4112  AffineMap lbMap = concatMapsSameInput(lbMaps, lbGroups);4113  AffineMap ubMap = concatMapsSameInput(ubMaps, ubGroups);4114  result.addAttribute(getLowerBoundsMapAttrStrName(),4115                      AffineMapAttr::get(lbMap));4116  result.addAttribute(getLowerBoundsGroupsAttrStrName(),4117                      builder.getI32TensorAttr(lbGroups));4118  result.addAttribute(getUpperBoundsMapAttrStrName(),4119                      AffineMapAttr::get(ubMap));4120  result.addAttribute(getUpperBoundsGroupsAttrStrName(),4121                      builder.getI32TensorAttr(ubGroups));4122  result.addAttribute(getStepsAttrStrName(), builder.getI64ArrayAttr(steps));4123  result.addOperands(lbArgs);4124  result.addOperands(ubArgs);4125 4126  // Create a region and a block for the body.4127  auto *bodyRegion = result.addRegion();4128  Block *body = builder.createBlock(bodyRegion);4129 4130  // Add all the block arguments.4131  for (unsigned i = 0, e = steps.size(); i < e; ++i)4132    body->addArgument(IndexType::get(builder.getContext()), result.location);4133  if (resultTypes.empty())4134    ensureTerminator(*bodyRegion, builder, result.location);4135}4136 4137SmallVector<Region *> AffineParallelOp::getLoopRegions() {4138  return {&getRegion()};4139}4140 4141unsigned AffineParallelOp::getNumDims() { return getSteps().size(); }4142 4143AffineParallelOp::operand_range AffineParallelOp::getLowerBoundsOperands() {4144  return getOperands().take_front(getLowerBoundsMap().getNumInputs());4145}4146 4147AffineParallelOp::operand_range AffineParallelOp::getUpperBoundsOperands() {4148  return getOperands().drop_front(getLowerBoundsMap().getNumInputs());4149}4150 4151AffineMap AffineParallelOp::getLowerBoundMap(unsigned pos) {4152  auto values = getLowerBoundsGroups().getValues<int32_t>();4153  unsigned start = 0;4154  for (unsigned i = 0; i < pos; ++i)4155    start += values[i];4156  return getLowerBoundsMap().getSliceMap(start, values[pos]);4157}4158 4159AffineMap AffineParallelOp::getUpperBoundMap(unsigned pos) {4160  auto values = getUpperBoundsGroups().getValues<int32_t>();4161  unsigned start = 0;4162  for (unsigned i = 0; i < pos; ++i)4163    start += values[i];4164  return getUpperBoundsMap().getSliceMap(start, values[pos]);4165}4166 4167AffineValueMap AffineParallelOp::getLowerBoundsValueMap() {4168  return AffineValueMap(getLowerBoundsMap(), getLowerBoundsOperands());4169}4170 4171AffineValueMap AffineParallelOp::getUpperBoundsValueMap() {4172  return AffineValueMap(getUpperBoundsMap(), getUpperBoundsOperands());4173}4174 4175std::optional<SmallVector<int64_t, 8>> AffineParallelOp::getConstantRanges() {4176  if (hasMinMaxBounds())4177    return std::nullopt;4178 4179  // Try to convert all the ranges to constant expressions.4180  SmallVector<int64_t, 8> out;4181  AffineValueMap rangesValueMap;4182  AffineValueMap::difference(getUpperBoundsValueMap(), getLowerBoundsValueMap(),4183                             &rangesValueMap);4184  out.reserve(rangesValueMap.getNumResults());4185  for (unsigned i = 0, e = rangesValueMap.getNumResults(); i < e; ++i) {4186    auto expr = rangesValueMap.getResult(i);4187    auto cst = dyn_cast<AffineConstantExpr>(expr);4188    if (!cst)4189      return std::nullopt;4190    out.push_back(cst.getValue());4191  }4192  return out;4193}4194 4195Block *AffineParallelOp::getBody() { return &getRegion().front(); }4196 4197OpBuilder AffineParallelOp::getBodyBuilder() {4198  return OpBuilder(getBody(), std::prev(getBody()->end()));4199}4200 4201void AffineParallelOp::setLowerBounds(ValueRange lbOperands, AffineMap map) {4202  assert(lbOperands.size() == map.getNumInputs() &&4203         "operands to map must match number of inputs");4204 4205  auto ubOperands = getUpperBoundsOperands();4206 4207  SmallVector<Value, 4> newOperands(lbOperands);4208  newOperands.append(ubOperands.begin(), ubOperands.end());4209  (*this)->setOperands(newOperands);4210 4211  setLowerBoundsMapAttr(AffineMapAttr::get(map));4212}4213 4214void AffineParallelOp::setUpperBounds(ValueRange ubOperands, AffineMap map) {4215  assert(ubOperands.size() == map.getNumInputs() &&4216         "operands to map must match number of inputs");4217 4218  SmallVector<Value, 4> newOperands(getLowerBoundsOperands());4219  newOperands.append(ubOperands.begin(), ubOperands.end());4220  (*this)->setOperands(newOperands);4221 4222  setUpperBoundsMapAttr(AffineMapAttr::get(map));4223}4224 4225void AffineParallelOp::setSteps(ArrayRef<int64_t> newSteps) {4226  setStepsAttr(getBodyBuilder().getI64ArrayAttr(newSteps));4227}4228 4229// check whether resultType match op or not in affine.parallel4230static bool isResultTypeMatchAtomicRMWKind(Type resultType,4231                                           arith::AtomicRMWKind op) {4232  switch (op) {4233  case arith::AtomicRMWKind::addf:4234    return isa<FloatType>(resultType);4235  case arith::AtomicRMWKind::addi:4236    return isa<IntegerType>(resultType);4237  case arith::AtomicRMWKind::assign:4238    return true;4239  case arith::AtomicRMWKind::mulf:4240    return isa<FloatType>(resultType);4241  case arith::AtomicRMWKind::muli:4242    return isa<IntegerType>(resultType);4243  case arith::AtomicRMWKind::maximumf:4244    return isa<FloatType>(resultType);4245  case arith::AtomicRMWKind::minimumf:4246    return isa<FloatType>(resultType);4247  case arith::AtomicRMWKind::maxs: {4248    auto intType = dyn_cast<IntegerType>(resultType);4249    return intType && intType.isSigned();4250  }4251  case arith::AtomicRMWKind::mins: {4252    auto intType = dyn_cast<IntegerType>(resultType);4253    return intType && intType.isSigned();4254  }4255  case arith::AtomicRMWKind::maxu: {4256    auto intType = dyn_cast<IntegerType>(resultType);4257    return intType && intType.isUnsigned();4258  }4259  case arith::AtomicRMWKind::minu: {4260    auto intType = dyn_cast<IntegerType>(resultType);4261    return intType && intType.isUnsigned();4262  }4263  case arith::AtomicRMWKind::ori:4264    return isa<IntegerType>(resultType);4265  case arith::AtomicRMWKind::andi:4266    return isa<IntegerType>(resultType);4267  default:4268    return false;4269  }4270}4271 4272LogicalResult AffineParallelOp::verify() {4273  auto numDims = getNumDims();4274  if (getLowerBoundsGroups().getNumElements() != numDims ||4275      getUpperBoundsGroups().getNumElements() != numDims ||4276      getSteps().size() != numDims || getBody()->getNumArguments() != numDims) {4277    return emitOpError() << "the number of region arguments ("4278                         << getBody()->getNumArguments()4279                         << ") and the number of map groups for lower ("4280                         << getLowerBoundsGroups().getNumElements()4281                         << ") and upper bound ("4282                         << getUpperBoundsGroups().getNumElements()4283                         << "), and the number of steps (" << getSteps().size()4284                         << ") must all match";4285  }4286 4287  unsigned expectedNumLBResults = 0;4288  for (APInt v : getLowerBoundsGroups()) {4289    unsigned results = v.getZExtValue();4290    if (results == 0)4291      return emitOpError()4292             << "expected lower bound map to have at least one result";4293    expectedNumLBResults += results;4294  }4295  if (expectedNumLBResults != getLowerBoundsMap().getNumResults())4296    return emitOpError() << "expected lower bounds map to have "4297                         << expectedNumLBResults << " results";4298  unsigned expectedNumUBResults = 0;4299  for (APInt v : getUpperBoundsGroups()) {4300    unsigned results = v.getZExtValue();4301    if (results == 0)4302      return emitOpError()4303             << "expected upper bound map to have at least one result";4304    expectedNumUBResults += results;4305  }4306  if (expectedNumUBResults != getUpperBoundsMap().getNumResults())4307    return emitOpError() << "expected upper bounds map to have "4308                         << expectedNumUBResults << " results";4309 4310  if (getReductions().size() != getNumResults())4311    return emitOpError("a reduction must be specified for each output");4312 4313  // Verify reduction ops are all valid and each result type matches reduction4314  // ops4315  for (auto it : llvm::enumerate((getReductions()))) {4316    Attribute attr = it.value();4317    auto intAttr = dyn_cast<IntegerAttr>(attr);4318    if (!intAttr || !arith::symbolizeAtomicRMWKind(intAttr.getInt()))4319      return emitOpError("invalid reduction attribute");4320    auto kind = arith::symbolizeAtomicRMWKind(intAttr.getInt()).value();4321    if (!isResultTypeMatchAtomicRMWKind(getResult(it.index()).getType(), kind))4322      return emitOpError("result type cannot match reduction attribute");4323  }4324 4325  // Verify that the bound operands are valid dimension/symbols.4326  /// Lower bounds.4327  if (failed(verifyDimAndSymbolIdentifiers(*this, getLowerBoundsOperands(),4328                                           getLowerBoundsMap().getNumDims())))4329    return failure();4330  /// Upper bounds.4331  if (failed(verifyDimAndSymbolIdentifiers(*this, getUpperBoundsOperands(),4332                                           getUpperBoundsMap().getNumDims())))4333    return failure();4334  return success();4335}4336 4337LogicalResult AffineValueMap::canonicalize() {4338  SmallVector<Value, 4> newOperands{operands};4339  auto newMap = getAffineMap();4340  composeAffineMapAndOperands(&newMap, &newOperands);4341  if (newMap == getAffineMap() && newOperands == operands)4342    return failure();4343  reset(newMap, newOperands);4344  return success();4345}4346 4347/// Canonicalize the bounds of the given loop.4348static LogicalResult canonicalizeLoopBounds(AffineParallelOp op) {4349  AffineValueMap lb = op.getLowerBoundsValueMap();4350  bool lbCanonicalized = succeeded(lb.canonicalize());4351 4352  AffineValueMap ub = op.getUpperBoundsValueMap();4353  bool ubCanonicalized = succeeded(ub.canonicalize());4354 4355  // Any canonicalization change always leads to updated map(s).4356  if (!lbCanonicalized && !ubCanonicalized)4357    return failure();4358 4359  if (lbCanonicalized)4360    op.setLowerBounds(lb.getOperands(), lb.getAffineMap());4361  if (ubCanonicalized)4362    op.setUpperBounds(ub.getOperands(), ub.getAffineMap());4363 4364  return success();4365}4366 4367LogicalResult AffineParallelOp::fold(FoldAdaptor adaptor,4368                                     SmallVectorImpl<OpFoldResult> &results) {4369  return canonicalizeLoopBounds(*this);4370}4371 4372/// Prints a lower(upper) bound of an affine parallel loop with max(min)4373/// conditions in it. `mapAttr` is a flat list of affine expressions and `group`4374/// identifies which of the those expressions form max/min groups. `operands`4375/// are the SSA values of dimensions and symbols and `keyword` is either "min"4376/// or "max".4377static void printMinMaxBound(OpAsmPrinter &p, AffineMapAttr mapAttr,4378                             DenseIntElementsAttr group, ValueRange operands,4379                             StringRef keyword) {4380  AffineMap map = mapAttr.getValue();4381  unsigned numDims = map.getNumDims();4382  ValueRange dimOperands = operands.take_front(numDims);4383  ValueRange symOperands = operands.drop_front(numDims);4384  unsigned start = 0;4385  for (llvm::APInt groupSize : group) {4386    if (start != 0)4387      p << ", ";4388 4389    unsigned size = groupSize.getZExtValue();4390    if (size == 1) {4391      p.printAffineExprOfSSAIds(map.getResult(start), dimOperands, symOperands);4392      ++start;4393    } else {4394      p << keyword << '(';4395      AffineMap submap = map.getSliceMap(start, size);4396      p.printAffineMapOfSSAIds(AffineMapAttr::get(submap), operands);4397      p << ')';4398      start += size;4399    }4400  }4401}4402 4403void AffineParallelOp::print(OpAsmPrinter &p) {4404  p << " (" << getBody()->getArguments() << ") = (";4405  printMinMaxBound(p, getLowerBoundsMapAttr(), getLowerBoundsGroupsAttr(),4406                   getLowerBoundsOperands(), "max");4407  p << ") to (";4408  printMinMaxBound(p, getUpperBoundsMapAttr(), getUpperBoundsGroupsAttr(),4409                   getUpperBoundsOperands(), "min");4410  p << ')';4411  SmallVector<int64_t, 8> steps = getSteps();4412  bool elideSteps = llvm::all_of(steps, [](int64_t step) { return step == 1; });4413  if (!elideSteps) {4414    p << " step (";4415    llvm::interleaveComma(steps, p);4416    p << ')';4417  }4418  if (getNumResults()) {4419    p << " reduce (";4420    llvm::interleaveComma(getReductions(), p, [&](auto &attr) {4421      arith::AtomicRMWKind sym = *arith::symbolizeAtomicRMWKind(4422          llvm::cast<IntegerAttr>(attr).getInt());4423      p << "\"" << arith::stringifyAtomicRMWKind(sym) << "\"";4424    });4425    p << ") -> (" << getResultTypes() << ")";4426  }4427 4428  p << ' ';4429  p.printRegion(getRegion(), /*printEntryBlockArgs=*/false,4430                /*printBlockTerminators=*/getNumResults());4431  p.printOptionalAttrDict(4432      (*this)->getAttrs(),4433      /*elidedAttrs=*/{AffineParallelOp::getReductionsAttrStrName(),4434                       AffineParallelOp::getLowerBoundsMapAttrStrName(),4435                       AffineParallelOp::getLowerBoundsGroupsAttrStrName(),4436                       AffineParallelOp::getUpperBoundsMapAttrStrName(),4437                       AffineParallelOp::getUpperBoundsGroupsAttrStrName(),4438                       AffineParallelOp::getStepsAttrStrName()});4439}4440 4441/// Given a list of lists of parsed operands, populates `uniqueOperands` with4442/// unique operands. Also populates `replacements with affine expressions of4443/// `kind` that can be used to update affine maps previously accepting a4444/// `operands` to accept `uniqueOperands` instead.4445static ParseResult deduplicateAndResolveOperands(4446    OpAsmParser &parser,4447    ArrayRef<SmallVector<OpAsmParser::UnresolvedOperand>> operands,4448    SmallVectorImpl<Value> &uniqueOperands,4449    SmallVectorImpl<AffineExpr> &replacements, AffineExprKind kind) {4450  assert((kind == AffineExprKind::DimId || kind == AffineExprKind::SymbolId) &&4451         "expected operands to be dim or symbol expression");4452 4453  Type indexType = parser.getBuilder().getIndexType();4454  for (const auto &list : operands) {4455    SmallVector<Value> valueOperands;4456    if (parser.resolveOperands(list, indexType, valueOperands))4457      return failure();4458    for (Value operand : valueOperands) {4459      unsigned pos = std::distance(uniqueOperands.begin(),4460                                   llvm::find(uniqueOperands, operand));4461      if (pos == uniqueOperands.size())4462        uniqueOperands.push_back(operand);4463      replacements.push_back(4464          kind == AffineExprKind::DimId4465              ? getAffineDimExpr(pos, parser.getContext())4466              : getAffineSymbolExpr(pos, parser.getContext()));4467    }4468  }4469  return success();4470}4471 4472namespace {4473enum class MinMaxKind { Min, Max };4474} // namespace4475 4476/// Parses an affine map that can contain a min/max for groups of its results,4477/// e.g., max(expr-1, expr-2), expr-3, max(expr-4, expr-5, expr-6). Populates4478/// `result` attributes with the map (flat list of expressions) and the grouping4479/// (list of integers that specify how many expressions to put into each4480/// min/max) attributes. Deduplicates repeated operands.4481///4482/// parallel-bound       ::= `(` parallel-group-list `)`4483/// parallel-group-list  ::= parallel-group (`,` parallel-group-list)?4484/// parallel-group       ::= simple-group | min-max-group4485/// simple-group         ::= expr-of-ssa-ids4486/// min-max-group        ::= ( `min` | `max` ) `(` expr-of-ssa-ids-list `)`4487/// expr-of-ssa-ids-list ::= expr-of-ssa-ids (`,` expr-of-ssa-id-list)?4488///4489/// Examples:4490///   (%0, min(%1 + %2, %3), %4, min(%5 floordiv 32, %6))4491///   (%0, max(%1 - 2 * %2))4492static ParseResult parseAffineMapWithMinMax(OpAsmParser &parser,4493                                            OperationState &result,4494                                            MinMaxKind kind) {4495  // Using `const` not `constexpr` below to workaround a MSVC optimizer bug,4496  // see: https://reviews.llvm.org/D134227#38217534497  const llvm::StringLiteral tmpAttrStrName = "__pseudo_bound_map";4498 4499  StringRef mapName = kind == MinMaxKind::Min4500                          ? AffineParallelOp::getUpperBoundsMapAttrStrName()4501                          : AffineParallelOp::getLowerBoundsMapAttrStrName();4502  StringRef groupsName =4503      kind == MinMaxKind::Min4504          ? AffineParallelOp::getUpperBoundsGroupsAttrStrName()4505          : AffineParallelOp::getLowerBoundsGroupsAttrStrName();4506 4507  if (failed(parser.parseLParen()))4508    return failure();4509 4510  if (succeeded(parser.parseOptionalRParen())) {4511    result.addAttribute(4512        mapName, AffineMapAttr::get(parser.getBuilder().getEmptyAffineMap()));4513    result.addAttribute(groupsName, parser.getBuilder().getI32TensorAttr({}));4514    return success();4515  }4516 4517  SmallVector<AffineExpr> flatExprs;4518  SmallVector<SmallVector<OpAsmParser::UnresolvedOperand>> flatDimOperands;4519  SmallVector<SmallVector<OpAsmParser::UnresolvedOperand>> flatSymOperands;4520  SmallVector<int32_t> numMapsPerGroup;4521  SmallVector<OpAsmParser::UnresolvedOperand> mapOperands;4522  auto parseOperands = [&]() {4523    if (succeeded(parser.parseOptionalKeyword(4524            kind == MinMaxKind::Min ? "min" : "max"))) {4525      mapOperands.clear();4526      AffineMapAttr map;4527      if (failed(parser.parseAffineMapOfSSAIds(mapOperands, map, tmpAttrStrName,4528                                               result.attributes,4529                                               OpAsmParser::Delimiter::Paren)))4530        return failure();4531      result.attributes.erase(tmpAttrStrName);4532      llvm::append_range(flatExprs, map.getValue().getResults());4533      auto operandsRef = llvm::ArrayRef(mapOperands);4534      auto dimsRef = operandsRef.take_front(map.getValue().getNumDims());4535      SmallVector<OpAsmParser::UnresolvedOperand> dims(dimsRef);4536      auto symsRef = operandsRef.drop_front(map.getValue().getNumDims());4537      SmallVector<OpAsmParser::UnresolvedOperand> syms(symsRef);4538      flatDimOperands.append(map.getValue().getNumResults(), dims);4539      flatSymOperands.append(map.getValue().getNumResults(), syms);4540      numMapsPerGroup.push_back(map.getValue().getNumResults());4541    } else {4542      if (failed(parser.parseAffineExprOfSSAIds(flatDimOperands.emplace_back(),4543                                                flatSymOperands.emplace_back(),4544                                                flatExprs.emplace_back())))4545        return failure();4546      numMapsPerGroup.push_back(1);4547    }4548    return success();4549  };4550  if (parser.parseCommaSeparatedList(parseOperands) || parser.parseRParen())4551    return failure();4552 4553  unsigned totalNumDims = 0;4554  unsigned totalNumSyms = 0;4555  for (unsigned i = 0, e = flatExprs.size(); i < e; ++i) {4556    unsigned numDims = flatDimOperands[i].size();4557    unsigned numSyms = flatSymOperands[i].size();4558    flatExprs[i] = flatExprs[i]4559                       .shiftDims(numDims, totalNumDims)4560                       .shiftSymbols(numSyms, totalNumSyms);4561    totalNumDims += numDims;4562    totalNumSyms += numSyms;4563  }4564 4565  // Deduplicate map operands.4566  SmallVector<Value> dimOperands, symOperands;4567  SmallVector<AffineExpr> dimRplacements, symRepacements;4568  if (deduplicateAndResolveOperands(parser, flatDimOperands, dimOperands,4569                                    dimRplacements, AffineExprKind::DimId) ||4570      deduplicateAndResolveOperands(parser, flatSymOperands, symOperands,4571                                    symRepacements, AffineExprKind::SymbolId))4572    return failure();4573 4574  result.operands.append(dimOperands.begin(), dimOperands.end());4575  result.operands.append(symOperands.begin(), symOperands.end());4576 4577  Builder &builder = parser.getBuilder();4578  auto flatMap = AffineMap::get(totalNumDims, totalNumSyms, flatExprs,4579                                parser.getContext());4580  flatMap = flatMap.replaceDimsAndSymbols(4581      dimRplacements, symRepacements, dimOperands.size(), symOperands.size());4582 4583  result.addAttribute(mapName, AffineMapAttr::get(flatMap));4584  result.addAttribute(groupsName, builder.getI32TensorAttr(numMapsPerGroup));4585  return success();4586}4587 4588//4589// operation ::= `affine.parallel` `(` ssa-ids `)` `=` parallel-bound4590//               `to` parallel-bound steps? region attr-dict?4591// steps     ::= `steps` `(` integer-literals `)`4592//4593ParseResult AffineParallelOp::parse(OpAsmParser &parser,4594                                    OperationState &result) {4595  auto &builder = parser.getBuilder();4596  auto indexType = builder.getIndexType();4597  SmallVector<OpAsmParser::Argument, 4> ivs;4598  if (parser.parseArgumentList(ivs, OpAsmParser::Delimiter::Paren) ||4599      parser.parseEqual() ||4600      parseAffineMapWithMinMax(parser, result, MinMaxKind::Max) ||4601      parser.parseKeyword("to") ||4602      parseAffineMapWithMinMax(parser, result, MinMaxKind::Min))4603    return failure();4604 4605  AffineMapAttr stepsMapAttr;4606  NamedAttrList stepsAttrs;4607  SmallVector<OpAsmParser::UnresolvedOperand, 4> stepsMapOperands;4608  if (failed(parser.parseOptionalKeyword("step"))) {4609    SmallVector<int64_t, 4> steps(ivs.size(), 1);4610    result.addAttribute(AffineParallelOp::getStepsAttrStrName(),4611                        builder.getI64ArrayAttr(steps));4612  } else {4613    if (parser.parseAffineMapOfSSAIds(stepsMapOperands, stepsMapAttr,4614                                      AffineParallelOp::getStepsAttrStrName(),4615                                      stepsAttrs,4616                                      OpAsmParser::Delimiter::Paren))4617      return failure();4618 4619    // Convert steps from an AffineMap into an I64ArrayAttr.4620    SmallVector<int64_t, 4> steps;4621    auto stepsMap = stepsMapAttr.getValue();4622    for (const auto &result : stepsMap.getResults()) {4623      auto constExpr = dyn_cast<AffineConstantExpr>(result);4624      if (!constExpr)4625        return parser.emitError(parser.getNameLoc(),4626                                "steps must be constant integers");4627      steps.push_back(constExpr.getValue());4628    }4629    result.addAttribute(AffineParallelOp::getStepsAttrStrName(),4630                        builder.getI64ArrayAttr(steps));4631  }4632 4633  // Parse optional clause of the form: `reduce ("addf", "maxf")`, where the4634  // quoted strings are a member of the enum AtomicRMWKind.4635  SmallVector<Attribute, 4> reductions;4636  if (succeeded(parser.parseOptionalKeyword("reduce"))) {4637    if (parser.parseLParen())4638      return failure();4639    auto parseAttributes = [&]() -> ParseResult {4640      // Parse a single quoted string via the attribute parsing, and then4641      // verify it is a member of the enum and convert to it's integer4642      // representation.4643      StringAttr attrVal;4644      NamedAttrList attrStorage;4645      auto loc = parser.getCurrentLocation();4646      if (parser.parseAttribute(attrVal, builder.getNoneType(), "reduce",4647                                attrStorage))4648        return failure();4649      std::optional<arith::AtomicRMWKind> reduction =4650          arith::symbolizeAtomicRMWKind(attrVal.getValue());4651      if (!reduction)4652        return parser.emitError(loc, "invalid reduction value: ") << attrVal;4653      reductions.push_back(4654          builder.getI64IntegerAttr(static_cast<int64_t>(reduction.value())));4655      // While we keep getting commas, keep parsing.4656      return success();4657    };4658    if (parser.parseCommaSeparatedList(parseAttributes) || parser.parseRParen())4659      return failure();4660  }4661  result.addAttribute(AffineParallelOp::getReductionsAttrStrName(),4662                      builder.getArrayAttr(reductions));4663 4664  // Parse return types of reductions (if any)4665  if (parser.parseOptionalArrowTypeList(result.types))4666    return failure();4667 4668  // Now parse the body.4669  Region *body = result.addRegion();4670  for (auto &iv : ivs)4671    iv.type = indexType;4672  if (parser.parseRegion(*body, ivs) ||4673      parser.parseOptionalAttrDict(result.attributes))4674    return failure();4675 4676  // Add a terminator if none was parsed.4677  AffineParallelOp::ensureTerminator(*body, builder, result.location);4678  return success();4679}4680 4681//===----------------------------------------------------------------------===//4682// AffineYieldOp4683//===----------------------------------------------------------------------===//4684 4685LogicalResult AffineYieldOp::verify() {4686  auto *parentOp = (*this)->getParentOp();4687  auto results = parentOp->getResults();4688  auto operands = getOperands();4689 4690  if (!isa<AffineParallelOp, AffineIfOp, AffineForOp>(parentOp))4691    return emitOpError() << "only terminates affine.if/for/parallel regions";4692  if (parentOp->getNumResults() != getNumOperands())4693    return emitOpError() << "parent of yield must have same number of "4694                            "results as the yield operands";4695  for (auto it : llvm::zip(results, operands)) {4696    if (std::get<0>(it).getType() != std::get<1>(it).getType())4697      return emitOpError() << "types mismatch between yield op and its parent";4698  }4699 4700  return success();4701}4702 4703//===----------------------------------------------------------------------===//4704// AffineVectorLoadOp4705//===----------------------------------------------------------------------===//4706 4707void AffineVectorLoadOp::build(OpBuilder &builder, OperationState &result,4708                               VectorType resultType, AffineMap map,4709                               ValueRange operands) {4710  assert(operands.size() == 1 + map.getNumInputs() && "inconsistent operands");4711  result.addOperands(operands);4712  if (map)4713    result.addAttribute(getMapAttrStrName(), AffineMapAttr::get(map));4714  result.types.push_back(resultType);4715}4716 4717void AffineVectorLoadOp::build(OpBuilder &builder, OperationState &result,4718                               VectorType resultType, Value memref,4719                               AffineMap map, ValueRange mapOperands) {4720  assert(map.getNumInputs() == mapOperands.size() && "inconsistent index info");4721  result.addOperands(memref);4722  result.addOperands(mapOperands);4723  result.addAttribute(getMapAttrStrName(), AffineMapAttr::get(map));4724  result.types.push_back(resultType);4725}4726 4727void AffineVectorLoadOp::build(OpBuilder &builder, OperationState &result,4728                               VectorType resultType, Value memref,4729                               ValueRange indices) {4730  auto memrefType = llvm::cast<MemRefType>(memref.getType());4731  int64_t rank = memrefType.getRank();4732  // Create identity map for memrefs with at least one dimension or () -> ()4733  // for zero-dimensional memrefs.4734  auto map =4735      rank ? builder.getMultiDimIdentityMap(rank) : builder.getEmptyAffineMap();4736  build(builder, result, resultType, memref, map, indices);4737}4738 4739void AffineVectorLoadOp::getCanonicalizationPatterns(RewritePatternSet &results,4740                                                     MLIRContext *context) {4741  results.add<SimplifyAffineOp<AffineVectorLoadOp>>(context);4742}4743 4744ParseResult AffineVectorLoadOp::parse(OpAsmParser &parser,4745                                      OperationState &result) {4746  auto &builder = parser.getBuilder();4747  auto indexTy = builder.getIndexType();4748 4749  MemRefType memrefType;4750  VectorType resultType;4751  OpAsmParser::UnresolvedOperand memrefInfo;4752  AffineMapAttr mapAttr;4753  SmallVector<OpAsmParser::UnresolvedOperand, 1> mapOperands;4754  return failure(4755      parser.parseOperand(memrefInfo) ||4756      parser.parseAffineMapOfSSAIds(mapOperands, mapAttr,4757                                    AffineVectorLoadOp::getMapAttrStrName(),4758                                    result.attributes) ||4759      parser.parseOptionalAttrDict(result.attributes) ||4760      parser.parseColonType(memrefType) || parser.parseComma() ||4761      parser.parseType(resultType) ||4762      parser.resolveOperand(memrefInfo, memrefType, result.operands) ||4763      parser.resolveOperands(mapOperands, indexTy, result.operands) ||4764      parser.addTypeToList(resultType, result.types));4765}4766 4767void AffineVectorLoadOp::print(OpAsmPrinter &p) {4768  p << " " << getMemRef() << '[';4769  if (AffineMapAttr mapAttr =4770          (*this)->getAttrOfType<AffineMapAttr>(getMapAttrStrName()))4771    p.printAffineMapOfSSAIds(mapAttr, getMapOperands());4772  p << ']';4773  p.printOptionalAttrDict((*this)->getAttrs(),4774                          /*elidedAttrs=*/{getMapAttrStrName()});4775  p << " : " << getMemRefType() << ", " << getType();4776}4777 4778/// Verify common invariants of affine.vector_load and affine.vector_store.4779static LogicalResult verifyVectorMemoryOp(Operation *op, MemRefType memrefType,4780                                          VectorType vectorType) {4781  // Check that memref and vector element types match.4782  if (memrefType.getElementType() != vectorType.getElementType())4783    return op->emitOpError(4784        "requires memref and vector types of the same elemental type");4785  return success();4786}4787 4788LogicalResult AffineVectorLoadOp::verify() {4789  MemRefType memrefType = getMemRefType();4790  if (failed(verifyMemoryOpIndexing(4791          *this, (*this)->getAttrOfType<AffineMapAttr>(getMapAttrStrName()),4792          getMapOperands(), memrefType,4793          /*numIndexOperands=*/getNumOperands() - 1)))4794    return failure();4795 4796  if (failed(verifyVectorMemoryOp(getOperation(), memrefType, getVectorType())))4797    return failure();4798 4799  return success();4800}4801 4802//===----------------------------------------------------------------------===//4803// AffineVectorStoreOp4804//===----------------------------------------------------------------------===//4805 4806void AffineVectorStoreOp::build(OpBuilder &builder, OperationState &result,4807                                Value valueToStore, Value memref, AffineMap map,4808                                ValueRange mapOperands) {4809  assert(map.getNumInputs() == mapOperands.size() && "inconsistent index info");4810  result.addOperands(valueToStore);4811  result.addOperands(memref);4812  result.addOperands(mapOperands);4813  result.addAttribute(getMapAttrStrName(), AffineMapAttr::get(map));4814}4815 4816// Use identity map.4817void AffineVectorStoreOp::build(OpBuilder &builder, OperationState &result,4818                                Value valueToStore, Value memref,4819                                ValueRange indices) {4820  auto memrefType = llvm::cast<MemRefType>(memref.getType());4821  int64_t rank = memrefType.getRank();4822  // Create identity map for memrefs with at least one dimension or () -> ()4823  // for zero-dimensional memrefs.4824  auto map =4825      rank ? builder.getMultiDimIdentityMap(rank) : builder.getEmptyAffineMap();4826  build(builder, result, valueToStore, memref, map, indices);4827}4828void AffineVectorStoreOp::getCanonicalizationPatterns(4829    RewritePatternSet &results, MLIRContext *context) {4830  results.add<SimplifyAffineOp<AffineVectorStoreOp>>(context);4831}4832 4833ParseResult AffineVectorStoreOp::parse(OpAsmParser &parser,4834                                       OperationState &result) {4835  auto indexTy = parser.getBuilder().getIndexType();4836 4837  MemRefType memrefType;4838  VectorType resultType;4839  OpAsmParser::UnresolvedOperand storeValueInfo;4840  OpAsmParser::UnresolvedOperand memrefInfo;4841  AffineMapAttr mapAttr;4842  SmallVector<OpAsmParser::UnresolvedOperand, 1> mapOperands;4843  return failure(4844      parser.parseOperand(storeValueInfo) || parser.parseComma() ||4845      parser.parseOperand(memrefInfo) ||4846      parser.parseAffineMapOfSSAIds(mapOperands, mapAttr,4847                                    AffineVectorStoreOp::getMapAttrStrName(),4848                                    result.attributes) ||4849      parser.parseOptionalAttrDict(result.attributes) ||4850      parser.parseColonType(memrefType) || parser.parseComma() ||4851      parser.parseType(resultType) ||4852      parser.resolveOperand(storeValueInfo, resultType, result.operands) ||4853      parser.resolveOperand(memrefInfo, memrefType, result.operands) ||4854      parser.resolveOperands(mapOperands, indexTy, result.operands));4855}4856 4857void AffineVectorStoreOp::print(OpAsmPrinter &p) {4858  p << " " << getValueToStore();4859  p << ", " << getMemRef() << '[';4860  if (AffineMapAttr mapAttr =4861          (*this)->getAttrOfType<AffineMapAttr>(getMapAttrStrName()))4862    p.printAffineMapOfSSAIds(mapAttr, getMapOperands());4863  p << ']';4864  p.printOptionalAttrDict((*this)->getAttrs(),4865                          /*elidedAttrs=*/{getMapAttrStrName()});4866  p << " : " << getMemRefType() << ", " << getValueToStore().getType();4867}4868 4869LogicalResult AffineVectorStoreOp::verify() {4870  MemRefType memrefType = getMemRefType();4871  if (failed(verifyMemoryOpIndexing(4872          *this, (*this)->getAttrOfType<AffineMapAttr>(getMapAttrStrName()),4873          getMapOperands(), memrefType,4874          /*numIndexOperands=*/getNumOperands() - 2)))4875    return failure();4876 4877  if (failed(verifyVectorMemoryOp(*this, memrefType, getVectorType())))4878    return failure();4879 4880  return success();4881}4882 4883//===----------------------------------------------------------------------===//4884// DelinearizeIndexOp4885//===----------------------------------------------------------------------===//4886 4887void AffineDelinearizeIndexOp::build(OpBuilder &odsBuilder,4888                                     OperationState &odsState,4889                                     Value linearIndex, ValueRange dynamicBasis,4890                                     ArrayRef<int64_t> staticBasis,4891                                     bool hasOuterBound) {4892  SmallVector<Type> returnTypes(hasOuterBound ? staticBasis.size()4893                                              : staticBasis.size() + 1,4894                                linearIndex.getType());4895  build(odsBuilder, odsState, returnTypes, linearIndex, dynamicBasis,4896        staticBasis);4897}4898 4899void AffineDelinearizeIndexOp::build(OpBuilder &odsBuilder,4900                                     OperationState &odsState,4901                                     Value linearIndex, ValueRange basis,4902                                     bool hasOuterBound) {4903  if (hasOuterBound && !basis.empty() && basis.front() == nullptr) {4904    hasOuterBound = false;4905    basis = basis.drop_front();4906  }4907  SmallVector<Value> dynamicBasis;4908  SmallVector<int64_t> staticBasis;4909  dispatchIndexOpFoldResults(getAsOpFoldResult(basis), dynamicBasis,4910                             staticBasis);4911  build(odsBuilder, odsState, linearIndex, dynamicBasis, staticBasis,4912        hasOuterBound);4913}4914 4915void AffineDelinearizeIndexOp::build(OpBuilder &odsBuilder,4916                                     OperationState &odsState,4917                                     Value linearIndex,4918                                     ArrayRef<OpFoldResult> basis,4919                                     bool hasOuterBound) {4920  if (hasOuterBound && !basis.empty() && basis.front() == OpFoldResult()) {4921    hasOuterBound = false;4922    basis = basis.drop_front();4923  }4924  SmallVector<Value> dynamicBasis;4925  SmallVector<int64_t> staticBasis;4926  dispatchIndexOpFoldResults(basis, dynamicBasis, staticBasis);4927  build(odsBuilder, odsState, linearIndex, dynamicBasis, staticBasis,4928        hasOuterBound);4929}4930 4931void AffineDelinearizeIndexOp::build(OpBuilder &odsBuilder,4932                                     OperationState &odsState,4933                                     Value linearIndex, ArrayRef<int64_t> basis,4934                                     bool hasOuterBound) {4935  build(odsBuilder, odsState, linearIndex, ValueRange{}, basis, hasOuterBound);4936}4937 4938LogicalResult AffineDelinearizeIndexOp::verify() {4939  ArrayRef<int64_t> staticBasis = getStaticBasis();4940  if (getNumResults() != staticBasis.size() &&4941      getNumResults() != staticBasis.size() + 1)4942    return emitOpError("should return an index for each basis element and up "4943                       "to one extra index");4944 4945  auto dynamicMarkersCount = llvm::count_if(staticBasis, ShapedType::isDynamic);4946  if (static_cast<size_t>(dynamicMarkersCount) != getDynamicBasis().size())4947    return emitOpError(4948        "mismatch between dynamic and static basis (kDynamic marker but no "4949        "corresponding dynamic basis entry) -- this can only happen due to an "4950        "incorrect fold/rewrite");4951 4952  if (!llvm::all_of(staticBasis, [](int64_t v) {4953        return v > 0 || ShapedType::isDynamic(v);4954      }))4955    return emitOpError("no basis element may be statically non-positive");4956 4957  return success();4958}4959 4960/// Given mixed basis of affine.delinearize_index/linearize_index replace4961/// constant SSA values with the constant integer value and return the new4962/// static basis. In case no such candidate for replacement exists, this utility4963/// returns std::nullopt.4964static std::optional<SmallVector<int64_t>>4965foldCstValueToCstAttrBasis(ArrayRef<OpFoldResult> mixedBasis,4966                           MutableOperandRange mutableDynamicBasis,4967                           ArrayRef<Attribute> dynamicBasis) {4968  uint64_t dynamicBasisIndex = 0;4969  for (OpFoldResult basis : dynamicBasis) {4970    if (basis) {4971      mutableDynamicBasis.erase(dynamicBasisIndex);4972    } else {4973      ++dynamicBasisIndex;4974    }4975  }4976 4977  // No constant SSA value exists.4978  if (dynamicBasisIndex == dynamicBasis.size())4979    return std::nullopt;4980 4981  SmallVector<int64_t> staticBasis;4982  for (OpFoldResult basis : mixedBasis) {4983    std::optional<int64_t> basisVal = getConstantIntValue(basis);4984    if (!basisVal)4985      staticBasis.push_back(ShapedType::kDynamic);4986    else4987      staticBasis.push_back(*basisVal);4988  }4989 4990  return staticBasis;4991}4992 4993LogicalResult4994AffineDelinearizeIndexOp::fold(FoldAdaptor adaptor,4995                               SmallVectorImpl<OpFoldResult> &result) {4996  std::optional<SmallVector<int64_t>> maybeStaticBasis =4997      foldCstValueToCstAttrBasis(getMixedBasis(), getDynamicBasisMutable(),4998                                 adaptor.getDynamicBasis());4999  if (maybeStaticBasis) {5000    setStaticBasis(*maybeStaticBasis);5001    return success();5002  }5003  // If we won't be doing any division or modulo (no basis or the one basis5004  // element is purely advisory), simply return the input value.5005  if (getNumResults() == 1) {5006    result.push_back(getLinearIndex());5007    return success();5008  }5009 5010  if (adaptor.getLinearIndex() == nullptr)5011    return failure();5012 5013  if (!adaptor.getDynamicBasis().empty())5014    return failure();5015 5016  int64_t highPart = cast<IntegerAttr>(adaptor.getLinearIndex()).getInt();5017  Type attrType = getLinearIndex().getType();5018 5019  ArrayRef<int64_t> staticBasis = getStaticBasis();5020  if (hasOuterBound())5021    staticBasis = staticBasis.drop_front();5022  for (int64_t modulus : llvm::reverse(staticBasis)) {5023    result.push_back(IntegerAttr::get(attrType, llvm::mod(highPart, modulus)));5024    highPart = llvm::divideFloorSigned(highPart, modulus);5025  }5026  result.push_back(IntegerAttr::get(attrType, highPart));5027  std::reverse(result.begin(), result.end());5028  return success();5029}5030 5031SmallVector<OpFoldResult> AffineDelinearizeIndexOp::getEffectiveBasis() {5032  OpBuilder builder(getContext());5033  if (hasOuterBound()) {5034    if (getStaticBasis().front() == ::mlir::ShapedType::kDynamic)5035      return getMixedValues(getStaticBasis().drop_front(),5036                            getDynamicBasis().drop_front(), builder);5037 5038    return getMixedValues(getStaticBasis().drop_front(), getDynamicBasis(),5039                          builder);5040  }5041 5042  return getMixedValues(getStaticBasis(), getDynamicBasis(), builder);5043}5044 5045SmallVector<OpFoldResult> AffineDelinearizeIndexOp::getPaddedBasis() {5046  SmallVector<OpFoldResult> ret = getMixedBasis();5047  if (!hasOuterBound())5048    ret.insert(ret.begin(), OpFoldResult());5049  return ret;5050}5051 5052namespace {5053 5054// Drops delinearization indices that correspond to unit-extent basis5055struct DropUnitExtentBasis5056    : public OpRewritePattern<affine::AffineDelinearizeIndexOp> {5057  using OpRewritePattern::OpRewritePattern;5058 5059  LogicalResult matchAndRewrite(affine::AffineDelinearizeIndexOp delinearizeOp,5060                                PatternRewriter &rewriter) const override {5061    SmallVector<Value> replacements(delinearizeOp->getNumResults(), nullptr);5062    std::optional<Value> zero = std::nullopt;5063    Location loc = delinearizeOp->getLoc();5064    auto getZero = [&]() -> Value {5065      if (!zero)5066        zero = arith::ConstantIndexOp::create(rewriter, loc, 0);5067      return zero.value();5068    };5069 5070    // Replace all indices corresponding to unit-extent basis with 0.5071    // Remaining basis can be used to get a new `affine.delinearize_index` op.5072    SmallVector<OpFoldResult> newBasis;5073    for (auto [index, basis] :5074         llvm::enumerate(delinearizeOp.getPaddedBasis())) {5075      std::optional<int64_t> basisVal =5076          basis ? getConstantIntValue(basis) : std::nullopt;5077      if (basisVal == 1)5078        replacements[index] = getZero();5079      else5080        newBasis.push_back(basis);5081    }5082 5083    if (newBasis.size() == delinearizeOp.getNumResults())5084      return rewriter.notifyMatchFailure(delinearizeOp,5085                                         "no unit basis elements");5086 5087    if (!newBasis.empty()) {5088      // Will drop the leading nullptr from `basis` if there was no outer bound.5089      auto newDelinearizeOp = affine::AffineDelinearizeIndexOp::create(5090          rewriter, loc, delinearizeOp.getLinearIndex(), newBasis);5091      int newIndex = 0;5092      // Map back the new delinearized indices to the values they replace.5093      for (auto &replacement : replacements) {5094        if (replacement)5095          continue;5096        replacement = newDelinearizeOp->getResult(newIndex++);5097      }5098    }5099 5100    rewriter.replaceOp(delinearizeOp, replacements);5101    return success();5102  }5103};5104 5105/// If a `affine.delinearize_index`'s input is a `affine.linearize_index5106/// disjoint` and the two operations end with the same basis elements,5107/// cancel those parts of the operations out because they are inverses5108/// of each other.5109///5110/// If the operations have the same basis, cancel them entirely.5111///5112/// The `disjoint` flag is needed on the `affine.linearize_index` because5113/// otherwise, there is no guarantee that the inputs to the linearization are5114/// in-bounds the way the outputs of the delinearization would be.5115struct CancelDelinearizeOfLinearizeDisjointExactTail5116    : public OpRewritePattern<affine::AffineDelinearizeIndexOp> {5117  using OpRewritePattern::OpRewritePattern;5118 5119  LogicalResult matchAndRewrite(affine::AffineDelinearizeIndexOp delinearizeOp,5120                                PatternRewriter &rewriter) const override {5121    auto linearizeOp = delinearizeOp.getLinearIndex()5122                           .getDefiningOp<affine::AffineLinearizeIndexOp>();5123    if (!linearizeOp)5124      return rewriter.notifyMatchFailure(delinearizeOp,5125                                         "index doesn't come from linearize");5126 5127    if (!linearizeOp.getDisjoint())5128      return rewriter.notifyMatchFailure(linearizeOp, "not disjoint");5129 5130    ValueRange linearizeIns = linearizeOp.getMultiIndex();5131    // Note: we use the full basis so we don't lose outer bounds later.5132    SmallVector<OpFoldResult> linearizeBasis = linearizeOp.getMixedBasis();5133    SmallVector<OpFoldResult> delinearizeBasis = delinearizeOp.getMixedBasis();5134    size_t numMatches = 0;5135    for (auto [linSize, delinSize] : llvm::zip(5136             llvm::reverse(linearizeBasis), llvm::reverse(delinearizeBasis))) {5137      if (linSize != delinSize)5138        break;5139      ++numMatches;5140    }5141 5142    if (numMatches == 0)5143      return rewriter.notifyMatchFailure(5144          delinearizeOp, "final basis element doesn't match linearize");5145 5146    // The easy case: everything lines up and the basis match sup completely.5147    if (numMatches == linearizeBasis.size() &&5148        numMatches == delinearizeBasis.size() &&5149        linearizeIns.size() == delinearizeOp.getNumResults()) {5150      rewriter.replaceOp(delinearizeOp, linearizeOp.getMultiIndex());5151      return success();5152    }5153 5154    Value newLinearize = affine::AffineLinearizeIndexOp::create(5155        rewriter, linearizeOp.getLoc(), linearizeIns.drop_back(numMatches),5156        ArrayRef<OpFoldResult>{linearizeBasis}.drop_back(numMatches),5157        linearizeOp.getDisjoint());5158    auto newDelinearize = affine::AffineDelinearizeIndexOp::create(5159        rewriter, delinearizeOp.getLoc(), newLinearize,5160        ArrayRef<OpFoldResult>{delinearizeBasis}.drop_back(numMatches),5161        delinearizeOp.hasOuterBound());5162    SmallVector<Value> mergedResults(newDelinearize.getResults());5163    mergedResults.append(linearizeIns.take_back(numMatches).begin(),5164                         linearizeIns.take_back(numMatches).end());5165    rewriter.replaceOp(delinearizeOp, mergedResults);5166    return success();5167  }5168};5169 5170/// If the input to a delinearization is a disjoint linearization, and the5171/// last k > 1 components of the delinearization basis multiply to the5172/// last component of the linearization basis, break the linearization and5173/// delinearization into two parts, peeling off the last input to linearization.5174///5175/// For example:5176///    %0 = affine.linearize_index [%z, %y, %x] by (3, 2, 32) : index5177///    %1:4 = affine.delinearize_index %0 by (2, 3, 8, 4) : index, ...5178/// becomes5179///    %0 = affine.linearize_index [%z, %y] by (3, 2) : index5180///    %1:2 = affine.delinearize_index %0 by (2, 3) : index5181///    %2:2 = affine.delinearize_index %x by (8, 4) : index5182/// where the original %1:4 is replaced by %1:2 ++ %2:25183struct SplitDelinearizeSpanningLastLinearizeArg final5184    : OpRewritePattern<affine::AffineDelinearizeIndexOp> {5185  using OpRewritePattern::OpRewritePattern;5186 5187  LogicalResult matchAndRewrite(affine::AffineDelinearizeIndexOp delinearizeOp,5188                                PatternRewriter &rewriter) const override {5189    auto linearizeOp = delinearizeOp.getLinearIndex()5190                           .getDefiningOp<affine::AffineLinearizeIndexOp>();5191    if (!linearizeOp)5192      return rewriter.notifyMatchFailure(delinearizeOp,5193                                         "index doesn't come from linearize");5194 5195    if (!linearizeOp.getDisjoint())5196      return rewriter.notifyMatchFailure(linearizeOp,5197                                         "linearize isn't disjoint");5198 5199    int64_t target = linearizeOp.getStaticBasis().back();5200    if (ShapedType::isDynamic(target))5201      return rewriter.notifyMatchFailure(5202          linearizeOp, "linearize ends with dynamic basis value");5203 5204    int64_t sizeToSplit = 1;5205    size_t elemsToSplit = 0;5206    ArrayRef<int64_t> basis = delinearizeOp.getStaticBasis();5207    for (int64_t basisElem : llvm::reverse(basis)) {5208      if (ShapedType::isDynamic(basisElem))5209        return rewriter.notifyMatchFailure(5210            delinearizeOp, "dynamic basis element while scanning for split");5211      sizeToSplit *= basisElem;5212      elemsToSplit += 1;5213 5214      if (sizeToSplit > target)5215        return rewriter.notifyMatchFailure(delinearizeOp,5216                                           "overshot last argument size");5217      if (sizeToSplit == target)5218        break;5219    }5220 5221    if (sizeToSplit < target)5222      return rewriter.notifyMatchFailure(5223          delinearizeOp, "product of known basis elements doesn't exceed last "5224                         "linearize argument");5225 5226    if (elemsToSplit < 2)5227      return rewriter.notifyMatchFailure(5228          delinearizeOp,5229          "need at least two elements to form the basis product");5230 5231    Value linearizeWithoutBack = affine::AffineLinearizeIndexOp::create(5232        rewriter, linearizeOp.getLoc(), linearizeOp.getMultiIndex().drop_back(),5233        linearizeOp.getDynamicBasis(), linearizeOp.getStaticBasis().drop_back(),5234        linearizeOp.getDisjoint());5235    auto delinearizeWithoutSplitPart = affine::AffineDelinearizeIndexOp::create(5236        rewriter, delinearizeOp.getLoc(), linearizeWithoutBack,5237        delinearizeOp.getDynamicBasis(), basis.drop_back(elemsToSplit),5238        delinearizeOp.hasOuterBound());5239    auto delinearizeBack = affine::AffineDelinearizeIndexOp::create(5240        rewriter, delinearizeOp.getLoc(), linearizeOp.getMultiIndex().back(),5241        basis.take_back(elemsToSplit), /*hasOuterBound=*/true);5242    SmallVector<Value> results = llvm::to_vector(5243        llvm::concat<Value>(delinearizeWithoutSplitPart.getResults(),5244                            delinearizeBack.getResults()));5245    rewriter.replaceOp(delinearizeOp, results);5246 5247    return success();5248  }5249};5250} // namespace5251 5252void affine::AffineDelinearizeIndexOp::getCanonicalizationPatterns(5253    RewritePatternSet &patterns, MLIRContext *context) {5254  patterns5255      .insert<CancelDelinearizeOfLinearizeDisjointExactTail,5256              DropUnitExtentBasis, SplitDelinearizeSpanningLastLinearizeArg>(5257          context);5258}5259 5260//===----------------------------------------------------------------------===//5261// LinearizeIndexOp5262//===----------------------------------------------------------------------===//5263 5264void AffineLinearizeIndexOp::build(OpBuilder &odsBuilder,5265                                   OperationState &odsState,5266                                   ValueRange multiIndex, ValueRange basis,5267                                   bool disjoint) {5268  if (!basis.empty() && basis.front() == Value())5269    basis = basis.drop_front();5270  SmallVector<Value> dynamicBasis;5271  SmallVector<int64_t> staticBasis;5272  dispatchIndexOpFoldResults(getAsOpFoldResult(basis), dynamicBasis,5273                             staticBasis);5274  build(odsBuilder, odsState, multiIndex, dynamicBasis, staticBasis, disjoint);5275}5276 5277void AffineLinearizeIndexOp::build(OpBuilder &odsBuilder,5278                                   OperationState &odsState,5279                                   ValueRange multiIndex,5280                                   ArrayRef<OpFoldResult> basis,5281                                   bool disjoint) {5282  if (!basis.empty() && basis.front() == OpFoldResult())5283    basis = basis.drop_front();5284  SmallVector<Value> dynamicBasis;5285  SmallVector<int64_t> staticBasis;5286  dispatchIndexOpFoldResults(basis, dynamicBasis, staticBasis);5287  build(odsBuilder, odsState, multiIndex, dynamicBasis, staticBasis, disjoint);5288}5289 5290void AffineLinearizeIndexOp::build(OpBuilder &odsBuilder,5291                                   OperationState &odsState,5292                                   ValueRange multiIndex,5293                                   ArrayRef<int64_t> basis, bool disjoint) {5294  build(odsBuilder, odsState, multiIndex, ValueRange{}, basis, disjoint);5295}5296 5297LogicalResult AffineLinearizeIndexOp::verify() {5298  size_t numIndexes = getMultiIndex().size();5299  size_t numBasisElems = getStaticBasis().size();5300  if (numIndexes != numBasisElems && numIndexes != numBasisElems + 1)5301    return emitOpError("should be passed a basis element for each index except "5302                       "possibly the first");5303 5304  auto dynamicMarkersCount =5305      llvm::count_if(getStaticBasis(), ShapedType::isDynamic);5306  if (static_cast<size_t>(dynamicMarkersCount) != getDynamicBasis().size())5307    return emitOpError(5308        "mismatch between dynamic and static basis (kDynamic marker but no "5309        "corresponding dynamic basis entry) -- this can only happen due to an "5310        "incorrect fold/rewrite");5311 5312  return success();5313}5314 5315OpFoldResult AffineLinearizeIndexOp::fold(FoldAdaptor adaptor) {5316  std::optional<SmallVector<int64_t>> maybeStaticBasis =5317      foldCstValueToCstAttrBasis(getMixedBasis(), getDynamicBasisMutable(),5318                                 adaptor.getDynamicBasis());5319  if (maybeStaticBasis) {5320    setStaticBasis(*maybeStaticBasis);5321    return getResult();5322  }5323  // No indices linearizes to zero.5324  if (getMultiIndex().empty())5325    return IntegerAttr::get(getResult().getType(), 0);5326 5327  // One single index linearizes to itself.5328  if (getMultiIndex().size() == 1)5329    return getMultiIndex().front();5330 5331  if (llvm::is_contained(adaptor.getMultiIndex(), nullptr))5332    return nullptr;5333 5334  if (!adaptor.getDynamicBasis().empty())5335    return nullptr;5336 5337  int64_t result = 0;5338  int64_t stride = 1;5339  for (auto [length, indexAttr] :5340       llvm::zip_first(llvm::reverse(getStaticBasis()),5341                       llvm::reverse(adaptor.getMultiIndex()))) {5342    result = result + cast<IntegerAttr>(indexAttr).getInt() * stride;5343    stride = stride * length;5344  }5345  // Handle the index element with no basis element.5346  if (!hasOuterBound())5347    result =5348        result +5349        cast<IntegerAttr>(adaptor.getMultiIndex().front()).getInt() * stride;5350 5351  return IntegerAttr::get(getResult().getType(), result);5352}5353 5354SmallVector<OpFoldResult> AffineLinearizeIndexOp::getEffectiveBasis() {5355  OpBuilder builder(getContext());5356  if (hasOuterBound()) {5357    if (getStaticBasis().front() == ::mlir::ShapedType::kDynamic)5358      return getMixedValues(getStaticBasis().drop_front(),5359                            getDynamicBasis().drop_front(), builder);5360 5361    return getMixedValues(getStaticBasis().drop_front(), getDynamicBasis(),5362                          builder);5363  }5364 5365  return getMixedValues(getStaticBasis(), getDynamicBasis(), builder);5366}5367 5368SmallVector<OpFoldResult> AffineLinearizeIndexOp::getPaddedBasis() {5369  SmallVector<OpFoldResult> ret = getMixedBasis();5370  if (!hasOuterBound())5371    ret.insert(ret.begin(), OpFoldResult());5372  return ret;5373}5374 5375namespace {5376/// Rewrite `affine.linearize_index disjoint [%...a, %x, %...b] by (%...c, 1,5377/// %...d)` to `affine.linearize_index disjoint [%...a, %...b] by (%...c,5378/// %...d)`.5379 5380/// Note that `disjoint` is required here, because, without it, we could have5381/// `affine.linearize_index [%...a, %c64, %...b] by (%...c, 1, %...d)`5382/// is a valid operation where the `%c64` cannot be trivially dropped.5383///5384/// Alternatively, if `%x` in the above is a known constant 0, remove it even if5385/// the operation isn't asserted to be `disjoint`.5386struct DropLinearizeUnitComponentsIfDisjointOrZero final5387    : OpRewritePattern<affine::AffineLinearizeIndexOp> {5388  using OpRewritePattern::OpRewritePattern;5389 5390  LogicalResult matchAndRewrite(affine::AffineLinearizeIndexOp op,5391                                PatternRewriter &rewriter) const override {5392    ValueRange multiIndex = op.getMultiIndex();5393    size_t numIndices = multiIndex.size();5394    SmallVector<Value> newIndices;5395    newIndices.reserve(numIndices);5396    SmallVector<OpFoldResult> newBasis;5397    newBasis.reserve(numIndices);5398 5399    if (!op.hasOuterBound()) {5400      newIndices.push_back(multiIndex.front());5401      multiIndex = multiIndex.drop_front();5402    }5403 5404    SmallVector<OpFoldResult> basis = op.getMixedBasis();5405    for (auto [index, basisElem] : llvm::zip_equal(multiIndex, basis)) {5406      std::optional<int64_t> basisEntry = getConstantIntValue(basisElem);5407      if (!basisEntry || *basisEntry != 1) {5408        newIndices.push_back(index);5409        newBasis.push_back(basisElem);5410        continue;5411      }5412 5413      std::optional<int64_t> indexValue = getConstantIntValue(index);5414      if (!op.getDisjoint() && (!indexValue || *indexValue != 0)) {5415        newIndices.push_back(index);5416        newBasis.push_back(basisElem);5417        continue;5418      }5419    }5420    if (newIndices.size() == numIndices)5421      return rewriter.notifyMatchFailure(op,5422                                         "no unit basis entries to replace");5423 5424    if (newIndices.empty()) {5425      rewriter.replaceOpWithNewOp<arith::ConstantIndexOp>(op, 0);5426      return success();5427    }5428    rewriter.replaceOpWithNewOp<affine::AffineLinearizeIndexOp>(5429        op, newIndices, newBasis, op.getDisjoint());5430    return success();5431  }5432};5433 5434OpFoldResult computeProduct(Location loc, OpBuilder &builder,5435                            ArrayRef<OpFoldResult> terms) {5436  int64_t nDynamic = 0;5437  SmallVector<Value> dynamicPart;5438  AffineExpr result = builder.getAffineConstantExpr(1);5439  for (OpFoldResult term : terms) {5440    if (!term)5441      return term;5442    std::optional<int64_t> maybeConst = getConstantIntValue(term);5443    if (maybeConst) {5444      result = result * builder.getAffineConstantExpr(*maybeConst);5445    } else {5446      dynamicPart.push_back(cast<Value>(term));5447      result = result * builder.getAffineSymbolExpr(nDynamic++);5448    }5449  }5450  if (auto constant = dyn_cast<AffineConstantExpr>(result))5451    return getAsIndexOpFoldResult(builder.getContext(), constant.getValue());5452  return AffineApplyOp::create(builder, loc, result, dynamicPart).getResult();5453}5454 5455/// If conseceutive outputs of a delinearize_index are linearized with the same5456/// bounds, canonicalize away the redundant arithmetic.5457///5458/// That is, if we have5459/// ```5460/// %s:N = affine.delinearize_index %x into (...a, B1, B2, ... BK, ...b)5461/// %t = affine.linearize_index [...c, %s#I, %s#(I + 1), ... %s#(I+K-1), ...d]5462///   by (...e, B1, B2, ..., BK, ...f)5463/// ```5464///5465/// We can rewrite this to5466/// ```5467/// B = B1 * B2 ... BK5468/// %sMerged:(N-K+1) affine.delinearize_index %x into (...a, B, ...b)5469/// %t = affine.linearize_index [...c, %s#I, ...d] by (...e, B, ...f)5470/// ```5471/// where we replace all results of %s unaffected by the change with results5472/// from %sMerged.5473///5474/// As a special case, if all results of the delinearize are merged in this way5475/// we can replace those usages with %x, thus cancelling the delinearization5476/// entirely, as in5477/// ```5478/// %s:3 = affine.delinearize_index %x into (2, 4, 8)5479/// %t = affine.linearize_index [%s#0, %s#1, %s#2, %c0] by (2, 4, 8, 16)5480/// ```5481/// becoming `%t = affine.linearize_index [%x, %c0] by (64, 16)`5482struct CancelLinearizeOfDelinearizePortion final5483    : OpRewritePattern<affine::AffineLinearizeIndexOp> {5484  using OpRewritePattern::OpRewritePattern;5485 5486private:5487  // Struct representing a case where the cancellation pattern5488  // applies. A `Match` means that `length` inputs to the linearize operation5489  // starting at `linStart` can be cancelled with `length` outputs of5490  // `delinearize`, starting from `delinStart`.5491  struct Match {5492    AffineDelinearizeIndexOp delinearize;5493    unsigned linStart = 0;5494    unsigned delinStart = 0;5495    unsigned length = 0;5496  };5497 5498public:5499  LogicalResult matchAndRewrite(affine::AffineLinearizeIndexOp linearizeOp,5500                                PatternRewriter &rewriter) const override {5501    SmallVector<Match> matches;5502 5503    const SmallVector<OpFoldResult> linBasis = linearizeOp.getPaddedBasis();5504    ArrayRef<OpFoldResult> linBasisRef = linBasis;5505 5506    ValueRange multiIndex = linearizeOp.getMultiIndex();5507    unsigned numLinArgs = multiIndex.size();5508    unsigned linArgIdx = 0;5509    // We only want to replace one run from the same delinearize op per5510    // pattern invocation lest we run into invalidation issues.5511    llvm::SmallPtrSet<Operation *, 2> alreadyMatchedDelinearize;5512    while (linArgIdx < numLinArgs) {5513      auto asResult = dyn_cast<OpResult>(multiIndex[linArgIdx]);5514      if (!asResult) {5515        linArgIdx++;5516        continue;5517      }5518 5519      auto delinearizeOp =5520          dyn_cast<AffineDelinearizeIndexOp>(asResult.getOwner());5521      if (!delinearizeOp) {5522        linArgIdx++;5523        continue;5524      }5525 5526      /// Result 0 of the delinearize and argument 0 of the linearize can5527      /// leave their maximum value unspecified. However, even if this happens5528      /// we can still sometimes start the match process. Specifically, if5529      /// - The argument we're matching is result 0 and argument 0 (so the5530      /// bounds don't matter). For example,5531      ///5532      ///     %0:2 = affine.delinearize_index %x into (8) : index, index5533      ///     %1 = affine.linearize_index [%s#0, %s#1, ...] (8, ...)5534      /// allows cancellation5535      /// - The delinearization doesn't specify a bound, but the linearization5536      ///  is `disjoint`, which asserts that the bound on the linearization is5537      ///  correct.5538      unsigned delinArgIdx = asResult.getResultNumber();5539      SmallVector<OpFoldResult> delinBasis = delinearizeOp.getPaddedBasis();5540      OpFoldResult firstDelinBound = delinBasis[delinArgIdx];5541      OpFoldResult firstLinBound = linBasis[linArgIdx];5542      bool boundsMatch = firstDelinBound == firstLinBound;5543      bool bothAtFront = linArgIdx == 0 && delinArgIdx == 0;5544      bool knownByDisjoint =5545          linearizeOp.getDisjoint() && delinArgIdx == 0 && !firstDelinBound;5546      if (!boundsMatch && !bothAtFront && !knownByDisjoint) {5547        linArgIdx++;5548        continue;5549      }5550 5551      unsigned j = 1;5552      unsigned numDelinOuts = delinearizeOp.getNumResults();5553      for (; j + linArgIdx < numLinArgs && j + delinArgIdx < numDelinOuts;5554           ++j) {5555        if (multiIndex[linArgIdx + j] !=5556            delinearizeOp.getResult(delinArgIdx + j))5557          break;5558        if (linBasis[linArgIdx + j] != delinBasis[delinArgIdx + j])5559          break;5560      }5561      // If there're multiple matches against the same delinearize_index,5562      // only rewrite the first one we find to prevent invalidations. The next5563      // ones will be taken care of by subsequent pattern invocations.5564      if (j <= 1 || !alreadyMatchedDelinearize.insert(delinearizeOp).second) {5565        linArgIdx++;5566        continue;5567      }5568      matches.push_back(Match{delinearizeOp, linArgIdx, delinArgIdx, j});5569      linArgIdx += j;5570    }5571 5572    if (matches.empty())5573      return rewriter.notifyMatchFailure(5574          linearizeOp, "no run of delinearize outputs to deal with");5575 5576    // Record all the delinearize replacements so we can do them after creating5577    // the new linearization operation, since the new operation might use5578    // outputs of something we're replacing.5579    SmallVector<SmallVector<Value>> delinearizeReplacements;5580 5581    SmallVector<Value> newIndex;5582    newIndex.reserve(numLinArgs);5583    SmallVector<OpFoldResult> newBasis;5584    newBasis.reserve(numLinArgs);5585    unsigned prevMatchEnd = 0;5586    for (Match m : matches) {5587      unsigned gap = m.linStart - prevMatchEnd;5588      llvm::append_range(newIndex, multiIndex.slice(prevMatchEnd, gap));5589      llvm::append_range(newBasis, linBasisRef.slice(prevMatchEnd, gap));5590      // Update here so we don't forget this during early continues5591      prevMatchEnd = m.linStart + m.length;5592 5593      PatternRewriter::InsertionGuard g(rewriter);5594      rewriter.setInsertionPoint(m.delinearize);5595 5596      ArrayRef<OpFoldResult> basisToMerge =5597          linBasisRef.slice(m.linStart, m.length);5598      // We use the slice from the linearize's basis above because of the5599      // "bounds inferred from `disjoint`" case above.5600      OpFoldResult newSize =5601          computeProduct(linearizeOp.getLoc(), rewriter, basisToMerge);5602 5603      // Trivial case where we can just skip past the delinearize all together5604      if (m.length == m.delinearize.getNumResults()) {5605        newIndex.push_back(m.delinearize.getLinearIndex());5606        newBasis.push_back(newSize);5607        // Pad out set of replacements so we don't do anything with this one.5608        delinearizeReplacements.push_back(SmallVector<Value>());5609        continue;5610      }5611 5612      SmallVector<Value> newDelinResults;5613      SmallVector<OpFoldResult> newDelinBasis = m.delinearize.getPaddedBasis();5614      newDelinBasis.erase(newDelinBasis.begin() + m.delinStart,5615                          newDelinBasis.begin() + m.delinStart + m.length);5616      newDelinBasis.insert(newDelinBasis.begin() + m.delinStart, newSize);5617      auto newDelinearize = AffineDelinearizeIndexOp::create(5618          rewriter, m.delinearize.getLoc(), m.delinearize.getLinearIndex(),5619          newDelinBasis);5620 5621      // Since there may be other uses of the indices we just merged together,5622      // create a residual affine.delinearize_index that delinearizes the5623      // merged output into its component parts.5624      Value combinedElem = newDelinearize.getResult(m.delinStart);5625      auto residualDelinearize = AffineDelinearizeIndexOp::create(5626          rewriter, m.delinearize.getLoc(), combinedElem, basisToMerge);5627 5628      // Swap all the uses of the unaffected delinearize outputs to the new5629      // delinearization so that the old code can be removed if this5630      // linearize_index is the only user of the merged results.5631      llvm::append_range(newDelinResults,5632                         newDelinearize.getResults().take_front(m.delinStart));5633      llvm::append_range(newDelinResults, residualDelinearize.getResults());5634      llvm::append_range(5635          newDelinResults,5636          newDelinearize.getResults().drop_front(m.delinStart + 1));5637 5638      delinearizeReplacements.push_back(newDelinResults);5639      newIndex.push_back(combinedElem);5640      newBasis.push_back(newSize);5641    }5642    llvm::append_range(newIndex, multiIndex.drop_front(prevMatchEnd));5643    llvm::append_range(newBasis, linBasisRef.drop_front(prevMatchEnd));5644    rewriter.replaceOpWithNewOp<AffineLinearizeIndexOp>(5645        linearizeOp, newIndex, newBasis, linearizeOp.getDisjoint());5646 5647    for (auto [m, newResults] :5648         llvm::zip_equal(matches, delinearizeReplacements)) {5649      if (newResults.empty())5650        continue;5651      rewriter.replaceOp(m.delinearize, newResults);5652    }5653 5654    return success();5655  }5656};5657 5658/// Strip leading zero from affine.linearize_index.5659///5660/// `affine.linearize_index [%c0, ...a] by (%x, ...b)` can be rewritten5661/// to `affine.linearize_index [...a] by (...b)` in all cases.5662struct DropLinearizeLeadingZero final5663    : OpRewritePattern<affine::AffineLinearizeIndexOp> {5664  using OpRewritePattern::OpRewritePattern;5665 5666  LogicalResult matchAndRewrite(affine::AffineLinearizeIndexOp op,5667                                PatternRewriter &rewriter) const override {5668    Value leadingIdx = op.getMultiIndex().front();5669    if (!matchPattern(leadingIdx, m_Zero()))5670      return failure();5671 5672    if (op.getMultiIndex().size() == 1) {5673      rewriter.replaceOp(op, leadingIdx);5674      return success();5675    }5676 5677    SmallVector<OpFoldResult> mixedBasis = op.getMixedBasis();5678    ArrayRef<OpFoldResult> newMixedBasis = mixedBasis;5679    if (op.hasOuterBound())5680      newMixedBasis = newMixedBasis.drop_front();5681 5682    rewriter.replaceOpWithNewOp<affine::AffineLinearizeIndexOp>(5683        op, op.getMultiIndex().drop_front(), newMixedBasis, op.getDisjoint());5684    return success();5685  }5686};5687} // namespace5688 5689void affine::AffineLinearizeIndexOp::getCanonicalizationPatterns(5690    RewritePatternSet &patterns, MLIRContext *context) {5691  patterns.add<CancelLinearizeOfDelinearizePortion, DropLinearizeLeadingZero,5692               DropLinearizeUnitComponentsIfDisjointOrZero>(context);5693}5694 5695//===----------------------------------------------------------------------===//5696// TableGen'd op method definitions5697//===----------------------------------------------------------------------===//5698 5699#define GET_OP_CLASSES5700#include "mlir/Dialect/Affine/IR/AffineOps.cpp.inc"5701