brintos

brintos / llvm-project-archived public Read only

0
0
Text · 152.1 KiB · 1035d7c Raw
3843 lines · cpp
1//===----------------------------------------------------------------------===//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/Arith/IR/Arith.h"10#include "mlir/Dialect/Arith/Utils/Utils.h"11#include "mlir/Dialect/MemRef/IR/MemRef.h"12#include "mlir/Dialect/Utils/StaticValueUtils.h"13#include "mlir/IR/AffineMap.h"14#include "mlir/IR/Builders.h"15#include "mlir/IR/BuiltinTypes.h"16#include "mlir/IR/Matchers.h"17#include "mlir/IR/OpDefinition.h"18#include "mlir/IR/PatternMatch.h"19#include "mlir/IR/TypeUtilities.h"20#include "mlir/Interfaces/InferTypeOpInterface.h"21#include "mlir/Interfaces/SideEffectInterfaces.h"22#include "mlir/Interfaces/Utils/InferIntRangeCommon.h"23#include "mlir/Interfaces/ViewLikeInterface.h"24#include "llvm/ADT/STLExtras.h"25#include "llvm/ADT/SmallBitVector.h"26 27using namespace mlir;28using namespace mlir::memref;29 30/// Materialize a single constant operation from a given attribute value with31/// the desired resultant type.32Operation *MemRefDialect::materializeConstant(OpBuilder &builder,33                                              Attribute value, Type type,34                                              Location loc) {35  return arith::ConstantOp::materialize(builder, value, type, loc);36}37 38//===----------------------------------------------------------------------===//39// Common canonicalization pattern support logic40//===----------------------------------------------------------------------===//41 42/// This is a common class used for patterns of the form43/// "someop(memrefcast) -> someop".  It folds the source of any memref.cast44/// into the root operation directly.45LogicalResult mlir::memref::foldMemRefCast(Operation *op, Value inner) {46  bool folded = false;47  for (OpOperand &operand : op->getOpOperands()) {48    auto cast = operand.get().getDefiningOp<CastOp>();49    if (cast && operand.get() != inner &&50        !llvm::isa<UnrankedMemRefType>(cast.getOperand().getType())) {51      operand.set(cast.getOperand());52      folded = true;53    }54  }55  return success(folded);56}57 58/// Return an unranked/ranked tensor type for the given unranked/ranked memref59/// type.60Type mlir::memref::getTensorTypeFromMemRefType(Type type) {61  if (auto memref = llvm::dyn_cast<MemRefType>(type))62    return RankedTensorType::get(memref.getShape(), memref.getElementType());63  if (auto memref = llvm::dyn_cast<UnrankedMemRefType>(type))64    return UnrankedTensorType::get(memref.getElementType());65  return NoneType::get(type.getContext());66}67 68OpFoldResult memref::getMixedSize(OpBuilder &builder, Location loc, Value value,69                                  int64_t dim) {70  auto memrefType = llvm::cast<MemRefType>(value.getType());71  if (memrefType.isDynamicDim(dim))72    return builder.createOrFold<memref::DimOp>(loc, value, dim);73 74  return builder.getIndexAttr(memrefType.getDimSize(dim));75}76 77SmallVector<OpFoldResult> memref::getMixedSizes(OpBuilder &builder,78                                                Location loc, Value value) {79  auto memrefType = llvm::cast<MemRefType>(value.getType());80  SmallVector<OpFoldResult> result;81  for (int64_t i = 0; i < memrefType.getRank(); ++i)82    result.push_back(getMixedSize(builder, loc, value, i));83  return result;84}85 86//===----------------------------------------------------------------------===//87// Utility functions for propagating static information88//===----------------------------------------------------------------------===//89 90/// Helper function that sets values[i] to constValues[i] if the latter is a91/// static value, as indicated by ShapedType::kDynamic.92///93/// If constValues[i] is dynamic, tries to extract a constant value from94/// value[i] to allow for additional folding opportunities. Also convertes all95/// existing attributes to index attributes. (They may be i64 attributes.)96static void constifyIndexValues(SmallVectorImpl<OpFoldResult> &values,97                                ArrayRef<int64_t> constValues) {98  assert(constValues.size() == values.size() &&99         "incorrect number of const values");100  for (auto [i, cstVal] : llvm::enumerate(constValues)) {101    Builder builder(values[i].getContext());102    if (ShapedType::isStatic(cstVal)) {103      // Constant value is known, use it directly.104      values[i] = builder.getIndexAttr(cstVal);105      continue;106    }107    if (std::optional<int64_t> cst = getConstantIntValue(values[i])) {108      // Try to extract a constant or convert an existing to index.109      values[i] = builder.getIndexAttr(*cst);110    }111  }112}113 114/// Helper function to retrieve a lossless memory-space cast, and the115/// corresponding new result memref type.116static std::tuple<MemorySpaceCastOpInterface, PtrLikeTypeInterface, Type>117getMemorySpaceCastInfo(BaseMemRefType resultTy, Value src) {118  MemorySpaceCastOpInterface castOp =119      MemorySpaceCastOpInterface::getIfPromotableCast(src);120 121  // Bail if the cast is not lossless.122  if (!castOp)123    return {};124 125  // Transform the source and target type of `castOp` to have the same metadata126  // as `resultTy`. Bail if not possible.127  FailureOr<PtrLikeTypeInterface> srcTy = resultTy.clonePtrWith(128      castOp.getSourcePtr().getType().getMemorySpace(), std::nullopt);129  if (failed(srcTy))130    return {};131 132  FailureOr<PtrLikeTypeInterface> tgtTy = resultTy.clonePtrWith(133      castOp.getTargetPtr().getType().getMemorySpace(), std::nullopt);134  if (failed(tgtTy))135    return {};136 137  // Check if this is a valid memory-space cast.138  if (!castOp.isValidMemorySpaceCast(*tgtTy, *srcTy))139    return {};140 141  return std::make_tuple(castOp, *tgtTy, *srcTy);142}143 144/// Implementation of `bubbleDownCasts` method for memref operations that145/// return a single memref result.146template <typename ConcreteOpTy>147static FailureOr<std::optional<SmallVector<Value>>>148bubbleDownCastsPassthroughOpImpl(ConcreteOpTy op, OpBuilder &builder,149                                 OpOperand &src) {150  auto [castOp, tgtTy, resTy] = getMemorySpaceCastInfo(op.getType(), src.get());151  // Bail if we cannot cast.152  if (!castOp)153    return failure();154 155  // Create the new operands.156  SmallVector<Value> operands;157  llvm::append_range(operands, op->getOperands());158  operands[src.getOperandNumber()] = castOp.getSourcePtr();159 160  // Create the new op and results.161  auto newOp = ConcreteOpTy::create(162      builder, op.getLoc(), TypeRange(resTy), operands, op.getProperties(),163      llvm::to_vector_of<NamedAttribute>(op->getDiscardableAttrs()));164 165  // Insert a memory-space cast to the original memory space of the op.166  MemorySpaceCastOpInterface result = castOp.cloneMemorySpaceCastOp(167      builder, tgtTy,168      cast<TypedValue<PtrLikeTypeInterface>>(newOp.getResult()));169  return std::optional<SmallVector<Value>>(170      SmallVector<Value>({result.getTargetPtr()}));171}172 173//===----------------------------------------------------------------------===//174// AllocOp / AllocaOp175//===----------------------------------------------------------------------===//176 177void AllocOp::getAsmResultNames(178    function_ref<void(Value, StringRef)> setNameFn) {179  setNameFn(getResult(), "alloc");180}181 182void AllocaOp::getAsmResultNames(183    function_ref<void(Value, StringRef)> setNameFn) {184  setNameFn(getResult(), "alloca");185}186 187template <typename AllocLikeOp>188static LogicalResult verifyAllocLikeOp(AllocLikeOp op) {189  static_assert(llvm::is_one_of<AllocLikeOp, AllocOp, AllocaOp>::value,190                "applies to only alloc or alloca");191  auto memRefType = llvm::dyn_cast<MemRefType>(op.getResult().getType());192  if (!memRefType)193    return op.emitOpError("result must be a memref");194 195  if (op.getDynamicSizes().size() != memRefType.getNumDynamicDims())196    return op.emitOpError("dimension operand count does not equal memref "197                          "dynamic dimension count");198 199  unsigned numSymbols = 0;200  if (!memRefType.getLayout().isIdentity())201    numSymbols = memRefType.getLayout().getAffineMap().getNumSymbols();202  if (op.getSymbolOperands().size() != numSymbols)203    return op.emitOpError("symbol operand count does not equal memref symbol "204                          "count: expected ")205           << numSymbols << ", got " << op.getSymbolOperands().size();206 207  return success();208}209 210LogicalResult AllocOp::verify() { return verifyAllocLikeOp(*this); }211 212LogicalResult AllocaOp::verify() {213  // An alloca op needs to have an ancestor with an allocation scope trait.214  if (!(*this)->getParentWithTrait<OpTrait::AutomaticAllocationScope>())215    return emitOpError(216        "requires an ancestor op with AutomaticAllocationScope trait");217 218  return verifyAllocLikeOp(*this);219}220 221namespace {222/// Fold constant dimensions into an alloc like operation.223template <typename AllocLikeOp>224struct SimplifyAllocConst : public OpRewritePattern<AllocLikeOp> {225  using OpRewritePattern<AllocLikeOp>::OpRewritePattern;226 227  LogicalResult matchAndRewrite(AllocLikeOp alloc,228                                PatternRewriter &rewriter) const override {229    // Check to see if any dimensions operands are constants.  If so, we can230    // substitute and drop them.231    if (llvm::none_of(alloc.getDynamicSizes(), [](Value operand) {232          APInt constSizeArg;233          if (!matchPattern(operand, m_ConstantInt(&constSizeArg)))234            return false;235          return constSizeArg.isNonNegative();236        }))237      return failure();238 239    auto memrefType = alloc.getType();240 241    // Ok, we have one or more constant operands.  Collect the non-constant ones242    // and keep track of the resultant memref type to build.243    SmallVector<int64_t, 4> newShapeConstants;244    newShapeConstants.reserve(memrefType.getRank());245    SmallVector<Value, 4> dynamicSizes;246 247    unsigned dynamicDimPos = 0;248    for (unsigned dim = 0, e = memrefType.getRank(); dim < e; ++dim) {249      int64_t dimSize = memrefType.getDimSize(dim);250      // If this is already static dimension, keep it.251      if (ShapedType::isStatic(dimSize)) {252        newShapeConstants.push_back(dimSize);253        continue;254      }255      auto dynamicSize = alloc.getDynamicSizes()[dynamicDimPos];256      APInt constSizeArg;257      if (matchPattern(dynamicSize, m_ConstantInt(&constSizeArg)) &&258          constSizeArg.isNonNegative()) {259        // Dynamic shape dimension will be folded.260        newShapeConstants.push_back(constSizeArg.getZExtValue());261      } else {262        // Dynamic shape dimension not folded; copy dynamicSize from old memref.263        newShapeConstants.push_back(ShapedType::kDynamic);264        dynamicSizes.push_back(dynamicSize);265      }266      dynamicDimPos++;267    }268 269    // Create new memref type (which will have fewer dynamic dimensions).270    MemRefType newMemRefType =271        MemRefType::Builder(memrefType).setShape(newShapeConstants);272    assert(dynamicSizes.size() == newMemRefType.getNumDynamicDims());273 274    // Create and insert the alloc op for the new memref.275    auto newAlloc = AllocLikeOp::create(rewriter, alloc.getLoc(), newMemRefType,276                                        dynamicSizes, alloc.getSymbolOperands(),277                                        alloc.getAlignmentAttr());278    // Insert a cast so we have the same type as the old alloc.279    rewriter.replaceOpWithNewOp<CastOp>(alloc, alloc.getType(), newAlloc);280    return success();281  }282};283 284/// Fold alloc operations with no users or only store and dealloc uses.285template <typename T>286struct SimplifyDeadAlloc : public OpRewritePattern<T> {287  using OpRewritePattern<T>::OpRewritePattern;288 289  LogicalResult matchAndRewrite(T alloc,290                                PatternRewriter &rewriter) const override {291    if (llvm::any_of(alloc->getUsers(), [&](Operation *op) {292          if (auto storeOp = dyn_cast<StoreOp>(op))293            return storeOp.getValue() == alloc;294          return !isa<DeallocOp>(op);295        }))296      return failure();297 298    for (Operation *user : llvm::make_early_inc_range(alloc->getUsers()))299      rewriter.eraseOp(user);300 301    rewriter.eraseOp(alloc);302    return success();303  }304};305} // namespace306 307void AllocOp::getCanonicalizationPatterns(RewritePatternSet &results,308                                          MLIRContext *context) {309  results.add<SimplifyAllocConst<AllocOp>, SimplifyDeadAlloc<AllocOp>>(context);310}311 312void AllocaOp::getCanonicalizationPatterns(RewritePatternSet &results,313                                           MLIRContext *context) {314  results.add<SimplifyAllocConst<AllocaOp>, SimplifyDeadAlloc<AllocaOp>>(315      context);316}317 318//===----------------------------------------------------------------------===//319// ReallocOp320//===----------------------------------------------------------------------===//321 322LogicalResult ReallocOp::verify() {323  auto sourceType = llvm::cast<MemRefType>(getOperand(0).getType());324  MemRefType resultType = getType();325 326  // The source memref should have identity layout (or none).327  if (!sourceType.getLayout().isIdentity())328    return emitError("unsupported layout for source memref type ")329           << sourceType;330 331  // The result memref should have identity layout (or none).332  if (!resultType.getLayout().isIdentity())333    return emitError("unsupported layout for result memref type ")334           << resultType;335 336  // The source memref and the result memref should be in the same memory space.337  if (sourceType.getMemorySpace() != resultType.getMemorySpace())338    return emitError("different memory spaces specified for source memref "339                     "type ")340           << sourceType << " and result memref type " << resultType;341 342  // The source memref and the result memref should have the same element type.343  if (sourceType.getElementType() != resultType.getElementType())344    return emitError("different element types specified for source memref "345                     "type ")346           << sourceType << " and result memref type " << resultType;347 348  // Verify that we have the dynamic dimension operand when it is needed.349  if (resultType.getNumDynamicDims() && !getDynamicResultSize())350    return emitError("missing dimension operand for result type ")351           << resultType;352  if (!resultType.getNumDynamicDims() && getDynamicResultSize())353    return emitError("unnecessary dimension operand for result type ")354           << resultType;355 356  return success();357}358 359void ReallocOp::getCanonicalizationPatterns(RewritePatternSet &results,360                                            MLIRContext *context) {361  results.add<SimplifyDeadAlloc<ReallocOp>>(context);362}363 364//===----------------------------------------------------------------------===//365// AllocaScopeOp366//===----------------------------------------------------------------------===//367 368void AllocaScopeOp::print(OpAsmPrinter &p) {369  bool printBlockTerminators = false;370 371  p << ' ';372  if (!getResults().empty()) {373    p << " -> (" << getResultTypes() << ")";374    printBlockTerminators = true;375  }376  p << ' ';377  p.printRegion(getBodyRegion(),378                /*printEntryBlockArgs=*/false,379                /*printBlockTerminators=*/printBlockTerminators);380  p.printOptionalAttrDict((*this)->getAttrs());381}382 383ParseResult AllocaScopeOp::parse(OpAsmParser &parser, OperationState &result) {384  // Create a region for the body.385  result.regions.reserve(1);386  Region *bodyRegion = result.addRegion();387 388  // Parse optional results type list.389  if (parser.parseOptionalArrowTypeList(result.types))390    return failure();391 392  // Parse the body region.393  if (parser.parseRegion(*bodyRegion, /*arguments=*/{}))394    return failure();395  AllocaScopeOp::ensureTerminator(*bodyRegion, parser.getBuilder(),396                                  result.location);397 398  // Parse the optional attribute list.399  if (parser.parseOptionalAttrDict(result.attributes))400    return failure();401 402  return success();403}404 405void AllocaScopeOp::getSuccessorRegions(406    RegionBranchPoint point, SmallVectorImpl<RegionSuccessor> &regions) {407  if (!point.isParent()) {408    regions.push_back(RegionSuccessor(getOperation(), getResults()));409    return;410  }411 412  regions.push_back(RegionSuccessor(&getBodyRegion()));413}414 415/// Given an operation, return whether this op is guaranteed to416/// allocate an AutomaticAllocationScopeResource417static bool isGuaranteedAutomaticAllocation(Operation *op) {418  MemoryEffectOpInterface interface = dyn_cast<MemoryEffectOpInterface>(op);419  if (!interface)420    return false;421  for (auto res : op->getResults()) {422    if (auto effect =423            interface.getEffectOnValue<MemoryEffects::Allocate>(res)) {424      if (isa<SideEffects::AutomaticAllocationScopeResource>(425              effect->getResource()))426        return true;427    }428  }429  return false;430}431 432/// Given an operation, return whether this op itself could433/// allocate an AutomaticAllocationScopeResource. Note that434/// this will not check whether an operation contained within435/// the op can allocate.436static bool isOpItselfPotentialAutomaticAllocation(Operation *op) {437  // This op itself doesn't create a stack allocation,438  // the inner allocation should be handled separately.439  if (op->hasTrait<OpTrait::HasRecursiveMemoryEffects>())440    return false;441  MemoryEffectOpInterface interface = dyn_cast<MemoryEffectOpInterface>(op);442  if (!interface)443    return true;444  for (auto res : op->getResults()) {445    if (auto effect =446            interface.getEffectOnValue<MemoryEffects::Allocate>(res)) {447      if (isa<SideEffects::AutomaticAllocationScopeResource>(448              effect->getResource()))449        return true;450    }451  }452  return false;453}454 455/// Return whether this op is the last non terminating op456/// in a region. That is to say, it is in a one-block region457/// and is only followed by a terminator. This prevents458/// extending the lifetime of allocations.459static bool lastNonTerminatorInRegion(Operation *op) {460  return op->getBlock()->mightHaveTerminator() &&461         op->getNextNode() == op->getBlock()->getTerminator() &&462         op->getParentRegion()->hasOneBlock();463}464 465/// Inline an AllocaScopeOp if either the direct parent is an allocation scope466/// or it contains no allocation.467struct AllocaScopeInliner : public OpRewritePattern<AllocaScopeOp> {468  using OpRewritePattern<AllocaScopeOp>::OpRewritePattern;469 470  LogicalResult matchAndRewrite(AllocaScopeOp op,471                                PatternRewriter &rewriter) const override {472    bool hasPotentialAlloca =473        op->walk<WalkOrder::PreOrder>([&](Operation *alloc) {474            if (alloc == op)475              return WalkResult::advance();476            if (isOpItselfPotentialAutomaticAllocation(alloc))477              return WalkResult::interrupt();478            if (alloc->hasTrait<OpTrait::AutomaticAllocationScope>())479              return WalkResult::skip();480            return WalkResult::advance();481          }).wasInterrupted();482 483    // If this contains no potential allocation, it is always legal to484    // inline. Otherwise, consider two conditions:485    if (hasPotentialAlloca) {486      // If the parent isn't an allocation scope, or we are not the last487      // non-terminator op in the parent, we will extend the lifetime.488      if (!op->getParentOp()->hasTrait<OpTrait::AutomaticAllocationScope>())489        return failure();490      if (!lastNonTerminatorInRegion(op))491        return failure();492    }493 494    Block *block = &op.getRegion().front();495    Operation *terminator = block->getTerminator();496    ValueRange results = terminator->getOperands();497    rewriter.inlineBlockBefore(block, op);498    rewriter.replaceOp(op, results);499    rewriter.eraseOp(terminator);500    return success();501  }502};503 504/// Move allocations into an allocation scope, if it is legal to505/// move them (e.g. their operands are available at the location506/// the op would be moved to).507struct AllocaScopeHoister : public OpRewritePattern<AllocaScopeOp> {508  using OpRewritePattern<AllocaScopeOp>::OpRewritePattern;509 510  LogicalResult matchAndRewrite(AllocaScopeOp op,511                                PatternRewriter &rewriter) const override {512 513    if (!op->getParentWithTrait<OpTrait::AutomaticAllocationScope>())514      return failure();515 516    Operation *lastParentWithoutScope = op->getParentOp();517 518    if (!lastParentWithoutScope ||519        lastParentWithoutScope->hasTrait<OpTrait::AutomaticAllocationScope>())520      return failure();521 522    // Only apply to if this is this last non-terminator523    // op in the block (lest lifetime be extended) of a one524    // block region525    if (!lastNonTerminatorInRegion(op) ||526        !lastNonTerminatorInRegion(lastParentWithoutScope))527      return failure();528 529    while (!lastParentWithoutScope->getParentOp()530                ->hasTrait<OpTrait::AutomaticAllocationScope>()) {531      lastParentWithoutScope = lastParentWithoutScope->getParentOp();532      if (!lastParentWithoutScope ||533          !lastNonTerminatorInRegion(lastParentWithoutScope))534        return failure();535    }536    assert(lastParentWithoutScope->getParentOp()537               ->hasTrait<OpTrait::AutomaticAllocationScope>());538 539    Region *containingRegion = nullptr;540    for (auto &r : lastParentWithoutScope->getRegions()) {541      if (r.isAncestor(op->getParentRegion())) {542        assert(containingRegion == nullptr &&543               "only one region can contain the op");544        containingRegion = &r;545      }546    }547    assert(containingRegion && "op must be contained in a region");548 549    SmallVector<Operation *> toHoist;550    op->walk([&](Operation *alloc) {551      if (!isGuaranteedAutomaticAllocation(alloc))552        return WalkResult::skip();553 554      // If any operand is not defined before the location of555      // lastParentWithoutScope (i.e. where we would hoist to), skip.556      if (llvm::any_of(alloc->getOperands(), [&](Value v) {557            return containingRegion->isAncestor(v.getParentRegion());558          }))559        return WalkResult::skip();560      toHoist.push_back(alloc);561      return WalkResult::advance();562    });563 564    if (toHoist.empty())565      return failure();566    rewriter.setInsertionPoint(lastParentWithoutScope);567    for (auto *op : toHoist) {568      auto *cloned = rewriter.clone(*op);569      rewriter.replaceOp(op, cloned->getResults());570    }571    return success();572  }573};574 575void AllocaScopeOp::getCanonicalizationPatterns(RewritePatternSet &results,576                                                MLIRContext *context) {577  results.add<AllocaScopeInliner, AllocaScopeHoister>(context);578}579 580//===----------------------------------------------------------------------===//581// AssumeAlignmentOp582//===----------------------------------------------------------------------===//583 584LogicalResult AssumeAlignmentOp::verify() {585  if (!llvm::isPowerOf2_32(getAlignment()))586    return emitOpError("alignment must be power of 2");587  return success();588}589 590void AssumeAlignmentOp::getAsmResultNames(591    function_ref<void(Value, StringRef)> setNameFn) {592  setNameFn(getResult(), "assume_align");593}594 595OpFoldResult AssumeAlignmentOp::fold(FoldAdaptor adaptor) {596  auto source = getMemref().getDefiningOp<AssumeAlignmentOp>();597  if (!source)598    return {};599  if (source.getAlignment() != getAlignment())600    return {};601  return getMemref();602}603 604FailureOr<std::optional<SmallVector<Value>>>605AssumeAlignmentOp::bubbleDownCasts(OpBuilder &builder) {606  return bubbleDownCastsPassthroughOpImpl(*this, builder, getMemrefMutable());607}608 609//===----------------------------------------------------------------------===//610// DistinctObjectsOp611//===----------------------------------------------------------------------===//612 613LogicalResult DistinctObjectsOp::verify() {614  if (getOperandTypes() != getResultTypes())615    return emitOpError("operand types and result types must match");616 617  if (getOperandTypes().empty())618    return emitOpError("expected at least one operand");619 620  return success();621}622 623LogicalResult DistinctObjectsOp::inferReturnTypes(624    MLIRContext * /*context*/, std::optional<Location> /*location*/,625    ValueRange operands, DictionaryAttr /*attributes*/,626    OpaqueProperties /*properties*/, RegionRange /*regions*/,627    SmallVectorImpl<Type> &inferredReturnTypes) {628  llvm::copy(operands.getTypes(), std::back_inserter(inferredReturnTypes));629  return success();630}631 632//===----------------------------------------------------------------------===//633// CastOp634//===----------------------------------------------------------------------===//635 636void CastOp::getAsmResultNames(function_ref<void(Value, StringRef)> setNameFn) {637  setNameFn(getResult(), "cast");638}639 640/// Determines whether MemRef_CastOp casts to a more dynamic version of the641/// source memref. This is useful to fold a memref.cast into a consuming op642/// and implement canonicalization patterns for ops in different dialects that643/// may consume the results of memref.cast operations. Such foldable memref.cast644/// operations are typically inserted as `view` and `subview` ops are645/// canonicalized, to preserve the type compatibility of their uses.646///647/// Returns true when all conditions are met:648/// 1. source and result are ranked memrefs with strided semantics and same649/// element type and rank.650/// 2. each of the source's size, offset or stride has more static information651/// than the corresponding result's size, offset or stride.652///653/// Example 1:654/// ```mlir655///   %1 = memref.cast %0 : memref<8x16xf32> to memref<?x?xf32>656///   %2 = consumer %1 ... : memref<?x?xf32> ...657/// ```658///659/// may fold into:660///661/// ```mlir662///   %2 = consumer %0 ... : memref<8x16xf32> ...663/// ```664///665/// Example 2:666/// ```667///   %1 = memref.cast %0 : memref<?x16xf32, affine_map<(i, j)->(16 * i + j)>>668///          to memref<?x?xf32>669///   consumer %1 : memref<?x?xf32> ...670/// ```671///672/// may fold into:673///674/// ```675///   consumer %0 ... : memref<?x16xf32, affine_map<(i, j)->(16 * i + j)>>676/// ```677bool CastOp::canFoldIntoConsumerOp(CastOp castOp) {678  MemRefType sourceType =679      llvm::dyn_cast<MemRefType>(castOp.getSource().getType());680  MemRefType resultType = llvm::dyn_cast<MemRefType>(castOp.getType());681 682  // Requires ranked MemRefType.683  if (!sourceType || !resultType)684    return false;685 686  // Requires same elemental type.687  if (sourceType.getElementType() != resultType.getElementType())688    return false;689 690  // Requires same rank.691  if (sourceType.getRank() != resultType.getRank())692    return false;693 694  // Only fold casts between strided memref forms.695  int64_t sourceOffset, resultOffset;696  SmallVector<int64_t, 4> sourceStrides, resultStrides;697  if (failed(sourceType.getStridesAndOffset(sourceStrides, sourceOffset)) ||698      failed(resultType.getStridesAndOffset(resultStrides, resultOffset)))699    return false;700 701  // If cast is towards more static sizes along any dimension, don't fold.702  for (auto it : llvm::zip(sourceType.getShape(), resultType.getShape())) {703    auto ss = std::get<0>(it), st = std::get<1>(it);704    if (ss != st)705      if (ShapedType::isDynamic(ss) && ShapedType::isStatic(st))706        return false;707  }708 709  // If cast is towards more static offset along any dimension, don't fold.710  if (sourceOffset != resultOffset)711    if (ShapedType::isDynamic(sourceOffset) &&712        ShapedType::isStatic(resultOffset))713      return false;714 715  // If cast is towards more static strides along any dimension, don't fold.716  for (auto it : llvm::zip(sourceStrides, resultStrides)) {717    auto ss = std::get<0>(it), st = std::get<1>(it);718    if (ss != st)719      if (ShapedType::isDynamic(ss) && ShapedType::isStatic(st))720        return false;721  }722 723  return true;724}725 726bool CastOp::areCastCompatible(TypeRange inputs, TypeRange outputs) {727  if (inputs.size() != 1 || outputs.size() != 1)728    return false;729  Type a = inputs.front(), b = outputs.front();730  auto aT = llvm::dyn_cast<MemRefType>(a);731  auto bT = llvm::dyn_cast<MemRefType>(b);732 733  auto uaT = llvm::dyn_cast<UnrankedMemRefType>(a);734  auto ubT = llvm::dyn_cast<UnrankedMemRefType>(b);735 736  if (aT && bT) {737    if (aT.getElementType() != bT.getElementType())738      return false;739    if (aT.getLayout() != bT.getLayout()) {740      int64_t aOffset, bOffset;741      SmallVector<int64_t, 4> aStrides, bStrides;742      if (failed(aT.getStridesAndOffset(aStrides, aOffset)) ||743          failed(bT.getStridesAndOffset(bStrides, bOffset)) ||744          aStrides.size() != bStrides.size())745        return false;746 747      // Strides along a dimension/offset are compatible if the value in the748      // source memref is static and the value in the target memref is the749      // same. They are also compatible if either one is dynamic (see750      // description of MemRefCastOp for details).751      auto checkCompatible = [](int64_t a, int64_t b) {752        return (ShapedType::isDynamic(a) || ShapedType::isDynamic(b) || a == b);753      };754      if (!checkCompatible(aOffset, bOffset))755        return false;756      for (const auto &aStride : enumerate(aStrides))757        if (!checkCompatible(aStride.value(), bStrides[aStride.index()]))758          return false;759    }760    if (aT.getMemorySpace() != bT.getMemorySpace())761      return false;762 763    // They must have the same rank, and any specified dimensions must match.764    if (aT.getRank() != bT.getRank())765      return false;766 767    for (unsigned i = 0, e = aT.getRank(); i != e; ++i) {768      int64_t aDim = aT.getDimSize(i), bDim = bT.getDimSize(i);769      if (ShapedType::isStatic(aDim) && ShapedType::isStatic(bDim) &&770          aDim != bDim)771        return false;772    }773    return true;774  } else {775    if (!aT && !uaT)776      return false;777    if (!bT && !ubT)778      return false;779    // Unranked to unranked casting is unsupported780    if (uaT && ubT)781      return false;782 783    auto aEltType = (aT) ? aT.getElementType() : uaT.getElementType();784    auto bEltType = (bT) ? bT.getElementType() : ubT.getElementType();785    if (aEltType != bEltType)786      return false;787 788    auto aMemSpace = (aT) ? aT.getMemorySpace() : uaT.getMemorySpace();789    auto bMemSpace = (bT) ? bT.getMemorySpace() : ubT.getMemorySpace();790    return aMemSpace == bMemSpace;791  }792 793  return false;794}795 796OpFoldResult CastOp::fold(FoldAdaptor adaptor) {797  return succeeded(foldMemRefCast(*this)) ? getResult() : Value();798}799 800FailureOr<std::optional<SmallVector<Value>>>801CastOp::bubbleDownCasts(OpBuilder &builder) {802  return bubbleDownCastsPassthroughOpImpl(*this, builder, getSourceMutable());803}804 805//===----------------------------------------------------------------------===//806// CopyOp807//===----------------------------------------------------------------------===//808 809namespace {810 811/// Fold memref.copy(%x, %x).812struct FoldSelfCopy : public OpRewritePattern<CopyOp> {813  using OpRewritePattern<CopyOp>::OpRewritePattern;814 815  LogicalResult matchAndRewrite(CopyOp copyOp,816                                PatternRewriter &rewriter) const override {817    if (copyOp.getSource() != copyOp.getTarget())818      return failure();819 820    rewriter.eraseOp(copyOp);821    return success();822  }823};824 825struct FoldEmptyCopy final : public OpRewritePattern<CopyOp> {826  using OpRewritePattern<CopyOp>::OpRewritePattern;827 828  static bool isEmptyMemRef(BaseMemRefType type) {829    return type.hasRank() && llvm::is_contained(type.getShape(), 0);830  }831 832  LogicalResult matchAndRewrite(CopyOp copyOp,833                                PatternRewriter &rewriter) const override {834    if (isEmptyMemRef(copyOp.getSource().getType()) ||835        isEmptyMemRef(copyOp.getTarget().getType())) {836      rewriter.eraseOp(copyOp);837      return success();838    }839 840    return failure();841  }842};843} // namespace844 845void CopyOp::getCanonicalizationPatterns(RewritePatternSet &results,846                                         MLIRContext *context) {847  results.add<FoldEmptyCopy, FoldSelfCopy>(context);848}849 850/// If the source/target of a CopyOp is a CastOp that does not modify the shape851/// and element type, the cast can be skipped. Such CastOps only cast the layout852/// of the type.853static LogicalResult FoldCopyOfCast(CopyOp op) {854  for (OpOperand &operand : op->getOpOperands()) {855    auto castOp = operand.get().getDefiningOp<memref::CastOp>();856    if (castOp && memref::CastOp::canFoldIntoConsumerOp(castOp)) {857      operand.set(castOp.getOperand());858      return success();859    }860  }861  return failure();862}863 864LogicalResult CopyOp::fold(FoldAdaptor adaptor,865                           SmallVectorImpl<OpFoldResult> &results) {866 867  /// copy(memrefcast) -> copy868  return FoldCopyOfCast(*this);869}870 871//===----------------------------------------------------------------------===//872// DeallocOp873//===----------------------------------------------------------------------===//874 875LogicalResult DeallocOp::fold(FoldAdaptor adaptor,876                              SmallVectorImpl<OpFoldResult> &results) {877  /// dealloc(memrefcast) -> dealloc878  return foldMemRefCast(*this);879}880 881//===----------------------------------------------------------------------===//882// DimOp883//===----------------------------------------------------------------------===//884 885void DimOp::getAsmResultNames(function_ref<void(Value, StringRef)> setNameFn) {886  setNameFn(getResult(), "dim");887}888 889void DimOp::build(OpBuilder &builder, OperationState &result, Value source,890                  int64_t index) {891  auto loc = result.location;892  Value indexValue = arith::ConstantIndexOp::create(builder, loc, index);893  build(builder, result, source, indexValue);894}895 896std::optional<int64_t> DimOp::getConstantIndex() {897  return getConstantIntValue(getIndex());898}899 900Speculation::Speculatability DimOp::getSpeculatability() {901  auto constantIndex = getConstantIndex();902  if (!constantIndex)903    return Speculation::NotSpeculatable;904 905  auto rankedSourceType = dyn_cast<MemRefType>(getSource().getType());906  if (!rankedSourceType)907    return Speculation::NotSpeculatable;908 909  if (rankedSourceType.getRank() <= constantIndex)910    return Speculation::NotSpeculatable;911 912  return Speculation::Speculatable;913}914 915void DimOp::inferResultRangesFromOptional(ArrayRef<IntegerValueRange> argRanges,916                                          SetIntLatticeFn setResultRange) {917  setResultRange(getResult(),918                 intrange::inferShapedDimOpInterface(*this, argRanges[1]));919}920 921/// Return a map with key being elements in `vals` and data being number of922/// occurences of it. Use std::map, since the `vals` here are strides and the923/// dynamic stride value is the same as the tombstone value for924/// `DenseMap<int64_t>`.925static std::map<int64_t, unsigned> getNumOccurences(ArrayRef<int64_t> vals) {926  std::map<int64_t, unsigned> numOccurences;927  for (auto val : vals)928    numOccurences[val]++;929  return numOccurences;930}931 932/// Given the `originalType` and a `candidateReducedType` whose shape is assumed933/// to be a subset of `originalType` with some `1` entries erased, return the934/// set of indices that specifies which of the entries of `originalShape` are935/// dropped to obtain `reducedShape`.936/// This accounts for cases where there are multiple unit-dims, but only a937/// subset of those are dropped. For MemRefTypes these can be disambiguated938/// using the strides. If a dimension is dropped the stride must be dropped too.939static FailureOr<llvm::SmallBitVector>940computeMemRefRankReductionMask(MemRefType originalType, MemRefType reducedType,941                               ArrayRef<OpFoldResult> sizes) {942  llvm::SmallBitVector unusedDims(originalType.getRank());943  if (originalType.getRank() == reducedType.getRank())944    return unusedDims;945 946  for (const auto &dim : llvm::enumerate(sizes))947    if (auto attr = llvm::dyn_cast_if_present<Attribute>(dim.value()))948      if (llvm::cast<IntegerAttr>(attr).getInt() == 1)949        unusedDims.set(dim.index());950 951  // Early exit for the case where the number of unused dims matches the number952  // of ranks reduced.953  if (static_cast<int64_t>(unusedDims.count()) + reducedType.getRank() ==954      originalType.getRank())955    return unusedDims;956 957  SmallVector<int64_t> originalStrides, candidateStrides;958  int64_t originalOffset, candidateOffset;959  if (failed(960          originalType.getStridesAndOffset(originalStrides, originalOffset)) ||961      failed(962          reducedType.getStridesAndOffset(candidateStrides, candidateOffset)))963    return failure();964 965  // For memrefs, a dimension is truly dropped if its corresponding stride is966  // also dropped. This is particularly important when more than one of the dims967  // is 1. Track the number of occurences of the strides in the original type968  // and the candidate type. For each unused dim that stride should not be969  // present in the candidate type. Note that there could be multiple dimensions970  // that have the same size. We dont need to exactly figure out which dim971  // corresponds to which stride, we just need to verify that the number of972  // reptitions of a stride in the original + number of unused dims with that973  // stride == number of repititions of a stride in the candidate.974  std::map<int64_t, unsigned> currUnaccountedStrides =975      getNumOccurences(originalStrides);976  std::map<int64_t, unsigned> candidateStridesNumOccurences =977      getNumOccurences(candidateStrides);978  for (size_t dim = 0, e = unusedDims.size(); dim != e; ++dim) {979    if (!unusedDims.test(dim))980      continue;981    int64_t originalStride = originalStrides[dim];982    if (currUnaccountedStrides[originalStride] >983        candidateStridesNumOccurences[originalStride]) {984      // This dim can be treated as dropped.985      currUnaccountedStrides[originalStride]--;986      continue;987    }988    if (currUnaccountedStrides[originalStride] ==989        candidateStridesNumOccurences[originalStride]) {990      // The stride for this is not dropped. Keep as is.991      unusedDims.reset(dim);992      continue;993    }994    if (currUnaccountedStrides[originalStride] <995        candidateStridesNumOccurences[originalStride]) {996      // This should never happen. Cant have a stride in the reduced rank type997      // that wasnt in the original one.998      return failure();999    }1000  }1001 1002  if ((int64_t)unusedDims.count() + reducedType.getRank() !=1003      originalType.getRank())1004    return failure();1005  return unusedDims;1006}1007 1008llvm::SmallBitVector SubViewOp::getDroppedDims() {1009  MemRefType sourceType = getSourceType();1010  MemRefType resultType = getType();1011  FailureOr<llvm::SmallBitVector> unusedDims =1012      computeMemRefRankReductionMask(sourceType, resultType, getMixedSizes());1013  assert(succeeded(unusedDims) && "unable to find unused dims of subview");1014  return *unusedDims;1015}1016 1017OpFoldResult DimOp::fold(FoldAdaptor adaptor) {1018  // All forms of folding require a known index.1019  auto index = llvm::dyn_cast_if_present<IntegerAttr>(adaptor.getIndex());1020  if (!index)1021    return {};1022 1023  // Folding for unranked types (UnrankedMemRefType) is not supported.1024  auto memrefType = llvm::dyn_cast<MemRefType>(getSource().getType());1025  if (!memrefType)1026    return {};1027 1028  // Out of bound indices produce undefined behavior but are still valid IR.1029  // Don't choke on them.1030  int64_t indexVal = index.getInt();1031  if (indexVal < 0 || indexVal >= memrefType.getRank())1032    return {};1033 1034  // Fold if the shape extent along the given index is known.1035  if (!memrefType.isDynamicDim(index.getInt())) {1036    Builder builder(getContext());1037    return builder.getIndexAttr(memrefType.getShape()[index.getInt()]);1038  }1039 1040  // The size at the given index is now known to be a dynamic size.1041  unsigned unsignedIndex = index.getValue().getZExtValue();1042 1043  // Fold dim to the size argument for an `AllocOp`, `ViewOp`, or `SubViewOp`.1044  Operation *definingOp = getSource().getDefiningOp();1045 1046  if (auto alloc = dyn_cast_or_null<AllocOp>(definingOp))1047    return *(alloc.getDynamicSizes().begin() +1048             memrefType.getDynamicDimIndex(unsignedIndex));1049 1050  if (auto alloca = dyn_cast_or_null<AllocaOp>(definingOp))1051    return *(alloca.getDynamicSizes().begin() +1052             memrefType.getDynamicDimIndex(unsignedIndex));1053 1054  if (auto view = dyn_cast_or_null<ViewOp>(definingOp))1055    return *(view.getDynamicSizes().begin() +1056             memrefType.getDynamicDimIndex(unsignedIndex));1057 1058  if (auto subview = dyn_cast_or_null<SubViewOp>(definingOp)) {1059    llvm::SmallBitVector unusedDims = subview.getDroppedDims();1060    unsigned resultIndex = 0;1061    unsigned sourceRank = subview.getSourceType().getRank();1062    unsigned sourceIndex = 0;1063    for (auto i : llvm::seq<unsigned>(0, sourceRank)) {1064      if (unusedDims.test(i))1065        continue;1066      if (resultIndex == unsignedIndex) {1067        sourceIndex = i;1068        break;1069      }1070      resultIndex++;1071    }1072    assert(subview.isDynamicSize(sourceIndex) &&1073           "expected dynamic subview size");1074    return subview.getDynamicSize(sourceIndex);1075  }1076 1077  // dim(memrefcast) -> dim1078  if (succeeded(foldMemRefCast(*this)))1079    return getResult();1080 1081  return {};1082}1083 1084namespace {1085/// Fold dim of a memref reshape operation to a load into the reshape's shape1086/// operand.1087struct DimOfMemRefReshape : public OpRewritePattern<DimOp> {1088  using OpRewritePattern<DimOp>::OpRewritePattern;1089 1090  LogicalResult matchAndRewrite(DimOp dim,1091                                PatternRewriter &rewriter) const override {1092    auto reshape = dim.getSource().getDefiningOp<ReshapeOp>();1093 1094    if (!reshape)1095      return rewriter.notifyMatchFailure(1096          dim, "Dim op is not defined by a reshape op.");1097 1098    // dim of a memref reshape can be folded if dim.getIndex() dominates the1099    // reshape. Instead of using `DominanceInfo` (which is usually costly) we1100    // cheaply check that either of the following conditions hold:1101    //      1. dim.getIndex() is defined in the same block as reshape but before1102    //      reshape.1103    //      2. dim.getIndex() is defined in a parent block of1104    //      reshape.1105 1106    // Check condition 11107    if (dim.getIndex().getParentBlock() == reshape->getBlock()) {1108      if (auto *definingOp = dim.getIndex().getDefiningOp()) {1109        if (reshape->isBeforeInBlock(definingOp)) {1110          return rewriter.notifyMatchFailure(1111              dim,1112              "dim.getIndex is not defined before reshape in the same block.");1113        }1114      } // else dim.getIndex is a block argument to reshape->getBlock and1115        // dominates reshape1116    } // Check condition 21117    else if (dim->getBlock() != reshape->getBlock() &&1118             !dim.getIndex().getParentRegion()->isProperAncestor(1119                 reshape->getParentRegion())) {1120      // If dim and reshape are in the same block but dim.getIndex() isn't, we1121      // already know dim.getIndex() dominates reshape without calling1122      // `isProperAncestor`1123      return rewriter.notifyMatchFailure(1124          dim, "dim.getIndex does not dominate reshape.");1125    }1126 1127    // Place the load directly after the reshape to ensure that the shape memref1128    // was not mutated.1129    rewriter.setInsertionPointAfter(reshape);1130    Location loc = dim.getLoc();1131    Value load =1132        LoadOp::create(rewriter, loc, reshape.getShape(), dim.getIndex());1133    if (load.getType() != dim.getType())1134      load = arith::IndexCastOp::create(rewriter, loc, dim.getType(), load);1135    rewriter.replaceOp(dim, load);1136    return success();1137  }1138};1139 1140} // namespace1141 1142void DimOp::getCanonicalizationPatterns(RewritePatternSet &results,1143                                        MLIRContext *context) {1144  results.add<DimOfMemRefReshape>(context);1145}1146 1147// ---------------------------------------------------------------------------1148// DmaStartOp1149// ---------------------------------------------------------------------------1150 1151void DmaStartOp::build(OpBuilder &builder, OperationState &result,1152                       Value srcMemRef, ValueRange srcIndices, Value destMemRef,1153                       ValueRange destIndices, Value numElements,1154                       Value tagMemRef, ValueRange tagIndices, Value stride,1155                       Value elementsPerStride) {1156  result.addOperands(srcMemRef);1157  result.addOperands(srcIndices);1158  result.addOperands(destMemRef);1159  result.addOperands(destIndices);1160  result.addOperands({numElements, tagMemRef});1161  result.addOperands(tagIndices);1162  if (stride)1163    result.addOperands({stride, elementsPerStride});1164}1165 1166void DmaStartOp::print(OpAsmPrinter &p) {1167  p << " " << getSrcMemRef() << '[' << getSrcIndices() << "], "1168    << getDstMemRef() << '[' << getDstIndices() << "], " << getNumElements()1169    << ", " << getTagMemRef() << '[' << getTagIndices() << ']';1170  if (isStrided())1171    p << ", " << getStride() << ", " << getNumElementsPerStride();1172 1173  p.printOptionalAttrDict((*this)->getAttrs());1174  p << " : " << getSrcMemRef().getType() << ", " << getDstMemRef().getType()1175    << ", " << getTagMemRef().getType();1176}1177 1178// Parse DmaStartOp.1179// Ex:1180//   %dma_id = dma_start %src[%i, %j], %dst[%k, %l], %size,1181//                       %tag[%index], %stride, %num_elt_per_stride :1182//                     : memref<3076 x f32, 0>,1183//                       memref<1024 x f32, 2>,1184//                       memref<1 x i32>1185//1186ParseResult DmaStartOp::parse(OpAsmParser &parser, OperationState &result) {1187  OpAsmParser::UnresolvedOperand srcMemRefInfo;1188  SmallVector<OpAsmParser::UnresolvedOperand, 4> srcIndexInfos;1189  OpAsmParser::UnresolvedOperand dstMemRefInfo;1190  SmallVector<OpAsmParser::UnresolvedOperand, 4> dstIndexInfos;1191  OpAsmParser::UnresolvedOperand numElementsInfo;1192  OpAsmParser::UnresolvedOperand tagMemrefInfo;1193  SmallVector<OpAsmParser::UnresolvedOperand, 4> tagIndexInfos;1194  SmallVector<OpAsmParser::UnresolvedOperand, 2> strideInfo;1195 1196  SmallVector<Type, 3> types;1197  auto indexType = parser.getBuilder().getIndexType();1198 1199  // Parse and resolve the following list of operands:1200  // *) source memref followed by its indices (in square brackets).1201  // *) destination memref followed by its indices (in square brackets).1202  // *) dma size in KiB.1203  if (parser.parseOperand(srcMemRefInfo) ||1204      parser.parseOperandList(srcIndexInfos, OpAsmParser::Delimiter::Square) ||1205      parser.parseComma() || parser.parseOperand(dstMemRefInfo) ||1206      parser.parseOperandList(dstIndexInfos, OpAsmParser::Delimiter::Square) ||1207      parser.parseComma() || parser.parseOperand(numElementsInfo) ||1208      parser.parseComma() || parser.parseOperand(tagMemrefInfo) ||1209      parser.parseOperandList(tagIndexInfos, OpAsmParser::Delimiter::Square))1210    return failure();1211 1212  // Parse optional stride and elements per stride.1213  if (parser.parseTrailingOperandList(strideInfo))1214    return failure();1215 1216  bool isStrided = strideInfo.size() == 2;1217  if (!strideInfo.empty() && !isStrided) {1218    return parser.emitError(parser.getNameLoc(),1219                            "expected two stride related operands");1220  }1221 1222  if (parser.parseColonTypeList(types))1223    return failure();1224  if (types.size() != 3)1225    return parser.emitError(parser.getNameLoc(), "fewer/more types expected");1226 1227  if (parser.resolveOperand(srcMemRefInfo, types[0], result.operands) ||1228      parser.resolveOperands(srcIndexInfos, indexType, result.operands) ||1229      parser.resolveOperand(dstMemRefInfo, types[1], result.operands) ||1230      parser.resolveOperands(dstIndexInfos, indexType, result.operands) ||1231      // size should be an index.1232      parser.resolveOperand(numElementsInfo, indexType, result.operands) ||1233      parser.resolveOperand(tagMemrefInfo, types[2], result.operands) ||1234      // tag indices should be index.1235      parser.resolveOperands(tagIndexInfos, indexType, result.operands))1236    return failure();1237 1238  if (isStrided) {1239    if (parser.resolveOperands(strideInfo, indexType, result.operands))1240      return failure();1241  }1242 1243  return success();1244}1245 1246LogicalResult DmaStartOp::verify() {1247  unsigned numOperands = getNumOperands();1248 1249  // Mandatory non-variadic operands are: src memref, dst memref, tag memref and1250  // the number of elements.1251  if (numOperands < 4)1252    return emitOpError("expected at least 4 operands");1253 1254  // Check types of operands. The order of these calls is important: the later1255  // calls rely on some type properties to compute the operand position.1256  // 1. Source memref.1257  if (!llvm::isa<MemRefType>(getSrcMemRef().getType()))1258    return emitOpError("expected source to be of memref type");1259  if (numOperands < getSrcMemRefRank() + 4)1260    return emitOpError() << "expected at least " << getSrcMemRefRank() + 41261                         << " operands";1262  if (!getSrcIndices().empty() &&1263      !llvm::all_of(getSrcIndices().getTypes(),1264                    [](Type t) { return t.isIndex(); }))1265    return emitOpError("expected source indices to be of index type");1266 1267  // 2. Destination memref.1268  if (!llvm::isa<MemRefType>(getDstMemRef().getType()))1269    return emitOpError("expected destination to be of memref type");1270  unsigned numExpectedOperands = getSrcMemRefRank() + getDstMemRefRank() + 4;1271  if (numOperands < numExpectedOperands)1272    return emitOpError() << "expected at least " << numExpectedOperands1273                         << " operands";1274  if (!getDstIndices().empty() &&1275      !llvm::all_of(getDstIndices().getTypes(),1276                    [](Type t) { return t.isIndex(); }))1277    return emitOpError("expected destination indices to be of index type");1278 1279  // 3. Number of elements.1280  if (!getNumElements().getType().isIndex())1281    return emitOpError("expected num elements to be of index type");1282 1283  // 4. Tag memref.1284  if (!llvm::isa<MemRefType>(getTagMemRef().getType()))1285    return emitOpError("expected tag to be of memref type");1286  numExpectedOperands += getTagMemRefRank();1287  if (numOperands < numExpectedOperands)1288    return emitOpError() << "expected at least " << numExpectedOperands1289                         << " operands";1290  if (!getTagIndices().empty() &&1291      !llvm::all_of(getTagIndices().getTypes(),1292                    [](Type t) { return t.isIndex(); }))1293    return emitOpError("expected tag indices to be of index type");1294 1295  // Optional stride-related operands must be either both present or both1296  // absent.1297  if (numOperands != numExpectedOperands &&1298      numOperands != numExpectedOperands + 2)1299    return emitOpError("incorrect number of operands");1300 1301  // 5. Strides.1302  if (isStrided()) {1303    if (!getStride().getType().isIndex() ||1304        !getNumElementsPerStride().getType().isIndex())1305      return emitOpError(1306          "expected stride and num elements per stride to be of type index");1307  }1308 1309  return success();1310}1311 1312LogicalResult DmaStartOp::fold(FoldAdaptor adaptor,1313                               SmallVectorImpl<OpFoldResult> &results) {1314  /// dma_start(memrefcast) -> dma_start1315  return foldMemRefCast(*this);1316}1317 1318// ---------------------------------------------------------------------------1319// DmaWaitOp1320// ---------------------------------------------------------------------------1321 1322LogicalResult DmaWaitOp::fold(FoldAdaptor adaptor,1323                              SmallVectorImpl<OpFoldResult> &results) {1324  /// dma_wait(memrefcast) -> dma_wait1325  return foldMemRefCast(*this);1326}1327 1328LogicalResult DmaWaitOp::verify() {1329  // Check that the number of tag indices matches the tagMemRef rank.1330  unsigned numTagIndices = getTagIndices().size();1331  unsigned tagMemRefRank = getTagMemRefRank();1332  if (numTagIndices != tagMemRefRank)1333    return emitOpError() << "expected tagIndices to have the same number of "1334                            "elements as the tagMemRef rank, expected "1335                         << tagMemRefRank << ", but got " << numTagIndices;1336  return success();1337}1338 1339//===----------------------------------------------------------------------===//1340// ExtractAlignedPointerAsIndexOp1341//===----------------------------------------------------------------------===//1342 1343void ExtractAlignedPointerAsIndexOp::getAsmResultNames(1344    function_ref<void(Value, StringRef)> setNameFn) {1345  setNameFn(getResult(), "intptr");1346}1347 1348//===----------------------------------------------------------------------===//1349// ExtractStridedMetadataOp1350//===----------------------------------------------------------------------===//1351 1352/// The number and type of the results are inferred from the1353/// shape of the source.1354LogicalResult ExtractStridedMetadataOp::inferReturnTypes(1355    MLIRContext *context, std::optional<Location> location,1356    ExtractStridedMetadataOp::Adaptor adaptor,1357    SmallVectorImpl<Type> &inferredReturnTypes) {1358  auto sourceType = llvm::dyn_cast<MemRefType>(adaptor.getSource().getType());1359  if (!sourceType)1360    return failure();1361 1362  unsigned sourceRank = sourceType.getRank();1363  IndexType indexType = IndexType::get(context);1364  auto memrefType =1365      MemRefType::get({}, sourceType.getElementType(),1366                      MemRefLayoutAttrInterface{}, sourceType.getMemorySpace());1367  // Base.1368  inferredReturnTypes.push_back(memrefType);1369  // Offset.1370  inferredReturnTypes.push_back(indexType);1371  // Sizes and strides.1372  for (unsigned i = 0; i < sourceRank * 2; ++i)1373    inferredReturnTypes.push_back(indexType);1374  return success();1375}1376 1377void ExtractStridedMetadataOp::getAsmResultNames(1378    function_ref<void(Value, StringRef)> setNameFn) {1379  setNameFn(getBaseBuffer(), "base_buffer");1380  setNameFn(getOffset(), "offset");1381  // For multi-result to work properly with pretty names and packed syntax `x:3`1382  // we can only give a pretty name to the first value in the pack.1383  if (!getSizes().empty()) {1384    setNameFn(getSizes().front(), "sizes");1385    setNameFn(getStrides().front(), "strides");1386  }1387}1388 1389/// Helper function to perform the replacement of all constant uses of `values`1390/// by a materialized constant extracted from `maybeConstants`.1391/// `values` and `maybeConstants` are expected to have the same size.1392template <typename Container>1393static bool replaceConstantUsesOf(OpBuilder &rewriter, Location loc,1394                                  Container values,1395                                  ArrayRef<OpFoldResult> maybeConstants) {1396  assert(values.size() == maybeConstants.size() &&1397         " expected values and maybeConstants of the same size");1398  bool atLeastOneReplacement = false;1399  for (auto [maybeConstant, result] : llvm::zip(maybeConstants, values)) {1400    // Don't materialize a constant if there are no uses: this would indice1401    // infinite loops in the driver.1402    if (result.use_empty() || maybeConstant == getAsOpFoldResult(result))1403      continue;1404    assert(isa<Attribute>(maybeConstant) &&1405           "The constified value should be either unchanged (i.e., == result) "1406           "or a constant");1407    Value constantVal = arith::ConstantIndexOp::create(1408        rewriter, loc,1409        llvm::cast<IntegerAttr>(cast<Attribute>(maybeConstant)).getInt());1410    for (Operation *op : llvm::make_early_inc_range(result.getUsers())) {1411      // modifyOpInPlace: lambda cannot capture structured bindings in C++171412      // yet.1413      op->replaceUsesOfWith(result, constantVal);1414      atLeastOneReplacement = true;1415    }1416  }1417  return atLeastOneReplacement;1418}1419 1420LogicalResult1421ExtractStridedMetadataOp::fold(FoldAdaptor adaptor,1422                               SmallVectorImpl<OpFoldResult> &results) {1423  OpBuilder builder(*this);1424 1425  bool atLeastOneReplacement = replaceConstantUsesOf(1426      builder, getLoc(), ArrayRef<TypedValue<IndexType>>(getOffset()),1427      getConstifiedMixedOffset());1428  atLeastOneReplacement |= replaceConstantUsesOf(builder, getLoc(), getSizes(),1429                                                 getConstifiedMixedSizes());1430  atLeastOneReplacement |= replaceConstantUsesOf(1431      builder, getLoc(), getStrides(), getConstifiedMixedStrides());1432 1433  // extract_strided_metadata(cast(x)) -> extract_strided_metadata(x).1434  if (auto prev = getSource().getDefiningOp<CastOp>())1435    if (isa<MemRefType>(prev.getSource().getType())) {1436      getSourceMutable().assign(prev.getSource());1437      atLeastOneReplacement = true;1438    }1439 1440  return success(atLeastOneReplacement);1441}1442 1443SmallVector<OpFoldResult> ExtractStridedMetadataOp::getConstifiedMixedSizes() {1444  SmallVector<OpFoldResult> values = getAsOpFoldResult(getSizes());1445  constifyIndexValues(values, getSource().getType().getShape());1446  return values;1447}1448 1449SmallVector<OpFoldResult>1450ExtractStridedMetadataOp::getConstifiedMixedStrides() {1451  SmallVector<OpFoldResult> values = getAsOpFoldResult(getStrides());1452  SmallVector<int64_t> staticValues;1453  int64_t unused;1454  LogicalResult status =1455      getSource().getType().getStridesAndOffset(staticValues, unused);1456  (void)status;1457  assert(succeeded(status) && "could not get strides from type");1458  constifyIndexValues(values, staticValues);1459  return values;1460}1461 1462OpFoldResult ExtractStridedMetadataOp::getConstifiedMixedOffset() {1463  OpFoldResult offsetOfr = getAsOpFoldResult(getOffset());1464  SmallVector<OpFoldResult> values(1, offsetOfr);1465  SmallVector<int64_t> staticValues, unused;1466  int64_t offset;1467  LogicalResult status =1468      getSource().getType().getStridesAndOffset(unused, offset);1469  (void)status;1470  assert(succeeded(status) && "could not get offset from type");1471  staticValues.push_back(offset);1472  constifyIndexValues(values, staticValues);1473  return values[0];1474}1475 1476//===----------------------------------------------------------------------===//1477// GenericAtomicRMWOp1478//===----------------------------------------------------------------------===//1479 1480void GenericAtomicRMWOp::build(OpBuilder &builder, OperationState &result,1481                               Value memref, ValueRange ivs) {1482  OpBuilder::InsertionGuard g(builder);1483  result.addOperands(memref);1484  result.addOperands(ivs);1485 1486  if (auto memrefType = llvm::dyn_cast<MemRefType>(memref.getType())) {1487    Type elementType = memrefType.getElementType();1488    result.addTypes(elementType);1489 1490    Region *bodyRegion = result.addRegion();1491    builder.createBlock(bodyRegion);1492    bodyRegion->addArgument(elementType, memref.getLoc());1493  }1494}1495 1496LogicalResult GenericAtomicRMWOp::verify() {1497  auto &body = getRegion();1498  if (body.getNumArguments() != 1)1499    return emitOpError("expected single number of entry block arguments");1500 1501  if (getResult().getType() != body.getArgument(0).getType())1502    return emitOpError("expected block argument of the same type result type");1503 1504  bool hasSideEffects =1505      body.walk([&](Operation *nestedOp) {1506            if (isMemoryEffectFree(nestedOp))1507              return WalkResult::advance();1508            nestedOp->emitError(1509                "body of 'memref.generic_atomic_rmw' should contain "1510                "only operations with no side effects");1511            return WalkResult::interrupt();1512          })1513          .wasInterrupted();1514  return hasSideEffects ? failure() : success();1515}1516 1517ParseResult GenericAtomicRMWOp::parse(OpAsmParser &parser,1518                                      OperationState &result) {1519  OpAsmParser::UnresolvedOperand memref;1520  Type memrefType;1521  SmallVector<OpAsmParser::UnresolvedOperand, 4> ivs;1522 1523  Type indexType = parser.getBuilder().getIndexType();1524  if (parser.parseOperand(memref) ||1525      parser.parseOperandList(ivs, OpAsmParser::Delimiter::Square) ||1526      parser.parseColonType(memrefType) ||1527      parser.resolveOperand(memref, memrefType, result.operands) ||1528      parser.resolveOperands(ivs, indexType, result.operands))1529    return failure();1530 1531  Region *body = result.addRegion();1532  if (parser.parseRegion(*body, {}) ||1533      parser.parseOptionalAttrDict(result.attributes))1534    return failure();1535  result.types.push_back(llvm::cast<MemRefType>(memrefType).getElementType());1536  return success();1537}1538 1539void GenericAtomicRMWOp::print(OpAsmPrinter &p) {1540  p << ' ' << getMemref() << "[" << getIndices()1541    << "] : " << getMemref().getType() << ' ';1542  p.printRegion(getRegion());1543  p.printOptionalAttrDict((*this)->getAttrs());1544}1545 1546//===----------------------------------------------------------------------===//1547// AtomicYieldOp1548//===----------------------------------------------------------------------===//1549 1550LogicalResult AtomicYieldOp::verify() {1551  Type parentType = (*this)->getParentOp()->getResultTypes().front();1552  Type resultType = getResult().getType();1553  if (parentType != resultType)1554    return emitOpError() << "types mismatch between yield op: " << resultType1555                         << " and its parent: " << parentType;1556  return success();1557}1558 1559//===----------------------------------------------------------------------===//1560// GlobalOp1561//===----------------------------------------------------------------------===//1562 1563static void printGlobalMemrefOpTypeAndInitialValue(OpAsmPrinter &p, GlobalOp op,1564                                                   TypeAttr type,1565                                                   Attribute initialValue) {1566  p << type;1567  if (!op.isExternal()) {1568    p << " = ";1569    if (op.isUninitialized())1570      p << "uninitialized";1571    else1572      p.printAttributeWithoutType(initialValue);1573  }1574}1575 1576static ParseResult1577parseGlobalMemrefOpTypeAndInitialValue(OpAsmParser &parser, TypeAttr &typeAttr,1578                                       Attribute &initialValue) {1579  Type type;1580  if (parser.parseType(type))1581    return failure();1582 1583  auto memrefType = llvm::dyn_cast<MemRefType>(type);1584  if (!memrefType || !memrefType.hasStaticShape())1585    return parser.emitError(parser.getNameLoc())1586           << "type should be static shaped memref, but got " << type;1587  typeAttr = TypeAttr::get(type);1588 1589  if (parser.parseOptionalEqual())1590    return success();1591 1592  if (succeeded(parser.parseOptionalKeyword("uninitialized"))) {1593    initialValue = UnitAttr::get(parser.getContext());1594    return success();1595  }1596 1597  Type tensorType = getTensorTypeFromMemRefType(memrefType);1598  if (parser.parseAttribute(initialValue, tensorType))1599    return failure();1600  if (!llvm::isa<ElementsAttr>(initialValue))1601    return parser.emitError(parser.getNameLoc())1602           << "initial value should be a unit or elements attribute";1603  return success();1604}1605 1606LogicalResult GlobalOp::verify() {1607  auto memrefType = llvm::dyn_cast<MemRefType>(getType());1608  if (!memrefType || !memrefType.hasStaticShape())1609    return emitOpError("type should be static shaped memref, but got ")1610           << getType();1611 1612  // Verify that the initial value, if present, is either a unit attribute or1613  // an elements attribute.1614  if (getInitialValue().has_value()) {1615    Attribute initValue = getInitialValue().value();1616    if (!llvm::isa<UnitAttr>(initValue) && !llvm::isa<ElementsAttr>(initValue))1617      return emitOpError("initial value should be a unit or elements "1618                         "attribute, but got ")1619             << initValue;1620 1621    // Check that the type of the initial value is compatible with the type of1622    // the global variable.1623    if (auto elementsAttr = llvm::dyn_cast<ElementsAttr>(initValue)) {1624      // Check the element types match.1625      auto initElementType =1626          cast<TensorType>(elementsAttr.getType()).getElementType();1627      auto memrefElementType = memrefType.getElementType();1628 1629      if (initElementType != memrefElementType)1630        return emitOpError("initial value element expected to be of type ")1631               << memrefElementType << ", but was of type " << initElementType;1632 1633      // Check the shapes match, given that memref globals can only produce1634      // statically shaped memrefs and elements literal type must have a static1635      // shape we can assume both types are shaped.1636      auto initShape = elementsAttr.getShapedType().getShape();1637      auto memrefShape = memrefType.getShape();1638      if (initShape != memrefShape)1639        return emitOpError("initial value shape expected to be ")1640               << memrefShape << " but was " << initShape;1641    }1642  }1643 1644  // TODO: verify visibility for declarations.1645  return success();1646}1647 1648ElementsAttr GlobalOp::getConstantInitValue() {1649  auto initVal = getInitialValue();1650  if (getConstant() && initVal.has_value())1651    return llvm::cast<ElementsAttr>(initVal.value());1652  return {};1653}1654 1655//===----------------------------------------------------------------------===//1656// GetGlobalOp1657//===----------------------------------------------------------------------===//1658 1659LogicalResult1660GetGlobalOp::verifySymbolUses(SymbolTableCollection &symbolTable) {1661  // Verify that the result type is same as the type of the referenced1662  // memref.global op.1663  auto global =1664      symbolTable.lookupNearestSymbolFrom<GlobalOp>(*this, getNameAttr());1665  if (!global)1666    return emitOpError("'")1667           << getName() << "' does not reference a valid global memref";1668 1669  Type resultType = getResult().getType();1670  if (global.getType() != resultType)1671    return emitOpError("result type ")1672           << resultType << " does not match type " << global.getType()1673           << " of the global memref @" << getName();1674  return success();1675}1676 1677//===----------------------------------------------------------------------===//1678// LoadOp1679//===----------------------------------------------------------------------===//1680 1681LogicalResult LoadOp::verify() {1682  if (static_cast<int64_t>(getIndices().size()) != getMemRefType().getRank()) {1683    return emitOpError("incorrect number of indices for load, expected ")1684           << getMemRefType().getRank() << " but got " << getIndices().size();1685  }1686  return success();1687}1688 1689OpFoldResult LoadOp::fold(FoldAdaptor adaptor) {1690  /// load(memrefcast) -> load1691  if (succeeded(foldMemRefCast(*this)))1692    return getResult();1693  return OpFoldResult();1694}1695 1696FailureOr<std::optional<SmallVector<Value>>>1697LoadOp::bubbleDownCasts(OpBuilder &builder) {1698  return mlir::detail::bubbleDownInPlaceMemorySpaceCastImpl(getMemrefMutable(),1699                                                            getResult());1700}1701 1702//===----------------------------------------------------------------------===//1703// MemorySpaceCastOp1704//===----------------------------------------------------------------------===//1705 1706void MemorySpaceCastOp::getAsmResultNames(1707    function_ref<void(Value, StringRef)> setNameFn) {1708  setNameFn(getResult(), "memspacecast");1709}1710 1711bool MemorySpaceCastOp::areCastCompatible(TypeRange inputs, TypeRange outputs) {1712  if (inputs.size() != 1 || outputs.size() != 1)1713    return false;1714  Type a = inputs.front(), b = outputs.front();1715  auto aT = llvm::dyn_cast<MemRefType>(a);1716  auto bT = llvm::dyn_cast<MemRefType>(b);1717 1718  auto uaT = llvm::dyn_cast<UnrankedMemRefType>(a);1719  auto ubT = llvm::dyn_cast<UnrankedMemRefType>(b);1720 1721  if (aT && bT) {1722    if (aT.getElementType() != bT.getElementType())1723      return false;1724    if (aT.getLayout() != bT.getLayout())1725      return false;1726    if (aT.getShape() != bT.getShape())1727      return false;1728    return true;1729  }1730  if (uaT && ubT) {1731    return uaT.getElementType() == ubT.getElementType();1732  }1733  return false;1734}1735 1736OpFoldResult MemorySpaceCastOp::fold(FoldAdaptor adaptor) {1737  // memory_space_cast(memory_space_cast(v, t1), t2) -> memory_space_cast(v,1738  // t2)1739  if (auto parentCast = getSource().getDefiningOp<MemorySpaceCastOp>()) {1740    getSourceMutable().assign(parentCast.getSource());1741    return getResult();1742  }1743  return Value{};1744}1745 1746TypedValue<PtrLikeTypeInterface> MemorySpaceCastOp::getSourcePtr() {1747  return getSource();1748}1749 1750TypedValue<PtrLikeTypeInterface> MemorySpaceCastOp::getTargetPtr() {1751  return getDest();1752}1753 1754bool MemorySpaceCastOp::isValidMemorySpaceCast(PtrLikeTypeInterface tgt,1755                                               PtrLikeTypeInterface src) {1756  return isa<BaseMemRefType>(tgt) &&1757         tgt.clonePtrWith(src.getMemorySpace(), std::nullopt) == src;1758}1759 1760MemorySpaceCastOpInterface MemorySpaceCastOp::cloneMemorySpaceCastOp(1761    OpBuilder &b, PtrLikeTypeInterface tgt,1762    TypedValue<PtrLikeTypeInterface> src) {1763  assert(isValidMemorySpaceCast(tgt, src.getType()) && "invalid arguments");1764  return MemorySpaceCastOp::create(b, getLoc(), tgt, src);1765}1766 1767/// The only cast we recognize as promotable is to the generic space.1768bool MemorySpaceCastOp::isSourcePromotable() {1769  return getDest().getType().getMemorySpace() == nullptr;1770}1771 1772//===----------------------------------------------------------------------===//1773// PrefetchOp1774//===----------------------------------------------------------------------===//1775 1776void PrefetchOp::print(OpAsmPrinter &p) {1777  p << " " << getMemref() << '[';1778  p.printOperands(getIndices());1779  p << ']' << ", " << (getIsWrite() ? "write" : "read");1780  p << ", locality<" << getLocalityHint();1781  p << ">, " << (getIsDataCache() ? "data" : "instr");1782  p.printOptionalAttrDict(1783      (*this)->getAttrs(),1784      /*elidedAttrs=*/{"localityHint", "isWrite", "isDataCache"});1785  p << " : " << getMemRefType();1786}1787 1788ParseResult PrefetchOp::parse(OpAsmParser &parser, OperationState &result) {1789  OpAsmParser::UnresolvedOperand memrefInfo;1790  SmallVector<OpAsmParser::UnresolvedOperand, 4> indexInfo;1791  IntegerAttr localityHint;1792  MemRefType type;1793  StringRef readOrWrite, cacheType;1794 1795  auto indexTy = parser.getBuilder().getIndexType();1796  auto i32Type = parser.getBuilder().getIntegerType(32);1797  if (parser.parseOperand(memrefInfo) ||1798      parser.parseOperandList(indexInfo, OpAsmParser::Delimiter::Square) ||1799      parser.parseComma() || parser.parseKeyword(&readOrWrite) ||1800      parser.parseComma() || parser.parseKeyword("locality") ||1801      parser.parseLess() ||1802      parser.parseAttribute(localityHint, i32Type, "localityHint",1803                            result.attributes) ||1804      parser.parseGreater() || parser.parseComma() ||1805      parser.parseKeyword(&cacheType) || parser.parseColonType(type) ||1806      parser.resolveOperand(memrefInfo, type, result.operands) ||1807      parser.resolveOperands(indexInfo, indexTy, result.operands))1808    return failure();1809 1810  if (readOrWrite != "read" && readOrWrite != "write")1811    return parser.emitError(parser.getNameLoc(),1812                            "rw specifier has to be 'read' or 'write'");1813  result.addAttribute(PrefetchOp::getIsWriteAttrStrName(),1814                      parser.getBuilder().getBoolAttr(readOrWrite == "write"));1815 1816  if (cacheType != "data" && cacheType != "instr")1817    return parser.emitError(parser.getNameLoc(),1818                            "cache type has to be 'data' or 'instr'");1819 1820  result.addAttribute(PrefetchOp::getIsDataCacheAttrStrName(),1821                      parser.getBuilder().getBoolAttr(cacheType == "data"));1822 1823  return success();1824}1825 1826LogicalResult PrefetchOp::verify() {1827  if (getNumOperands() != 1 + getMemRefType().getRank())1828    return emitOpError("too few indices");1829 1830  return success();1831}1832 1833LogicalResult PrefetchOp::fold(FoldAdaptor adaptor,1834                               SmallVectorImpl<OpFoldResult> &results) {1835  // prefetch(memrefcast) -> prefetch1836  return foldMemRefCast(*this);1837}1838 1839//===----------------------------------------------------------------------===//1840// RankOp1841//===----------------------------------------------------------------------===//1842 1843OpFoldResult RankOp::fold(FoldAdaptor adaptor) {1844  // Constant fold rank when the rank of the operand is known.1845  auto type = getOperand().getType();1846  auto shapedType = llvm::dyn_cast<ShapedType>(type);1847  if (shapedType && shapedType.hasRank())1848    return IntegerAttr::get(IndexType::get(getContext()), shapedType.getRank());1849  return IntegerAttr();1850}1851 1852//===----------------------------------------------------------------------===//1853// ReinterpretCastOp1854//===----------------------------------------------------------------------===//1855 1856void ReinterpretCastOp::getAsmResultNames(1857    function_ref<void(Value, StringRef)> setNameFn) {1858  setNameFn(getResult(), "reinterpret_cast");1859}1860 1861/// Build a ReinterpretCastOp with all dynamic entries: `staticOffsets`,1862/// `staticSizes` and `staticStrides` are automatically filled with1863/// source-memref-rank sentinel values that encode dynamic entries.1864void ReinterpretCastOp::build(OpBuilder &b, OperationState &result,1865                              MemRefType resultType, Value source,1866                              OpFoldResult offset, ArrayRef<OpFoldResult> sizes,1867                              ArrayRef<OpFoldResult> strides,1868                              ArrayRef<NamedAttribute> attrs) {1869  SmallVector<int64_t> staticOffsets, staticSizes, staticStrides;1870  SmallVector<Value> dynamicOffsets, dynamicSizes, dynamicStrides;1871  dispatchIndexOpFoldResults(offset, dynamicOffsets, staticOffsets);1872  dispatchIndexOpFoldResults(sizes, dynamicSizes, staticSizes);1873  dispatchIndexOpFoldResults(strides, dynamicStrides, staticStrides);1874  result.addAttributes(attrs);1875  build(b, result, resultType, source, dynamicOffsets, dynamicSizes,1876        dynamicStrides, b.getDenseI64ArrayAttr(staticOffsets),1877        b.getDenseI64ArrayAttr(staticSizes),1878        b.getDenseI64ArrayAttr(staticStrides));1879}1880 1881void ReinterpretCastOp::build(OpBuilder &b, OperationState &result,1882                              Value source, OpFoldResult offset,1883                              ArrayRef<OpFoldResult> sizes,1884                              ArrayRef<OpFoldResult> strides,1885                              ArrayRef<NamedAttribute> attrs) {1886  auto sourceType = cast<BaseMemRefType>(source.getType());1887  SmallVector<int64_t> staticOffsets, staticSizes, staticStrides;1888  SmallVector<Value> dynamicOffsets, dynamicSizes, dynamicStrides;1889  dispatchIndexOpFoldResults(offset, dynamicOffsets, staticOffsets);1890  dispatchIndexOpFoldResults(sizes, dynamicSizes, staticSizes);1891  dispatchIndexOpFoldResults(strides, dynamicStrides, staticStrides);1892  auto stridedLayout = StridedLayoutAttr::get(1893      b.getContext(), staticOffsets.front(), staticStrides);1894  auto resultType = MemRefType::get(staticSizes, sourceType.getElementType(),1895                                    stridedLayout, sourceType.getMemorySpace());1896  build(b, result, resultType, source, offset, sizes, strides, attrs);1897}1898 1899void ReinterpretCastOp::build(OpBuilder &b, OperationState &result,1900                              MemRefType resultType, Value source,1901                              int64_t offset, ArrayRef<int64_t> sizes,1902                              ArrayRef<int64_t> strides,1903                              ArrayRef<NamedAttribute> attrs) {1904  SmallVector<OpFoldResult> sizeValues =1905      llvm::to_vector<4>(llvm::map_range(sizes, [&](int64_t v) -> OpFoldResult {1906        return b.getI64IntegerAttr(v);1907      }));1908  SmallVector<OpFoldResult> strideValues = llvm::to_vector<4>(1909      llvm::map_range(strides, [&](int64_t v) -> OpFoldResult {1910        return b.getI64IntegerAttr(v);1911      }));1912  build(b, result, resultType, source, b.getI64IntegerAttr(offset), sizeValues,1913        strideValues, attrs);1914}1915 1916void ReinterpretCastOp::build(OpBuilder &b, OperationState &result,1917                              MemRefType resultType, Value source, Value offset,1918                              ValueRange sizes, ValueRange strides,1919                              ArrayRef<NamedAttribute> attrs) {1920  SmallVector<OpFoldResult> sizeValues = llvm::to_vector<4>(1921      llvm::map_range(sizes, [](Value v) -> OpFoldResult { return v; }));1922  SmallVector<OpFoldResult> strideValues = llvm::to_vector<4>(1923      llvm::map_range(strides, [](Value v) -> OpFoldResult { return v; }));1924  build(b, result, resultType, source, offset, sizeValues, strideValues, attrs);1925}1926 1927// TODO: ponder whether we want to allow missing trailing sizes/strides that are1928// completed automatically, like we have for subview and extract_slice.1929LogicalResult ReinterpretCastOp::verify() {1930  // The source and result memrefs should be in the same memory space.1931  auto srcType = llvm::cast<BaseMemRefType>(getSource().getType());1932  auto resultType = llvm::cast<MemRefType>(getType());1933  if (srcType.getMemorySpace() != resultType.getMemorySpace())1934    return emitError("different memory spaces specified for source type ")1935           << srcType << " and result memref type " << resultType;1936  if (srcType.getElementType() != resultType.getElementType())1937    return emitError("different element types specified for source type ")1938           << srcType << " and result memref type " << resultType;1939 1940  // Match sizes in result memref type and in static_sizes attribute.1941  for (auto [idx, resultSize, expectedSize] :1942       llvm::enumerate(resultType.getShape(), getStaticSizes())) {1943    if (ShapedType::isStatic(resultSize) && resultSize != expectedSize)1944      return emitError("expected result type with size = ")1945             << (ShapedType::isDynamic(expectedSize)1946                     ? std::string("dynamic")1947                     : std::to_string(expectedSize))1948             << " instead of " << resultSize << " in dim = " << idx;1949  }1950 1951  // Match offset and strides in static_offset and static_strides attributes. If1952  // result memref type has no affine map specified, this will assume an1953  // identity layout.1954  int64_t resultOffset;1955  SmallVector<int64_t, 4> resultStrides;1956  if (failed(resultType.getStridesAndOffset(resultStrides, resultOffset)))1957    return emitError("expected result type to have strided layout but found ")1958           << resultType;1959 1960  // Match offset in result memref type and in static_offsets attribute.1961  int64_t expectedOffset = getStaticOffsets().front();1962  if (ShapedType::isStatic(resultOffset) && resultOffset != expectedOffset)1963    return emitError("expected result type with offset = ")1964           << (ShapedType::isDynamic(expectedOffset)1965                   ? std::string("dynamic")1966                   : std::to_string(expectedOffset))1967           << " instead of " << resultOffset;1968 1969  // Match strides in result memref type and in static_strides attribute.1970  for (auto [idx, resultStride, expectedStride] :1971       llvm::enumerate(resultStrides, getStaticStrides())) {1972    if (ShapedType::isStatic(resultStride) && resultStride != expectedStride)1973      return emitError("expected result type with stride = ")1974             << (ShapedType::isDynamic(expectedStride)1975                     ? std::string("dynamic")1976                     : std::to_string(expectedStride))1977             << " instead of " << resultStride << " in dim = " << idx;1978  }1979 1980  return success();1981}1982 1983OpFoldResult ReinterpretCastOp::fold(FoldAdaptor /*operands*/) {1984  Value src = getSource();1985  auto getPrevSrc = [&]() -> Value {1986    // reinterpret_cast(reinterpret_cast(x)) -> reinterpret_cast(x).1987    if (auto prev = src.getDefiningOp<ReinterpretCastOp>())1988      return prev.getSource();1989 1990    // reinterpret_cast(cast(x)) -> reinterpret_cast(x).1991    if (auto prev = src.getDefiningOp<CastOp>())1992      return prev.getSource();1993 1994    // reinterpret_cast(subview(x)) -> reinterpret_cast(x) if subview offsets1995    // are 0.1996    if (auto prev = src.getDefiningOp<SubViewOp>())1997      if (llvm::all_of(prev.getMixedOffsets(), isZeroInteger))1998        return prev.getSource();1999 2000    return nullptr;2001  };2002 2003  if (auto prevSrc = getPrevSrc()) {2004    getSourceMutable().assign(prevSrc);2005    return getResult();2006  }2007 2008  // reinterpret_cast(x) w/o offset/shape/stride changes -> x2009  if (ShapedType::isStaticShape(getType().getShape()) &&2010      src.getType() == getType() && getStaticOffsets().front() == 0) {2011    return src;2012  }2013 2014  return nullptr;2015}2016 2017SmallVector<OpFoldResult> ReinterpretCastOp::getConstifiedMixedSizes() {2018  SmallVector<OpFoldResult> values = getMixedSizes();2019  constifyIndexValues(values, getType().getShape());2020  return values;2021}2022 2023SmallVector<OpFoldResult> ReinterpretCastOp::getConstifiedMixedStrides() {2024  SmallVector<OpFoldResult> values = getMixedStrides();2025  SmallVector<int64_t> staticValues;2026  int64_t unused;2027  LogicalResult status = getType().getStridesAndOffset(staticValues, unused);2028  (void)status;2029  assert(succeeded(status) && "could not get strides from type");2030  constifyIndexValues(values, staticValues);2031  return values;2032}2033 2034OpFoldResult ReinterpretCastOp::getConstifiedMixedOffset() {2035  SmallVector<OpFoldResult> values = getMixedOffsets();2036  assert(values.size() == 1 &&2037         "reinterpret_cast must have one and only one offset");2038  SmallVector<int64_t> staticValues, unused;2039  int64_t offset;2040  LogicalResult status = getType().getStridesAndOffset(unused, offset);2041  (void)status;2042  assert(succeeded(status) && "could not get offset from type");2043  staticValues.push_back(offset);2044  constifyIndexValues(values, staticValues);2045  return values[0];2046}2047 2048namespace {2049/// Replace the sequence:2050/// ```2051/// base, offset, sizes, strides = extract_strided_metadata src2052/// dst = reinterpret_cast base to offset, sizes, strides2053/// ```2054/// With2055///2056/// ```2057/// dst = memref.cast src2058/// ```2059///2060/// Note: The cast operation is only inserted when the type of dst and src2061/// are not the same. E.g., when going from <4xf32> to <?xf32>.2062///2063/// This pattern also matches when the offset, sizes, and strides don't come2064/// directly from the `extract_strided_metadata`'s results but it can be2065/// statically proven that they would hold the same values.2066///2067/// For instance, the following sequence would be replaced:2068/// ```2069/// base, offset, sizes, strides =2070///   extract_strided_metadata memref : memref<3x4xty>2071/// dst = reinterpret_cast base to 0, [3, 4], strides2072/// ```2073/// Because we know (thanks to the type of the input memref) that variable2074/// `offset` and `sizes` will respectively hold 0 and [3, 4].2075///2076/// Similarly, the following sequence would be replaced:2077/// ```2078/// c0 = arith.constant 02079/// c4 = arith.constant 42080/// base, offset, sizes, strides =2081///   extract_strided_metadata memref : memref<3x4xty>2082/// dst = reinterpret_cast base to c0, [3, c4], strides2083/// ```2084/// Because we know that `offset`and `c0` will hold 02085/// and `c4` will hold 4.2086///2087/// If the pattern above does not match, the input of the2088/// extract_strided_metadata is always folded into the input of the2089/// reinterpret_cast operator. This allows for dead code elimination to get rid2090/// of the extract_strided_metadata in some cases.2091struct ReinterpretCastOpExtractStridedMetadataFolder2092    : public OpRewritePattern<ReinterpretCastOp> {2093public:2094  using OpRewritePattern<ReinterpretCastOp>::OpRewritePattern;2095 2096  LogicalResult matchAndRewrite(ReinterpretCastOp op,2097                                PatternRewriter &rewriter) const override {2098    auto extractStridedMetadata =2099        op.getSource().getDefiningOp<ExtractStridedMetadataOp>();2100    if (!extractStridedMetadata)2101      return failure();2102 2103    // Check if the reinterpret cast reconstructs a memref with the exact same2104    // properties as the extract strided metadata.2105    auto isReinterpretCastNoop = [&]() -> bool {2106      // First, check that the strides are the same.2107      if (!llvm::equal(extractStridedMetadata.getConstifiedMixedStrides(),2108                       op.getConstifiedMixedStrides()))2109        return false;2110 2111      // Second, check the sizes.2112      if (!llvm::equal(extractStridedMetadata.getConstifiedMixedSizes(),2113                       op.getConstifiedMixedSizes()))2114        return false;2115 2116      // Finally, check the offset.2117      assert(op.getMixedOffsets().size() == 1 &&2118             "reinterpret_cast with more than one offset should have been "2119             "rejected by the verifier");2120      return extractStridedMetadata.getConstifiedMixedOffset() ==2121             op.getConstifiedMixedOffset();2122    };2123 2124    if (!isReinterpretCastNoop()) {2125      // If the extract_strided_metadata / reinterpret_cast pair can't be2126      // completely folded, then we could fold the input of the2127      // extract_strided_metadata into the input of the reinterpret_cast2128      // input. For some cases (e.g., static dimensions) the2129      // the extract_strided_metadata is eliminated by dead code elimination.2130      //2131      // reinterpret_cast(extract_strided_metadata(x)) -> reinterpret_cast(x).2132      //2133      // We can always fold the input of a extract_strided_metadata operator2134      // to the input of a reinterpret_cast operator, because they point to2135      // the same memory. Note that the reinterpret_cast does not use the2136      // layout of its input memref, only its base memory pointer which is2137      // the same as the base pointer returned by the extract_strided_metadata2138      // operator and the base pointer of the extract_strided_metadata memref2139      // input.2140      rewriter.modifyOpInPlace(op, [&]() {2141        op.getSourceMutable().assign(extractStridedMetadata.getSource());2142      });2143      return success();2144    }2145 2146    // At this point, we know that the back and forth between extract strided2147    // metadata and reinterpret cast is a noop. However, the final type of the2148    // reinterpret cast may not be exactly the same as the original memref.2149    // E.g., it could be changing a dimension from static to dynamic. Check that2150    // here and add a cast if necessary.2151    Type srcTy = extractStridedMetadata.getSource().getType();2152    if (srcTy == op.getResult().getType())2153      rewriter.replaceOp(op, extractStridedMetadata.getSource());2154    else2155      rewriter.replaceOpWithNewOp<CastOp>(op, op.getType(),2156                                          extractStridedMetadata.getSource());2157 2158    return success();2159  }2160};2161 2162struct ReinterpretCastOpConstantFolder2163    : public OpRewritePattern<ReinterpretCastOp> {2164public:2165  using OpRewritePattern<ReinterpretCastOp>::OpRewritePattern;2166 2167  LogicalResult matchAndRewrite(ReinterpretCastOp op,2168                                PatternRewriter &rewriter) const override {2169    unsigned srcStaticCount = llvm::count_if(2170        llvm::concat<OpFoldResult>(op.getMixedOffsets(), op.getMixedSizes(),2171                                   op.getMixedStrides()),2172        [](OpFoldResult ofr) { return isa<Attribute>(ofr); });2173 2174    SmallVector<OpFoldResult> offsets = {op.getConstifiedMixedOffset()};2175    SmallVector<OpFoldResult> sizes = op.getConstifiedMixedSizes();2176    SmallVector<OpFoldResult> strides = op.getConstifiedMixedStrides();2177 2178    // TODO: Using counting comparison instead of direct comparison because2179    // getMixedValues (and therefore ReinterpretCastOp::getMixed...) returns2180    // IntegerAttrs, while constifyIndexValues (and therefore2181    // ReinterpretCastOp::getConstifiedMixed...) returns IndexAttrs.2182    if (srcStaticCount ==2183        llvm::count_if(llvm::concat<OpFoldResult>(offsets, sizes, strides),2184                       [](OpFoldResult ofr) { return isa<Attribute>(ofr); }))2185      return failure();2186 2187    auto newReinterpretCast = ReinterpretCastOp::create(2188        rewriter, op->getLoc(), op.getSource(), offsets[0], sizes, strides);2189 2190    rewriter.replaceOpWithNewOp<CastOp>(op, op.getType(), newReinterpretCast);2191    return success();2192  }2193};2194} // namespace2195 2196void ReinterpretCastOp::getCanonicalizationPatterns(RewritePatternSet &results,2197                                                    MLIRContext *context) {2198  results.add<ReinterpretCastOpExtractStridedMetadataFolder,2199              ReinterpretCastOpConstantFolder>(context);2200}2201 2202FailureOr<std::optional<SmallVector<Value>>>2203ReinterpretCastOp::bubbleDownCasts(OpBuilder &builder) {2204  return bubbleDownCastsPassthroughOpImpl(*this, builder, getSourceMutable());2205}2206 2207//===----------------------------------------------------------------------===//2208// Reassociative reshape ops2209//===----------------------------------------------------------------------===//2210 2211void CollapseShapeOp::getAsmResultNames(2212    function_ref<void(Value, StringRef)> setNameFn) {2213  setNameFn(getResult(), "collapse_shape");2214}2215 2216void ExpandShapeOp::getAsmResultNames(2217    function_ref<void(Value, StringRef)> setNameFn) {2218  setNameFn(getResult(), "expand_shape");2219}2220 2221LogicalResult ExpandShapeOp::reifyResultShapes(2222    OpBuilder &builder, ReifiedRankedShapedTypeDims &reifiedResultShapes) {2223  reifiedResultShapes = {2224      getMixedValues(getStaticOutputShape(), getOutputShape(), builder)};2225  return success();2226}2227 2228/// Helper function for verifying the shape of ExpandShapeOp and ResultShapeOp2229/// result and operand. Layout maps are verified separately.2230///2231/// If `allowMultipleDynamicDimsPerGroup`, multiple dynamic dimensions are2232/// allowed in a reassocation group.2233static LogicalResult2234verifyCollapsedShape(Operation *op, ArrayRef<int64_t> collapsedShape,2235                     ArrayRef<int64_t> expandedShape,2236                     ArrayRef<ReassociationIndices> reassociation,2237                     bool allowMultipleDynamicDimsPerGroup) {2238  // There must be one reassociation group per collapsed dimension.2239  if (collapsedShape.size() != reassociation.size())2240    return op->emitOpError("invalid number of reassociation groups: found ")2241           << reassociation.size() << ", expected " << collapsedShape.size();2242 2243  // The next expected expanded dimension index (while iterating over2244  // reassociation indices).2245  int64_t nextDim = 0;2246  for (const auto &it : llvm::enumerate(reassociation)) {2247    ReassociationIndices group = it.value();2248    int64_t collapsedDim = it.index();2249 2250    bool foundDynamic = false;2251    for (int64_t expandedDim : group) {2252      if (expandedDim != nextDim++)2253        return op->emitOpError("reassociation indices must be contiguous");2254 2255      if (expandedDim >= static_cast<int64_t>(expandedShape.size()))2256        return op->emitOpError("reassociation index ")2257               << expandedDim << " is out of bounds";2258 2259      // Check if there are multiple dynamic dims in a reassociation group.2260      if (ShapedType::isDynamic(expandedShape[expandedDim])) {2261        if (foundDynamic && !allowMultipleDynamicDimsPerGroup)2262          return op->emitOpError(2263              "at most one dimension in a reassociation group may be dynamic");2264        foundDynamic = true;2265      }2266    }2267 2268    // ExpandShapeOp/CollapseShapeOp may not be used to cast dynamicity.2269    if (ShapedType::isDynamic(collapsedShape[collapsedDim]) != foundDynamic)2270      return op->emitOpError("collapsed dim (")2271             << collapsedDim2272             << ") must be dynamic if and only if reassociation group is "2273                "dynamic";2274 2275    // If all dims in the reassociation group are static, the size of the2276    // collapsed dim can be verified.2277    if (!foundDynamic) {2278      int64_t groupSize = 1;2279      for (int64_t expandedDim : group)2280        groupSize *= expandedShape[expandedDim];2281      if (groupSize != collapsedShape[collapsedDim])2282        return op->emitOpError("collapsed dim size (")2283               << collapsedShape[collapsedDim]2284               << ") must equal reassociation group size (" << groupSize << ")";2285    }2286  }2287 2288  if (collapsedShape.empty()) {2289    // Rank 0: All expanded dimensions must be 1.2290    for (int64_t d : expandedShape)2291      if (d != 1)2292        return op->emitOpError(2293            "rank 0 memrefs can only be extended/collapsed with/from ones");2294  } else if (nextDim != static_cast<int64_t>(expandedShape.size())) {2295    // Rank >= 1: Number of dimensions among all reassociation groups must match2296    // the result memref rank.2297    return op->emitOpError("expanded rank (")2298           << expandedShape.size()2299           << ") inconsistent with number of reassociation indices (" << nextDim2300           << ")";2301  }2302 2303  return success();2304}2305 2306SmallVector<AffineMap, 4> CollapseShapeOp::getReassociationMaps() {2307  return getSymbolLessAffineMaps(getReassociationExprs());2308}2309 2310SmallVector<ReassociationExprs, 4> CollapseShapeOp::getReassociationExprs() {2311  return convertReassociationIndicesToExprs(getContext(),2312                                            getReassociationIndices());2313}2314 2315SmallVector<AffineMap, 4> ExpandShapeOp::getReassociationMaps() {2316  return getSymbolLessAffineMaps(getReassociationExprs());2317}2318 2319SmallVector<ReassociationExprs, 4> ExpandShapeOp::getReassociationExprs() {2320  return convertReassociationIndicesToExprs(getContext(),2321                                            getReassociationIndices());2322}2323 2324/// Compute the layout map after expanding a given source MemRef type with the2325/// specified reassociation indices.2326static FailureOr<StridedLayoutAttr>2327computeExpandedLayoutMap(MemRefType srcType, ArrayRef<int64_t> resultShape,2328                         ArrayRef<ReassociationIndices> reassociation) {2329  int64_t srcOffset;2330  SmallVector<int64_t> srcStrides;2331  if (failed(srcType.getStridesAndOffset(srcStrides, srcOffset)))2332    return failure();2333  assert(srcStrides.size() == reassociation.size() && "invalid reassociation");2334 2335  // 1-1 mapping between srcStrides and reassociation packs.2336  // Each srcStride starts with the given value and gets expanded according to2337  // the proper entries in resultShape.2338  // Example:2339  //   srcStrides     =                   [10000,  1 ,    100   ],2340  //   reassociations =                   [  [0], [1], [2, 3, 4]],2341  //   resultSizes    = [2, 5, 4, 3, 2] = [  [2], [5], [4, 3, 2]]2342  //     -> For the purpose of stride calculation, the useful sizes are:2343  //                    [x, x, x, 3, 2] = [  [x], [x], [x, 3, 2]].2344  //   resultStrides = [10000, 1, 600, 200, 100]2345  // Note that a stride does not get expanded along the first entry of each2346  // shape pack.2347  SmallVector<int64_t> reverseResultStrides;2348  reverseResultStrides.reserve(resultShape.size());2349  unsigned shapeIndex = resultShape.size() - 1;2350  for (auto it : llvm::reverse(llvm::zip(reassociation, srcStrides))) {2351    ReassociationIndices reassoc = std::get<0>(it);2352    int64_t currentStrideToExpand = std::get<1>(it);2353    for (unsigned idx = 0, e = reassoc.size(); idx < e; ++idx) {2354      reverseResultStrides.push_back(currentStrideToExpand);2355      currentStrideToExpand =2356          (SaturatedInteger::wrap(currentStrideToExpand) *2357           SaturatedInteger::wrap(resultShape[shapeIndex--]))2358              .asInteger();2359    }2360  }2361  auto resultStrides = llvm::to_vector<8>(llvm::reverse(reverseResultStrides));2362  resultStrides.resize(resultShape.size(), 1);2363  return StridedLayoutAttr::get(srcType.getContext(), srcOffset, resultStrides);2364}2365 2366FailureOr<MemRefType> ExpandShapeOp::computeExpandedType(2367    MemRefType srcType, ArrayRef<int64_t> resultShape,2368    ArrayRef<ReassociationIndices> reassociation) {2369  if (srcType.getLayout().isIdentity()) {2370    // If the source is contiguous (i.e., no layout map specified), so is the2371    // result.2372    MemRefLayoutAttrInterface layout;2373    return MemRefType::get(resultShape, srcType.getElementType(), layout,2374                           srcType.getMemorySpace());2375  }2376 2377  // Source may not be contiguous. Compute the layout map.2378  FailureOr<StridedLayoutAttr> computedLayout =2379      computeExpandedLayoutMap(srcType, resultShape, reassociation);2380  if (failed(computedLayout))2381    return failure();2382  return MemRefType::get(resultShape, srcType.getElementType(), *computedLayout,2383                         srcType.getMemorySpace());2384}2385 2386FailureOr<SmallVector<OpFoldResult>>2387ExpandShapeOp::inferOutputShape(OpBuilder &b, Location loc,2388                                MemRefType expandedType,2389                                ArrayRef<ReassociationIndices> reassociation,2390                                ArrayRef<OpFoldResult> inputShape) {2391  std::optional<SmallVector<OpFoldResult>> outputShape =2392      inferExpandShapeOutputShape(b, loc, expandedType, reassociation,2393                                  inputShape);2394  if (!outputShape)2395    return failure();2396  return *outputShape;2397}2398 2399void ExpandShapeOp::build(OpBuilder &builder, OperationState &result,2400                          Type resultType, Value src,2401                          ArrayRef<ReassociationIndices> reassociation,2402                          ArrayRef<OpFoldResult> outputShape) {2403  auto [staticOutputShape, dynamicOutputShape] =2404      decomposeMixedValues(SmallVector<OpFoldResult>(outputShape));2405  build(builder, result, llvm::cast<MemRefType>(resultType), src,2406        getReassociationIndicesAttribute(builder, reassociation),2407        dynamicOutputShape, staticOutputShape);2408}2409 2410void ExpandShapeOp::build(OpBuilder &builder, OperationState &result,2411                          Type resultType, Value src,2412                          ArrayRef<ReassociationIndices> reassociation) {2413  SmallVector<OpFoldResult> inputShape =2414      getMixedSizes(builder, result.location, src);2415  MemRefType memrefResultTy = llvm::cast<MemRefType>(resultType);2416  FailureOr<SmallVector<OpFoldResult>> outputShape = inferOutputShape(2417      builder, result.location, memrefResultTy, reassociation, inputShape);2418  // Failure of this assertion usually indicates presence of multiple2419  // dynamic dimensions in the same reassociation group.2420  assert(succeeded(outputShape) && "unable to infer output shape");2421  build(builder, result, memrefResultTy, src, reassociation, *outputShape);2422}2423 2424void ExpandShapeOp::build(OpBuilder &builder, OperationState &result,2425                          ArrayRef<int64_t> resultShape, Value src,2426                          ArrayRef<ReassociationIndices> reassociation) {2427  // Only ranked memref source values are supported.2428  auto srcType = llvm::cast<MemRefType>(src.getType());2429  FailureOr<MemRefType> resultType =2430      ExpandShapeOp::computeExpandedType(srcType, resultShape, reassociation);2431  // Failure of this assertion usually indicates a problem with the source2432  // type, e.g., could not get strides/offset.2433  assert(succeeded(resultType) && "could not compute layout");2434  build(builder, result, *resultType, src, reassociation);2435}2436 2437void ExpandShapeOp::build(OpBuilder &builder, OperationState &result,2438                          ArrayRef<int64_t> resultShape, Value src,2439                          ArrayRef<ReassociationIndices> reassociation,2440                          ArrayRef<OpFoldResult> outputShape) {2441  // Only ranked memref source values are supported.2442  auto srcType = llvm::cast<MemRefType>(src.getType());2443  FailureOr<MemRefType> resultType =2444      ExpandShapeOp::computeExpandedType(srcType, resultShape, reassociation);2445  // Failure of this assertion usually indicates a problem with the source2446  // type, e.g., could not get strides/offset.2447  assert(succeeded(resultType) && "could not compute layout");2448  build(builder, result, *resultType, src, reassociation, outputShape);2449}2450 2451LogicalResult ExpandShapeOp::verify() {2452  MemRefType srcType = getSrcType();2453  MemRefType resultType = getResultType();2454 2455  if (srcType.getRank() > resultType.getRank()) {2456    auto r0 = srcType.getRank();2457    auto r1 = resultType.getRank();2458    return emitOpError("has source rank ")2459           << r0 << " and result rank " << r1 << ". This is not an expansion ("2460           << r0 << " > " << r1 << ").";2461  }2462 2463  // Verify result shape.2464  if (failed(verifyCollapsedShape(getOperation(), srcType.getShape(),2465                                  resultType.getShape(),2466                                  getReassociationIndices(),2467                                  /*allowMultipleDynamicDimsPerGroup=*/true)))2468    return failure();2469 2470  // Compute expected result type (including layout map).2471  FailureOr<MemRefType> expectedResultType = ExpandShapeOp::computeExpandedType(2472      srcType, resultType.getShape(), getReassociationIndices());2473  if (failed(expectedResultType))2474    return emitOpError("invalid source layout map");2475 2476  // Check actual result type.2477  if (*expectedResultType != resultType)2478    return emitOpError("expected expanded type to be ")2479           << *expectedResultType << " but found " << resultType;2480 2481  if ((int64_t)getStaticOutputShape().size() != resultType.getRank())2482    return emitOpError("expected number of static shape bounds to be equal to "2483                       "the output rank (")2484           << resultType.getRank() << ") but found "2485           << getStaticOutputShape().size() << " inputs instead";2486 2487  if ((int64_t)getOutputShape().size() !=2488      llvm::count(getStaticOutputShape(), ShapedType::kDynamic))2489    return emitOpError("mismatch in dynamic dims in output_shape and "2490                       "static_output_shape: static_output_shape has ")2491           << llvm::count(getStaticOutputShape(), ShapedType::kDynamic)2492           << " dynamic dims while output_shape has " << getOutputShape().size()2493           << " values";2494 2495  // Verify if provided output shapes are in agreement with output type.2496  DenseI64ArrayAttr staticOutputShapes = getStaticOutputShapeAttr();2497  ArrayRef<int64_t> resShape = getResult().getType().getShape();2498  for (auto [pos, shape] : llvm::enumerate(resShape)) {2499    if (ShapedType::isStatic(shape) && shape != staticOutputShapes[pos]) {2500      return emitOpError("invalid output shape provided at pos ") << pos;2501    }2502  }2503 2504  return success();2505}2506 2507void ExpandShapeOp::getCanonicalizationPatterns(RewritePatternSet &results,2508                                                MLIRContext *context) {2509  results.add<2510      ComposeReassociativeReshapeOps<ExpandShapeOp, ReshapeOpKind::kExpand>,2511      ComposeExpandOfCollapseOp<ExpandShapeOp, CollapseShapeOp>>(context);2512}2513 2514FailureOr<std::optional<SmallVector<Value>>>2515ExpandShapeOp::bubbleDownCasts(OpBuilder &builder) {2516  return bubbleDownCastsPassthroughOpImpl(*this, builder, getSrcMutable());2517}2518 2519/// Compute the layout map after collapsing a given source MemRef type with the2520/// specified reassociation indices.2521///2522/// Note: All collapsed dims in a reassociation group must be contiguous. It is2523/// not possible to check this by inspecting a MemRefType in the general case.2524/// If non-contiguity cannot be checked statically, the collapse is assumed to2525/// be valid (and thus accepted by this function) unless `strict = true`.2526static FailureOr<StridedLayoutAttr>2527computeCollapsedLayoutMap(MemRefType srcType,2528                          ArrayRef<ReassociationIndices> reassociation,2529                          bool strict = false) {2530  int64_t srcOffset;2531  SmallVector<int64_t> srcStrides;2532  auto srcShape = srcType.getShape();2533  if (failed(srcType.getStridesAndOffset(srcStrides, srcOffset)))2534    return failure();2535 2536  // The result stride of a reassociation group is the stride of the last entry2537  // of the reassociation. (TODO: Should be the minimum stride in the2538  // reassociation because strides are not necessarily sorted. E.g., when using2539  // memref.transpose.) Dimensions of size 1 should be skipped, because their2540  // strides are meaningless and could have any arbitrary value.2541  SmallVector<int64_t> resultStrides;2542  resultStrides.reserve(reassociation.size());2543  for (const ReassociationIndices &reassoc : reassociation) {2544    ArrayRef<int64_t> ref = llvm::ArrayRef(reassoc);2545    while (srcShape[ref.back()] == 1 && ref.size() > 1)2546      ref = ref.drop_back();2547    if (ShapedType::isStatic(srcShape[ref.back()]) || ref.size() == 1) {2548      resultStrides.push_back(srcStrides[ref.back()]);2549    } else {2550      // Dynamically-sized dims may turn out to be dims of size 1 at runtime, so2551      // the corresponding stride may have to be skipped. (See above comment.)2552      // Therefore, the result stride cannot be statically determined and must2553      // be dynamic.2554      resultStrides.push_back(ShapedType::kDynamic);2555    }2556  }2557 2558  // Validate that each reassociation group is contiguous.2559  unsigned resultStrideIndex = resultStrides.size() - 1;2560  for (const ReassociationIndices &reassoc : llvm::reverse(reassociation)) {2561    auto trailingReassocs = ArrayRef<int64_t>(reassoc).drop_front();2562    auto stride = SaturatedInteger::wrap(resultStrides[resultStrideIndex--]);2563    for (int64_t idx : llvm::reverse(trailingReassocs)) {2564      stride = stride * SaturatedInteger::wrap(srcShape[idx]);2565 2566      // Both source and result stride must have the same static value. In that2567      // case, we can be sure, that the dimensions are collapsible (because they2568      // are contiguous).2569      // If `strict = false` (default during op verification), we accept cases2570      // where one or both strides are dynamic. This is best effort: We reject2571      // ops where obviously non-contiguous dims are collapsed, but accept ops2572      // where we cannot be sure statically. Such ops may fail at runtime. See2573      // the op documentation for details.2574      auto srcStride = SaturatedInteger::wrap(srcStrides[idx - 1]);2575      if (strict && (stride.saturated || srcStride.saturated))2576        return failure();2577 2578      // Dimensions of size 1 should be skipped, because their strides are2579      // meaningless and could have any arbitrary value.2580      if (srcShape[idx - 1] == 1)2581        continue;2582 2583      if (!stride.saturated && !srcStride.saturated && stride != srcStride)2584        return failure();2585    }2586  }2587  return StridedLayoutAttr::get(srcType.getContext(), srcOffset, resultStrides);2588}2589 2590bool CollapseShapeOp::isGuaranteedCollapsible(2591    MemRefType srcType, ArrayRef<ReassociationIndices> reassociation) {2592  // MemRefs with identity layout are always collapsible.2593  if (srcType.getLayout().isIdentity())2594    return true;2595 2596  return succeeded(computeCollapsedLayoutMap(srcType, reassociation,2597                                             /*strict=*/true));2598}2599 2600MemRefType CollapseShapeOp::computeCollapsedType(2601    MemRefType srcType, ArrayRef<ReassociationIndices> reassociation) {2602  SmallVector<int64_t> resultShape;2603  resultShape.reserve(reassociation.size());2604  for (const ReassociationIndices &group : reassociation) {2605    auto groupSize = SaturatedInteger::wrap(1);2606    for (int64_t srcDim : group)2607      groupSize =2608          groupSize * SaturatedInteger::wrap(srcType.getDimSize(srcDim));2609    resultShape.push_back(groupSize.asInteger());2610  }2611 2612  if (srcType.getLayout().isIdentity()) {2613    // If the source is contiguous (i.e., no layout map specified), so is the2614    // result.2615    MemRefLayoutAttrInterface layout;2616    return MemRefType::get(resultShape, srcType.getElementType(), layout,2617                           srcType.getMemorySpace());2618  }2619 2620  // Source may not be fully contiguous. Compute the layout map.2621  // Note: Dimensions that are collapsed into a single dim are assumed to be2622  // contiguous.2623  FailureOr<StridedLayoutAttr> computedLayout =2624      computeCollapsedLayoutMap(srcType, reassociation);2625  assert(succeeded(computedLayout) &&2626         "invalid source layout map or collapsing non-contiguous dims");2627  return MemRefType::get(resultShape, srcType.getElementType(), *computedLayout,2628                         srcType.getMemorySpace());2629}2630 2631void CollapseShapeOp::build(OpBuilder &b, OperationState &result, Value src,2632                            ArrayRef<ReassociationIndices> reassociation,2633                            ArrayRef<NamedAttribute> attrs) {2634  auto srcType = llvm::cast<MemRefType>(src.getType());2635  MemRefType resultType =2636      CollapseShapeOp::computeCollapsedType(srcType, reassociation);2637  result.addAttribute(::mlir::getReassociationAttrName(),2638                      getReassociationIndicesAttribute(b, reassociation));2639  build(b, result, resultType, src, attrs);2640}2641 2642LogicalResult CollapseShapeOp::verify() {2643  MemRefType srcType = getSrcType();2644  MemRefType resultType = getResultType();2645 2646  if (srcType.getRank() < resultType.getRank()) {2647    auto r0 = srcType.getRank();2648    auto r1 = resultType.getRank();2649    return emitOpError("has source rank ")2650           << r0 << " and result rank " << r1 << ". This is not a collapse ("2651           << r0 << " < " << r1 << ").";2652  }2653 2654  // Verify result shape.2655  if (failed(verifyCollapsedShape(getOperation(), resultType.getShape(),2656                                  srcType.getShape(), getReassociationIndices(),2657                                  /*allowMultipleDynamicDimsPerGroup=*/true)))2658    return failure();2659 2660  // Compute expected result type (including layout map).2661  MemRefType expectedResultType;2662  if (srcType.getLayout().isIdentity()) {2663    // If the source is contiguous (i.e., no layout map specified), so is the2664    // result.2665    MemRefLayoutAttrInterface layout;2666    expectedResultType =2667        MemRefType::get(resultType.getShape(), srcType.getElementType(), layout,2668                        srcType.getMemorySpace());2669  } else {2670    // Source may not be fully contiguous. Compute the layout map.2671    // Note: Dimensions that are collapsed into a single dim are assumed to be2672    // contiguous.2673    FailureOr<StridedLayoutAttr> computedLayout =2674        computeCollapsedLayoutMap(srcType, getReassociationIndices());2675    if (failed(computedLayout))2676      return emitOpError(2677          "invalid source layout map or collapsing non-contiguous dims");2678    expectedResultType =2679        MemRefType::get(resultType.getShape(), srcType.getElementType(),2680                        *computedLayout, srcType.getMemorySpace());2681  }2682 2683  if (expectedResultType != resultType)2684    return emitOpError("expected collapsed type to be ")2685           << expectedResultType << " but found " << resultType;2686 2687  return success();2688}2689 2690struct CollapseShapeOpMemRefCastFolder2691    : public OpRewritePattern<CollapseShapeOp> {2692public:2693  using OpRewritePattern<CollapseShapeOp>::OpRewritePattern;2694 2695  LogicalResult matchAndRewrite(CollapseShapeOp op,2696                                PatternRewriter &rewriter) const override {2697    auto cast = op.getOperand().getDefiningOp<CastOp>();2698    if (!cast)2699      return failure();2700 2701    if (!CastOp::canFoldIntoConsumerOp(cast))2702      return failure();2703 2704    Type newResultType = CollapseShapeOp::computeCollapsedType(2705        llvm::cast<MemRefType>(cast.getOperand().getType()),2706        op.getReassociationIndices());2707 2708    if (newResultType == op.getResultType()) {2709      rewriter.modifyOpInPlace(2710          op, [&]() { op.getSrcMutable().assign(cast.getSource()); });2711    } else {2712      Value newOp =2713          CollapseShapeOp::create(rewriter, op->getLoc(), cast.getSource(),2714                                  op.getReassociationIndices());2715      rewriter.replaceOpWithNewOp<CastOp>(op, op.getType(), newOp);2716    }2717    return success();2718  }2719};2720 2721void CollapseShapeOp::getCanonicalizationPatterns(RewritePatternSet &results,2722                                                  MLIRContext *context) {2723  results.add<2724      ComposeReassociativeReshapeOps<CollapseShapeOp, ReshapeOpKind::kCollapse>,2725      ComposeCollapseOfExpandOp<CollapseShapeOp, ExpandShapeOp, CastOp,2726                                memref::DimOp, MemRefType>,2727      CollapseShapeOpMemRefCastFolder>(context);2728}2729 2730OpFoldResult ExpandShapeOp::fold(FoldAdaptor adaptor) {2731  return foldReshapeOp<ExpandShapeOp, CollapseShapeOp>(*this,2732                                                       adaptor.getOperands());2733}2734 2735OpFoldResult CollapseShapeOp::fold(FoldAdaptor adaptor) {2736  return foldReshapeOp<CollapseShapeOp, ExpandShapeOp>(*this,2737                                                       adaptor.getOperands());2738}2739 2740FailureOr<std::optional<SmallVector<Value>>>2741CollapseShapeOp::bubbleDownCasts(OpBuilder &builder) {2742  return bubbleDownCastsPassthroughOpImpl(*this, builder, getSrcMutable());2743}2744 2745//===----------------------------------------------------------------------===//2746// ReshapeOp2747//===----------------------------------------------------------------------===//2748 2749void ReshapeOp::getAsmResultNames(2750    function_ref<void(Value, StringRef)> setNameFn) {2751  setNameFn(getResult(), "reshape");2752}2753 2754LogicalResult ReshapeOp::verify() {2755  Type operandType = getSource().getType();2756  Type resultType = getResult().getType();2757 2758  Type operandElementType =2759      llvm::cast<ShapedType>(operandType).getElementType();2760  Type resultElementType = llvm::cast<ShapedType>(resultType).getElementType();2761  if (operandElementType != resultElementType)2762    return emitOpError("element types of source and destination memref "2763                       "types should be the same");2764 2765  if (auto operandMemRefType = llvm::dyn_cast<MemRefType>(operandType))2766    if (!operandMemRefType.getLayout().isIdentity())2767      return emitOpError("source memref type should have identity affine map");2768 2769  int64_t shapeSize =2770      llvm::cast<MemRefType>(getShape().getType()).getDimSize(0);2771  auto resultMemRefType = llvm::dyn_cast<MemRefType>(resultType);2772  if (resultMemRefType) {2773    if (!resultMemRefType.getLayout().isIdentity())2774      return emitOpError("result memref type should have identity affine map");2775    if (shapeSize == ShapedType::kDynamic)2776      return emitOpError("cannot use shape operand with dynamic length to "2777                         "reshape to statically-ranked memref type");2778    if (shapeSize != resultMemRefType.getRank())2779      return emitOpError(2780          "length of shape operand differs from the result's memref rank");2781  }2782  return success();2783}2784 2785FailureOr<std::optional<SmallVector<Value>>>2786ReshapeOp::bubbleDownCasts(OpBuilder &builder) {2787  return bubbleDownCastsPassthroughOpImpl(*this, builder, getSourceMutable());2788}2789 2790//===----------------------------------------------------------------------===//2791// StoreOp2792//===----------------------------------------------------------------------===//2793 2794LogicalResult StoreOp::verify() {2795  if (getNumOperands() != 2 + getMemRefType().getRank())2796    return emitOpError("store index operand count not equal to memref rank");2797 2798  return success();2799}2800 2801LogicalResult StoreOp::fold(FoldAdaptor adaptor,2802                            SmallVectorImpl<OpFoldResult> &results) {2803  /// store(memrefcast) -> store2804  return foldMemRefCast(*this, getValueToStore());2805}2806 2807FailureOr<std::optional<SmallVector<Value>>>2808StoreOp::bubbleDownCasts(OpBuilder &builder) {2809  return mlir::detail::bubbleDownInPlaceMemorySpaceCastImpl(getMemrefMutable(),2810                                                            ValueRange());2811}2812 2813//===----------------------------------------------------------------------===//2814// SubViewOp2815//===----------------------------------------------------------------------===//2816 2817void SubViewOp::getAsmResultNames(2818    function_ref<void(Value, StringRef)> setNameFn) {2819  setNameFn(getResult(), "subview");2820}2821 2822/// A subview result type can be fully inferred from the source type and the2823/// static representation of offsets, sizes and strides. Special sentinels2824/// encode the dynamic case.2825MemRefType SubViewOp::inferResultType(MemRefType sourceMemRefType,2826                                      ArrayRef<int64_t> staticOffsets,2827                                      ArrayRef<int64_t> staticSizes,2828                                      ArrayRef<int64_t> staticStrides) {2829  unsigned rank = sourceMemRefType.getRank();2830  (void)rank;2831  assert(staticOffsets.size() == rank && "staticOffsets length mismatch");2832  assert(staticSizes.size() == rank && "staticSizes length mismatch");2833  assert(staticStrides.size() == rank && "staticStrides length mismatch");2834 2835  // Extract source offset and strides.2836  auto [sourceStrides, sourceOffset] = sourceMemRefType.getStridesAndOffset();2837 2838  // Compute target offset whose value is:2839  //   `sourceOffset + sum_i(staticOffset_i * sourceStrides_i)`.2840  int64_t targetOffset = sourceOffset;2841  for (auto it : llvm::zip(staticOffsets, sourceStrides)) {2842    auto staticOffset = std::get<0>(it), sourceStride = std::get<1>(it);2843    targetOffset = (SaturatedInteger::wrap(targetOffset) +2844                    SaturatedInteger::wrap(staticOffset) *2845                        SaturatedInteger::wrap(sourceStride))2846                       .asInteger();2847  }2848 2849  // Compute target stride whose value is:2850  //   `sourceStrides_i * staticStrides_i`.2851  SmallVector<int64_t, 4> targetStrides;2852  targetStrides.reserve(staticOffsets.size());2853  for (auto it : llvm::zip(sourceStrides, staticStrides)) {2854    auto sourceStride = std::get<0>(it), staticStride = std::get<1>(it);2855    targetStrides.push_back((SaturatedInteger::wrap(sourceStride) *2856                             SaturatedInteger::wrap(staticStride))2857                                .asInteger());2858  }2859 2860  // The type is now known.2861  return MemRefType::get(staticSizes, sourceMemRefType.getElementType(),2862                         StridedLayoutAttr::get(sourceMemRefType.getContext(),2863                                                targetOffset, targetStrides),2864                         sourceMemRefType.getMemorySpace());2865}2866 2867MemRefType SubViewOp::inferResultType(MemRefType sourceMemRefType,2868                                      ArrayRef<OpFoldResult> offsets,2869                                      ArrayRef<OpFoldResult> sizes,2870                                      ArrayRef<OpFoldResult> strides) {2871  SmallVector<int64_t> staticOffsets, staticSizes, staticStrides;2872  SmallVector<Value> dynamicOffsets, dynamicSizes, dynamicStrides;2873  dispatchIndexOpFoldResults(offsets, dynamicOffsets, staticOffsets);2874  dispatchIndexOpFoldResults(sizes, dynamicSizes, staticSizes);2875  dispatchIndexOpFoldResults(strides, dynamicStrides, staticStrides);2876  if (!hasValidSizesOffsets(staticOffsets))2877    return {};2878  if (!hasValidSizesOffsets(staticSizes))2879    return {};2880  if (!hasValidStrides(staticStrides))2881    return {};2882  return SubViewOp::inferResultType(sourceMemRefType, staticOffsets,2883                                    staticSizes, staticStrides);2884}2885 2886MemRefType SubViewOp::inferRankReducedResultType(2887    ArrayRef<int64_t> resultShape, MemRefType sourceRankedTensorType,2888    ArrayRef<int64_t> offsets, ArrayRef<int64_t> sizes,2889    ArrayRef<int64_t> strides) {2890  MemRefType inferredType =2891      inferResultType(sourceRankedTensorType, offsets, sizes, strides);2892  assert(inferredType.getRank() >= static_cast<int64_t>(resultShape.size()) &&2893         "expected ");2894  if (inferredType.getRank() == static_cast<int64_t>(resultShape.size()))2895    return inferredType;2896 2897  // Compute which dimensions are dropped.2898  std::optional<llvm::SmallDenseSet<unsigned>> dimsToProject =2899      computeRankReductionMask(inferredType.getShape(), resultShape);2900  assert(dimsToProject.has_value() && "invalid rank reduction");2901 2902  // Compute the layout and result type.2903  auto inferredLayout = llvm::cast<StridedLayoutAttr>(inferredType.getLayout());2904  SmallVector<int64_t> rankReducedStrides;2905  rankReducedStrides.reserve(resultShape.size());2906  for (auto [idx, value] : llvm::enumerate(inferredLayout.getStrides())) {2907    if (!dimsToProject->contains(idx))2908      rankReducedStrides.push_back(value);2909  }2910  return MemRefType::get(resultShape, inferredType.getElementType(),2911                         StridedLayoutAttr::get(inferredLayout.getContext(),2912                                                inferredLayout.getOffset(),2913                                                rankReducedStrides),2914                         inferredType.getMemorySpace());2915}2916 2917MemRefType SubViewOp::inferRankReducedResultType(2918    ArrayRef<int64_t> resultShape, MemRefType sourceRankedTensorType,2919    ArrayRef<OpFoldResult> offsets, ArrayRef<OpFoldResult> sizes,2920    ArrayRef<OpFoldResult> strides) {2921  SmallVector<int64_t> staticOffsets, staticSizes, staticStrides;2922  SmallVector<Value> dynamicOffsets, dynamicSizes, dynamicStrides;2923  dispatchIndexOpFoldResults(offsets, dynamicOffsets, staticOffsets);2924  dispatchIndexOpFoldResults(sizes, dynamicSizes, staticSizes);2925  dispatchIndexOpFoldResults(strides, dynamicStrides, staticStrides);2926  return SubViewOp::inferRankReducedResultType(2927      resultShape, sourceRankedTensorType, staticOffsets, staticSizes,2928      staticStrides);2929}2930 2931// Build a SubViewOp with mixed static and dynamic entries and custom result2932// type. If the type passed is nullptr, it is inferred.2933void SubViewOp::build(OpBuilder &b, OperationState &result,2934                      MemRefType resultType, Value source,2935                      ArrayRef<OpFoldResult> offsets,2936                      ArrayRef<OpFoldResult> sizes,2937                      ArrayRef<OpFoldResult> strides,2938                      ArrayRef<NamedAttribute> attrs) {2939  SmallVector<int64_t> staticOffsets, staticSizes, staticStrides;2940  SmallVector<Value> dynamicOffsets, dynamicSizes, dynamicStrides;2941  dispatchIndexOpFoldResults(offsets, dynamicOffsets, staticOffsets);2942  dispatchIndexOpFoldResults(sizes, dynamicSizes, staticSizes);2943  dispatchIndexOpFoldResults(strides, dynamicStrides, staticStrides);2944  auto sourceMemRefType = llvm::cast<MemRefType>(source.getType());2945  // Structuring implementation this way avoids duplication between builders.2946  if (!resultType) {2947    resultType = SubViewOp::inferResultType(sourceMemRefType, staticOffsets,2948                                            staticSizes, staticStrides);2949  }2950  result.addAttributes(attrs);2951  build(b, result, resultType, source, dynamicOffsets, dynamicSizes,2952        dynamicStrides, b.getDenseI64ArrayAttr(staticOffsets),2953        b.getDenseI64ArrayAttr(staticSizes),2954        b.getDenseI64ArrayAttr(staticStrides));2955}2956 2957// Build a SubViewOp with mixed static and dynamic entries and inferred result2958// type.2959void SubViewOp::build(OpBuilder &b, OperationState &result, Value source,2960                      ArrayRef<OpFoldResult> offsets,2961                      ArrayRef<OpFoldResult> sizes,2962                      ArrayRef<OpFoldResult> strides,2963                      ArrayRef<NamedAttribute> attrs) {2964  build(b, result, MemRefType(), source, offsets, sizes, strides, attrs);2965}2966 2967// Build a SubViewOp with static entries and inferred result type.2968void SubViewOp::build(OpBuilder &b, OperationState &result, Value source,2969                      ArrayRef<int64_t> offsets, ArrayRef<int64_t> sizes,2970                      ArrayRef<int64_t> strides,2971                      ArrayRef<NamedAttribute> attrs) {2972  SmallVector<OpFoldResult> offsetValues = llvm::to_vector<4>(2973      llvm::map_range(offsets, [&](int64_t v) -> OpFoldResult {2974        return b.getI64IntegerAttr(v);2975      }));2976  SmallVector<OpFoldResult> sizeValues =2977      llvm::to_vector<4>(llvm::map_range(sizes, [&](int64_t v) -> OpFoldResult {2978        return b.getI64IntegerAttr(v);2979      }));2980  SmallVector<OpFoldResult> strideValues = llvm::to_vector<4>(2981      llvm::map_range(strides, [&](int64_t v) -> OpFoldResult {2982        return b.getI64IntegerAttr(v);2983      }));2984  build(b, result, source, offsetValues, sizeValues, strideValues, attrs);2985}2986 2987// Build a SubViewOp with dynamic entries and custom result type. If the2988// type passed is nullptr, it is inferred.2989void SubViewOp::build(OpBuilder &b, OperationState &result,2990                      MemRefType resultType, Value source,2991                      ArrayRef<int64_t> offsets, ArrayRef<int64_t> sizes,2992                      ArrayRef<int64_t> strides,2993                      ArrayRef<NamedAttribute> attrs) {2994  SmallVector<OpFoldResult> offsetValues = llvm::to_vector<4>(2995      llvm::map_range(offsets, [&](int64_t v) -> OpFoldResult {2996        return b.getI64IntegerAttr(v);2997      }));2998  SmallVector<OpFoldResult> sizeValues =2999      llvm::to_vector<4>(llvm::map_range(sizes, [&](int64_t v) -> OpFoldResult {3000        return b.getI64IntegerAttr(v);3001      }));3002  SmallVector<OpFoldResult> strideValues = llvm::to_vector<4>(3003      llvm::map_range(strides, [&](int64_t v) -> OpFoldResult {3004        return b.getI64IntegerAttr(v);3005      }));3006  build(b, result, resultType, source, offsetValues, sizeValues, strideValues,3007        attrs);3008}3009 3010// Build a SubViewOp with dynamic entries and custom result type. If the type3011// passed is nullptr, it is inferred.3012void SubViewOp::build(OpBuilder &b, OperationState &result,3013                      MemRefType resultType, Value source, ValueRange offsets,3014                      ValueRange sizes, ValueRange strides,3015                      ArrayRef<NamedAttribute> attrs) {3016  SmallVector<OpFoldResult> offsetValues = llvm::to_vector<4>(3017      llvm::map_range(offsets, [](Value v) -> OpFoldResult { return v; }));3018  SmallVector<OpFoldResult> sizeValues = llvm::to_vector<4>(3019      llvm::map_range(sizes, [](Value v) -> OpFoldResult { return v; }));3020  SmallVector<OpFoldResult> strideValues = llvm::to_vector<4>(3021      llvm::map_range(strides, [](Value v) -> OpFoldResult { return v; }));3022  build(b, result, resultType, source, offsetValues, sizeValues, strideValues);3023}3024 3025// Build a SubViewOp with dynamic entries and inferred result type.3026void SubViewOp::build(OpBuilder &b, OperationState &result, Value source,3027                      ValueRange offsets, ValueRange sizes, ValueRange strides,3028                      ArrayRef<NamedAttribute> attrs) {3029  build(b, result, MemRefType(), source, offsets, sizes, strides, attrs);3030}3031 3032/// For ViewLikeOpInterface.3033Value SubViewOp::getViewSource() { return getSource(); }3034 3035/// Return true if `t1` and `t2` have equal offsets (both dynamic or of same3036/// static value).3037static bool haveCompatibleOffsets(MemRefType t1, MemRefType t2) {3038  int64_t t1Offset, t2Offset;3039  SmallVector<int64_t> t1Strides, t2Strides;3040  auto res1 = t1.getStridesAndOffset(t1Strides, t1Offset);3041  auto res2 = t2.getStridesAndOffset(t2Strides, t2Offset);3042  return succeeded(res1) && succeeded(res2) && t1Offset == t2Offset;3043}3044 3045/// Return true if `t1` and `t2` have equal strides (both dynamic or of same3046/// static value). Dimensions of `t1` may be dropped in `t2`; these must be3047/// marked as dropped in `droppedDims`.3048static bool haveCompatibleStrides(MemRefType t1, MemRefType t2,3049                                  const llvm::SmallBitVector &droppedDims) {3050  assert(size_t(t1.getRank()) == droppedDims.size() &&3051         "incorrect number of bits");3052  assert(size_t(t1.getRank() - t2.getRank()) == droppedDims.count() &&3053         "incorrect number of dropped dims");3054  int64_t t1Offset, t2Offset;3055  SmallVector<int64_t> t1Strides, t2Strides;3056  auto res1 = t1.getStridesAndOffset(t1Strides, t1Offset);3057  auto res2 = t2.getStridesAndOffset(t2Strides, t2Offset);3058  if (failed(res1) || failed(res2))3059    return false;3060  for (int64_t i = 0, j = 0, e = t1.getRank(); i < e; ++i) {3061    if (droppedDims[i])3062      continue;3063    if (t1Strides[i] != t2Strides[j])3064      return false;3065    ++j;3066  }3067  return true;3068}3069 3070static LogicalResult produceSubViewErrorMsg(SliceVerificationResult result,3071                                            SubViewOp op, Type expectedType) {3072  auto memrefType = llvm::cast<ShapedType>(expectedType);3073  switch (result) {3074  case SliceVerificationResult::Success:3075    return success();3076  case SliceVerificationResult::RankTooLarge:3077    return op->emitError("expected result rank to be smaller or equal to ")3078           << "the source rank, but got " << op.getType();3079  case SliceVerificationResult::SizeMismatch:3080    return op->emitError("expected result type to be ")3081           << expectedType3082           << " or a rank-reduced version. (mismatch of result sizes), but got "3083           << op.getType();3084  case SliceVerificationResult::ElemTypeMismatch:3085    return op->emitError("expected result element type to be ")3086           << memrefType.getElementType() << ", but got " << op.getType();3087  case SliceVerificationResult::MemSpaceMismatch:3088    return op->emitError(3089               "expected result and source memory spaces to match, but got ")3090           << op.getType();3091  case SliceVerificationResult::LayoutMismatch:3092    return op->emitError("expected result type to be ")3093           << expectedType3094           << " or a rank-reduced version. (mismatch of result layout), but "3095              "got "3096           << op.getType();3097  }3098  llvm_unreachable("unexpected subview verification result");3099}3100 3101/// Verifier for SubViewOp.3102LogicalResult SubViewOp::verify() {3103  MemRefType baseType = getSourceType();3104  MemRefType subViewType = getType();3105  ArrayRef<int64_t> staticOffsets = getStaticOffsets();3106  ArrayRef<int64_t> staticSizes = getStaticSizes();3107  ArrayRef<int64_t> staticStrides = getStaticStrides();3108 3109  // The base memref and the view memref should be in the same memory space.3110  if (baseType.getMemorySpace() != subViewType.getMemorySpace())3111    return emitError("different memory spaces specified for base memref "3112                     "type ")3113           << baseType << " and subview memref type " << subViewType;3114 3115  // Verify that the base memref type has a strided layout map.3116  if (!baseType.isStrided())3117    return emitError("base type ") << baseType << " is not strided";3118 3119  // Compute the expected result type, assuming that there are no rank3120  // reductions.3121  MemRefType expectedType = SubViewOp::inferResultType(3122      baseType, staticOffsets, staticSizes, staticStrides);3123 3124  // Verify all properties of a shaped type: rank, element type and dimension3125  // sizes. This takes into account potential rank reductions.3126  auto shapedTypeVerification = isRankReducedType(3127      /*originalType=*/expectedType, /*candidateReducedType=*/subViewType);3128  if (shapedTypeVerification != SliceVerificationResult::Success)3129    return produceSubViewErrorMsg(shapedTypeVerification, *this, expectedType);3130 3131  // Make sure that the memory space did not change.3132  if (expectedType.getMemorySpace() != subViewType.getMemorySpace())3133    return produceSubViewErrorMsg(SliceVerificationResult::MemSpaceMismatch,3134                                  *this, expectedType);3135 3136  // Verify the offset of the layout map.3137  if (!haveCompatibleOffsets(expectedType, subViewType))3138    return produceSubViewErrorMsg(SliceVerificationResult::LayoutMismatch,3139                                  *this, expectedType);3140 3141  // The only thing that's left to verify now are the strides. First, compute3142  // the unused dimensions due to rank reductions. We have to look at sizes and3143  // strides to decide which dimensions were dropped. This function also3144  // partially verifies strides in case of rank reductions.3145  auto unusedDims = computeMemRefRankReductionMask(expectedType, subViewType,3146                                                   getMixedSizes());3147  if (failed(unusedDims))3148    return produceSubViewErrorMsg(SliceVerificationResult::LayoutMismatch,3149                                  *this, expectedType);3150 3151  // Strides must match.3152  if (!haveCompatibleStrides(expectedType, subViewType, *unusedDims))3153    return produceSubViewErrorMsg(SliceVerificationResult::LayoutMismatch,3154                                  *this, expectedType);3155 3156  // Verify that offsets, sizes, strides do not run out-of-bounds with respect3157  // to the base memref.3158  SliceBoundsVerificationResult boundsResult =3159      verifyInBoundsSlice(baseType.getShape(), staticOffsets, staticSizes,3160                          staticStrides, /*generateErrorMessage=*/true);3161  if (!boundsResult.isValid)3162    return getOperation()->emitError(boundsResult.errorMessage);3163 3164  return success();3165}3166 3167raw_ostream &mlir::operator<<(raw_ostream &os, const Range &range) {3168  return os << "range " << range.offset << ":" << range.size << ":"3169            << range.stride;3170}3171 3172/// Return the list of Range (i.e. offset, size, stride). Each Range3173/// entry contains either the dynamic value or a ConstantIndexOp constructed3174/// with `b` at location `loc`.3175SmallVector<Range, 8> mlir::getOrCreateRanges(OffsetSizeAndStrideOpInterface op,3176                                              OpBuilder &b, Location loc) {3177  std::array<unsigned, 3> ranks = op.getArrayAttrMaxRanks();3178  assert(ranks[0] == ranks[1] && "expected offset and sizes of equal ranks");3179  assert(ranks[1] == ranks[2] && "expected sizes and strides of equal ranks");3180  SmallVector<Range, 8> res;3181  unsigned rank = ranks[0];3182  res.reserve(rank);3183  for (unsigned idx = 0; idx < rank; ++idx) {3184    Value offset =3185        op.isDynamicOffset(idx)3186            ? op.getDynamicOffset(idx)3187            : arith::ConstantIndexOp::create(b, loc, op.getStaticOffset(idx));3188    Value size =3189        op.isDynamicSize(idx)3190            ? op.getDynamicSize(idx)3191            : arith::ConstantIndexOp::create(b, loc, op.getStaticSize(idx));3192    Value stride =3193        op.isDynamicStride(idx)3194            ? op.getDynamicStride(idx)3195            : arith::ConstantIndexOp::create(b, loc, op.getStaticStride(idx));3196    res.emplace_back(Range{offset, size, stride});3197  }3198  return res;3199}3200 3201/// Compute the canonical result type of a SubViewOp. Call `inferResultType`3202/// to deduce the result type for the given `sourceType`. Additionally, reduce3203/// the rank of the inferred result type if `currentResultType` is lower rank3204/// than `currentSourceType`. Use this signature if `sourceType` is updated3205/// together with the result type. In this case, it is important to compute3206/// the dropped dimensions using `currentSourceType` whose strides align with3207/// `currentResultType`.3208static MemRefType getCanonicalSubViewResultType(3209    MemRefType currentResultType, MemRefType currentSourceType,3210    MemRefType sourceType, ArrayRef<OpFoldResult> mixedOffsets,3211    ArrayRef<OpFoldResult> mixedSizes, ArrayRef<OpFoldResult> mixedStrides) {3212  MemRefType nonRankReducedType = SubViewOp::inferResultType(3213      sourceType, mixedOffsets, mixedSizes, mixedStrides);3214  FailureOr<llvm::SmallBitVector> unusedDims = computeMemRefRankReductionMask(3215      currentSourceType, currentResultType, mixedSizes);3216  if (failed(unusedDims))3217    return nullptr;3218 3219  auto layout = llvm::cast<StridedLayoutAttr>(nonRankReducedType.getLayout());3220  SmallVector<int64_t> shape, strides;3221  unsigned numDimsAfterReduction =3222      nonRankReducedType.getRank() - unusedDims->count();3223  shape.reserve(numDimsAfterReduction);3224  strides.reserve(numDimsAfterReduction);3225  for (const auto &[idx, size, stride] :3226       llvm::zip(llvm::seq<unsigned>(0, nonRankReducedType.getRank()),3227                 nonRankReducedType.getShape(), layout.getStrides())) {3228    if (unusedDims->test(idx))3229      continue;3230    shape.push_back(size);3231    strides.push_back(stride);3232  }3233 3234  return MemRefType::get(shape, nonRankReducedType.getElementType(),3235                         StridedLayoutAttr::get(sourceType.getContext(),3236                                                layout.getOffset(), strides),3237                         nonRankReducedType.getMemorySpace());3238}3239 3240Value mlir::memref::createCanonicalRankReducingSubViewOp(3241    OpBuilder &b, Location loc, Value memref, ArrayRef<int64_t> targetShape) {3242  auto memrefType = llvm::cast<MemRefType>(memref.getType());3243  unsigned rank = memrefType.getRank();3244  SmallVector<OpFoldResult> offsets(rank, b.getIndexAttr(0));3245  SmallVector<OpFoldResult> sizes = getMixedSizes(b, loc, memref);3246  SmallVector<OpFoldResult> strides(rank, b.getIndexAttr(1));3247  MemRefType targetType = SubViewOp::inferRankReducedResultType(3248      targetShape, memrefType, offsets, sizes, strides);3249  return b.createOrFold<memref::SubViewOp>(loc, targetType, memref, offsets,3250                                           sizes, strides);3251}3252 3253FailureOr<Value> SubViewOp::rankReduceIfNeeded(OpBuilder &b, Location loc,3254                                               Value value,3255                                               ArrayRef<int64_t> desiredShape) {3256  auto sourceMemrefType = llvm::dyn_cast<MemRefType>(value.getType());3257  assert(sourceMemrefType && "not a ranked memref type");3258  auto sourceShape = sourceMemrefType.getShape();3259  if (sourceShape.equals(desiredShape))3260    return value;3261  auto maybeRankReductionMask =3262      mlir::computeRankReductionMask(sourceShape, desiredShape);3263  if (!maybeRankReductionMask)3264    return failure();3265  return createCanonicalRankReducingSubViewOp(b, loc, value, desiredShape);3266}3267 3268/// Helper method to check if a `subview` operation is trivially a no-op. This3269/// is the case if the all offsets are zero, all strides are 1, and the source3270/// shape is same as the size of the subview. In such cases, the subview can3271/// be folded into its source.3272static bool isTrivialSubViewOp(SubViewOp subViewOp) {3273  if (subViewOp.getSourceType().getRank() != subViewOp.getType().getRank())3274    return false;3275 3276  auto mixedOffsets = subViewOp.getMixedOffsets();3277  auto mixedSizes = subViewOp.getMixedSizes();3278  auto mixedStrides = subViewOp.getMixedStrides();3279 3280  // Check offsets are zero.3281  if (llvm::any_of(mixedOffsets, [](OpFoldResult ofr) {3282        std::optional<int64_t> intValue = getConstantIntValue(ofr);3283        return !intValue || intValue.value() != 0;3284      }))3285    return false;3286 3287  // Check strides are one.3288  if (llvm::any_of(mixedStrides, [](OpFoldResult ofr) {3289        std::optional<int64_t> intValue = getConstantIntValue(ofr);3290        return !intValue || intValue.value() != 1;3291      }))3292    return false;3293 3294  // Check all size values are static and matches the (static) source shape.3295  ArrayRef<int64_t> sourceShape = subViewOp.getSourceType().getShape();3296  for (const auto &size : llvm::enumerate(mixedSizes)) {3297    std::optional<int64_t> intValue = getConstantIntValue(size.value());3298    if (!intValue || *intValue != sourceShape[size.index()])3299      return false;3300  }3301  // All conditions met. The `SubViewOp` is foldable as a no-op.3302  return true;3303}3304 3305namespace {3306/// Pattern to rewrite a subview op with MemRefCast arguments.3307/// This essentially pushes memref.cast past its consuming subview when3308/// `canFoldIntoConsumerOp` is true.3309///3310/// Example:3311/// ```3312///   %0 = memref.cast %V : memref<16x16xf32> to memref<?x?xf32>3313///   %1 = memref.subview %0[0, 0][3, 4][1, 1] :3314///     memref<?x?xf32> to memref<3x4xf32, strided<[?, 1], offset: ?>>3315/// ```3316/// is rewritten into:3317/// ```3318///   %0 = memref.subview %V: memref<16x16xf32> to memref<3x4xf32, #[[map0]]>3319///   %1 = memref.cast %0: memref<3x4xf32, strided<[16, 1], offset: 0>> to3320///     memref<3x4xf32, strided<[?, 1], offset: ?>>3321/// ```3322class SubViewOpMemRefCastFolder final : public OpRewritePattern<SubViewOp> {3323public:3324  using OpRewritePattern<SubViewOp>::OpRewritePattern;3325 3326  LogicalResult matchAndRewrite(SubViewOp subViewOp,3327                                PatternRewriter &rewriter) const override {3328    // Any constant operand, just return to let SubViewOpConstantFolder kick3329    // in.3330    if (llvm::any_of(subViewOp.getOperands(), [](Value operand) {3331          return matchPattern(operand, matchConstantIndex());3332        }))3333      return failure();3334 3335    auto castOp = subViewOp.getSource().getDefiningOp<CastOp>();3336    if (!castOp)3337      return failure();3338 3339    if (!CastOp::canFoldIntoConsumerOp(castOp))3340      return failure();3341 3342    // Compute the SubViewOp result type after folding the MemRefCastOp. Use3343    // the MemRefCastOp source operand type to infer the result type and the3344    // current SubViewOp source operand type to compute the dropped dimensions3345    // if the operation is rank-reducing.3346    auto resultType = getCanonicalSubViewResultType(3347        subViewOp.getType(), subViewOp.getSourceType(),3348        llvm::cast<MemRefType>(castOp.getSource().getType()),3349        subViewOp.getMixedOffsets(), subViewOp.getMixedSizes(),3350        subViewOp.getMixedStrides());3351    if (!resultType)3352      return failure();3353 3354    Value newSubView = SubViewOp::create(3355        rewriter, subViewOp.getLoc(), resultType, castOp.getSource(),3356        subViewOp.getOffsets(), subViewOp.getSizes(), subViewOp.getStrides(),3357        subViewOp.getStaticOffsets(), subViewOp.getStaticSizes(),3358        subViewOp.getStaticStrides());3359    rewriter.replaceOpWithNewOp<CastOp>(subViewOp, subViewOp.getType(),3360                                        newSubView);3361    return success();3362  }3363};3364 3365/// Canonicalize subview ops that are no-ops. When the source shape is not3366/// same as a result shape due to use of `affine_map`.3367class TrivialSubViewOpFolder final : public OpRewritePattern<SubViewOp> {3368public:3369  using OpRewritePattern<SubViewOp>::OpRewritePattern;3370 3371  LogicalResult matchAndRewrite(SubViewOp subViewOp,3372                                PatternRewriter &rewriter) const override {3373    if (!isTrivialSubViewOp(subViewOp))3374      return failure();3375    if (subViewOp.getSourceType() == subViewOp.getType()) {3376      rewriter.replaceOp(subViewOp, subViewOp.getSource());3377      return success();3378    }3379    rewriter.replaceOpWithNewOp<CastOp>(subViewOp, subViewOp.getType(),3380                                        subViewOp.getSource());3381    return success();3382  }3383};3384} // namespace3385 3386/// Return the canonical type of the result of a subview.3387struct SubViewReturnTypeCanonicalizer {3388  MemRefType operator()(SubViewOp op, ArrayRef<OpFoldResult> mixedOffsets,3389                        ArrayRef<OpFoldResult> mixedSizes,3390                        ArrayRef<OpFoldResult> mixedStrides) {3391    // Infer a memref type without taking into account any rank reductions.3392    MemRefType resTy = SubViewOp::inferResultType(3393        op.getSourceType(), mixedOffsets, mixedSizes, mixedStrides);3394    if (!resTy)3395      return {};3396    MemRefType nonReducedType = resTy;3397 3398    // Directly return the non-rank reduced type if there are no dropped dims.3399    llvm::SmallBitVector droppedDims = op.getDroppedDims();3400    if (droppedDims.none())3401      return nonReducedType;3402 3403    // Take the strides and offset from the non-rank reduced type.3404    auto [nonReducedStrides, offset] = nonReducedType.getStridesAndOffset();3405 3406    // Drop dims from shape and strides.3407    SmallVector<int64_t> targetShape;3408    SmallVector<int64_t> targetStrides;3409    for (int64_t i = 0; i < static_cast<int64_t>(mixedSizes.size()); ++i) {3410      if (droppedDims.test(i))3411        continue;3412      targetStrides.push_back(nonReducedStrides[i]);3413      targetShape.push_back(nonReducedType.getDimSize(i));3414    }3415 3416    return MemRefType::get(targetShape, nonReducedType.getElementType(),3417                           StridedLayoutAttr::get(nonReducedType.getContext(),3418                                                  offset, targetStrides),3419                           nonReducedType.getMemorySpace());3420  }3421};3422 3423/// A canonicalizer wrapper to replace SubViewOps.3424struct SubViewCanonicalizer {3425  void operator()(PatternRewriter &rewriter, SubViewOp op, SubViewOp newOp) {3426    rewriter.replaceOpWithNewOp<CastOp>(op, op.getType(), newOp);3427  }3428};3429 3430void SubViewOp::getCanonicalizationPatterns(RewritePatternSet &results,3431                                            MLIRContext *context) {3432  results3433      .add<OpWithOffsetSizesAndStridesConstantArgumentFolder<3434               SubViewOp, SubViewReturnTypeCanonicalizer, SubViewCanonicalizer>,3435           SubViewOpMemRefCastFolder, TrivialSubViewOpFolder>(context);3436}3437 3438OpFoldResult SubViewOp::fold(FoldAdaptor adaptor) {3439  MemRefType sourceMemrefType = getSource().getType();3440  MemRefType resultMemrefType = getResult().getType();3441  auto resultLayout =3442      dyn_cast_if_present<StridedLayoutAttr>(resultMemrefType.getLayout());3443 3444  if (resultMemrefType == sourceMemrefType &&3445      resultMemrefType.hasStaticShape() &&3446      (!resultLayout || resultLayout.hasStaticLayout())) {3447    return getViewSource();3448  }3449 3450  // Fold subview(subview(x)), where both subviews have the same size and the3451  // second subview's offsets are all zero. (I.e., the second subview is a3452  // no-op.)3453  if (auto srcSubview = getViewSource().getDefiningOp<SubViewOp>()) {3454    auto srcSizes = srcSubview.getMixedSizes();3455    auto sizes = getMixedSizes();3456    auto offsets = getMixedOffsets();3457    bool allOffsetsZero = llvm::all_of(offsets, isZeroInteger);3458    auto strides = getMixedStrides();3459    bool allStridesOne = llvm::all_of(strides, isOneInteger);3460    bool allSizesSame = llvm::equal(sizes, srcSizes);3461    if (allOffsetsZero && allStridesOne && allSizesSame &&3462        resultMemrefType == sourceMemrefType)3463      return getViewSource();3464  }3465 3466  return {};3467}3468 3469FailureOr<std::optional<SmallVector<Value>>>3470SubViewOp::bubbleDownCasts(OpBuilder &builder) {3471  return bubbleDownCastsPassthroughOpImpl(*this, builder, getSourceMutable());3472}3473 3474void SubViewOp::inferStridedMetadataRanges(3475    ArrayRef<StridedMetadataRange> ranges, GetIntRangeFn getIntRange,3476    SetStridedMetadataRangeFn setMetadata, int32_t indexBitwidth) {3477  auto isUninitialized =3478      +[](IntegerValueRange range) { return range.isUninitialized(); };3479 3480  // Bail early if any of the operands metadata is not ready:3481  SmallVector<IntegerValueRange> offsetOperands =3482      getIntValueRanges(getMixedOffsets(), getIntRange, indexBitwidth);3483  if (llvm::any_of(offsetOperands, isUninitialized))3484    return;3485 3486  SmallVector<IntegerValueRange> sizeOperands =3487      getIntValueRanges(getMixedSizes(), getIntRange, indexBitwidth);3488  if (llvm::any_of(sizeOperands, isUninitialized))3489    return;3490 3491  SmallVector<IntegerValueRange> stridesOperands =3492      getIntValueRanges(getMixedStrides(), getIntRange, indexBitwidth);3493  if (llvm::any_of(stridesOperands, isUninitialized))3494    return;3495 3496  StridedMetadataRange sourceRange =3497      ranges[getSourceMutable().getOperandNumber()];3498  if (sourceRange.isUninitialized())3499    return;3500 3501  ArrayRef<ConstantIntRanges> srcStrides = sourceRange.getStrides();3502 3503  // Get the dropped dims.3504  llvm::SmallBitVector droppedDims = getDroppedDims();3505 3506  // Compute the new offset, strides and sizes.3507  ConstantIntRanges offset = sourceRange.getOffsets()[0];3508  SmallVector<ConstantIntRanges> strides, sizes;3509 3510  for (size_t i = 0, e = droppedDims.size(); i < e; ++i) {3511    bool dropped = droppedDims.test(i);3512    // Compute the new offset.3513    ConstantIntRanges off =3514        intrange::inferMul({offsetOperands[i].getValue(), srcStrides[i]});3515    offset = intrange::inferAdd({offset, off});3516 3517    // Skip dropped dimensions.3518    if (dropped)3519      continue;3520    // Multiply the strides.3521    strides.push_back(3522        intrange::inferMul({stridesOperands[i].getValue(), srcStrides[i]}));3523    // Get the sizes.3524    sizes.push_back(sizeOperands[i].getValue());3525  }3526 3527  setMetadata(getResult(),3528              StridedMetadataRange::getRanked(3529                  SmallVector<ConstantIntRanges>({std::move(offset)}),3530                  std::move(sizes), std::move(strides)));3531}3532 3533//===----------------------------------------------------------------------===//3534// TransposeOp3535//===----------------------------------------------------------------------===//3536 3537void TransposeOp::getAsmResultNames(3538    function_ref<void(Value, StringRef)> setNameFn) {3539  setNameFn(getResult(), "transpose");3540}3541 3542/// Build a strided memref type by applying `permutationMap` to `memRefType`.3543static MemRefType inferTransposeResultType(MemRefType memRefType,3544                                           AffineMap permutationMap) {3545  auto originalSizes = memRefType.getShape();3546  auto [originalStrides, offset] = memRefType.getStridesAndOffset();3547  assert(originalStrides.size() == static_cast<unsigned>(memRefType.getRank()));3548 3549  // Compute permuted sizes and strides.3550  auto sizes = applyPermutationMap<int64_t>(permutationMap, originalSizes);3551  auto strides = applyPermutationMap<int64_t>(permutationMap, originalStrides);3552 3553  return MemRefType::Builder(memRefType)3554      .setShape(sizes)3555      .setLayout(3556          StridedLayoutAttr::get(memRefType.getContext(), offset, strides));3557}3558 3559void TransposeOp::build(OpBuilder &b, OperationState &result, Value in,3560                        AffineMapAttr permutation,3561                        ArrayRef<NamedAttribute> attrs) {3562  auto permutationMap = permutation.getValue();3563  assert(permutationMap);3564 3565  auto memRefType = llvm::cast<MemRefType>(in.getType());3566  // Compute result type.3567  MemRefType resultType = inferTransposeResultType(memRefType, permutationMap);3568 3569  result.addAttribute(TransposeOp::getPermutationAttrStrName(), permutation);3570  build(b, result, resultType, in, attrs);3571}3572 3573// transpose $in $permutation attr-dict : type($in) `to` type(results)3574void TransposeOp::print(OpAsmPrinter &p) {3575  p << " " << getIn() << " " << getPermutation();3576  p.printOptionalAttrDict((*this)->getAttrs(), {getPermutationAttrStrName()});3577  p << " : " << getIn().getType() << " to " << getType();3578}3579 3580ParseResult TransposeOp::parse(OpAsmParser &parser, OperationState &result) {3581  OpAsmParser::UnresolvedOperand in;3582  AffineMap permutation;3583  MemRefType srcType, dstType;3584  if (parser.parseOperand(in) || parser.parseAffineMap(permutation) ||3585      parser.parseOptionalAttrDict(result.attributes) ||3586      parser.parseColonType(srcType) ||3587      parser.resolveOperand(in, srcType, result.operands) ||3588      parser.parseKeywordType("to", dstType) ||3589      parser.addTypeToList(dstType, result.types))3590    return failure();3591 3592  result.addAttribute(TransposeOp::getPermutationAttrStrName(),3593                      AffineMapAttr::get(permutation));3594  return success();3595}3596 3597LogicalResult TransposeOp::verify() {3598  if (!getPermutation().isPermutation())3599    return emitOpError("expected a permutation map");3600  if (getPermutation().getNumDims() != getIn().getType().getRank())3601    return emitOpError("expected a permutation map of same rank as the input");3602 3603  auto srcType = llvm::cast<MemRefType>(getIn().getType());3604  auto resultType = llvm::cast<MemRefType>(getType());3605  auto canonicalResultType = inferTransposeResultType(srcType, getPermutation())3606                                 .canonicalizeStridedLayout();3607 3608  if (resultType.canonicalizeStridedLayout() != canonicalResultType)3609    return emitOpError("result type ")3610           << resultType3611           << " is not equivalent to the canonical transposed input type "3612           << canonicalResultType;3613  return success();3614}3615 3616OpFoldResult TransposeOp::fold(FoldAdaptor) {3617  // First check for identity permutation, we can fold it away if input and3618  // result types are identical already.3619  if (getPermutation().isIdentity() && getType() == getIn().getType())3620    return getIn();3621  // Fold two consecutive memref.transpose Ops into one by composing their3622  // permutation maps.3623  if (auto otherTransposeOp = getIn().getDefiningOp<memref::TransposeOp>()) {3624    AffineMap composedPermutation =3625        getPermutation().compose(otherTransposeOp.getPermutation());3626    getInMutable().assign(otherTransposeOp.getIn());3627    setPermutation(composedPermutation);3628    return getResult();3629  }3630  return {};3631}3632 3633FailureOr<std::optional<SmallVector<Value>>>3634TransposeOp::bubbleDownCasts(OpBuilder &builder) {3635  return bubbleDownCastsPassthroughOpImpl(*this, builder, getInMutable());3636}3637 3638//===----------------------------------------------------------------------===//3639// ViewOp3640//===----------------------------------------------------------------------===//3641 3642void ViewOp::getAsmResultNames(function_ref<void(Value, StringRef)> setNameFn) {3643  setNameFn(getResult(), "view");3644}3645 3646LogicalResult ViewOp::verify() {3647  auto baseType = llvm::cast<MemRefType>(getOperand(0).getType());3648  auto viewType = getType();3649 3650  // The base memref should have identity layout map (or none).3651  if (!baseType.getLayout().isIdentity())3652    return emitError("unsupported map for base memref type ") << baseType;3653 3654  // The result memref should have identity layout map (or none).3655  if (!viewType.getLayout().isIdentity())3656    return emitError("unsupported map for result memref type ") << viewType;3657 3658  // The base memref and the view memref should be in the same memory space.3659  if (baseType.getMemorySpace() != viewType.getMemorySpace())3660    return emitError("different memory spaces specified for base memref "3661                     "type ")3662           << baseType << " and view memref type " << viewType;3663 3664  // Verify that we have the correct number of sizes for the result type.3665  unsigned numDynamicDims = viewType.getNumDynamicDims();3666  if (getSizes().size() != numDynamicDims)3667    return emitError("incorrect number of size operands for type ") << viewType;3668 3669  return success();3670}3671 3672Value ViewOp::getViewSource() { return getSource(); }3673 3674OpFoldResult ViewOp::fold(FoldAdaptor adaptor) {3675  MemRefType sourceMemrefType = getSource().getType();3676  MemRefType resultMemrefType = getResult().getType();3677 3678  if (resultMemrefType == sourceMemrefType && resultMemrefType.hasStaticShape())3679    return getViewSource();3680 3681  return {};3682}3683 3684namespace {3685 3686struct ViewOpShapeFolder : public OpRewritePattern<ViewOp> {3687  using OpRewritePattern<ViewOp>::OpRewritePattern;3688 3689  LogicalResult matchAndRewrite(ViewOp viewOp,3690                                PatternRewriter &rewriter) const override {3691    // Return if none of the operands are constants.3692    if (llvm::none_of(viewOp.getOperands(), [](Value operand) {3693          return matchPattern(operand, matchConstantIndex());3694        }))3695      return failure();3696 3697    // Get result memref type.3698    auto memrefType = viewOp.getType();3699 3700    // Get offset from old memref view type 'memRefType'.3701    int64_t oldOffset;3702    SmallVector<int64_t, 4> oldStrides;3703    if (failed(memrefType.getStridesAndOffset(oldStrides, oldOffset)))3704      return failure();3705    assert(oldOffset == 0 && "Expected 0 offset");3706 3707    SmallVector<Value, 4> newOperands;3708 3709    // Offset cannot be folded into result type.3710 3711    // Fold any dynamic dim operands which are produced by a constant.3712    SmallVector<int64_t, 4> newShapeConstants;3713    newShapeConstants.reserve(memrefType.getRank());3714 3715    unsigned dynamicDimPos = 0;3716    unsigned rank = memrefType.getRank();3717    for (unsigned dim = 0, e = rank; dim < e; ++dim) {3718      int64_t dimSize = memrefType.getDimSize(dim);3719      // If this is already static dimension, keep it.3720      if (ShapedType::isStatic(dimSize)) {3721        newShapeConstants.push_back(dimSize);3722        continue;3723      }3724      auto *defOp = viewOp.getSizes()[dynamicDimPos].getDefiningOp();3725      if (auto constantIndexOp =3726              dyn_cast_or_null<arith::ConstantIndexOp>(defOp)) {3727        // Dynamic shape dimension will be folded.3728        newShapeConstants.push_back(constantIndexOp.value());3729      } else {3730        // Dynamic shape dimension not folded; copy operand from old memref.3731        newShapeConstants.push_back(dimSize);3732        newOperands.push_back(viewOp.getSizes()[dynamicDimPos]);3733      }3734      dynamicDimPos++;3735    }3736 3737    // Create new memref type with constant folded dims.3738    MemRefType newMemRefType =3739        MemRefType::Builder(memrefType).setShape(newShapeConstants);3740    // Nothing new, don't fold.3741    if (newMemRefType == memrefType)3742      return failure();3743 3744    // Create new ViewOp.3745    auto newViewOp = ViewOp::create(rewriter, viewOp.getLoc(), newMemRefType,3746                                    viewOp.getOperand(0), viewOp.getByteShift(),3747                                    newOperands);3748    // Insert a cast so we have the same type as the old memref type.3749    rewriter.replaceOpWithNewOp<CastOp>(viewOp, viewOp.getType(), newViewOp);3750    return success();3751  }3752};3753 3754struct ViewOpMemrefCastFolder : public OpRewritePattern<ViewOp> {3755  using OpRewritePattern<ViewOp>::OpRewritePattern;3756 3757  LogicalResult matchAndRewrite(ViewOp viewOp,3758                                PatternRewriter &rewriter) const override {3759    Value memrefOperand = viewOp.getOperand(0);3760    CastOp memrefCastOp = memrefOperand.getDefiningOp<CastOp>();3761    if (!memrefCastOp)3762      return failure();3763    Value allocOperand = memrefCastOp.getOperand();3764    AllocOp allocOp = allocOperand.getDefiningOp<AllocOp>();3765    if (!allocOp)3766      return failure();3767    rewriter.replaceOpWithNewOp<ViewOp>(viewOp, viewOp.getType(), allocOperand,3768                                        viewOp.getByteShift(),3769                                        viewOp.getSizes());3770    return success();3771  }3772};3773 3774} // namespace3775 3776void ViewOp::getCanonicalizationPatterns(RewritePatternSet &results,3777                                         MLIRContext *context) {3778  results.add<ViewOpShapeFolder, ViewOpMemrefCastFolder>(context);3779}3780 3781FailureOr<std::optional<SmallVector<Value>>>3782ViewOp::bubbleDownCasts(OpBuilder &builder) {3783  return bubbleDownCastsPassthroughOpImpl(*this, builder, getSourceMutable());3784}3785 3786//===----------------------------------------------------------------------===//3787// AtomicRMWOp3788//===----------------------------------------------------------------------===//3789 3790LogicalResult AtomicRMWOp::verify() {3791  if (getMemRefType().getRank() != getNumOperands() - 2)3792    return emitOpError(3793        "expects the number of subscripts to be equal to memref rank");3794  switch (getKind()) {3795  case arith::AtomicRMWKind::addf:3796  case arith::AtomicRMWKind::maximumf:3797  case arith::AtomicRMWKind::minimumf:3798  case arith::AtomicRMWKind::mulf:3799    if (!llvm::isa<FloatType>(getValue().getType()))3800      return emitOpError() << "with kind '"3801                           << arith::stringifyAtomicRMWKind(getKind())3802                           << "' expects a floating-point type";3803    break;3804  case arith::AtomicRMWKind::addi:3805  case arith::AtomicRMWKind::maxs:3806  case arith::AtomicRMWKind::maxu:3807  case arith::AtomicRMWKind::mins:3808  case arith::AtomicRMWKind::minu:3809  case arith::AtomicRMWKind::muli:3810  case arith::AtomicRMWKind::ori:3811  case arith::AtomicRMWKind::xori:3812  case arith::AtomicRMWKind::andi:3813    if (!llvm::isa<IntegerType>(getValue().getType()))3814      return emitOpError() << "with kind '"3815                           << arith::stringifyAtomicRMWKind(getKind())3816                           << "' expects an integer type";3817    break;3818  default:3819    break;3820  }3821  return success();3822}3823 3824OpFoldResult AtomicRMWOp::fold(FoldAdaptor adaptor) {3825  /// atomicrmw(memrefcast) -> atomicrmw3826  if (succeeded(foldMemRefCast(*this, getValue())))3827    return getResult();3828  return OpFoldResult();3829}3830 3831FailureOr<std::optional<SmallVector<Value>>>3832AtomicRMWOp::bubbleDownCasts(OpBuilder &builder) {3833  return mlir::detail::bubbleDownInPlaceMemorySpaceCastImpl(getMemrefMutable(),3834                                                            getResult());3835}3836 3837//===----------------------------------------------------------------------===//3838// TableGen'd op method definitions3839//===----------------------------------------------------------------------===//3840 3841#define GET_OP_CLASSES3842#include "mlir/Dialect/MemRef/IR/MemRefOps.cpp.inc"3843