1307cfdf5SNicolas Vasilache //===- Vectorization.cpp - Implementation of linalg Vectorization ---------===// 2307cfdf5SNicolas Vasilache // 3307cfdf5SNicolas Vasilache // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4307cfdf5SNicolas Vasilache // See https://llvm.org/LICENSE.txt for license information. 5307cfdf5SNicolas Vasilache // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6307cfdf5SNicolas Vasilache // 7307cfdf5SNicolas Vasilache //===----------------------------------------------------------------------===// 8307cfdf5SNicolas Vasilache // 9307cfdf5SNicolas Vasilache // This file implements the linalg dialect Vectorization transformations. 10307cfdf5SNicolas Vasilache // 11307cfdf5SNicolas Vasilache //===----------------------------------------------------------------------===// 12307cfdf5SNicolas Vasilache 13307cfdf5SNicolas Vasilache #include "mlir/Dialect/Linalg/Analysis/DependenceAnalysis.h" 14307cfdf5SNicolas Vasilache #include "mlir/Dialect/Linalg/IR/LinalgOps.h" 15307cfdf5SNicolas Vasilache #include "mlir/Dialect/Linalg/Transforms/Transforms.h" 16307cfdf5SNicolas Vasilache #include "mlir/Dialect/Linalg/Utils/Utils.h" 17307cfdf5SNicolas Vasilache #include "mlir/Dialect/StandardOps/EDSC/Intrinsics.h" 18307cfdf5SNicolas Vasilache #include "mlir/Dialect/Utils/StructuredOpsUtils.h" 19307cfdf5SNicolas Vasilache #include "mlir/Dialect/Vector/EDSC/Intrinsics.h" 20307cfdf5SNicolas Vasilache #include "mlir/Dialect/Vector/VectorOps.h" 21307cfdf5SNicolas Vasilache #include "mlir/IR/AffineExpr.h" 22307cfdf5SNicolas Vasilache #include "mlir/IR/Matchers.h" 23307cfdf5SNicolas Vasilache #include "mlir/IR/PatternMatch.h" 24307cfdf5SNicolas Vasilache #include "mlir/Pass/Pass.h" 25307cfdf5SNicolas Vasilache #include "mlir/Support/LLVM.h" 26307cfdf5SNicolas Vasilache #include "llvm/Support/Debug.h" 27307cfdf5SNicolas Vasilache #include "llvm/Support/raw_ostream.h" 28307cfdf5SNicolas Vasilache #include <type_traits> 29307cfdf5SNicolas Vasilache 30307cfdf5SNicolas Vasilache using namespace mlir; 31307cfdf5SNicolas Vasilache using namespace mlir::edsc; 32307cfdf5SNicolas Vasilache using namespace mlir::edsc::intrinsics; 33307cfdf5SNicolas Vasilache using namespace mlir::linalg; 34307cfdf5SNicolas Vasilache 35307cfdf5SNicolas Vasilache using llvm::dbgs; 36307cfdf5SNicolas Vasilache 37307cfdf5SNicolas Vasilache #define DEBUG_TYPE "linalg-vectorization" 38307cfdf5SNicolas Vasilache 39307cfdf5SNicolas Vasilache static bool hasMultiplyAddBody(linalg::GenericOp op) { 40307cfdf5SNicolas Vasilache auto &r = op.region(); 41307cfdf5SNicolas Vasilache if (!llvm::hasSingleElement(r)) 42307cfdf5SNicolas Vasilache return false; 43307cfdf5SNicolas Vasilache if (!llvm::hasNItems(r.front().begin(), r.front().end(), 3)) 44307cfdf5SNicolas Vasilache return false; 45307cfdf5SNicolas Vasilache 46307cfdf5SNicolas Vasilache using mlir::matchers::m_Val; 47307cfdf5SNicolas Vasilache auto a = m_Val(r.front().getArgument(0)); 48307cfdf5SNicolas Vasilache auto b = m_Val(r.front().getArgument(1)); 49307cfdf5SNicolas Vasilache auto c = m_Val(r.front().getArgument(2)); 50307cfdf5SNicolas Vasilache // TODO: Update this detection once we have matcher support for specifying 51307cfdf5SNicolas Vasilache // that any permutation of operands matches. 52307cfdf5SNicolas Vasilache auto pattern1 = m_Op<YieldOp>(m_Op<AddFOp>(m_Op<MulFOp>(a, b), c)); 53307cfdf5SNicolas Vasilache auto pattern2 = m_Op<YieldOp>(m_Op<AddFOp>(c, m_Op<MulFOp>(a, b))); 54307cfdf5SNicolas Vasilache auto pattern3 = m_Op<YieldOp>(m_Op<AddFOp>(m_Op<MulFOp>(b, a), c)); 55307cfdf5SNicolas Vasilache auto pattern4 = m_Op<YieldOp>(m_Op<AddFOp>(c, m_Op<MulFOp>(b, a))); 56307cfdf5SNicolas Vasilache return pattern1.match(&r.front().back()) || 57307cfdf5SNicolas Vasilache pattern2.match(&r.front().back()) || 58307cfdf5SNicolas Vasilache pattern3.match(&r.front().back()) || pattern4.match(&r.front().back()); 59307cfdf5SNicolas Vasilache } 60307cfdf5SNicolas Vasilache 61307cfdf5SNicolas Vasilache // TODO: Should be Tablegen'd from a single source that generates the op itself. 62307cfdf5SNicolas Vasilache static bool isRowMajorMatmul(linalg::GenericOp genericOp) { 63307cfdf5SNicolas Vasilache return genericOp.getNumInputs() == 2 && genericOp.getNumOutputs() == 1 && 64307cfdf5SNicolas Vasilache isRowMajorMatmul(genericOp.indexing_maps()) && 65307cfdf5SNicolas Vasilache hasMultiplyAddBody(genericOp); 66307cfdf5SNicolas Vasilache } 67307cfdf5SNicolas Vasilache 68307cfdf5SNicolas Vasilache // TODO: This is in fact much more general than just vectorization for matmul 69307cfdf5SNicolas Vasilache // and fill ops. 70307cfdf5SNicolas Vasilache LogicalResult mlir::linalg::vectorizeLinalgOpPrecondition(Operation *op) { 71307cfdf5SNicolas Vasilache auto linalgOp = cast<linalg::LinalgOp>(op); 72307cfdf5SNicolas Vasilache // All types must be static shape to go to vector. 73307cfdf5SNicolas Vasilache for (Value operand : linalgOp.getInputsAndOutputBuffers()) 74307cfdf5SNicolas Vasilache if (!operand.getType().cast<ShapedType>().hasStaticShape()) 75307cfdf5SNicolas Vasilache return failure(); 76307cfdf5SNicolas Vasilache for (Type outputTensorType : linalgOp.getOutputTensorTypes()) 77307cfdf5SNicolas Vasilache if (!outputTensorType.cast<ShapedType>().hasStaticShape()) 78307cfdf5SNicolas Vasilache return failure(); 79307cfdf5SNicolas Vasilache if (isa<linalg::MatmulOp>(op) || isa<linalg::FillOp>(op)) 80307cfdf5SNicolas Vasilache return success(); 81307cfdf5SNicolas Vasilache 82307cfdf5SNicolas Vasilache auto genericOp = dyn_cast<linalg::GenericOp>(op); 83307cfdf5SNicolas Vasilache if (!genericOp || !::isRowMajorMatmul(genericOp)) 84307cfdf5SNicolas Vasilache return failure(); 85307cfdf5SNicolas Vasilache 86307cfdf5SNicolas Vasilache // TODO(ntv): non-identity layout. 87307cfdf5SNicolas Vasilache auto isStaticMemRefWithIdentityLayout = [](Value v) { 88307cfdf5SNicolas Vasilache auto m = v.getType().dyn_cast<MemRefType>(); 89307cfdf5SNicolas Vasilache if (!m || !m.hasStaticShape() || !m.getAffineMaps().empty()) 90307cfdf5SNicolas Vasilache return false; 91307cfdf5SNicolas Vasilache return true; 92307cfdf5SNicolas Vasilache }; 93307cfdf5SNicolas Vasilache return success(llvm::all_of(genericOp.getInputsAndOutputBuffers(), 94307cfdf5SNicolas Vasilache isStaticMemRefWithIdentityLayout)); 95307cfdf5SNicolas Vasilache } 96307cfdf5SNicolas Vasilache 97307cfdf5SNicolas Vasilache void mlir::linalg::vectorizeLinalgOp(OpBuilder &builder, Operation *op) { 98307cfdf5SNicolas Vasilache assert(succeeded(vectorizeLinalgOpPrecondition(op))); 99307cfdf5SNicolas Vasilache 100307cfdf5SNicolas Vasilache if (auto convOp = dyn_cast<linalg::ConvOp>(op)) { 101307cfdf5SNicolas Vasilache // TODO: add a level of indirection to linalg.generic. 102307cfdf5SNicolas Vasilache if (convOp.padding()) 103307cfdf5SNicolas Vasilache llvm_unreachable("Unexpected conv with padding"); 104307cfdf5SNicolas Vasilache } 105307cfdf5SNicolas Vasilache 1061ee11432SNicolas Vasilache StringRef dbgPref = "\n[" DEBUG_TYPE "]: "; 1071ee11432SNicolas Vasilache (void)dbgPref; 108307cfdf5SNicolas Vasilache edsc::ScopedContext scope(builder, op->getLoc()); 109307cfdf5SNicolas Vasilache if (auto fillOp = dyn_cast<linalg::FillOp>(op)) { 110307cfdf5SNicolas Vasilache // Vectorize fill as a vector.broadcast. 1111ee11432SNicolas Vasilache LLVM_DEBUG(dbgs() << dbgPref 1121ee11432SNicolas Vasilache << "Rewrite linalg.fill as vector.broadcast: " << *op); 113307cfdf5SNicolas Vasilache Value memref = vector_type_cast(fillOp.getOutputBuffer(0)); 114307cfdf5SNicolas Vasilache Value dst = std_load(memref); 115307cfdf5SNicolas Vasilache Value res = vector_broadcast(dst.getType(), fillOp.value()); 116307cfdf5SNicolas Vasilache std_store(res, memref); 117307cfdf5SNicolas Vasilache return; 118307cfdf5SNicolas Vasilache } 119307cfdf5SNicolas Vasilache 120307cfdf5SNicolas Vasilache // Vectorize other ops as vector contraction (currently only matmul). 1211ee11432SNicolas Vasilache LLVM_DEBUG(dbgs() << dbgPref 1221ee11432SNicolas Vasilache << "Rewrite linalg op as vector.contract: " << *op); 123*9534192cSNicolas Vasilache auto extractVectorTypeFromScalarView = [](Value v) { 124*9534192cSNicolas Vasilache MemRefType mt = v.getType().cast<MemRefType>(); 125*9534192cSNicolas Vasilache return VectorType::get(mt.getShape(), mt.getElementType()); 126*9534192cSNicolas Vasilache }; 127307cfdf5SNicolas Vasilache auto linalgOp = cast<linalg::LinalgOp>(op); 128*9534192cSNicolas Vasilache Value viewA = linalgOp.getInput(0); 129*9534192cSNicolas Vasilache Value viewB = linalgOp.getInput(1); 130*9534192cSNicolas Vasilache Value viewC = linalgOp.getOutputBuffer(0); 131*9534192cSNicolas Vasilache Value zero = std_constant_index(0); 132*9534192cSNicolas Vasilache SmallVector<Value, 4> indicesA(linalgOp.getInputShapedType(0).getRank(), 133*9534192cSNicolas Vasilache zero); 134*9534192cSNicolas Vasilache SmallVector<Value, 4> indicesB(linalgOp.getInputShapedType(1).getRank(), 135*9534192cSNicolas Vasilache zero); 136*9534192cSNicolas Vasilache SmallVector<Value, 4> indicesC(linalgOp.getOutputShapedType(0).getRank(), 137*9534192cSNicolas Vasilache zero); 138*9534192cSNicolas Vasilache Value a = vector_transfer_read(extractVectorTypeFromScalarView(viewA), viewA, 139*9534192cSNicolas Vasilache indicesA); 140*9534192cSNicolas Vasilache Value b = vector_transfer_read(extractVectorTypeFromScalarView(viewB), viewB, 141*9534192cSNicolas Vasilache indicesB); 142*9534192cSNicolas Vasilache Value c = vector_transfer_read(extractVectorTypeFromScalarView(viewC), viewC, 143*9534192cSNicolas Vasilache indicesC); 144307cfdf5SNicolas Vasilache Value res = vector_contract(a, b, c, linalgOp.indexing_maps(), 145307cfdf5SNicolas Vasilache linalgOp.iterator_types()); 146*9534192cSNicolas Vasilache vector_transfer_write(res, viewC, indicesC); 147307cfdf5SNicolas Vasilache } 1481ee11432SNicolas Vasilache 1491ee11432SNicolas Vasilache /// Check whether there is any interleaved use of any `values` between `firstOp` 1501ee11432SNicolas Vasilache /// and `secondOp`. Conservatively return `true` if any op or value is in a 1511ee11432SNicolas Vasilache /// different block. 1521ee11432SNicolas Vasilache static bool mayExistInterleavedUses(Operation *firstOp, Operation *secondOp, 1531ee11432SNicolas Vasilache ValueRange values) { 1541ee11432SNicolas Vasilache StringRef dbgPref = "\n[" DEBUG_TYPE "]: "; 1551ee11432SNicolas Vasilache (void)dbgPref; 1561ee11432SNicolas Vasilache if (firstOp->getBlock() != secondOp->getBlock() || 1571ee11432SNicolas Vasilache !firstOp->isBeforeInBlock(secondOp)) { 1581ee11432SNicolas Vasilache LLVM_DEBUG(llvm::dbgs() 1591ee11432SNicolas Vasilache << dbgPref << "interleavedUses precondition failed, firstOp: " 1601ee11432SNicolas Vasilache << *firstOp << ", second op: " << *secondOp); 1611ee11432SNicolas Vasilache return true; 1621ee11432SNicolas Vasilache } 1631ee11432SNicolas Vasilache for (auto v : values) { 1641ee11432SNicolas Vasilache for (auto &u : v.getUses()) { 1651ee11432SNicolas Vasilache Operation *owner = u.getOwner(); 1661ee11432SNicolas Vasilache if (owner == firstOp || owner == secondOp) 1671ee11432SNicolas Vasilache continue; 1681ee11432SNicolas Vasilache // TODO: this is too conservative, use dominance info in the future. 1691ee11432SNicolas Vasilache if (owner->getBlock() == firstOp->getBlock() && 1701ee11432SNicolas Vasilache (owner->isBeforeInBlock(firstOp) || secondOp->isBeforeInBlock(owner))) 1711ee11432SNicolas Vasilache continue; 1721ee11432SNicolas Vasilache LLVM_DEBUG(llvm::dbgs() 1731ee11432SNicolas Vasilache << dbgPref << " found interleaved op " << *owner 1741ee11432SNicolas Vasilache << ", firstOp: " << *firstOp << ", second op: " << *secondOp); 1751ee11432SNicolas Vasilache return true; 1761ee11432SNicolas Vasilache } 1771ee11432SNicolas Vasilache } 1781ee11432SNicolas Vasilache return false; 1791ee11432SNicolas Vasilache } 1801ee11432SNicolas Vasilache 1811ee11432SNicolas Vasilache /// Return the unique subview use of `v` if it is indeed unique, null otherwise. 1821ee11432SNicolas Vasilache static SubViewOp getSubViewUseIfUnique(Value v) { 1831ee11432SNicolas Vasilache SubViewOp subViewOp; 1841ee11432SNicolas Vasilache for (auto &u : v.getUses()) { 1851ee11432SNicolas Vasilache if (auto newSubViewOp = dyn_cast<SubViewOp>(u.getOwner())) { 1861ee11432SNicolas Vasilache if (subViewOp) 1871ee11432SNicolas Vasilache return SubViewOp(); 1881ee11432SNicolas Vasilache subViewOp = newSubViewOp; 1891ee11432SNicolas Vasilache } 1901ee11432SNicolas Vasilache } 1911ee11432SNicolas Vasilache return subViewOp; 1921ee11432SNicolas Vasilache } 1931ee11432SNicolas Vasilache 1941ee11432SNicolas Vasilache /// TODO: use interfaces, side-effects and aliasing analysis as appropriate, 1951ee11432SNicolas Vasilache /// when available. 1961ee11432SNicolas Vasilache LogicalResult LinalgCopyVTRForwardingPattern::matchAndRewrite( 1971ee11432SNicolas Vasilache vector::TransferReadOp xferOp, PatternRewriter &rewriter) const { 1981ee11432SNicolas Vasilache 1991ee11432SNicolas Vasilache // Transfer into `view`. 2001ee11432SNicolas Vasilache Value viewOrAlloc = xferOp.memref(); 2011ee11432SNicolas Vasilache if (!viewOrAlloc.getDefiningOp<ViewOp>() && 2021ee11432SNicolas Vasilache !viewOrAlloc.getDefiningOp<AllocOp>()) 2031ee11432SNicolas Vasilache return failure(); 2041ee11432SNicolas Vasilache 2051ee11432SNicolas Vasilache StringRef dbgPref = "\n[" DEBUG_TYPE "]: VTRForwarding: "; 2061ee11432SNicolas Vasilache (void)dbgPref; 2071ee11432SNicolas Vasilache LLVM_DEBUG(llvm::dbgs() << dbgPref << viewOrAlloc); 2081ee11432SNicolas Vasilache 2091ee11432SNicolas Vasilache // Ensure there is exactly one subview of `viewOrAlloc` defining `subView`. 2101ee11432SNicolas Vasilache SubViewOp subViewOp = getSubViewUseIfUnique(viewOrAlloc); 2111ee11432SNicolas Vasilache if (!subViewOp) 2121ee11432SNicolas Vasilache return failure(); 2131ee11432SNicolas Vasilache Value subView = subViewOp.getResult(); 2141ee11432SNicolas Vasilache LLVM_DEBUG(llvm::dbgs() << dbgPref << "with subView " << subView); 2151ee11432SNicolas Vasilache 2161ee11432SNicolas Vasilache // Find the copy into `subView` without interleaved uses. 2171ee11432SNicolas Vasilache CopyOp copyOp; 2181ee11432SNicolas Vasilache for (auto &u : subView.getUses()) { 2191ee11432SNicolas Vasilache if (auto newCopyOp = dyn_cast<CopyOp>(u.getOwner())) { 2201ee11432SNicolas Vasilache if (newCopyOp.getOutputBuffer(0) != subView) 2211ee11432SNicolas Vasilache continue; 2221ee11432SNicolas Vasilache LLVM_DEBUG(llvm::dbgs() << dbgPref << "copy candidate " << *newCopyOp); 2231ee11432SNicolas Vasilache if (mayExistInterleavedUses(newCopyOp, xferOp, {viewOrAlloc, subView})) 2241ee11432SNicolas Vasilache continue; 2251ee11432SNicolas Vasilache copyOp = newCopyOp; 2261ee11432SNicolas Vasilache break; 2271ee11432SNicolas Vasilache } 2281ee11432SNicolas Vasilache } 2291ee11432SNicolas Vasilache if (!copyOp) 2301ee11432SNicolas Vasilache return failure(); 2311ee11432SNicolas Vasilache LLVM_DEBUG(llvm::dbgs() << dbgPref << "with copy " << *copyOp); 2321ee11432SNicolas Vasilache 2331ee11432SNicolas Vasilache // Find the fill into `viewOrAlloc` without interleaved uses before the copy. 2341ee11432SNicolas Vasilache FillOp maybeFillOp; 2351ee11432SNicolas Vasilache for (auto &u : viewOrAlloc.getUses()) { 2361ee11432SNicolas Vasilache if (auto newFillOp = dyn_cast<FillOp>(u.getOwner())) { 2371ee11432SNicolas Vasilache if (newFillOp.getOutputBuffer(0) != viewOrAlloc) 2381ee11432SNicolas Vasilache continue; 2391ee11432SNicolas Vasilache LLVM_DEBUG(llvm::dbgs() << dbgPref << "fill candidate " << *newFillOp); 2401ee11432SNicolas Vasilache if (mayExistInterleavedUses(newFillOp, copyOp, {viewOrAlloc, subView})) 2411ee11432SNicolas Vasilache continue; 2421ee11432SNicolas Vasilache maybeFillOp = newFillOp; 2431ee11432SNicolas Vasilache break; 2441ee11432SNicolas Vasilache } 2451ee11432SNicolas Vasilache } 2461ee11432SNicolas Vasilache // Ensure padding matches. 2471ee11432SNicolas Vasilache if (maybeFillOp && xferOp.padding() != maybeFillOp.value()) 2481ee11432SNicolas Vasilache return failure(); 2491ee11432SNicolas Vasilache if (maybeFillOp) 2501ee11432SNicolas Vasilache LLVM_DEBUG(llvm::dbgs() << dbgPref << "with maybeFillOp " << *maybeFillOp); 2511ee11432SNicolas Vasilache 2521ee11432SNicolas Vasilache // `in` is the subview that linalg.copy reads. Replace it. 2531ee11432SNicolas Vasilache Value in = copyOp.getInput(0); 2541ee11432SNicolas Vasilache 2551ee11432SNicolas Vasilache Value res = rewriter.create<vector::TransferReadOp>( 2561ee11432SNicolas Vasilache xferOp.getLoc(), xferOp.getVectorType(), in, xferOp.indices(), 2571ee11432SNicolas Vasilache xferOp.permutation_map(), xferOp.padding(), 2581ee11432SNicolas Vasilache xferOp.masked() ? *xferOp.masked() : ArrayAttr()); 2591ee11432SNicolas Vasilache 2601ee11432SNicolas Vasilache if (maybeFillOp) 2611ee11432SNicolas Vasilache rewriter.eraseOp(maybeFillOp); 2621ee11432SNicolas Vasilache rewriter.eraseOp(copyOp); 2631ee11432SNicolas Vasilache rewriter.replaceOp(xferOp, res); 2641ee11432SNicolas Vasilache 2651ee11432SNicolas Vasilache return success(); 2661ee11432SNicolas Vasilache } 2671ee11432SNicolas Vasilache 2681ee11432SNicolas Vasilache /// TODO: use interfaces, side-effects and aliasing analysis as appropriate, 2691ee11432SNicolas Vasilache /// when available. 2701ee11432SNicolas Vasilache LogicalResult LinalgCopyVTWForwardingPattern::matchAndRewrite( 2711ee11432SNicolas Vasilache vector::TransferWriteOp xferOp, PatternRewriter &rewriter) const { 2721ee11432SNicolas Vasilache // Transfer into `viewOrAlloc`. 2731ee11432SNicolas Vasilache Value viewOrAlloc = xferOp.memref(); 2741ee11432SNicolas Vasilache if (!viewOrAlloc.getDefiningOp<ViewOp>() && 2751ee11432SNicolas Vasilache !viewOrAlloc.getDefiningOp<AllocOp>()) 2761ee11432SNicolas Vasilache return failure(); 2771ee11432SNicolas Vasilache 2781ee11432SNicolas Vasilache // Ensure there is exactly one subview of `viewOrAlloc` defining `subView`. 2791ee11432SNicolas Vasilache SubViewOp subViewOp = getSubViewUseIfUnique(viewOrAlloc); 2801ee11432SNicolas Vasilache if (!subViewOp) 2811ee11432SNicolas Vasilache return failure(); 2821ee11432SNicolas Vasilache Value subView = subViewOp.getResult(); 2831ee11432SNicolas Vasilache 2841ee11432SNicolas Vasilache // Find the copy from `subView` without interleaved uses. 2851ee11432SNicolas Vasilache CopyOp copyOp; 2861ee11432SNicolas Vasilache for (auto &u : subViewOp.getResult().getUses()) { 2871ee11432SNicolas Vasilache if (auto newCopyOp = dyn_cast<CopyOp>(u.getOwner())) { 2881ee11432SNicolas Vasilache if (newCopyOp.getInput(0) != subView) 2891ee11432SNicolas Vasilache continue; 2901ee11432SNicolas Vasilache if (mayExistInterleavedUses(xferOp, newCopyOp, {viewOrAlloc, subView})) 2911ee11432SNicolas Vasilache continue; 2921ee11432SNicolas Vasilache copyOp = newCopyOp; 2931ee11432SNicolas Vasilache break; 2941ee11432SNicolas Vasilache } 2951ee11432SNicolas Vasilache } 2961ee11432SNicolas Vasilache if (!copyOp) 2971ee11432SNicolas Vasilache return failure(); 2981ee11432SNicolas Vasilache 2991ee11432SNicolas Vasilache // `out` is the subview copied into that we replace. 3001ee11432SNicolas Vasilache Value out = copyOp.getOutputBuffer(0); 3011ee11432SNicolas Vasilache 3021ee11432SNicolas Vasilache // Forward vector.transfer into copy. 3031ee11432SNicolas Vasilache rewriter.create<vector::TransferWriteOp>( 3041ee11432SNicolas Vasilache xferOp.getLoc(), xferOp.vector(), out, xferOp.indices(), 3051ee11432SNicolas Vasilache xferOp.permutation_map(), 3061ee11432SNicolas Vasilache xferOp.masked() ? *xferOp.masked() : ArrayAttr()); 3071ee11432SNicolas Vasilache 3081ee11432SNicolas Vasilache rewriter.eraseOp(copyOp); 3091ee11432SNicolas Vasilache rewriter.eraseOp(xferOp); 3101ee11432SNicolas Vasilache 3111ee11432SNicolas Vasilache return success(); 3121ee11432SNicolas Vasilache } 313