10f241638SMatthias Springer //===- VectorToSCF.cpp - Convert vector to SCF dialect ----------*- C++ -*-===//
24ead2cf7SAlex Zinenko //
34ead2cf7SAlex Zinenko // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
44ead2cf7SAlex Zinenko // See https://llvm.org/LICENSE.txt for license information.
54ead2cf7SAlex Zinenko // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
64ead2cf7SAlex Zinenko //
74ead2cf7SAlex Zinenko //===----------------------------------------------------------------------===//
84ead2cf7SAlex Zinenko //
90f241638SMatthias Springer // This file implements lowering of vector transfer operations to SCF.
104ead2cf7SAlex Zinenko //
114ead2cf7SAlex Zinenko //===----------------------------------------------------------------------===//
124ead2cf7SAlex Zinenko 
134ead2cf7SAlex Zinenko #include <type_traits>
144ead2cf7SAlex Zinenko 
154ead2cf7SAlex Zinenko #include "mlir/Conversion/VectorToSCF/VectorToSCF.h"
165f9e0466SNicolas Vasilache 
175f9e0466SNicolas Vasilache #include "../PassDetail.h"
186825bfe2SNicolas Vasilache #include "mlir/Dialect/Affine/IR/AffineOps.h"
196825bfe2SNicolas Vasilache #include "mlir/Dialect/Affine/Utils.h"
20a54f4eaeSMogball #include "mlir/Dialect/Arithmetic/IR/Arithmetic.h"
2166f878ceSMatthias Springer #include "mlir/Dialect/MemRef/IR/MemRef.h"
226825bfe2SNicolas Vasilache #include "mlir/Dialect/SCF/SCF.h"
23*8e4c806eSNicolas Vasilache #include "mlir/Dialect/Vector/VectorTransforms.h"
244ead2cf7SAlex Zinenko #include "mlir/IR/Builders.h"
256825bfe2SNicolas Vasilache #include "mlir/IR/ImplicitLocOpBuilder.h"
265f9e0466SNicolas Vasilache #include "mlir/Pass/Pass.h"
27b6eb26fdSRiver Riddle #include "mlir/Transforms/GreedyPatternRewriteDriver.h"
285f9e0466SNicolas Vasilache #include "mlir/Transforms/Passes.h"
294ead2cf7SAlex Zinenko 
304ead2cf7SAlex Zinenko using namespace mlir;
314ead2cf7SAlex Zinenko using vector::TransferReadOp;
324ead2cf7SAlex Zinenko using vector::TransferWriteOp;
334ead2cf7SAlex Zinenko 
34350dadaaSBenjamin Kramer namespace {
350f241638SMatthias Springer 
360f241638SMatthias Springer /// Attribute name used for labeling transfer ops during progressive lowering.
370f241638SMatthias Springer static const char kPassLabel[] = "__vector_to_scf_lowering__";
380f241638SMatthias Springer 
392ca887deSMatthias Springer /// Patterns that inherit from this struct have access to
402ca887deSMatthias Springer /// VectorTransferToSCFOptions.
412ca887deSMatthias Springer template <typename OpTy>
422ca887deSMatthias Springer struct VectorToSCFPattern : public OpRewritePattern<OpTy> {
432ca887deSMatthias Springer   explicit VectorToSCFPattern(MLIRContext *context,
442ca887deSMatthias Springer                               VectorTransferToSCFOptions opt)
452ca887deSMatthias Springer       : OpRewritePattern<OpTy>(context), options(opt) {}
462ca887deSMatthias Springer 
472ca887deSMatthias Springer   VectorTransferToSCFOptions options;
482ca887deSMatthias Springer };
490f241638SMatthias Springer 
500f241638SMatthias Springer /// Given a vector transfer op, calculate which dimension of the `source`
510f241638SMatthias Springer /// memref should be unpacked in the next application of TransferOpConversion.
520f241638SMatthias Springer /// A return value of None indicates a broadcast.
530f241638SMatthias Springer template <typename OpTy>
540f241638SMatthias Springer static Optional<int64_t> unpackedDim(OpTy xferOp) {
550f241638SMatthias Springer   auto map = xferOp.permutation_map();
560f241638SMatthias Springer   if (auto expr = map.getResult(0).template dyn_cast<AffineDimExpr>()) {
570f241638SMatthias Springer     return expr.getPosition();
587c3c5b11SNicolas Vasilache   }
590f241638SMatthias Springer   assert(xferOp.isBroadcastDim(0) &&
600f241638SMatthias Springer          "Expected AffineDimExpr or AffineConstantExpr");
610f241638SMatthias Springer   return None;
620f241638SMatthias Springer }
630f241638SMatthias Springer 
640f241638SMatthias Springer /// Compute the permutation map for the new (N-1)-D vector transfer op. This
650f241638SMatthias Springer /// map is identical to the current permutation map, but the first result is
660f241638SMatthias Springer /// omitted.
670f241638SMatthias Springer template <typename OpTy>
686825bfe2SNicolas Vasilache static AffineMap unpackedPermutationMap(OpBuilder &b, OpTy xferOp) {
690f241638SMatthias Springer   auto map = xferOp.permutation_map();
700f241638SMatthias Springer   return AffineMap::get(map.getNumDims(), 0, map.getResults().drop_front(),
716825bfe2SNicolas Vasilache                         b.getContext());
720f241638SMatthias Springer }
730f241638SMatthias Springer 
740f241638SMatthias Springer /// Calculate the indices for the new vector transfer op.
750f241638SMatthias Springer ///
760f241638SMatthias Springer /// E.g.: transfer_read %A[%a, %b, %c, %d] ... : vector<5x4x3xf32> ...
770f241638SMatthias Springer ///       --> transfer_read %A[%a, %b + iv, %c, %d] ... vector<4x3f32>
780f241638SMatthias Springer ///                                 ^^^^^^
790f241638SMatthias Springer ///              `iv` is the iteration variable of the (new) surrounding loop.
800f241638SMatthias Springer template <typename OpTy>
816825bfe2SNicolas Vasilache static void getXferIndices(OpBuilder &b, OpTy xferOp, Value iv,
820f241638SMatthias Springer                            SmallVector<Value, 8> &indices) {
830f241638SMatthias Springer   typename OpTy::Adaptor adaptor(xferOp);
840f241638SMatthias Springer   // Corresponding memref dim of the vector dim that is unpacked.
850f241638SMatthias Springer   auto dim = unpackedDim(xferOp);
860f241638SMatthias Springer   auto prevIndices = adaptor.indices();
870f241638SMatthias Springer   indices.append(prevIndices.begin(), prevIndices.end());
880f241638SMatthias Springer 
896825bfe2SNicolas Vasilache   Location loc = xferOp.getLoc();
900f241638SMatthias Springer   bool isBroadcast = !dim.hasValue();
910f241638SMatthias Springer   if (!isBroadcast) {
926825bfe2SNicolas Vasilache     AffineExpr d0, d1;
936825bfe2SNicolas Vasilache     bindDims(xferOp.getContext(), d0, d1);
946825bfe2SNicolas Vasilache     Value offset = adaptor.indices()[dim.getValue()];
956825bfe2SNicolas Vasilache     indices[dim.getValue()] =
966825bfe2SNicolas Vasilache         makeComposedAffineApply(b, loc, d0 + d1, {offset, iv});
970f241638SMatthias Springer   }
980f241638SMatthias Springer }
990f241638SMatthias Springer 
1006825bfe2SNicolas Vasilache static void maybeYieldValue(OpBuilder &b, Location loc, bool hasRetVal,
1010f241638SMatthias Springer                             Value value) {
1020f241638SMatthias Springer   if (hasRetVal) {
103558e7401SMatthias Springer     assert(value && "Expected non-empty value");
1046825bfe2SNicolas Vasilache     b.create<scf::YieldOp>(loc, value);
1050f241638SMatthias Springer   } else {
1066825bfe2SNicolas Vasilache     b.create<scf::YieldOp>(loc);
1070f241638SMatthias Springer   }
1080f241638SMatthias Springer }
1090f241638SMatthias Springer 
1100f241638SMatthias Springer /// Generates a boolean Value that is true if the iv-th bit in xferOp's mask
1110f241638SMatthias Springer /// is set to true. No such check is generated under following circumstances:
1120f241638SMatthias Springer /// * xferOp does not have a mask.
1130f241638SMatthias Springer /// * xferOp's mask is not 1D. (In case of (N>1)-D, a subvector of the mask is
1140f241638SMatthias Springer ///   computed and attached to the new transfer op in the pattern.)
1150f241638SMatthias Springer /// * The to-be-unpacked dim of xferOp is a broadcast.
1160f241638SMatthias Springer template <typename OpTy>
1176825bfe2SNicolas Vasilache static Value generateMaskCheck(OpBuilder &b, OpTy xferOp, Value iv) {
1180f241638SMatthias Springer   if (!xferOp.mask())
1190f241638SMatthias Springer     return Value();
1200f241638SMatthias Springer   if (xferOp.getMaskType().getRank() != 1)
1210f241638SMatthias Springer     return Value();
1220f241638SMatthias Springer   if (xferOp.isBroadcastDim(0))
1230f241638SMatthias Springer     return Value();
1240f241638SMatthias Springer 
1256825bfe2SNicolas Vasilache   Location loc = xferOp.getLoc();
126a54f4eaeSMogball   Value ivI32 = b.create<arith::IndexCastOp>(
127a54f4eaeSMogball       loc, IntegerType::get(b.getContext(), 32), iv);
1286825bfe2SNicolas Vasilache   return b.create<vector::ExtractElementOp>(loc, xferOp.mask(), ivI32);
1290f241638SMatthias Springer }
1300f241638SMatthias Springer 
1310f241638SMatthias Springer /// Helper function TransferOpConversion and TransferOp1dConversion.
1320f241638SMatthias Springer /// Generate an in-bounds check if the transfer op may go out-of-bounds on the
1330f241638SMatthias Springer /// specified dimension `dim` with the loop iteration variable `iv`.
1340f241638SMatthias Springer /// E.g., when unpacking dimension 0 from:
1350f241638SMatthias Springer /// ```
1360f241638SMatthias Springer /// %vec = vector.transfer_read %A[%a, %b] %cst
1370f241638SMatthias Springer ///     : vector<5x4xf32>, memref<?x?xf32>
1380f241638SMatthias Springer /// ```
1390f241638SMatthias Springer /// An if check similar to this will be generated inside the loop:
1400f241638SMatthias Springer /// ```
1410f241638SMatthias Springer /// %d = memref.dim %A, %c0 : memref<?x?xf32>
1420f241638SMatthias Springer /// if (%a + iv < %d) {
1430f241638SMatthias Springer ///   (in-bounds case)
1440f241638SMatthias Springer /// } else {
1450f241638SMatthias Springer ///   (out-of-bounds case)
1460f241638SMatthias Springer /// }
1470f241638SMatthias Springer /// ```
1480f241638SMatthias Springer ///
1490f241638SMatthias Springer /// If the transfer is 1D and has a mask, this function generates a more complex
1500f241638SMatthias Springer /// check also accounts for potentially masked out elements.
1510f241638SMatthias Springer ///
1520f241638SMatthias Springer /// This function variant returns the value returned by `inBoundsCase` or
1530f241638SMatthias Springer /// `outOfBoundsCase`. The MLIR type of the return value must be specified in
1540f241638SMatthias Springer /// `resultTypes`.
1550f241638SMatthias Springer template <typename OpTy>
1560f241638SMatthias Springer static Value generateInBoundsCheck(
1576825bfe2SNicolas Vasilache     OpBuilder &b, OpTy xferOp, Value iv, Optional<int64_t> dim,
1580f241638SMatthias Springer     TypeRange resultTypes,
1590f241638SMatthias Springer     function_ref<Value(OpBuilder &, Location)> inBoundsCase,
1600f241638SMatthias Springer     function_ref<Value(OpBuilder &, Location)> outOfBoundsCase = nullptr) {
1610f241638SMatthias Springer   bool hasRetVal = !resultTypes.empty();
1620f241638SMatthias Springer   Value cond; // Condition to be built...
1630f241638SMatthias Springer 
1640f241638SMatthias Springer   // Condition check 1: Access in-bounds?
1650f241638SMatthias Springer   bool isBroadcast = !dim.hasValue(); // No in-bounds check for broadcasts.
1666825bfe2SNicolas Vasilache   Location loc = xferOp.getLoc();
1676825bfe2SNicolas Vasilache   ImplicitLocOpBuilder lb(xferOp.getLoc(), b);
1680f241638SMatthias Springer   if (!xferOp.isDimInBounds(0) && !isBroadcast) {
1692c115eccSMatthias Springer     Value memrefDim = vector::createOrFoldDimOp(b, loc, xferOp.source(), *dim);
1706825bfe2SNicolas Vasilache     AffineExpr d0, d1;
1716825bfe2SNicolas Vasilache     bindDims(xferOp.getContext(), d0, d1);
1726825bfe2SNicolas Vasilache     Value base = xferOp.indices()[dim.getValue()];
1736825bfe2SNicolas Vasilache     Value memrefIdx = makeComposedAffineApply(b, loc, d0 + d1, {base, iv});
174a54f4eaeSMogball     cond = lb.create<arith::CmpIOp>(arith::CmpIPredicate::sgt, memrefDim,
175a54f4eaeSMogball                                     memrefIdx);
1760f241638SMatthias Springer   }
1770f241638SMatthias Springer 
1780f241638SMatthias Springer   // Condition check 2: Masked in?
1796825bfe2SNicolas Vasilache   if (auto maskCond = generateMaskCheck(b, xferOp, iv)) {
1806825bfe2SNicolas Vasilache     if (cond)
181a54f4eaeSMogball       cond = lb.create<arith::AndIOp>(cond, maskCond);
1826825bfe2SNicolas Vasilache     else
1830f241638SMatthias Springer       cond = maskCond;
1840f241638SMatthias Springer   }
1850f241638SMatthias Springer 
1860f241638SMatthias Springer   // If the condition is non-empty, generate an SCF::IfOp.
1870f241638SMatthias Springer   if (cond) {
1886825bfe2SNicolas Vasilache     auto check = lb.create<scf::IfOp>(
1896825bfe2SNicolas Vasilache         resultTypes, cond,
1900f241638SMatthias Springer         /*thenBuilder=*/
1916825bfe2SNicolas Vasilache         [&](OpBuilder &b, Location loc) {
1926825bfe2SNicolas Vasilache           maybeYieldValue(b, loc, hasRetVal, inBoundsCase(b, loc));
193cadb7ccfSAlex Zinenko         },
1940f241638SMatthias Springer         /*elseBuilder=*/
1956825bfe2SNicolas Vasilache         [&](OpBuilder &b, Location loc) {
1960f241638SMatthias Springer           if (outOfBoundsCase) {
1976825bfe2SNicolas Vasilache             maybeYieldValue(b, loc, hasRetVal, outOfBoundsCase(b, loc));
1987c3c5b11SNicolas Vasilache           } else {
1996825bfe2SNicolas Vasilache             b.create<scf::YieldOp>(loc);
2007c3c5b11SNicolas Vasilache           }
2017c3c5b11SNicolas Vasilache         });
2027c3c5b11SNicolas Vasilache 
2030f241638SMatthias Springer     return hasRetVal ? check.getResult(0) : Value();
2044ead2cf7SAlex Zinenko   }
2054ead2cf7SAlex Zinenko 
2060f241638SMatthias Springer   // Condition is empty, no need for an SCF::IfOp.
2076825bfe2SNicolas Vasilache   return inBoundsCase(b, loc);
2080f241638SMatthias Springer }
2090f241638SMatthias Springer 
2100f241638SMatthias Springer /// In this function variant, `inBoundsCase` and `outOfBoundsCase` do not have
2110f241638SMatthias Springer /// a return value. Consequently, this function does not have a return value.
2120f241638SMatthias Springer template <typename OpTy>
2130f241638SMatthias Springer static void generateInBoundsCheck(
2146825bfe2SNicolas Vasilache     OpBuilder &b, OpTy xferOp, Value iv, Optional<int64_t> dim,
2150f241638SMatthias Springer     function_ref<void(OpBuilder &, Location)> inBoundsCase,
2160f241638SMatthias Springer     function_ref<void(OpBuilder &, Location)> outOfBoundsCase = nullptr) {
2170f241638SMatthias Springer   generateInBoundsCheck(
2186825bfe2SNicolas Vasilache       b, xferOp, iv, dim, /*resultTypes=*/TypeRange(),
2190f241638SMatthias Springer       /*inBoundsCase=*/
2206825bfe2SNicolas Vasilache       [&](OpBuilder &b, Location loc) {
2216825bfe2SNicolas Vasilache         inBoundsCase(b, loc);
2220f241638SMatthias Springer         return Value();
2230f241638SMatthias Springer       },
2240f241638SMatthias Springer       /*outOfBoundsCase=*/
2256825bfe2SNicolas Vasilache       [&](OpBuilder &b, Location loc) {
2260f241638SMatthias Springer         if (outOfBoundsCase)
2276825bfe2SNicolas Vasilache           outOfBoundsCase(b, loc);
2280f241638SMatthias Springer         return Value();
2290f241638SMatthias Springer       });
2300f241638SMatthias Springer }
2310f241638SMatthias Springer 
2320f241638SMatthias Springer /// Given an ArrayAttr, return a copy where the first element is dropped.
2336825bfe2SNicolas Vasilache static ArrayAttr dropFirstElem(OpBuilder &b, ArrayAttr attr) {
2340f241638SMatthias Springer   if (!attr)
2350f241638SMatthias Springer     return attr;
2366825bfe2SNicolas Vasilache   return ArrayAttr::get(b.getContext(), attr.getValue().drop_front());
2370f241638SMatthias Springer }
2380f241638SMatthias Springer 
2390f241638SMatthias Springer /// Add the pass label to a vector transfer op if its rank is not the target
2400f241638SMatthias Springer /// rank.
2410f241638SMatthias Springer template <typename OpTy>
2426825bfe2SNicolas Vasilache static void maybeApplyPassLabel(OpBuilder &b, OpTy newXferOp,
2432ca887deSMatthias Springer                                 unsigned targetRank) {
2442ca887deSMatthias Springer   if (newXferOp.getVectorType().getRank() > targetRank)
2456825bfe2SNicolas Vasilache     newXferOp->setAttr(kPassLabel, b.getUnitAttr());
2460f241638SMatthias Springer }
2470f241638SMatthias Springer 
248558e7401SMatthias Springer /// Return true if this transfer op operates on a source tensor.
249558e7401SMatthias Springer template <typename OpTy>
250558e7401SMatthias Springer static bool isTensorOp(OpTy xferOp) {
251558e7401SMatthias Springer   if (xferOp.getShapedType().template isa<RankedTensorType>()) {
252558e7401SMatthias Springer     if (xferOp.getOperationName().equals(TransferWriteOp::getOperationName())) {
253558e7401SMatthias Springer       // TransferWriteOps on tensors have a result.
254558e7401SMatthias Springer       assert(xferOp->getNumResults() > 0);
255558e7401SMatthias Springer     }
256558e7401SMatthias Springer     return true;
257558e7401SMatthias Springer   }
258558e7401SMatthias Springer   return false;
259558e7401SMatthias Springer }
260558e7401SMatthias Springer 
261a088bed4SMatthias Springer namespace lowering_n_d {
262a088bed4SMatthias Springer 
263a088bed4SMatthias Springer /// Helper data structure for data and mask buffers.
264a088bed4SMatthias Springer struct BufferAllocs {
265a088bed4SMatthias Springer   Value dataBuffer;
266a088bed4SMatthias Springer   Value maskBuffer;
267a088bed4SMatthias Springer };
268a088bed4SMatthias Springer 
269a088bed4SMatthias Springer /// Allocate temporary buffers for data (vector) and mask (if present).
270a088bed4SMatthias Springer /// TODO: Parallelism and threadlocal considerations.
271a088bed4SMatthias Springer template <typename OpTy>
2726825bfe2SNicolas Vasilache static BufferAllocs allocBuffers(OpBuilder &b, OpTy xferOp) {
2736825bfe2SNicolas Vasilache   Location loc = xferOp.getLoc();
274a088bed4SMatthias Springer   OpBuilder::InsertionGuard guard(b);
275a088bed4SMatthias Springer   Operation *scope =
276a088bed4SMatthias Springer       xferOp->template getParentWithTrait<OpTrait::AutomaticAllocationScope>();
277a088bed4SMatthias Springer   assert(scope && "Expected op to be inside automatic allocation scope");
278a088bed4SMatthias Springer   b.setInsertionPointToStart(&scope->getRegion(0).front());
279a088bed4SMatthias Springer 
280a088bed4SMatthias Springer   BufferAllocs result;
281a088bed4SMatthias Springer   auto bufferType = MemRefType::get({}, xferOp.getVectorType());
2826825bfe2SNicolas Vasilache   result.dataBuffer = b.create<memref::AllocaOp>(loc, bufferType);
283a088bed4SMatthias Springer 
284a088bed4SMatthias Springer   if (xferOp.mask()) {
285a088bed4SMatthias Springer     auto maskType = MemRefType::get({}, xferOp.mask().getType());
2866825bfe2SNicolas Vasilache     auto maskBuffer = b.create<memref::AllocaOp>(loc, maskType);
287fb7ec1f1SMatthias Springer     b.setInsertionPoint(xferOp);
2886825bfe2SNicolas Vasilache     b.create<memref::StoreOp>(loc, xferOp.mask(), maskBuffer);
2896825bfe2SNicolas Vasilache     result.maskBuffer = b.create<memref::LoadOp>(loc, maskBuffer);
290a088bed4SMatthias Springer   }
291a088bed4SMatthias Springer 
292a088bed4SMatthias Springer   return result;
293a088bed4SMatthias Springer }
294a088bed4SMatthias Springer 
295a088bed4SMatthias Springer /// Given a MemRefType with VectorType element type, unpack one dimension from
296a088bed4SMatthias Springer /// the VectorType into the MemRefType.
297a088bed4SMatthias Springer ///
298a088bed4SMatthias Springer /// E.g.: memref<9xvector<5x6xf32>> --> memref<9x5xvector<6xf32>>
299a088bed4SMatthias Springer static MemRefType unpackOneDim(MemRefType type) {
300a088bed4SMatthias Springer   auto vectorType = type.getElementType().dyn_cast<VectorType>();
301a088bed4SMatthias Springer   auto memrefShape = type.getShape();
302a088bed4SMatthias Springer   SmallVector<int64_t, 8> newMemrefShape;
303a088bed4SMatthias Springer   newMemrefShape.append(memrefShape.begin(), memrefShape.end());
304a088bed4SMatthias Springer   newMemrefShape.push_back(vectorType.getDimSize(0));
305a088bed4SMatthias Springer   return MemRefType::get(newMemrefShape,
306a088bed4SMatthias Springer                          VectorType::get(vectorType.getShape().drop_front(),
307a088bed4SMatthias Springer                                          vectorType.getElementType()));
308a088bed4SMatthias Springer }
309a088bed4SMatthias Springer 
3100f241638SMatthias Springer /// Given a transfer op, find the memref from which the mask is loaded. This
3110f241638SMatthias Springer /// is similar to Strategy<TransferWriteOp>::getBuffer.
3120f241638SMatthias Springer template <typename OpTy>
3130f241638SMatthias Springer static Value getMaskBuffer(OpTy xferOp) {
3140f241638SMatthias Springer   assert(xferOp.mask() && "Expected that transfer op has mask");
3150f241638SMatthias Springer   auto loadOp = xferOp.mask().template getDefiningOp<memref::LoadOp>();
3160f241638SMatthias Springer   assert(loadOp && "Expected transfer op mask produced by LoadOp");
3170f241638SMatthias Springer   return loadOp.getMemRef();
3180f241638SMatthias Springer }
3190f241638SMatthias Springer 
3200f241638SMatthias Springer /// Codegen strategy, depending on the operation.
3210f241638SMatthias Springer template <typename OpTy>
3220f241638SMatthias Springer struct Strategy;
3230f241638SMatthias Springer 
3240f241638SMatthias Springer /// Code strategy for vector TransferReadOp.
3254ead2cf7SAlex Zinenko template <>
3260f241638SMatthias Springer struct Strategy<TransferReadOp> {
3270f241638SMatthias Springer   /// Find the StoreOp that is used for writing the current TransferReadOp's
3280f241638SMatthias Springer   /// result to the temporary buffer allocation.
3290f241638SMatthias Springer   static memref::StoreOp getStoreOp(TransferReadOp xferOp) {
3300f241638SMatthias Springer     assert(xferOp->hasOneUse() && "Expected exactly one use of TransferReadOp");
3310f241638SMatthias Springer     auto storeOp = dyn_cast<memref::StoreOp>((*xferOp->use_begin()).getOwner());
3320f241638SMatthias Springer     assert(storeOp && "Expected TransferReadOp result used by StoreOp");
3330f241638SMatthias Springer     return storeOp;
3347c3c5b11SNicolas Vasilache   }
3354ead2cf7SAlex Zinenko 
3360f241638SMatthias Springer   /// Find the temporary buffer allocation. All labeled TransferReadOps are
3370f241638SMatthias Springer   /// used like this, where %buf is either the buffer allocation or a type cast
3380f241638SMatthias Springer   /// of the buffer allocation:
3390f241638SMatthias Springer   /// ```
3400f241638SMatthias Springer   /// %vec = vector.transfer_read ... { __vector_to_scf_lowering__ } ...
3410f241638SMatthias Springer   /// memref.store %vec, %buf[...] ...
3420f241638SMatthias Springer   /// ```
3430f241638SMatthias Springer   static Value getBuffer(TransferReadOp xferOp) {
3440f241638SMatthias Springer     return getStoreOp(xferOp).getMemRef();
3451870e787SNicolas Vasilache   }
3460f241638SMatthias Springer 
3470f241638SMatthias Springer   /// Retrieve the indices of the current StoreOp that stores into the buffer.
3480f241638SMatthias Springer   static void getBufferIndices(TransferReadOp xferOp,
3490f241638SMatthias Springer                                SmallVector<Value, 8> &indices) {
3500f241638SMatthias Springer     auto storeOp = getStoreOp(xferOp);
3510f241638SMatthias Springer     auto prevIndices = memref::StoreOpAdaptor(storeOp).indices();
3520f241638SMatthias Springer     indices.append(prevIndices.begin(), prevIndices.end());
3530f241638SMatthias Springer   }
3540f241638SMatthias Springer 
3550f241638SMatthias Springer   /// Rewrite the TransferReadOp, assuming that there are no out-of-bounds
3560f241638SMatthias Springer   /// accesses on the to-be-unpacked dimension.
3570f241638SMatthias Springer   ///
3580f241638SMatthias Springer   /// 1. Generate a new (N-1)-d TransferReadOp using the loop iteration
3590f241638SMatthias Springer   ///    variable `iv`.
3600f241638SMatthias Springer   /// 2. Store the result into the (already `vector.type_cast`ed) buffer.
3610f241638SMatthias Springer   ///
3620f241638SMatthias Springer   /// E.g.:
3630f241638SMatthias Springer   /// ```
3640f241638SMatthias Springer   /// %vec = vector.transfer_read %A[%a+%i, %b, %c], %cst
3650f241638SMatthias Springer   ///     : memref<?x?x?xf32>, vector<4x3xf32>
3660f241638SMatthias Springer   /// memref.store %vec, %buf[%i] : memref<5xvector<4x3xf32>>
3670f241638SMatthias Springer   /// ```
3680f241638SMatthias Springer   /// Is rewritten to:
3690f241638SMatthias Springer   /// ```
3700f241638SMatthias Springer   /// %casted = vector.type_cast %buf
3710f241638SMatthias Springer   ///     : memref<5xvector<4x3xf32>> to memref<5x4xvector<3xf32>>
3720f241638SMatthias Springer   /// for %j = 0 to 4 {
3730f241638SMatthias Springer   ///   %vec = vector.transfer_read %A[%a+%i, %b+%j, %c], %cst
3740f241638SMatthias Springer   ///       : memref<?x?x?xf32>, vector<3xf32>
3750f241638SMatthias Springer   ///   memref.store %vec, %casted[%i, %j] : memref<5x4xvector<3xf32>>
3760f241638SMatthias Springer   /// }
3770f241638SMatthias Springer   /// ```
3780f241638SMatthias Springer   ///
3790f241638SMatthias Springer   /// Note: The loop and type cast are generated in TransferOpConversion.
3800f241638SMatthias Springer   ///       The original TransferReadOp and store op are deleted in `cleanup`.
3810f241638SMatthias Springer   /// Note: The `mask` operand is set in TransferOpConversion.
3826825bfe2SNicolas Vasilache   static TransferReadOp rewriteOp(OpBuilder &b,
3832ca887deSMatthias Springer                                   VectorTransferToSCFOptions options,
384558e7401SMatthias Springer                                   TransferReadOp xferOp, Value buffer, Value iv,
385558e7401SMatthias Springer                                   ValueRange /*loopState*/) {
3860f241638SMatthias Springer     SmallVector<Value, 8> storeIndices;
3870f241638SMatthias Springer     getBufferIndices(xferOp, storeIndices);
3880f241638SMatthias Springer     storeIndices.push_back(iv);
3890f241638SMatthias Springer 
3900f241638SMatthias Springer     SmallVector<Value, 8> xferIndices;
3916825bfe2SNicolas Vasilache     getXferIndices(b, xferOp, iv, xferIndices);
3920f241638SMatthias Springer 
3936825bfe2SNicolas Vasilache     Location loc = xferOp.getLoc();
3940f241638SMatthias Springer     auto bufferType = buffer.getType().dyn_cast<ShapedType>();
3950f241638SMatthias Springer     auto vecType = bufferType.getElementType().dyn_cast<VectorType>();
3966825bfe2SNicolas Vasilache     auto inBoundsAttr = dropFirstElem(b, xferOp.in_boundsAttr());
3976825bfe2SNicolas Vasilache     auto newXferOp = b.create<vector::TransferReadOp>(
3986825bfe2SNicolas Vasilache         loc, vecType, xferOp.source(), xferIndices,
3996825bfe2SNicolas Vasilache         AffineMapAttr::get(unpackedPermutationMap(b, xferOp)), xferOp.padding(),
4006825bfe2SNicolas Vasilache         Value(), inBoundsAttr);
4010f241638SMatthias Springer 
4026825bfe2SNicolas Vasilache     maybeApplyPassLabel(b, newXferOp, options.targetRank);
4030f241638SMatthias Springer 
4046825bfe2SNicolas Vasilache     b.create<memref::StoreOp>(loc, newXferOp.vector(), buffer, storeIndices);
4056825bfe2SNicolas Vasilache     return newXferOp;
4060f241638SMatthias Springer   }
4070f241638SMatthias Springer 
4080f241638SMatthias Springer   /// Handle out-of-bounds accesses on the to-be-unpacked dimension: Write
4090f241638SMatthias Springer   /// padding value to the temporary buffer.
410558e7401SMatthias Springer   static Value handleOutOfBoundsDim(OpBuilder &b, TransferReadOp xferOp,
411558e7401SMatthias Springer                                     Value buffer, Value iv,
412558e7401SMatthias Springer                                     ValueRange /*loopState*/) {
4130f241638SMatthias Springer     SmallVector<Value, 8> storeIndices;
4140f241638SMatthias Springer     getBufferIndices(xferOp, storeIndices);
4150f241638SMatthias Springer     storeIndices.push_back(iv);
4160f241638SMatthias Springer 
4176825bfe2SNicolas Vasilache     Location loc = xferOp.getLoc();
4180f241638SMatthias Springer     auto bufferType = buffer.getType().dyn_cast<ShapedType>();
4190f241638SMatthias Springer     auto vecType = bufferType.getElementType().dyn_cast<VectorType>();
4206825bfe2SNicolas Vasilache     auto vec = b.create<SplatOp>(loc, vecType, xferOp.padding());
4216825bfe2SNicolas Vasilache     b.create<memref::StoreOp>(loc, vec, buffer, storeIndices);
422558e7401SMatthias Springer 
423558e7401SMatthias Springer     return Value();
4240f241638SMatthias Springer   }
4250f241638SMatthias Springer 
4260f241638SMatthias Springer   /// Cleanup after rewriting the op.
427558e7401SMatthias Springer   static void cleanup(PatternRewriter &rewriter, TransferReadOp xferOp,
428558e7401SMatthias Springer                       scf::ForOp /*forOp*/) {
4290f241638SMatthias Springer     rewriter.eraseOp(getStoreOp(xferOp));
4300f241638SMatthias Springer     rewriter.eraseOp(xferOp);
4310f241638SMatthias Springer   }
432558e7401SMatthias Springer 
433558e7401SMatthias Springer   /// Return the initial loop state for the generated scf.for loop.
434558e7401SMatthias Springer   static Value initialLoopState(TransferReadOp xferOp) { return Value(); }
4354ead2cf7SAlex Zinenko };
4367c3c5b11SNicolas Vasilache 
4370f241638SMatthias Springer /// Codegen strategy for vector TransferWriteOp.
4380f241638SMatthias Springer template <>
4390f241638SMatthias Springer struct Strategy<TransferWriteOp> {
4400f241638SMatthias Springer   /// Find the temporary buffer allocation. All labeled TransferWriteOps are
4410f241638SMatthias Springer   /// used like this, where %buf is either the buffer allocation or a type cast
4420f241638SMatthias Springer   /// of the buffer allocation:
4430f241638SMatthias Springer   /// ```
4440f241638SMatthias Springer   /// %vec = memref.load %buf[...] ...
4450f241638SMatthias Springer   /// vector.transfer_write %vec ... { __vector_to_scf_lowering__ } ...
4460f241638SMatthias Springer   /// ```
4470f241638SMatthias Springer   static Value getBuffer(TransferWriteOp xferOp) {
4480f241638SMatthias Springer     auto loadOp = xferOp.vector().getDefiningOp<memref::LoadOp>();
4490f241638SMatthias Springer     assert(loadOp && "Expected transfer op vector produced by LoadOp");
4500f241638SMatthias Springer     return loadOp.getMemRef();
4517c3c5b11SNicolas Vasilache   }
4524ead2cf7SAlex Zinenko 
4530f241638SMatthias Springer   /// Retrieve the indices of the current LoadOp that loads from the buffer.
4540f241638SMatthias Springer   static void getBufferIndices(TransferWriteOp xferOp,
4550f241638SMatthias Springer                                SmallVector<Value, 8> &indices) {
4560f241638SMatthias Springer     auto loadOp = xferOp.vector().getDefiningOp<memref::LoadOp>();
4570f241638SMatthias Springer     auto prevIndices = memref::LoadOpAdaptor(loadOp).indices();
4580f241638SMatthias Springer     indices.append(prevIndices.begin(), prevIndices.end());
4590f241638SMatthias Springer   }
4600f241638SMatthias Springer 
4610f241638SMatthias Springer   /// Rewrite the TransferWriteOp, assuming that there are no out-of-bounds
4620f241638SMatthias Springer   /// accesses on the to-be-unpacked dimension.
4630f241638SMatthias Springer   ///
4640f241638SMatthias Springer   /// 1. Load an (N-1)-d vector from the (already `vector.type_cast`ed) buffer,
4650f241638SMatthias Springer   ///    using the loop iteration variable `iv`.
4660f241638SMatthias Springer   /// 2. Generate a new (N-1)-d TransferWriteOp, writing the loaded vector back
4670f241638SMatthias Springer   ///    to memory.
4680f241638SMatthias Springer   ///
4690f241638SMatthias Springer   /// Note: For more details, see comments on Strategy<TransferReadOp>.
4706825bfe2SNicolas Vasilache   static TransferWriteOp rewriteOp(OpBuilder &b,
4712ca887deSMatthias Springer                                    VectorTransferToSCFOptions options,
4722ca887deSMatthias Springer                                    TransferWriteOp xferOp, Value buffer,
473558e7401SMatthias Springer                                    Value iv, ValueRange loopState) {
4740f241638SMatthias Springer     SmallVector<Value, 8> loadIndices;
4750f241638SMatthias Springer     getBufferIndices(xferOp, loadIndices);
4760f241638SMatthias Springer     loadIndices.push_back(iv);
4770f241638SMatthias Springer 
4780f241638SMatthias Springer     SmallVector<Value, 8> xferIndices;
4796825bfe2SNicolas Vasilache     getXferIndices(b, xferOp, iv, xferIndices);
4800f241638SMatthias Springer 
4816825bfe2SNicolas Vasilache     Location loc = xferOp.getLoc();
4826825bfe2SNicolas Vasilache     auto vec = b.create<memref::LoadOp>(loc, buffer, loadIndices);
4836825bfe2SNicolas Vasilache     auto inBoundsAttr = dropFirstElem(b, xferOp.in_boundsAttr());
484558e7401SMatthias Springer     auto source = loopState.empty() ? xferOp.source() : loopState[0];
485558e7401SMatthias Springer     Type type = isTensorOp(xferOp) ? xferOp.getShapedType() : Type();
4866825bfe2SNicolas Vasilache     auto newXferOp = b.create<vector::TransferWriteOp>(
487558e7401SMatthias Springer         loc, type, vec, source, xferIndices,
4886825bfe2SNicolas Vasilache         AffineMapAttr::get(unpackedPermutationMap(b, xferOp)), Value(),
4890f241638SMatthias Springer         inBoundsAttr);
4900f241638SMatthias Springer 
4916825bfe2SNicolas Vasilache     maybeApplyPassLabel(b, newXferOp, options.targetRank);
4920f241638SMatthias Springer 
4936825bfe2SNicolas Vasilache     return newXferOp;
4940f241638SMatthias Springer   }
4950f241638SMatthias Springer 
4960f241638SMatthias Springer   /// Handle out-of-bounds accesses on the to-be-unpacked dimension.
497558e7401SMatthias Springer   static Value handleOutOfBoundsDim(OpBuilder &b, TransferWriteOp xferOp,
498558e7401SMatthias Springer                                     Value buffer, Value iv,
499558e7401SMatthias Springer                                     ValueRange loopState) {
500558e7401SMatthias Springer     return isTensorOp(xferOp) ? loopState[0] : Value();
501558e7401SMatthias Springer   }
5020f241638SMatthias Springer 
5030f241638SMatthias Springer   /// Cleanup after rewriting the op.
504558e7401SMatthias Springer   static void cleanup(PatternRewriter &rewriter, TransferWriteOp xferOp,
505558e7401SMatthias Springer                       scf::ForOp forOp) {
506558e7401SMatthias Springer     if (isTensorOp(xferOp)) {
507558e7401SMatthias Springer       assert(forOp->getNumResults() == 1 && "Expected one for loop result");
508558e7401SMatthias Springer       rewriter.replaceOp(xferOp, forOp->getResult(0));
509558e7401SMatthias Springer     } else {
5100f241638SMatthias Springer       rewriter.eraseOp(xferOp);
5110f241638SMatthias Springer     }
512558e7401SMatthias Springer   }
513558e7401SMatthias Springer 
514558e7401SMatthias Springer   /// Return the initial loop state for the generated scf.for loop.
515558e7401SMatthias Springer   static Value initialLoopState(TransferWriteOp xferOp) {
516558e7401SMatthias Springer     return isTensorOp(xferOp) ? xferOp.source() : Value();
517558e7401SMatthias Springer   }
5180f241638SMatthias Springer };
5190f241638SMatthias Springer 
5200f241638SMatthias Springer template <typename OpTy>
521fb7ec1f1SMatthias Springer LogicalResult checkPrepareXferOp(OpTy xferOp,
522fb7ec1f1SMatthias Springer                                  VectorTransferToSCFOptions options) {
5230f241638SMatthias Springer   if (xferOp->hasAttr(kPassLabel))
5240f241638SMatthias Springer     return failure();
525fb7ec1f1SMatthias Springer   if (xferOp.getVectorType().getRank() <= options.targetRank)
5260f241638SMatthias Springer     return failure();
527558e7401SMatthias Springer   if (isTensorOp(xferOp) && !options.lowerTensors)
5288fb48979SMatthias Springer     return failure();
529f718a53dSMatthias Springer   // Transfer ops that modify the element type are not supported atm.
530f718a53dSMatthias Springer   if (xferOp.getVectorType().getElementType() !=
531f718a53dSMatthias Springer       xferOp.getShapedType().getElementType())
532f718a53dSMatthias Springer     return failure();
5330f241638SMatthias Springer   return success();
5340f241638SMatthias Springer }
5350f241638SMatthias Springer 
5360f241638SMatthias Springer /// Prepare a TransferReadOp for progressive lowering.
5370f241638SMatthias Springer ///
5380f241638SMatthias Springer /// 1. Allocate a temporary buffer.
5390f241638SMatthias Springer /// 2. Label the TransferReadOp, marking it eligible for progressive lowering.
5400f241638SMatthias Springer /// 3. Store the result of the TransferReadOp into the temporary buffer.
5410f241638SMatthias Springer /// 4. Load the result from the temporary buffer and replace all uses of the
5420f241638SMatthias Springer ///    original TransferReadOp with this load.
5430f241638SMatthias Springer ///
5440f241638SMatthias Springer /// E.g.:
5450f241638SMatthias Springer /// ```
5460f241638SMatthias Springer /// %vec = vector.transfer_read %A[%a, %b, %c], %cst
5470f241638SMatthias Springer ///     : vector<5x4xf32>, memref<?x?x?xf32>
5480f241638SMatthias Springer /// ```
5490f241638SMatthias Springer /// is rewritten to:
5500f241638SMatthias Springer /// ```
5510f241638SMatthias Springer /// %0 = memref.alloca() : memref<vector<5x4xf32>>
5520f241638SMatthias Springer /// %1 = vector.transfer_read %A[%a, %b, %c], %cst
5530f241638SMatthias Springer ///     { __vector_to_scf_lowering__ } : vector<5x4xf32>, memref<?x?x?xf32>
5540f241638SMatthias Springer /// memref.store %1, %0[] : memref<vector<5x4xf32>>
5550f241638SMatthias Springer /// %vec = memref.load %0[] : memref<vector<5x4xf32>>
5560f241638SMatthias Springer /// ```
5570f241638SMatthias Springer ///
5580f241638SMatthias Springer /// Note: A second temporary buffer may be allocated for the `mask` operand.
5592ca887deSMatthias Springer struct PrepareTransferReadConversion
5602ca887deSMatthias Springer     : public VectorToSCFPattern<TransferReadOp> {
5612ca887deSMatthias Springer   using VectorToSCFPattern<TransferReadOp>::VectorToSCFPattern;
5620f241638SMatthias Springer 
5630f241638SMatthias Springer   LogicalResult matchAndRewrite(TransferReadOp xferOp,
5640f241638SMatthias Springer                                 PatternRewriter &rewriter) const override {
565fb7ec1f1SMatthias Springer     if (checkPrepareXferOp(xferOp, options).failed())
5660f241638SMatthias Springer       return failure();
5670f241638SMatthias Springer 
5686825bfe2SNicolas Vasilache     auto buffers = allocBuffers(rewriter, xferOp);
5690f241638SMatthias Springer     auto *newXfer = rewriter.clone(*xferOp.getOperation());
5700f241638SMatthias Springer     newXfer->setAttr(kPassLabel, rewriter.getUnitAttr());
5710f241638SMatthias Springer     if (xferOp.mask()) {
5720f241638SMatthias Springer       dyn_cast<TransferReadOp>(newXfer).maskMutable().assign(
5730f241638SMatthias Springer           buffers.maskBuffer);
5740f241638SMatthias Springer     }
5750f241638SMatthias Springer 
5766825bfe2SNicolas Vasilache     Location loc = xferOp.getLoc();
5776825bfe2SNicolas Vasilache     rewriter.create<memref::StoreOp>(loc, newXfer->getResult(0),
5786825bfe2SNicolas Vasilache                                      buffers.dataBuffer);
5790f241638SMatthias Springer     rewriter.replaceOpWithNewOp<memref::LoadOp>(xferOp, buffers.dataBuffer);
5804ead2cf7SAlex Zinenko 
5814ead2cf7SAlex Zinenko     return success();
5824ead2cf7SAlex Zinenko   }
5830f241638SMatthias Springer };
5840f241638SMatthias Springer 
5850f241638SMatthias Springer /// Prepare a TransferWriteOp for progressive lowering.
5860f241638SMatthias Springer ///
5870f241638SMatthias Springer /// 1. Allocate a temporary buffer.
5880f241638SMatthias Springer /// 2. Store the vector into the buffer.
5890f241638SMatthias Springer /// 3. Load the vector from the buffer again.
5900f241638SMatthias Springer /// 4. Use the loaded vector as a TransferWriteOp operand and label the op,
5910f241638SMatthias Springer ///    marking it eligible for progressive lowering via TransferOpConversion.
5920f241638SMatthias Springer ///
5930f241638SMatthias Springer /// E.g.:
5940f241638SMatthias Springer /// ```
5950f241638SMatthias Springer /// vector.transfer_write %vec, %A[%a, %b, %c]
5960f241638SMatthias Springer ///     : vector<5x4xf32>, memref<?x?x?xf32>
5970f241638SMatthias Springer /// ```
5980f241638SMatthias Springer /// is rewritten to:
5990f241638SMatthias Springer /// ```
6000f241638SMatthias Springer /// %0 = memref.alloca() : memref<vector<5x4xf32>>
6010f241638SMatthias Springer /// memref.store %vec, %0[] : memref<vector<5x4xf32>>
6020f241638SMatthias Springer /// %1 = memref.load %0[] : memref<vector<5x4xf32>>
6030f241638SMatthias Springer /// vector.transfer_write %1, %A[%a, %b, %c] { __vector_to_scf_lowering__ }
6040f241638SMatthias Springer ///     : vector<5x4xf32>, memref<?x?x?xf32>
6050f241638SMatthias Springer /// ```
6060f241638SMatthias Springer ///
6070f241638SMatthias Springer /// Note: A second temporary buffer may be allocated for the `mask` operand.
6080f241638SMatthias Springer struct PrepareTransferWriteConversion
6092ca887deSMatthias Springer     : public VectorToSCFPattern<TransferWriteOp> {
6102ca887deSMatthias Springer   using VectorToSCFPattern<TransferWriteOp>::VectorToSCFPattern;
6110f241638SMatthias Springer 
6120f241638SMatthias Springer   LogicalResult matchAndRewrite(TransferWriteOp xferOp,
6130f241638SMatthias Springer                                 PatternRewriter &rewriter) const override {
614fb7ec1f1SMatthias Springer     if (checkPrepareXferOp(xferOp, options).failed())
6150f241638SMatthias Springer       return failure();
6160f241638SMatthias Springer 
6176825bfe2SNicolas Vasilache     Location loc = xferOp.getLoc();
6186825bfe2SNicolas Vasilache     auto buffers = allocBuffers(rewriter, xferOp);
6196825bfe2SNicolas Vasilache     rewriter.create<memref::StoreOp>(loc, xferOp.vector(), buffers.dataBuffer);
6206825bfe2SNicolas Vasilache     auto loadedVec = rewriter.create<memref::LoadOp>(loc, buffers.dataBuffer);
6210f241638SMatthias Springer     rewriter.updateRootInPlace(xferOp, [&]() {
6220f241638SMatthias Springer       xferOp.vectorMutable().assign(loadedVec);
6230f241638SMatthias Springer       xferOp->setAttr(kPassLabel, rewriter.getUnitAttr());
6240f241638SMatthias Springer     });
6250f241638SMatthias Springer 
6260f241638SMatthias Springer     if (xferOp.mask()) {
6270f241638SMatthias Springer       rewriter.updateRootInPlace(
6280f241638SMatthias Springer           xferOp, [&]() { xferOp.maskMutable().assign(buffers.maskBuffer); });
6290f241638SMatthias Springer     }
6300f241638SMatthias Springer 
6310f241638SMatthias Springer     return success();
6320f241638SMatthias Springer   }
6330f241638SMatthias Springer };
6340f241638SMatthias Springer 
6350f241638SMatthias Springer /// Progressive lowering of vector transfer ops: Unpack one dimension.
6360f241638SMatthias Springer ///
6370f241638SMatthias Springer /// 1. Unpack one dimension from the current buffer type and cast the buffer
6380f241638SMatthias Springer ///    to that new type. E.g.:
6390f241638SMatthias Springer ///    ```
6400f241638SMatthias Springer ///    %vec = memref.load %0[%1] : memref<5xvector<4x3xf32>>
6410f241638SMatthias Springer ///    vector.transfer_write %vec ...
6420f241638SMatthias Springer ///    ```
6430f241638SMatthias Springer ///    The following cast is generated:
6440f241638SMatthias Springer ///    ```
6450f241638SMatthias Springer ///    %casted = vector.type_cast %0
6460f241638SMatthias Springer ///        : memref<5xvector<4x3xf32>> to memref<5x4xvector<3xf32>>
6470f241638SMatthias Springer ///    ```
6480f241638SMatthias Springer /// 2. Generate a for loop and rewrite the transfer op according to the
6490f241638SMatthias Springer ///    corresponding Strategy<OpTy>. If the to-be-unpacked dimension can be
6500f241638SMatthias Springer ///    out-of-bounds, generate an if-check and handle both cases separately.
6510f241638SMatthias Springer /// 3. Clean up according to the corresponding Strategy<OpTy>.
652558e7401SMatthias Springer ///
653558e7401SMatthias Springer /// Note: If the transfer op is a TransferWriteOp and operates on a tensor
654558e7401SMatthias Springer /// source (as opposed to a memref source), then each iteration of the generated
655558e7401SMatthias Springer /// scf.for loop yields the new tensor value. E.g.:
656558e7401SMatthias Springer /// ```
657558e7401SMatthias Springer /// %result = scf.for i = 0 to 5 {
658558e7401SMatthias Springer ///   %0 = memref.load %buffer[i] : memref<5xvector<4x3xf32>>
659558e7401SMatthias Springer ///   %1 = vector.transfer_write %0, %source[...]
660558e7401SMatthias Springer ///       : vector<4x3xf32>, tensor<5x4x3xf32>
661558e7401SMatthias Springer ///   scf.yield %1 : tensor<5x4x3xf32>
662558e7401SMatthias Springer /// }
663558e7401SMatthias Springer /// ```
6640f241638SMatthias Springer template <typename OpTy>
6652ca887deSMatthias Springer struct TransferOpConversion : public VectorToSCFPattern<OpTy> {
6662ca887deSMatthias Springer   using VectorToSCFPattern<OpTy>::VectorToSCFPattern;
6670f241638SMatthias Springer 
668700b64dcSMatthias Springer   void initialize() {
669700b64dcSMatthias Springer     // This pattern recursively unpacks one dimension at a time. The recursion
670700b64dcSMatthias Springer     // bounded as the rank is strictly decreasing.
671700b64dcSMatthias Springer     this->setHasBoundedRewriteRecursion();
672700b64dcSMatthias Springer   }
673700b64dcSMatthias Springer 
6740f241638SMatthias Springer   LogicalResult matchAndRewrite(OpTy xferOp,
6750f241638SMatthias Springer                                 PatternRewriter &rewriter) const override {
6760f241638SMatthias Springer     if (!xferOp->hasAttr(kPassLabel))
6770f241638SMatthias Springer       return failure();
6780f241638SMatthias Springer 
6790f241638SMatthias Springer     // Find and cast data buffer. How the buffer can be found depends on OpTy.
6806825bfe2SNicolas Vasilache     ImplicitLocOpBuilder locB(xferOp.getLoc(), rewriter);
6810f241638SMatthias Springer     auto dataBuffer = Strategy<OpTy>::getBuffer(xferOp);
6820f241638SMatthias Springer     auto dataBufferType = dataBuffer.getType().template dyn_cast<MemRefType>();
6830f241638SMatthias Springer     auto castedDataType = unpackOneDim(dataBufferType);
6846825bfe2SNicolas Vasilache     auto castedDataBuffer =
6856825bfe2SNicolas Vasilache         locB.create<vector::TypeCastOp>(castedDataType, dataBuffer);
6860f241638SMatthias Springer 
6870f241638SMatthias Springer     // If the xferOp has a mask: Find and cast mask buffer.
6880f241638SMatthias Springer     Value castedMaskBuffer;
6890f241638SMatthias Springer     if (xferOp.mask()) {
6900f241638SMatthias Springer       auto maskBuffer = getMaskBuffer(xferOp);
6910f241638SMatthias Springer       auto maskBufferType =
6920f241638SMatthias Springer           maskBuffer.getType().template dyn_cast<MemRefType>();
6930f241638SMatthias Springer       if (xferOp.isBroadcastDim(0) || xferOp.getMaskType().getRank() == 1) {
6940f241638SMatthias Springer         // Do not unpack a dimension of the mask, if:
6950f241638SMatthias Springer         // * To-be-unpacked transfer op dimension is a broadcast.
6960f241638SMatthias Springer         // * Mask is 1D, i.e., the mask cannot be further unpacked.
6970f241638SMatthias Springer         //   (That means that all remaining dimensions of the transfer op must
6980f241638SMatthias Springer         //   be broadcasted.)
6990f241638SMatthias Springer         castedMaskBuffer = maskBuffer;
7000f241638SMatthias Springer       } else {
7010f241638SMatthias Springer         auto castedMaskType = unpackOneDim(maskBufferType);
7026825bfe2SNicolas Vasilache         castedMaskBuffer =
7036825bfe2SNicolas Vasilache             locB.create<vector::TypeCastOp>(castedMaskType, maskBuffer);
7040f241638SMatthias Springer       }
7050f241638SMatthias Springer     }
7060f241638SMatthias Springer 
7070f241638SMatthias Springer     // Loop bounds and step.
708a54f4eaeSMogball     auto lb = locB.create<arith::ConstantIndexOp>(0);
709a54f4eaeSMogball     auto ub = locB.create<arith::ConstantIndexOp>(
7106825bfe2SNicolas Vasilache         castedDataType.getDimSize(castedDataType.getRank() - 1));
711a54f4eaeSMogball     auto step = locB.create<arith::ConstantIndexOp>(1);
712558e7401SMatthias Springer     // TransferWriteOps that operate on tensors return the modified tensor and
713558e7401SMatthias Springer     // require a loop state.
714558e7401SMatthias Springer     auto loopState = Strategy<OpTy>::initialLoopState(xferOp);
7150f241638SMatthias Springer 
7160f241638SMatthias Springer     // Generate for loop.
717558e7401SMatthias Springer     auto result = locB.create<scf::ForOp>(
718558e7401SMatthias Springer         lb, ub, step, loopState ? ValueRange(loopState) : ValueRange(),
719558e7401SMatthias Springer         [&](OpBuilder &b, Location loc, Value iv, ValueRange loopState) {
720558e7401SMatthias Springer           Type stateType = loopState.empty() ? Type() : loopState[0].getType();
721558e7401SMatthias Springer 
722558e7401SMatthias Springer           auto result = generateInBoundsCheck(
7236825bfe2SNicolas Vasilache               b, xferOp, iv, unpackedDim(xferOp),
724558e7401SMatthias Springer               stateType ? TypeRange(stateType) : TypeRange(),
7250f241638SMatthias Springer               /*inBoundsCase=*/
7266825bfe2SNicolas Vasilache               [&](OpBuilder &b, Location loc) {
7270f241638SMatthias Springer                 // Create new transfer op.
7282ca887deSMatthias Springer                 OpTy newXfer = Strategy<OpTy>::rewriteOp(
729558e7401SMatthias Springer                     b, this->options, xferOp, castedDataBuffer, iv, loopState);
7300f241638SMatthias Springer 
7310f241638SMatthias Springer                 // If old transfer op has a mask: Set mask on new transfer op.
7320f241638SMatthias Springer                 // Special case: If the mask of the old transfer op is 1D and
7330f241638SMatthias Springer                 // the
7340f241638SMatthias Springer                 //               unpacked dim is not a broadcast, no mask is
7350f241638SMatthias Springer                 //               needed on the new transfer op.
7360f241638SMatthias Springer                 if (xferOp.mask() && (xferOp.isBroadcastDim(0) ||
7370f241638SMatthias Springer                                       xferOp.getMaskType().getRank() > 1)) {
7380f241638SMatthias Springer                   OpBuilder::InsertionGuard guard(b);
7390f241638SMatthias Springer                   b.setInsertionPoint(newXfer); // Insert load before newXfer.
7400f241638SMatthias Springer 
7410f241638SMatthias Springer                   SmallVector<Value, 8> loadIndices;
7420f241638SMatthias Springer                   Strategy<OpTy>::getBufferIndices(xferOp, loadIndices);
7430f241638SMatthias Springer                   // In case of broadcast: Use same indices to load from memref
7440f241638SMatthias Springer                   // as before.
7450f241638SMatthias Springer                   if (!xferOp.isBroadcastDim(0))
7460f241638SMatthias Springer                     loadIndices.push_back(iv);
7470f241638SMatthias Springer 
7486825bfe2SNicolas Vasilache                   auto mask = b.create<memref::LoadOp>(loc, castedMaskBuffer,
7496825bfe2SNicolas Vasilache                                                        loadIndices);
7500f241638SMatthias Springer                   rewriter.updateRootInPlace(
7510f241638SMatthias Springer                       newXfer, [&]() { newXfer.maskMutable().assign(mask); });
7520f241638SMatthias Springer                 }
753558e7401SMatthias Springer 
754558e7401SMatthias Springer                 return loopState.empty() ? Value() : newXfer->getResult(0);
7550f241638SMatthias Springer               },
7560f241638SMatthias Springer               /*outOfBoundsCase=*/
7570f241638SMatthias Springer               [&](OpBuilder &b, Location /*loc*/) {
758558e7401SMatthias Springer                 return Strategy<OpTy>::handleOutOfBoundsDim(
759558e7401SMatthias Springer                     b, xferOp, castedDataBuffer, iv, loopState);
7600f241638SMatthias Springer               });
7610f241638SMatthias Springer 
762558e7401SMatthias Springer           maybeYieldValue(b, loc, !loopState.empty(), result);
763558e7401SMatthias Springer         });
764558e7401SMatthias Springer 
765558e7401SMatthias Springer     Strategy<OpTy>::cleanup(rewriter, xferOp, result);
7660f241638SMatthias Springer     return success();
7670f241638SMatthias Springer   }
7680f241638SMatthias Springer };
7690f241638SMatthias Springer 
770a088bed4SMatthias Springer } // namespace lowering_n_d
771a088bed4SMatthias Springer 
772a088bed4SMatthias Springer namespace lowering_n_d_unrolled {
773a088bed4SMatthias Springer 
7740f241638SMatthias Springer /// If the original transfer op has a mask, compute the mask of the new transfer
7750f241638SMatthias Springer /// op (for the current iteration `i`) and assign it.
7760f241638SMatthias Springer template <typename OpTy>
7776825bfe2SNicolas Vasilache static void maybeAssignMask(OpBuilder &b, OpTy xferOp, OpTy newXferOp,
7780f241638SMatthias Springer                             int64_t i) {
7790f241638SMatthias Springer   if (!xferOp.mask())
7800f241638SMatthias Springer     return;
7810f241638SMatthias Springer 
7820f241638SMatthias Springer   if (xferOp.isBroadcastDim(0)) {
7830f241638SMatthias Springer     // To-be-unpacked dimension is a broadcast, which does not have a
7840f241638SMatthias Springer     // corresponding mask dimension. Mask attribute remains unchanged.
7850f241638SMatthias Springer     newXferOp.maskMutable().assign(xferOp.mask());
7860f241638SMatthias Springer     return;
7870f241638SMatthias Springer   }
7880f241638SMatthias Springer 
7890f241638SMatthias Springer   if (xferOp.getMaskType().getRank() > 1) {
7900f241638SMatthias Springer     // Unpack one dimension of the mask.
7916825bfe2SNicolas Vasilache     OpBuilder::InsertionGuard guard(b);
7926825bfe2SNicolas Vasilache     b.setInsertionPoint(newXferOp); // Insert load before newXfer.
7930f241638SMatthias Springer 
7940f241638SMatthias Springer     llvm::SmallVector<int64_t, 1> indices({i});
7956825bfe2SNicolas Vasilache     Location loc = xferOp.getLoc();
7966825bfe2SNicolas Vasilache     auto newMask = b.create<vector::ExtractOp>(loc, xferOp.mask(), indices);
7970f241638SMatthias Springer     newXferOp.maskMutable().assign(newMask);
7980f241638SMatthias Springer   }
7990f241638SMatthias Springer 
8000f241638SMatthias Springer   // If we end up here: The mask of the old transfer op is 1D and the unpacked
8010f241638SMatthias Springer   // dim is not a broadcast, so no mask is needed on the new transfer op.
8020f241638SMatthias Springer   // `generateInBoundsCheck` will have evaluated the mask already.
8030f241638SMatthias Springer }
8040f241638SMatthias Springer 
8050f241638SMatthias Springer /// Progressive lowering of vector TransferReadOp with unrolling: Unpack one
8060f241638SMatthias Springer /// dimension. This is similar to TransferOpConversion<TransferReadOp>, but no
8070f241638SMatthias Springer /// memref buffer is allocated and the SCF loop is fully unrolled.
8080f241638SMatthias Springer ///
8090f241638SMatthias Springer /// ```
8100f241638SMatthias Springer /// E.g.:
8110f241638SMatthias Springer /// ```
8120f241638SMatthias Springer /// %vec = vector.transfer_read %A[%a, %b, %c], %padding
8130f241638SMatthias Springer ///     : memref<?x?x?xf32>, vector<5x4xf32>
8140f241638SMatthias Springer /// ```
8150f241638SMatthias Springer /// is rewritten to IR such as (simplified):
8160f241638SMatthias Springer /// ```
8170f241638SMatthias Springer /// %v_init = splat %padding : vector<5x4xf32>
8180f241638SMatthias Springer /// %tmp0 = vector.transfer_read %A[%a, %b, %c], %padding
8190f241638SMatthias Springer ///     : memref<?x?x?xf32>, vector<4xf32>
8200f241638SMatthias Springer /// %v0 = vector.insert %tmp0, %v_init[0] : vector<4xf32> into vector<5x4xf32>
8210f241638SMatthias Springer /// %tmp1 = vector.transfer_read %A[%a, %b + 1, %c], %padding
8220f241638SMatthias Springer ///     : memref<?x?x?xf32>, vector<4xf32>
8230f241638SMatthias Springer /// %v1 = vector.insert %tmp1, %v0[1] : vector<4xf32> into vector<5x4xf32>
8240f241638SMatthias Springer /// ...
8250f241638SMatthias Springer /// %tmp4 = vector.transfer_read %A[%a, %b + 4, %c], %padding
8260f241638SMatthias Springer ///     : memref<?x?x?xf32>, vector<4xf32>
8270f241638SMatthias Springer /// %vec = vector.insert %tmp1, %v3[4] : vector<4xf32> into vector<5x4xf32>
8280f241638SMatthias Springer /// ```
8290f241638SMatthias Springer ///
8300f241638SMatthias Springer /// Note: As an optimization, if the result of the original TransferReadOp
8310f241638SMatthias Springer /// was directly inserted into another vector, no new %v_init vector is created.
8320f241638SMatthias Springer /// Instead, the new TransferReadOp results are inserted into that vector.
8332ca887deSMatthias Springer struct UnrollTransferReadConversion
8342ca887deSMatthias Springer     : public VectorToSCFPattern<TransferReadOp> {
8352ca887deSMatthias Springer   using VectorToSCFPattern<TransferReadOp>::VectorToSCFPattern;
8360f241638SMatthias Springer 
837700b64dcSMatthias Springer   void initialize() {
838700b64dcSMatthias Springer     // This pattern recursively unpacks one dimension at a time. The recursion
839700b64dcSMatthias Springer     // bounded as the rank is strictly decreasing.
840700b64dcSMatthias Springer     setHasBoundedRewriteRecursion();
841700b64dcSMatthias Springer   }
842700b64dcSMatthias Springer 
8430f241638SMatthias Springer   /// Return the vector into which the newly created TransferReadOp results
8440f241638SMatthias Springer   /// are inserted.
8450f241638SMatthias Springer   Value getResultVector(TransferReadOp xferOp,
8460f241638SMatthias Springer                         PatternRewriter &rewriter) const {
8470f241638SMatthias Springer     if (auto insertOp = getInsertOp(xferOp))
8480f241638SMatthias Springer       return insertOp.dest();
8496825bfe2SNicolas Vasilache     Location loc = xferOp.getLoc();
8506825bfe2SNicolas Vasilache     return rewriter.create<SplatOp>(loc, xferOp.getVectorType(),
8516825bfe2SNicolas Vasilache                                     xferOp.padding());
8520f241638SMatthias Springer   }
8530f241638SMatthias Springer 
8540f241638SMatthias Springer   /// If the result of the TransferReadOp has exactly one user, which is a
8550f241638SMatthias Springer   /// vector::InsertOp, return that operation.
8560f241638SMatthias Springer   vector::InsertOp getInsertOp(TransferReadOp xferOp) const {
8570f241638SMatthias Springer     if (xferOp->hasOneUse()) {
8580f241638SMatthias Springer       Operation *xferOpUser = *xferOp->getUsers().begin();
8590f241638SMatthias Springer       if (auto insertOp = dyn_cast<vector::InsertOp>(xferOpUser))
8600f241638SMatthias Springer         return insertOp;
8610f241638SMatthias Springer     }
8620f241638SMatthias Springer 
8630f241638SMatthias Springer     return vector::InsertOp();
8640f241638SMatthias Springer   }
8650f241638SMatthias Springer 
8660f241638SMatthias Springer   /// If the result of the TransferReadOp has exactly one user, which is a
8670f241638SMatthias Springer   /// vector::InsertOp, return that operation's indices.
8680f241638SMatthias Springer   void getInsertionIndices(TransferReadOp xferOp,
8690f241638SMatthias Springer                            SmallVector<int64_t, 8> &indices) const {
8700f241638SMatthias Springer     if (auto insertOp = getInsertOp(xferOp)) {
8710f241638SMatthias Springer       llvm::for_each(insertOp.position(), [&](Attribute attr) {
8720f241638SMatthias Springer         indices.push_back(attr.dyn_cast<IntegerAttr>().getInt());
8730f241638SMatthias Springer       });
8740f241638SMatthias Springer     }
8750f241638SMatthias Springer   }
8760f241638SMatthias Springer 
8770f241638SMatthias Springer   /// Rewrite the op: Unpack one dimension. Can handle masks, out-of-bounds
8780f241638SMatthias Springer   /// accesses, and broadcasts and transposes in permutation maps.
8790f241638SMatthias Springer   LogicalResult matchAndRewrite(TransferReadOp xferOp,
8800f241638SMatthias Springer                                 PatternRewriter &rewriter) const override {
8812ca887deSMatthias Springer     if (xferOp.getVectorType().getRank() <= options.targetRank)
8820f241638SMatthias Springer       return failure();
883bd20756dSMatthias Springer     if (isTensorOp(xferOp) && !options.lowerTensors)
8848fb48979SMatthias Springer       return failure();
885f718a53dSMatthias Springer     // Transfer ops that modify the element type are not supported atm.
886f718a53dSMatthias Springer     if (xferOp.getVectorType().getElementType() !=
887f718a53dSMatthias Springer         xferOp.getShapedType().getElementType())
888f718a53dSMatthias Springer       return failure();
8890f241638SMatthias Springer 
8900f241638SMatthias Springer     auto insertOp = getInsertOp(xferOp);
8910f241638SMatthias Springer     auto vec = getResultVector(xferOp, rewriter);
8920f241638SMatthias Springer     auto vecType = vec.getType().dyn_cast<VectorType>();
8930f241638SMatthias Springer     auto xferVecType = xferOp.getVectorType();
8940f241638SMatthias Springer     auto newXferVecType = VectorType::get(xferVecType.getShape().drop_front(),
8950f241638SMatthias Springer                                           xferVecType.getElementType());
8960f241638SMatthias Springer     int64_t dimSize = xferVecType.getShape()[0];
8970f241638SMatthias Springer 
8980f241638SMatthias Springer     // Generate fully unrolled loop of transfer ops.
8996825bfe2SNicolas Vasilache     Location loc = xferOp.getLoc();
9000f241638SMatthias Springer     for (int64_t i = 0; i < dimSize; ++i) {
901a54f4eaeSMogball       Value iv = rewriter.create<arith::ConstantIndexOp>(loc, i);
9020f241638SMatthias Springer 
9030f241638SMatthias Springer       vec = generateInBoundsCheck(
9046825bfe2SNicolas Vasilache           rewriter, xferOp, iv, unpackedDim(xferOp), TypeRange(vecType),
9050f241638SMatthias Springer           /*inBoundsCase=*/
9060f241638SMatthias Springer           [&](OpBuilder &b, Location loc) {
9070f241638SMatthias Springer             // Indices for the new transfer op.
9080f241638SMatthias Springer             SmallVector<Value, 8> xferIndices;
9096825bfe2SNicolas Vasilache             getXferIndices(b, xferOp, iv, xferIndices);
9100f241638SMatthias Springer 
9110f241638SMatthias Springer             // Indices for the new vector.insert op.
9120f241638SMatthias Springer             SmallVector<int64_t, 8> insertionIndices;
9130f241638SMatthias Springer             getInsertionIndices(xferOp, insertionIndices);
9140f241638SMatthias Springer             insertionIndices.push_back(i);
9150f241638SMatthias Springer 
9160f241638SMatthias Springer             auto inBoundsAttr = dropFirstElem(b, xferOp.in_boundsAttr());
9176825bfe2SNicolas Vasilache             auto newXferOp = b.create<vector::TransferReadOp>(
9186825bfe2SNicolas Vasilache                 loc, newXferVecType, xferOp.source(), xferIndices,
9196825bfe2SNicolas Vasilache                 AffineMapAttr::get(unpackedPermutationMap(b, xferOp)),
9206825bfe2SNicolas Vasilache                 xferOp.padding(), Value(), inBoundsAttr);
9210f241638SMatthias Springer             maybeAssignMask(b, xferOp, newXferOp, i);
9226825bfe2SNicolas Vasilache             return b.create<vector::InsertOp>(loc, newXferOp, vec,
9236825bfe2SNicolas Vasilache                                               insertionIndices);
9240f241638SMatthias Springer           },
9250f241638SMatthias Springer           /*outOfBoundsCase=*/
9260f241638SMatthias Springer           [&](OpBuilder &b, Location loc) {
9270f241638SMatthias Springer             // Loop through original (unmodified) vector.
9280f241638SMatthias Springer             return vec;
9290f241638SMatthias Springer           });
9300f241638SMatthias Springer     }
9310f241638SMatthias Springer 
9320f241638SMatthias Springer     if (insertOp) {
9330f241638SMatthias Springer       // Rewrite single user of the old TransferReadOp, which was an InsertOp.
9340f241638SMatthias Springer       rewriter.replaceOp(insertOp, vec);
9350f241638SMatthias Springer       rewriter.eraseOp(xferOp);
9360f241638SMatthias Springer     } else {
9370f241638SMatthias Springer       rewriter.replaceOp(xferOp, vec);
9380f241638SMatthias Springer     }
9390f241638SMatthias Springer 
9400f241638SMatthias Springer     return success();
9410f241638SMatthias Springer   }
9420f241638SMatthias Springer };
9430f241638SMatthias Springer 
9440f241638SMatthias Springer /// Progressive lowering of vector TransferWriteOp with unrolling: Unpack one
9450f241638SMatthias Springer /// dimension. This is similar to TransferOpConversion<TransferWriteOp>, but no
9460f241638SMatthias Springer /// memref buffer is allocated and the SCF loop is fully unrolled.
9470f241638SMatthias Springer ///
9480f241638SMatthias Springer /// ```
9490f241638SMatthias Springer /// E.g.:
9500f241638SMatthias Springer /// ```
9510f241638SMatthias Springer /// vector.transfer_write %vec, %A[%a, %b, %c]
9520f241638SMatthias Springer ///     : vector<5x4xf32>, memref<?x?x?xf32>
9530f241638SMatthias Springer /// ```
9540f241638SMatthias Springer /// is rewritten to IR such as (simplified):
9550f241638SMatthias Springer /// ```
9560f241638SMatthias Springer /// %v0 = vector.extract %vec[0] : vector<5x4xf32>
9570f241638SMatthias Springer /// vector.transfer_write %v0, %A[%a, %b, %c] : vector<4xf32>, memref<...>
9580f241638SMatthias Springer /// %v1 = vector.extract %vec[1] : vector<5x4xf32>
9590f241638SMatthias Springer /// vector.transfer_write %v1, %A[%a, %b + 1, %c] : vector<4xf32>, memref<...>
9600f241638SMatthias Springer /// ...
9610f241638SMatthias Springer /// %v4 = vector.extract %vec[4] : vector<5x4xf32>
9620f241638SMatthias Springer /// vector.transfer_write %v4, %A[%a, %b + 4, %c] : vector<4xf32>, memref<...>
9630f241638SMatthias Springer /// ```
9640f241638SMatthias Springer ///
9650f241638SMatthias Springer /// Note: As an optimization, if the vector of the original TransferWriteOp
9660f241638SMatthias Springer /// was directly extracted from another vector via an ExtractOp `a`, extract
9670f241638SMatthias Springer /// the vectors for the newly generated TransferWriteOps from `a`'s input. By
9680f241638SMatthias Springer /// doing so, `a` may become dead, and the number of ExtractOps generated during
9690f241638SMatthias Springer /// recursive application of this pattern will be minimal.
9700f241638SMatthias Springer struct UnrollTransferWriteConversion
9712ca887deSMatthias Springer     : public VectorToSCFPattern<TransferWriteOp> {
9722ca887deSMatthias Springer   using VectorToSCFPattern<TransferWriteOp>::VectorToSCFPattern;
9730f241638SMatthias Springer 
974700b64dcSMatthias Springer   void initialize() {
975700b64dcSMatthias Springer     // This pattern recursively unpacks one dimension at a time. The recursion
976700b64dcSMatthias Springer     // bounded as the rank is strictly decreasing.
977700b64dcSMatthias Springer     setHasBoundedRewriteRecursion();
978700b64dcSMatthias Springer   }
979700b64dcSMatthias Springer 
9800f241638SMatthias Springer   /// Return the vector from which newly generated ExtracOps will extract.
9810f241638SMatthias Springer   Value getDataVector(TransferWriteOp xferOp) const {
9820f241638SMatthias Springer     if (auto extractOp = getExtractOp(xferOp))
9830f241638SMatthias Springer       return extractOp.vector();
9840f241638SMatthias Springer     return xferOp.vector();
9850f241638SMatthias Springer   }
9860f241638SMatthias Springer 
9870f241638SMatthias Springer   /// If the input of the given TransferWriteOp is an ExtractOp, return it.
9880f241638SMatthias Springer   vector::ExtractOp getExtractOp(TransferWriteOp xferOp) const {
9890f241638SMatthias Springer     if (auto *op = xferOp.vector().getDefiningOp())
9900f241638SMatthias Springer       return dyn_cast<vector::ExtractOp>(op);
9910f241638SMatthias Springer     return vector::ExtractOp();
9920f241638SMatthias Springer   }
9930f241638SMatthias Springer 
9940f241638SMatthias Springer   /// If the input of the given TransferWriteOp is an ExtractOp, return its
9950f241638SMatthias Springer   /// indices.
9960f241638SMatthias Springer   void getExtractionIndices(TransferWriteOp xferOp,
9970f241638SMatthias Springer                             SmallVector<int64_t, 8> &indices) const {
9980f241638SMatthias Springer     if (auto extractOp = getExtractOp(xferOp)) {
9990f241638SMatthias Springer       llvm::for_each(extractOp.position(), [&](Attribute attr) {
10000f241638SMatthias Springer         indices.push_back(attr.dyn_cast<IntegerAttr>().getInt());
10010f241638SMatthias Springer       });
10020f241638SMatthias Springer     }
10030f241638SMatthias Springer   }
10040f241638SMatthias Springer 
10050f241638SMatthias Springer   /// Rewrite the op: Unpack one dimension. Can handle masks, out-of-bounds
10060f241638SMatthias Springer   /// accesses, and broadcasts and transposes in permutation maps.
10070f241638SMatthias Springer   LogicalResult matchAndRewrite(TransferWriteOp xferOp,
10080f241638SMatthias Springer                                 PatternRewriter &rewriter) const override {
10092ca887deSMatthias Springer     if (xferOp.getVectorType().getRank() <= options.targetRank)
10100f241638SMatthias Springer       return failure();
1011bd20756dSMatthias Springer     if (isTensorOp(xferOp) && !options.lowerTensors)
10128fb48979SMatthias Springer       return failure();
1013f718a53dSMatthias Springer     // Transfer ops that modify the element type are not supported atm.
1014f718a53dSMatthias Springer     if (xferOp.getVectorType().getElementType() !=
1015f718a53dSMatthias Springer         xferOp.getShapedType().getElementType())
1016f718a53dSMatthias Springer       return failure();
10170f241638SMatthias Springer 
10180f241638SMatthias Springer     auto vec = getDataVector(xferOp);
10190f241638SMatthias Springer     auto xferVecType = xferOp.getVectorType();
10200f241638SMatthias Springer     int64_t dimSize = xferVecType.getShape()[0];
1021bd20756dSMatthias Springer     auto source = xferOp.source(); // memref or tensor to be written to.
1022bd20756dSMatthias Springer     auto sourceType = isTensorOp(xferOp) ? xferOp.getShapedType() : Type();
10230f241638SMatthias Springer 
10240f241638SMatthias Springer     // Generate fully unrolled loop of transfer ops.
10256825bfe2SNicolas Vasilache     Location loc = xferOp.getLoc();
10260f241638SMatthias Springer     for (int64_t i = 0; i < dimSize; ++i) {
1027a54f4eaeSMogball       Value iv = rewriter.create<arith::ConstantIndexOp>(loc, i);
10280f241638SMatthias Springer 
1029bd20756dSMatthias Springer       auto updatedSource = generateInBoundsCheck(
10306825bfe2SNicolas Vasilache           rewriter, xferOp, iv, unpackedDim(xferOp),
1031bd20756dSMatthias Springer           isTensorOp(xferOp) ? TypeRange(sourceType) : TypeRange(),
1032bd20756dSMatthias Springer           /*inBoundsCase=*/
1033bd20756dSMatthias Springer           [&](OpBuilder &b, Location loc) {
10340f241638SMatthias Springer             // Indices for the new transfer op.
10350f241638SMatthias Springer             SmallVector<Value, 8> xferIndices;
10366825bfe2SNicolas Vasilache             getXferIndices(b, xferOp, iv, xferIndices);
10370f241638SMatthias Springer 
10380f241638SMatthias Springer             // Indices for the new vector.extract op.
10390f241638SMatthias Springer             SmallVector<int64_t, 8> extractionIndices;
10400f241638SMatthias Springer             getExtractionIndices(xferOp, extractionIndices);
10410f241638SMatthias Springer             extractionIndices.push_back(i);
10420f241638SMatthias Springer 
10436825bfe2SNicolas Vasilache             auto extracted =
10446825bfe2SNicolas Vasilache                 b.create<vector::ExtractOp>(loc, vec, extractionIndices);
10450f241638SMatthias Springer             auto inBoundsAttr = dropFirstElem(b, xferOp.in_boundsAttr());
10466825bfe2SNicolas Vasilache             auto newXferOp = b.create<vector::TransferWriteOp>(
1047bd20756dSMatthias Springer                 loc, sourceType, extracted, source, xferIndices,
10486825bfe2SNicolas Vasilache                 AffineMapAttr::get(unpackedPermutationMap(b, xferOp)), Value(),
10496825bfe2SNicolas Vasilache                 inBoundsAttr);
10500f241638SMatthias Springer 
10510f241638SMatthias Springer             maybeAssignMask(b, xferOp, newXferOp, i);
1052bd20756dSMatthias Springer 
1053bd20756dSMatthias Springer             return isTensorOp(xferOp) ? newXferOp->getResult(0) : Value();
1054bd20756dSMatthias Springer           },
1055bd20756dSMatthias Springer           /*outOfBoundsCase=*/
1056bd20756dSMatthias Springer           [&](OpBuilder &b, Location loc) {
1057bd20756dSMatthias Springer             return isTensorOp(xferOp) ? source : Value();
10580f241638SMatthias Springer           });
1059bd20756dSMatthias Springer 
1060bd20756dSMatthias Springer       if (isTensorOp(xferOp))
1061bd20756dSMatthias Springer         source = updatedSource;
10620f241638SMatthias Springer     }
10630f241638SMatthias Springer 
1064bd20756dSMatthias Springer     if (isTensorOp(xferOp))
1065bd20756dSMatthias Springer       rewriter.replaceOp(xferOp, source);
1066bd20756dSMatthias Springer     else
10670f241638SMatthias Springer       rewriter.eraseOp(xferOp);
1068bd20756dSMatthias Springer 
10690f241638SMatthias Springer     return success();
10700f241638SMatthias Springer   }
10710f241638SMatthias Springer };
10720f241638SMatthias Springer 
1073a088bed4SMatthias Springer } // namespace lowering_n_d_unrolled
1074a088bed4SMatthias Springer 
1075a088bed4SMatthias Springer namespace lowering_1_d {
1076a088bed4SMatthias Springer 
10770f241638SMatthias Springer /// Compute the indices into the memref for the LoadOp/StoreOp generated as
10780f241638SMatthias Springer /// part of TransferOp1dConversion. Return the memref dimension on which
10790f241638SMatthias Springer /// the transfer is operating. A return value of None indicates a broadcast.
10800f241638SMatthias Springer template <typename OpTy>
10810f241638SMatthias Springer static Optional<int64_t>
10826825bfe2SNicolas Vasilache get1dMemrefIndices(OpBuilder &b, OpTy xferOp, Value iv,
10830f241638SMatthias Springer                    SmallVector<Value, 8> &memrefIndices) {
10840f241638SMatthias Springer   auto indices = xferOp.indices();
10850f241638SMatthias Springer   auto map = xferOp.permutation_map();
10860f241638SMatthias Springer 
10870f241638SMatthias Springer   memrefIndices.append(indices.begin(), indices.end());
10880f241638SMatthias Springer   assert(map.getNumResults() == 1 &&
10890f241638SMatthias Springer          "Expected 1 permutation map result for 1D transfer");
10900f241638SMatthias Springer   if (auto expr = map.getResult(0).template dyn_cast<AffineDimExpr>()) {
10916825bfe2SNicolas Vasilache     Location loc = xferOp.getLoc();
10920f241638SMatthias Springer     auto dim = expr.getPosition();
10936825bfe2SNicolas Vasilache     AffineExpr d0, d1;
10946825bfe2SNicolas Vasilache     bindDims(xferOp.getContext(), d0, d1);
10956825bfe2SNicolas Vasilache     Value offset = memrefIndices[dim];
10966825bfe2SNicolas Vasilache     memrefIndices[dim] = makeComposedAffineApply(b, loc, d0 + d1, {offset, iv});
10970f241638SMatthias Springer     return dim;
10980f241638SMatthias Springer   }
10990f241638SMatthias Springer 
11000f241638SMatthias Springer   assert(xferOp.isBroadcastDim(0) &&
11010f241638SMatthias Springer          "Expected AffineDimExpr or AffineConstantExpr");
11020f241638SMatthias Springer   return None;
11030f241638SMatthias Springer }
11040f241638SMatthias Springer 
11050f241638SMatthias Springer /// Codegen strategy for TransferOp1dConversion, depending on the
11060f241638SMatthias Springer /// operation.
11070f241638SMatthias Springer template <typename OpTy>
11080f241638SMatthias Springer struct Strategy1d;
11090f241638SMatthias Springer 
11100f241638SMatthias Springer /// Codegen strategy for TransferReadOp.
11110f241638SMatthias Springer template <>
11120f241638SMatthias Springer struct Strategy1d<TransferReadOp> {
11136825bfe2SNicolas Vasilache   static void generateForLoopBody(OpBuilder &b, Location loc,
11140f241638SMatthias Springer                                   TransferReadOp xferOp, Value iv,
11150f241638SMatthias Springer                                   ValueRange loopState) {
11160f241638SMatthias Springer     SmallVector<Value, 8> indices;
11176825bfe2SNicolas Vasilache     auto dim = get1dMemrefIndices(b, xferOp, iv, indices);
1118a54f4eaeSMogball     Value ivI32 = b.create<arith::IndexCastOp>(
1119a54f4eaeSMogball         loc, IntegerType::get(b.getContext(), 32), iv);
11200f241638SMatthias Springer     auto vec = loopState[0];
11210f241638SMatthias Springer 
11220f241638SMatthias Springer     // In case of out-of-bounds access, leave `vec` as is (was initialized with
11230f241638SMatthias Springer     // padding value).
11240f241638SMatthias Springer     auto nextVec = generateInBoundsCheck(
11256825bfe2SNicolas Vasilache         b, xferOp, iv, dim, TypeRange(xferOp.getVectorType()),
11260f241638SMatthias Springer         /*inBoundsCase=*/
11276825bfe2SNicolas Vasilache         [&](OpBuilder &b, Location loc) {
11286825bfe2SNicolas Vasilache           Value val = b.create<memref::LoadOp>(loc, xferOp.source(), indices);
11296825bfe2SNicolas Vasilache           return b.create<vector::InsertElementOp>(loc, val, vec, ivI32);
11300f241638SMatthias Springer         },
11310f241638SMatthias Springer         /*outOfBoundsCase=*/
11320f241638SMatthias Springer         [&](OpBuilder & /*b*/, Location loc) { return vec; });
11336825bfe2SNicolas Vasilache     b.create<scf::YieldOp>(loc, nextVec);
11340f241638SMatthias Springer   }
11350f241638SMatthias Springer 
11366825bfe2SNicolas Vasilache   static Value initialLoopState(OpBuilder &b, TransferReadOp xferOp) {
11370f241638SMatthias Springer     // Inititalize vector with padding value.
11386825bfe2SNicolas Vasilache     Location loc = xferOp.getLoc();
11396825bfe2SNicolas Vasilache     return b.create<SplatOp>(loc, xferOp.getVectorType(), xferOp.padding());
11400f241638SMatthias Springer   }
11410f241638SMatthias Springer };
11420f241638SMatthias Springer 
11430f241638SMatthias Springer /// Codegen strategy for TransferWriteOp.
11440f241638SMatthias Springer template <>
11450f241638SMatthias Springer struct Strategy1d<TransferWriteOp> {
11466825bfe2SNicolas Vasilache   static void generateForLoopBody(OpBuilder &b, Location loc,
11470f241638SMatthias Springer                                   TransferWriteOp xferOp, Value iv,
11480f241638SMatthias Springer                                   ValueRange /*loopState*/) {
11490f241638SMatthias Springer     SmallVector<Value, 8> indices;
11506825bfe2SNicolas Vasilache     auto dim = get1dMemrefIndices(b, xferOp, iv, indices);
1151a54f4eaeSMogball     Value ivI32 = b.create<arith::IndexCastOp>(
1152a54f4eaeSMogball         loc, IntegerType::get(b.getContext(), 32), iv);
11530f241638SMatthias Springer 
11540f241638SMatthias Springer     // Nothing to do in case of out-of-bounds access.
11550f241638SMatthias Springer     generateInBoundsCheck(
11566825bfe2SNicolas Vasilache         b, xferOp, iv, dim,
11576825bfe2SNicolas Vasilache         /*inBoundsCase=*/[&](OpBuilder &b, Location loc) {
11586825bfe2SNicolas Vasilache           auto val =
11596825bfe2SNicolas Vasilache               b.create<vector::ExtractElementOp>(loc, xferOp.vector(), ivI32);
11606825bfe2SNicolas Vasilache           b.create<memref::StoreOp>(loc, val, xferOp.source(), indices);
11610f241638SMatthias Springer         });
11626825bfe2SNicolas Vasilache     b.create<scf::YieldOp>(loc);
11630f241638SMatthias Springer   }
11640f241638SMatthias Springer 
11656825bfe2SNicolas Vasilache   static Value initialLoopState(OpBuilder &b, TransferWriteOp xferOp) {
11666825bfe2SNicolas Vasilache     return Value();
11676825bfe2SNicolas Vasilache   }
11680f241638SMatthias Springer };
11690f241638SMatthias Springer 
11700f241638SMatthias Springer /// Return true if the last dimension of the MemRefType has unit stride.
11710f241638SMatthias Springer static bool isLastMemrefDimUnitStride(MemRefType type) {
11720f241638SMatthias Springer   int64_t offset;
11730f241638SMatthias Springer   SmallVector<int64_t, 4> strides;
11740f241638SMatthias Springer   auto successStrides = getStridesAndOffset(type, strides, offset);
11755017b0f8SMatthias Springer   return succeeded(successStrides) && (strides.empty() || strides.back() == 1);
11760f241638SMatthias Springer }
11770f241638SMatthias Springer 
11780f241638SMatthias Springer /// Lower a 1D vector transfer op to SCF using scalar loads/stores. This is
11790f241638SMatthias Springer /// necessary in cases where a 1D vector transfer op cannot be lowered into
11800f241638SMatthias Springer /// vector load/stores due to non-unit strides or broadcasts:
11810f241638SMatthias Springer ///
11820f241638SMatthias Springer /// * Transfer dimension is not the last memref dimension
11830f241638SMatthias Springer /// * Transfer dimension is a broadcast (i.e., scalar load + broadcast)
11840f241638SMatthias Springer /// * Memref has a layout map with non-unit stride on the last dimension
11850f241638SMatthias Springer ///
11860f241638SMatthias Springer /// This pattern generates IR as follows:
11870f241638SMatthias Springer ///
11880f241638SMatthias Springer /// 1. Generate a for loop iterating over each vector element.
11890f241638SMatthias Springer /// 2. Inside the loop, generate a InsertElementOp or ExtractElementOp,
11900f241638SMatthias Springer ///    depending on OpTy.
11910f241638SMatthias Springer ///
11920f241638SMatthias Springer /// TODO: In some cases (no masking, etc.), LLVM::MatrixColumnMajorLoadOp
11930f241638SMatthias Springer ///       can be generated instead of TransferOp1dConversion. Add such a pattern
11940f241638SMatthias Springer ///       to ConvertVectorToLLVM.
11950f241638SMatthias Springer ///
11960f241638SMatthias Springer /// E.g.:
11970f241638SMatthias Springer /// ```
11980f241638SMatthias Springer /// vector.transfer_write %vec, %A[%a, %b]
11990f241638SMatthias Springer ///    {permutation_map = affine_map<(d0, d1) -> (d0)>, in_bounds = [true]}
12000f241638SMatthias Springer ///    : vector<9xf32>, memref<?x?xf32>
12010f241638SMatthias Springer /// ```
12020f241638SMatthias Springer /// Is rewritten to approximately the following pseudo-IR:
12030f241638SMatthias Springer /// ```
12040f241638SMatthias Springer /// for i = 0 to 9 {
12050f241638SMatthias Springer ///   %t = vector.extractelement %vec[i] : vector<9xf32>
12060f241638SMatthias Springer ///   memref.store %t, %arg0[%a + i, %b] : memref<?x?xf32>
12070f241638SMatthias Springer /// }
12080f241638SMatthias Springer /// ```
12090f241638SMatthias Springer template <typename OpTy>
12102ca887deSMatthias Springer struct TransferOp1dConversion : public VectorToSCFPattern<OpTy> {
12112ca887deSMatthias Springer   using VectorToSCFPattern<OpTy>::VectorToSCFPattern;
12120f241638SMatthias Springer 
12130f241638SMatthias Springer   LogicalResult matchAndRewrite(OpTy xferOp,
12140f241638SMatthias Springer                                 PatternRewriter &rewriter) const override {
12150f241638SMatthias Springer     auto map = xferOp.permutation_map();
12160f241638SMatthias Springer     auto memRefType = xferOp.getShapedType().template dyn_cast<MemRefType>();
12170f241638SMatthias Springer 
12180f241638SMatthias Springer     if (!memRefType)
12190f241638SMatthias Springer       return failure();
12200f241638SMatthias Springer     if (xferOp.getVectorType().getRank() != 1)
12210f241638SMatthias Springer       return failure();
12220f241638SMatthias Springer     if (map.isMinorIdentity() && isLastMemrefDimUnitStride(memRefType))
12230f241638SMatthias Springer       return failure(); // Handled by ConvertVectorToLLVM
12240f241638SMatthias Springer 
12250f241638SMatthias Springer     // Loop bounds, step, state...
12266825bfe2SNicolas Vasilache     Location loc = xferOp.getLoc();
12270f241638SMatthias Springer     auto vecType = xferOp.getVectorType();
1228a54f4eaeSMogball     auto lb = rewriter.create<arith::ConstantIndexOp>(loc, 0);
1229a54f4eaeSMogball     auto ub =
1230a54f4eaeSMogball         rewriter.create<arith::ConstantIndexOp>(loc, vecType.getDimSize(0));
1231a54f4eaeSMogball     auto step = rewriter.create<arith::ConstantIndexOp>(loc, 1);
12326825bfe2SNicolas Vasilache     auto loopState = Strategy1d<OpTy>::initialLoopState(rewriter, xferOp);
12330f241638SMatthias Springer 
12340f241638SMatthias Springer     // Generate for loop.
12350f241638SMatthias Springer     rewriter.replaceOpWithNewOp<scf::ForOp>(
12360f241638SMatthias Springer         xferOp, lb, ub, step, loopState ? ValueRange(loopState) : ValueRange(),
12376825bfe2SNicolas Vasilache         [&](OpBuilder &b, Location loc, Value iv, ValueRange loopState) {
12386825bfe2SNicolas Vasilache           Strategy1d<OpTy>::generateForLoopBody(b, loc, xferOp, iv, loopState);
12390f241638SMatthias Springer         });
12400f241638SMatthias Springer 
12410f241638SMatthias Springer     return success();
12420f241638SMatthias Springer   }
12430f241638SMatthias Springer };
12444ead2cf7SAlex Zinenko 
1245a088bed4SMatthias Springer } // namespace lowering_1_d
1246df63eedeSBenjamin Kramer } // namespace
1247df63eedeSBenjamin Kramer 
124851d30c34SBenjamin Kramer namespace mlir {
124951d30c34SBenjamin Kramer 
12503393cc4cSNicolas Vasilache void populateVectorToSCFConversionPatterns(
1251dc4e913bSChris Lattner     RewritePatternSet &patterns, const VectorTransferToSCFOptions &options) {
12520f241638SMatthias Springer   if (options.unroll) {
1253a088bed4SMatthias Springer     patterns.add<lowering_n_d_unrolled::UnrollTransferReadConversion,
1254a088bed4SMatthias Springer                  lowering_n_d_unrolled::UnrollTransferWriteConversion>(
12552ca887deSMatthias Springer         patterns.getContext(), options);
12560f241638SMatthias Springer   } else {
1257a088bed4SMatthias Springer     patterns.add<lowering_n_d::PrepareTransferReadConversion,
1258a088bed4SMatthias Springer                  lowering_n_d::PrepareTransferWriteConversion,
1259a088bed4SMatthias Springer                  lowering_n_d::TransferOpConversion<TransferReadOp>,
1260a088bed4SMatthias Springer                  lowering_n_d::TransferOpConversion<TransferWriteOp>>(
1261a088bed4SMatthias Springer         patterns.getContext(), options);
12620f241638SMatthias Springer   }
12630f241638SMatthias Springer 
12642ca887deSMatthias Springer   if (options.targetRank == 1) {
1265a088bed4SMatthias Springer     patterns.add<lowering_1_d::TransferOp1dConversion<TransferReadOp>,
1266a088bed4SMatthias Springer                  lowering_1_d::TransferOp1dConversion<TransferWriteOp>>(
1267a088bed4SMatthias Springer         patterns.getContext(), options);
12680f241638SMatthias Springer   }
12694ead2cf7SAlex Zinenko }
12703393cc4cSNicolas Vasilache 
12713393cc4cSNicolas Vasilache } // namespace mlir
12723393cc4cSNicolas Vasilache 
12735f9e0466SNicolas Vasilache namespace {
12745f9e0466SNicolas Vasilache 
12755f9e0466SNicolas Vasilache struct ConvertVectorToSCFPass
12765f9e0466SNicolas Vasilache     : public ConvertVectorToSCFBase<ConvertVectorToSCFPass> {
12775f9e0466SNicolas Vasilache   ConvertVectorToSCFPass() = default;
12785f9e0466SNicolas Vasilache   ConvertVectorToSCFPass(const VectorTransferToSCFOptions &options) {
12795f9e0466SNicolas Vasilache     this->fullUnroll = options.unroll;
12802ca887deSMatthias Springer     this->targetRank = options.targetRank;
1281fb7ec1f1SMatthias Springer     this->lowerPermutationMaps = options.lowerPermutationMaps;
1282558e7401SMatthias Springer     this->lowerTensors = options.lowerTensors;
12835f9e0466SNicolas Vasilache   }
12845f9e0466SNicolas Vasilache 
12855f9e0466SNicolas Vasilache   void runOnFunction() override {
12862ca887deSMatthias Springer     VectorTransferToSCFOptions options;
1287fb7ec1f1SMatthias Springer     options.unroll = fullUnroll;
1288fb7ec1f1SMatthias Springer     options.targetRank = targetRank;
1289fb7ec1f1SMatthias Springer     options.lowerPermutationMaps = lowerPermutationMaps;
1290558e7401SMatthias Springer     options.lowerTensors = lowerTensors;
1291fb7ec1f1SMatthias Springer 
1292fb7ec1f1SMatthias Springer     // Lower permutation maps first.
1293fb7ec1f1SMatthias Springer     if (lowerPermutationMaps) {
1294fb7ec1f1SMatthias Springer       RewritePatternSet lowerTransferPatterns(getFunction().getContext());
1295fb7ec1f1SMatthias Springer       mlir::vector::populateVectorTransferPermutationMapLoweringPatterns(
1296fb7ec1f1SMatthias Springer           lowerTransferPatterns);
1297fb7ec1f1SMatthias Springer       (void)applyPatternsAndFoldGreedily(getFunction(),
1298fb7ec1f1SMatthias Springer                                          std::move(lowerTransferPatterns));
1299fb7ec1f1SMatthias Springer     }
13002ca887deSMatthias Springer 
1301dc4e913bSChris Lattner     RewritePatternSet patterns(getFunction().getContext());
13022ca887deSMatthias Springer     populateVectorToSCFConversionPatterns(patterns, options);
1303e21adfa3SRiver Riddle     (void)applyPatternsAndFoldGreedily(getFunction(), std::move(patterns));
13045f9e0466SNicolas Vasilache   }
13055f9e0466SNicolas Vasilache };
13065f9e0466SNicolas Vasilache 
13075f9e0466SNicolas Vasilache } // namespace
13085f9e0466SNicolas Vasilache 
13095f9e0466SNicolas Vasilache std::unique_ptr<Pass>
13105f9e0466SNicolas Vasilache mlir::createConvertVectorToSCFPass(const VectorTransferToSCFOptions &options) {
13115f9e0466SNicolas Vasilache   return std::make_unique<ConvertVectorToSCFPass>(options);
13125f9e0466SNicolas Vasilache }
1313