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 3956c638b5SNicolas Vasilache static bool hasMultiplyAddBody(Region &r) { 40307cfdf5SNicolas Vasilache if (!llvm::hasSingleElement(r)) 41307cfdf5SNicolas Vasilache return false; 42307cfdf5SNicolas Vasilache if (!llvm::hasNItems(r.front().begin(), r.front().end(), 3)) 43307cfdf5SNicolas Vasilache return false; 44307cfdf5SNicolas Vasilache 45307cfdf5SNicolas Vasilache using mlir::matchers::m_Val; 46706d992cSRahul Joshi auto a = m_Val(r.getArgument(0)); 47706d992cSRahul Joshi auto b = m_Val(r.getArgument(1)); 48706d992cSRahul Joshi auto c = m_Val(r.getArgument(2)); 49307cfdf5SNicolas Vasilache // TODO: Update this detection once we have matcher support for specifying 50307cfdf5SNicolas Vasilache // that any permutation of operands matches. 51136eb79aSFrederik Gossen auto pattern1 = m_Op<linalg::YieldOp>(m_Op<AddFOp>(m_Op<MulFOp>(a, b), c)); 52136eb79aSFrederik Gossen auto pattern2 = m_Op<linalg::YieldOp>(m_Op<AddFOp>(c, m_Op<MulFOp>(a, b))); 53136eb79aSFrederik Gossen auto pattern3 = m_Op<linalg::YieldOp>(m_Op<AddFOp>(m_Op<MulFOp>(b, a), c)); 54136eb79aSFrederik Gossen auto pattern4 = m_Op<linalg::YieldOp>(m_Op<AddFOp>(c, m_Op<MulFOp>(b, a))); 55136eb79aSFrederik Gossen auto pattern5 = m_Op<linalg::YieldOp>(m_Op<AddIOp>(m_Op<MulIOp>(a, b), c)); 56136eb79aSFrederik Gossen auto pattern6 = m_Op<linalg::YieldOp>(m_Op<AddIOp>(c, m_Op<MulIOp>(a, b))); 57136eb79aSFrederik Gossen auto pattern7 = m_Op<linalg::YieldOp>(m_Op<AddIOp>(m_Op<MulIOp>(b, a), c)); 58136eb79aSFrederik Gossen auto pattern8 = m_Op<linalg::YieldOp>(m_Op<AddIOp>(c, m_Op<MulIOp>(b, a))); 59307cfdf5SNicolas Vasilache return pattern1.match(&r.front().back()) || 60307cfdf5SNicolas Vasilache pattern2.match(&r.front().back()) || 61bf561dd2SBenjamin Kramer pattern3.match(&r.front().back()) || 62bf561dd2SBenjamin Kramer pattern4.match(&r.front().back()) || 63bf561dd2SBenjamin Kramer pattern5.match(&r.front().back()) || 64bf561dd2SBenjamin Kramer pattern6.match(&r.front().back()) || 65bf561dd2SBenjamin Kramer pattern7.match(&r.front().back()) || pattern8.match(&r.front().back()); 66307cfdf5SNicolas Vasilache } 67307cfdf5SNicolas Vasilache 68307cfdf5SNicolas Vasilache // TODO: Should be Tablegen'd from a single source that generates the op itself. 6956c638b5SNicolas Vasilache static LogicalResult isContraction(Operation *op) { 7056c638b5SNicolas Vasilache // TODO: interface for named ops. 7156c638b5SNicolas Vasilache if (isa<linalg::BatchMatmulOp, linalg::MatmulOp, linalg::MatvecOp, 725638df19SEugene Burmako linalg::VecmatOp, linalg::DotOp>(op)) 7356c638b5SNicolas Vasilache return success(); 7456c638b5SNicolas Vasilache 7556c638b5SNicolas Vasilache auto genericOp = dyn_cast<linalg::GenericOp>(op); 7656c638b5SNicolas Vasilache if (!genericOp) 7756c638b5SNicolas Vasilache return failure(); 7856c638b5SNicolas Vasilache 79d289a97fSRiver Riddle auto mapRange = genericOp.indexing_maps().getAsValueRange<AffineMapAttr>(); 8056c638b5SNicolas Vasilache return success( 8156c638b5SNicolas Vasilache genericOp.getNumInputs() == 2 && genericOp.getNumOutputs() == 1 && 8256c638b5SNicolas Vasilache llvm::all_of(mapRange, 8356c638b5SNicolas Vasilache [](AffineMap m) { return m.isProjectedPermutation(); }) && 8456c638b5SNicolas Vasilache hasMultiplyAddBody(genericOp.region())); 85307cfdf5SNicolas Vasilache } 86307cfdf5SNicolas Vasilache 87307cfdf5SNicolas Vasilache LogicalResult mlir::linalg::vectorizeLinalgOpPrecondition(Operation *op) { 88307cfdf5SNicolas Vasilache auto linalgOp = cast<linalg::LinalgOp>(op); 89307cfdf5SNicolas Vasilache // All types must be static shape to go to vector. 90307cfdf5SNicolas Vasilache for (Value operand : linalgOp.getInputsAndOutputBuffers()) 91307cfdf5SNicolas Vasilache if (!operand.getType().cast<ShapedType>().hasStaticShape()) 92307cfdf5SNicolas Vasilache return failure(); 93307cfdf5SNicolas Vasilache for (Type outputTensorType : linalgOp.getOutputTensorTypes()) 94307cfdf5SNicolas Vasilache if (!outputTensorType.cast<ShapedType>().hasStaticShape()) 95307cfdf5SNicolas Vasilache return failure(); 9656c638b5SNicolas Vasilache 97a1b9fb22SThomas Raoux if (isa<linalg::FillOp, linalg::CopyOp>(op)) 98307cfdf5SNicolas Vasilache return success(); 99307cfdf5SNicolas Vasilache 10056c638b5SNicolas Vasilache return isContraction(op); 101307cfdf5SNicolas Vasilache } 102307cfdf5SNicolas Vasilache 103307cfdf5SNicolas Vasilache void mlir::linalg::vectorizeLinalgOp(OpBuilder &builder, Operation *op) { 104307cfdf5SNicolas Vasilache assert(succeeded(vectorizeLinalgOpPrecondition(op))); 105307cfdf5SNicolas Vasilache 1061ee11432SNicolas Vasilache StringRef dbgPref = "\n[" DEBUG_TYPE "]: "; 1071ee11432SNicolas Vasilache (void)dbgPref; 108307cfdf5SNicolas Vasilache edsc::ScopedContext scope(builder, op->getLoc()); 10956c638b5SNicolas Vasilache // In the case of 0-D memrefs, return null and special case to scalar load or 11056c638b5SNicolas Vasilache // store later. 1119534192cSNicolas Vasilache auto extractVectorTypeFromScalarView = [](Value v) { 1129534192cSNicolas Vasilache MemRefType mt = v.getType().cast<MemRefType>(); 11356c638b5SNicolas Vasilache return mt.getShape().empty() 11456c638b5SNicolas Vasilache ? VectorType() 11556c638b5SNicolas Vasilache : VectorType::get(mt.getShape(), mt.getElementType()); 1169534192cSNicolas Vasilache }; 117*29d1fba7SThomas Raoux if (auto fillOp = dyn_cast<linalg::FillOp>(op)) { 118*29d1fba7SThomas Raoux // Vectorize fill as a vector.broadcast. 119*29d1fba7SThomas Raoux LLVM_DEBUG(dbgs() << dbgPref 120*29d1fba7SThomas Raoux << "Rewrite linalg.fill as vector.broadcast: " << *op); 121*29d1fba7SThomas Raoux Value viewOutput = fillOp.output(); 122*29d1fba7SThomas Raoux if (VectorType outputType = extractVectorTypeFromScalarView(viewOutput)) { 123*29d1fba7SThomas Raoux auto vecType = 124*29d1fba7SThomas Raoux VectorType::get(fillOp.getOutputBufferType(0).getShape(), 125*29d1fba7SThomas Raoux fillOp.getOutputBufferType(0).getElementType()); 126*29d1fba7SThomas Raoux Value vector = vector_broadcast(vecType, fillOp.value()); 127*29d1fba7SThomas Raoux Value zero = std_constant_index(0); 128*29d1fba7SThomas Raoux SmallVector<Value, 4> indicesOutput(outputType.getRank(), zero); 129*29d1fba7SThomas Raoux vector_transfer_write(vector, viewOutput, indicesOutput); 130*29d1fba7SThomas Raoux } else { 131*29d1fba7SThomas Raoux std_store(fillOp.value(), viewOutput); 132*29d1fba7SThomas Raoux } 133*29d1fba7SThomas Raoux return; 134*29d1fba7SThomas Raoux } 135a1b9fb22SThomas Raoux if (auto copyOp = dyn_cast<linalg::CopyOp>(op)) { 136a1b9fb22SThomas Raoux // Vectorize copy as a vector.transfer_read+vector.transfer_write. 137a1b9fb22SThomas Raoux LLVM_DEBUG(dbgs() << dbgPref 138a1b9fb22SThomas Raoux << "Rewrite linalg.copy as vector.transfer_read + " 139a1b9fb22SThomas Raoux "vector.transfer_write: " 140a1b9fb22SThomas Raoux << *op); 141a1b9fb22SThomas Raoux Value zero = std_constant_index(0); 142a1b9fb22SThomas Raoux Value viewInput = copyOp.input(); 143a1b9fb22SThomas Raoux Value viewOutput = copyOp.output(); 144a1b9fb22SThomas Raoux Value vector; 145a1b9fb22SThomas Raoux if (VectorType inputType = extractVectorTypeFromScalarView(viewInput)) { 146a1b9fb22SThomas Raoux SmallVector<Value, 4> indicesInput(inputType.getRank(), zero); 147a1b9fb22SThomas Raoux if (copyOp.inputPermutation()) 148a1b9fb22SThomas Raoux vector = vector_transfer_read( 149a1b9fb22SThomas Raoux extractVectorTypeFromScalarView(viewInput), viewInput, indicesInput, 150a1b9fb22SThomas Raoux copyOp.inputPermutation().getValue()); 151a1b9fb22SThomas Raoux else 152a1b9fb22SThomas Raoux vector = 153a1b9fb22SThomas Raoux vector_transfer_read(extractVectorTypeFromScalarView(viewInput), 154a1b9fb22SThomas Raoux viewInput, indicesInput); 155a1b9fb22SThomas Raoux } else { 156a1b9fb22SThomas Raoux vector = std_load(viewInput).value; 157a1b9fb22SThomas Raoux } 158a1b9fb22SThomas Raoux if (VectorType outputType = extractVectorTypeFromScalarView(viewOutput)) { 159a1b9fb22SThomas Raoux SmallVector<Value, 4> indicesOutput(outputType.getRank(), zero); 160a1b9fb22SThomas Raoux if (copyOp.outputPermutation()) 161a1b9fb22SThomas Raoux vector_transfer_write(vector, viewOutput, indicesOutput, 162a1b9fb22SThomas Raoux copyOp.outputPermutation().getValue()); 163a1b9fb22SThomas Raoux else 164a1b9fb22SThomas Raoux vector_transfer_write(vector, viewOutput, indicesOutput); 165a1b9fb22SThomas Raoux } else { 166a1b9fb22SThomas Raoux std_store(vector, viewOutput); 167a1b9fb22SThomas Raoux } 168a1b9fb22SThomas Raoux return; 169a1b9fb22SThomas Raoux } 170a1b9fb22SThomas Raoux 171a1b9fb22SThomas Raoux assert(succeeded(isContraction(op)) && "Expected contraction"); 172a1b9fb22SThomas Raoux 173a1b9fb22SThomas Raoux // Vectorize other ops as vector contraction. 174a1b9fb22SThomas Raoux // TODO: interface. 175a1b9fb22SThomas Raoux LLVM_DEBUG(dbgs() << dbgPref 176a1b9fb22SThomas Raoux << "Rewrite linalg op as vector.contract: " << *op); 177307cfdf5SNicolas Vasilache auto linalgOp = cast<linalg::LinalgOp>(op); 1789534192cSNicolas Vasilache Value viewA = linalgOp.getInput(0); 1799534192cSNicolas Vasilache Value viewB = linalgOp.getInput(1); 1809534192cSNicolas Vasilache Value viewC = linalgOp.getOutputBuffer(0); 18156c638b5SNicolas Vasilache VectorType vtA = extractVectorTypeFromScalarView(viewA); 18256c638b5SNicolas Vasilache VectorType vtB = extractVectorTypeFromScalarView(viewB); 18356c638b5SNicolas Vasilache VectorType vtC = extractVectorTypeFromScalarView(viewC); 1849534192cSNicolas Vasilache Value zero = std_constant_index(0); 18556c638b5SNicolas Vasilache SmallVector<Value, 4> indicesA, indicesB, indicesC; 18656c638b5SNicolas Vasilache if (vtA) 18756c638b5SNicolas Vasilache indicesA = SmallVector<Value, 4>(vtA.getRank(), zero); 18856c638b5SNicolas Vasilache if (vtB) 18956c638b5SNicolas Vasilache indicesB = SmallVector<Value, 4>(vtB.getRank(), zero); 19056c638b5SNicolas Vasilache if (vtC) 19156c638b5SNicolas Vasilache indicesC = SmallVector<Value, 4>(vtC.getRank(), zero); 19256c638b5SNicolas Vasilache Value a = vtA ? vector_transfer_read(vtA, viewA, indicesA).value 19356c638b5SNicolas Vasilache : std_load(viewA, indicesA).value; 19456c638b5SNicolas Vasilache Value b = vtB ? vector_transfer_read(vtB, viewB, indicesB).value 19556c638b5SNicolas Vasilache : std_load(viewB, indicesB).value; 19656c638b5SNicolas Vasilache Value c = vtC ? vector_transfer_read(vtC, viewC, indicesC).value 19756c638b5SNicolas Vasilache : std_load(viewC, indicesC).value; 198307cfdf5SNicolas Vasilache Value res = vector_contract(a, b, c, linalgOp.indexing_maps(), 199307cfdf5SNicolas Vasilache linalgOp.iterator_types()); 20056c638b5SNicolas Vasilache if (vtC) 2019534192cSNicolas Vasilache vector_transfer_write(res, viewC, indicesC); 20256c638b5SNicolas Vasilache else 20356c638b5SNicolas Vasilache std_store(res, viewC, indicesC); 204307cfdf5SNicolas Vasilache } 2051ee11432SNicolas Vasilache 2061ee11432SNicolas Vasilache /// Check whether there is any interleaved use of any `values` between `firstOp` 2071ee11432SNicolas Vasilache /// and `secondOp`. Conservatively return `true` if any op or value is in a 2081ee11432SNicolas Vasilache /// different block. 2091ee11432SNicolas Vasilache static bool mayExistInterleavedUses(Operation *firstOp, Operation *secondOp, 2101ee11432SNicolas Vasilache ValueRange values) { 2111ee11432SNicolas Vasilache StringRef dbgPref = "\n[" DEBUG_TYPE "]: "; 2121ee11432SNicolas Vasilache (void)dbgPref; 2131ee11432SNicolas Vasilache if (firstOp->getBlock() != secondOp->getBlock() || 2141ee11432SNicolas Vasilache !firstOp->isBeforeInBlock(secondOp)) { 2151ee11432SNicolas Vasilache LLVM_DEBUG(llvm::dbgs() 2161ee11432SNicolas Vasilache << dbgPref << "interleavedUses precondition failed, firstOp: " 2171ee11432SNicolas Vasilache << *firstOp << ", second op: " << *secondOp); 2181ee11432SNicolas Vasilache return true; 2191ee11432SNicolas Vasilache } 2201ee11432SNicolas Vasilache for (auto v : values) { 2211ee11432SNicolas Vasilache for (auto &u : v.getUses()) { 2221ee11432SNicolas Vasilache Operation *owner = u.getOwner(); 2231ee11432SNicolas Vasilache if (owner == firstOp || owner == secondOp) 2241ee11432SNicolas Vasilache continue; 2251ee11432SNicolas Vasilache // TODO: this is too conservative, use dominance info in the future. 2261ee11432SNicolas Vasilache if (owner->getBlock() == firstOp->getBlock() && 2271ee11432SNicolas Vasilache (owner->isBeforeInBlock(firstOp) || secondOp->isBeforeInBlock(owner))) 2281ee11432SNicolas Vasilache continue; 2291ee11432SNicolas Vasilache LLVM_DEBUG(llvm::dbgs() 2301ee11432SNicolas Vasilache << dbgPref << " found interleaved op " << *owner 2311ee11432SNicolas Vasilache << ", firstOp: " << *firstOp << ", second op: " << *secondOp); 2321ee11432SNicolas Vasilache return true; 2331ee11432SNicolas Vasilache } 2341ee11432SNicolas Vasilache } 2351ee11432SNicolas Vasilache return false; 2361ee11432SNicolas Vasilache } 2371ee11432SNicolas Vasilache 2381ee11432SNicolas Vasilache /// Return the unique subview use of `v` if it is indeed unique, null otherwise. 2391ee11432SNicolas Vasilache static SubViewOp getSubViewUseIfUnique(Value v) { 2401ee11432SNicolas Vasilache SubViewOp subViewOp; 2411ee11432SNicolas Vasilache for (auto &u : v.getUses()) { 2421ee11432SNicolas Vasilache if (auto newSubViewOp = dyn_cast<SubViewOp>(u.getOwner())) { 2431ee11432SNicolas Vasilache if (subViewOp) 2441ee11432SNicolas Vasilache return SubViewOp(); 2451ee11432SNicolas Vasilache subViewOp = newSubViewOp; 2461ee11432SNicolas Vasilache } 2471ee11432SNicolas Vasilache } 2481ee11432SNicolas Vasilache return subViewOp; 2491ee11432SNicolas Vasilache } 2501ee11432SNicolas Vasilache 2511ee11432SNicolas Vasilache /// TODO: use interfaces, side-effects and aliasing analysis as appropriate, 2521ee11432SNicolas Vasilache /// when available. 2531ee11432SNicolas Vasilache LogicalResult LinalgCopyVTRForwardingPattern::matchAndRewrite( 2541ee11432SNicolas Vasilache vector::TransferReadOp xferOp, PatternRewriter &rewriter) const { 2551ee11432SNicolas Vasilache 2561ee11432SNicolas Vasilache // Transfer into `view`. 2571ee11432SNicolas Vasilache Value viewOrAlloc = xferOp.memref(); 2581ee11432SNicolas Vasilache if (!viewOrAlloc.getDefiningOp<ViewOp>() && 2591ee11432SNicolas Vasilache !viewOrAlloc.getDefiningOp<AllocOp>()) 2601ee11432SNicolas Vasilache return failure(); 2611ee11432SNicolas Vasilache 2621ee11432SNicolas Vasilache StringRef dbgPref = "\n[" DEBUG_TYPE "]: VTRForwarding: "; 2631ee11432SNicolas Vasilache (void)dbgPref; 2641ee11432SNicolas Vasilache LLVM_DEBUG(llvm::dbgs() << dbgPref << viewOrAlloc); 2651ee11432SNicolas Vasilache 2661ee11432SNicolas Vasilache // Ensure there is exactly one subview of `viewOrAlloc` defining `subView`. 2671ee11432SNicolas Vasilache SubViewOp subViewOp = getSubViewUseIfUnique(viewOrAlloc); 2681ee11432SNicolas Vasilache if (!subViewOp) 2691ee11432SNicolas Vasilache return failure(); 2701ee11432SNicolas Vasilache Value subView = subViewOp.getResult(); 2711ee11432SNicolas Vasilache LLVM_DEBUG(llvm::dbgs() << dbgPref << "with subView " << subView); 2721ee11432SNicolas Vasilache 2731ee11432SNicolas Vasilache // Find the copy into `subView` without interleaved uses. 2741ee11432SNicolas Vasilache CopyOp copyOp; 2751ee11432SNicolas Vasilache for (auto &u : subView.getUses()) { 2761ee11432SNicolas Vasilache if (auto newCopyOp = dyn_cast<CopyOp>(u.getOwner())) { 2771ee11432SNicolas Vasilache if (newCopyOp.getOutputBuffer(0) != subView) 2781ee11432SNicolas Vasilache continue; 2791ee11432SNicolas Vasilache LLVM_DEBUG(llvm::dbgs() << dbgPref << "copy candidate " << *newCopyOp); 2801ee11432SNicolas Vasilache if (mayExistInterleavedUses(newCopyOp, xferOp, {viewOrAlloc, subView})) 2811ee11432SNicolas Vasilache continue; 2821ee11432SNicolas Vasilache copyOp = newCopyOp; 2831ee11432SNicolas Vasilache break; 2841ee11432SNicolas Vasilache } 2851ee11432SNicolas Vasilache } 2861ee11432SNicolas Vasilache if (!copyOp) 2871ee11432SNicolas Vasilache return failure(); 2881ee11432SNicolas Vasilache LLVM_DEBUG(llvm::dbgs() << dbgPref << "with copy " << *copyOp); 2891ee11432SNicolas Vasilache 2901ee11432SNicolas Vasilache // Find the fill into `viewOrAlloc` without interleaved uses before the copy. 2911ee11432SNicolas Vasilache FillOp maybeFillOp; 2921ee11432SNicolas Vasilache for (auto &u : viewOrAlloc.getUses()) { 2931ee11432SNicolas Vasilache if (auto newFillOp = dyn_cast<FillOp>(u.getOwner())) { 2941ee11432SNicolas Vasilache if (newFillOp.getOutputBuffer(0) != viewOrAlloc) 2951ee11432SNicolas Vasilache continue; 2961ee11432SNicolas Vasilache LLVM_DEBUG(llvm::dbgs() << dbgPref << "fill candidate " << *newFillOp); 2971ee11432SNicolas Vasilache if (mayExistInterleavedUses(newFillOp, copyOp, {viewOrAlloc, subView})) 2981ee11432SNicolas Vasilache continue; 2991ee11432SNicolas Vasilache maybeFillOp = newFillOp; 3001ee11432SNicolas Vasilache break; 3011ee11432SNicolas Vasilache } 3021ee11432SNicolas Vasilache } 3031ee11432SNicolas Vasilache // Ensure padding matches. 3041ee11432SNicolas Vasilache if (maybeFillOp && xferOp.padding() != maybeFillOp.value()) 3051ee11432SNicolas Vasilache return failure(); 3061ee11432SNicolas Vasilache if (maybeFillOp) 3071ee11432SNicolas Vasilache LLVM_DEBUG(llvm::dbgs() << dbgPref << "with maybeFillOp " << *maybeFillOp); 3081ee11432SNicolas Vasilache 3091ee11432SNicolas Vasilache // `in` is the subview that linalg.copy reads. Replace it. 3101ee11432SNicolas Vasilache Value in = copyOp.getInput(0); 3111ee11432SNicolas Vasilache 312512da70bSNicolas Vasilache // linalg.copy + linalg.fill can be used to create a padded local buffer. 313512da70bSNicolas Vasilache // The `masked` attribute is only valid on this padded buffer. 314512da70bSNicolas Vasilache // When forwarding to vector.transfer_read, the attribute must be reset 315512da70bSNicolas Vasilache // conservatively. 3161ee11432SNicolas Vasilache Value res = rewriter.create<vector::TransferReadOp>( 3171ee11432SNicolas Vasilache xferOp.getLoc(), xferOp.getVectorType(), in, xferOp.indices(), 318512da70bSNicolas Vasilache xferOp.permutation_map(), xferOp.padding(), ArrayAttr()); 3191ee11432SNicolas Vasilache 3201ee11432SNicolas Vasilache if (maybeFillOp) 3211ee11432SNicolas Vasilache rewriter.eraseOp(maybeFillOp); 3221ee11432SNicolas Vasilache rewriter.eraseOp(copyOp); 3231ee11432SNicolas Vasilache rewriter.replaceOp(xferOp, res); 3241ee11432SNicolas Vasilache 3251ee11432SNicolas Vasilache return success(); 3261ee11432SNicolas Vasilache } 3271ee11432SNicolas Vasilache 3281ee11432SNicolas Vasilache /// TODO: use interfaces, side-effects and aliasing analysis as appropriate, 3291ee11432SNicolas Vasilache /// when available. 3301ee11432SNicolas Vasilache LogicalResult LinalgCopyVTWForwardingPattern::matchAndRewrite( 3311ee11432SNicolas Vasilache vector::TransferWriteOp xferOp, PatternRewriter &rewriter) const { 3321ee11432SNicolas Vasilache // Transfer into `viewOrAlloc`. 3331ee11432SNicolas Vasilache Value viewOrAlloc = xferOp.memref(); 3341ee11432SNicolas Vasilache if (!viewOrAlloc.getDefiningOp<ViewOp>() && 3351ee11432SNicolas Vasilache !viewOrAlloc.getDefiningOp<AllocOp>()) 3361ee11432SNicolas Vasilache return failure(); 3371ee11432SNicolas Vasilache 3381ee11432SNicolas Vasilache // Ensure there is exactly one subview of `viewOrAlloc` defining `subView`. 3391ee11432SNicolas Vasilache SubViewOp subViewOp = getSubViewUseIfUnique(viewOrAlloc); 3401ee11432SNicolas Vasilache if (!subViewOp) 3411ee11432SNicolas Vasilache return failure(); 3421ee11432SNicolas Vasilache Value subView = subViewOp.getResult(); 3431ee11432SNicolas Vasilache 3441ee11432SNicolas Vasilache // Find the copy from `subView` without interleaved uses. 3451ee11432SNicolas Vasilache CopyOp copyOp; 3461ee11432SNicolas Vasilache for (auto &u : subViewOp.getResult().getUses()) { 3471ee11432SNicolas Vasilache if (auto newCopyOp = dyn_cast<CopyOp>(u.getOwner())) { 3481ee11432SNicolas Vasilache if (newCopyOp.getInput(0) != subView) 3491ee11432SNicolas Vasilache continue; 3501ee11432SNicolas Vasilache if (mayExistInterleavedUses(xferOp, newCopyOp, {viewOrAlloc, subView})) 3511ee11432SNicolas Vasilache continue; 3521ee11432SNicolas Vasilache copyOp = newCopyOp; 3531ee11432SNicolas Vasilache break; 3541ee11432SNicolas Vasilache } 3551ee11432SNicolas Vasilache } 3561ee11432SNicolas Vasilache if (!copyOp) 3571ee11432SNicolas Vasilache return failure(); 3581ee11432SNicolas Vasilache 3591ee11432SNicolas Vasilache // `out` is the subview copied into that we replace. 3601ee11432SNicolas Vasilache Value out = copyOp.getOutputBuffer(0); 3611ee11432SNicolas Vasilache 3621ee11432SNicolas Vasilache // Forward vector.transfer into copy. 363512da70bSNicolas Vasilache // linalg.copy + linalg.fill can be used to create a padded local buffer. 364512da70bSNicolas Vasilache // The `masked` attribute is only valid on this padded buffer. 365512da70bSNicolas Vasilache // When forwarding to vector.transfer_write, the attribute must be reset 366512da70bSNicolas Vasilache // conservatively. 3671ee11432SNicolas Vasilache rewriter.create<vector::TransferWriteOp>( 3681ee11432SNicolas Vasilache xferOp.getLoc(), xferOp.vector(), out, xferOp.indices(), 369512da70bSNicolas Vasilache xferOp.permutation_map(), ArrayAttr()); 3701ee11432SNicolas Vasilache 3711ee11432SNicolas Vasilache rewriter.eraseOp(copyOp); 3721ee11432SNicolas Vasilache rewriter.eraseOp(xferOp); 3731ee11432SNicolas Vasilache 3741ee11432SNicolas Vasilache return success(); 3751ee11432SNicolas Vasilache } 37667b37f57SJakub Lichman 37767b37f57SJakub Lichman template <class ConvOp, int N> 37867b37f57SJakub Lichman LogicalResult ConvOpVectorization<ConvOp, N>::matchAndRewrite( 37967b37f57SJakub Lichman ConvOp op, PatternRewriter &rewriter) const { 38067b37f57SJakub Lichman Location loc = op.getLoc(); 38167b37f57SJakub Lichman MLIRContext *context = op.getContext(); 38267b37f57SJakub Lichman edsc::ScopedContext scope(rewriter, loc); 38367b37f57SJakub Lichman 38467b37f57SJakub Lichman ShapedType inShapeType = op.getInputShapedType(0); 38567b37f57SJakub Lichman ShapedType kShapeType = op.getInputShapedType(1); 38667b37f57SJakub Lichman 38767b37f57SJakub Lichman ArrayRef<int64_t> inShape = inShapeType.getShape(); 38867b37f57SJakub Lichman ArrayRef<int64_t> kShape = kShapeType.getShape(); 38967b37f57SJakub Lichman 39067b37f57SJakub Lichman if (!inShapeType.hasStaticShape() || !kShapeType.hasStaticShape()) 39167b37f57SJakub Lichman return failure(); 39267b37f57SJakub Lichman 39367b37f57SJakub Lichman SmallVector<AffineExpr, 4> mapping; 3940b17d475SJakub Lichman SmallVector<int64_t, 4> vectorDims; 3950b17d475SJakub Lichman // Fail to apply when the size of not vectorized dimension is not 1. 39667b37f57SJakub Lichman for (unsigned i = 0; i < N; i++) { 39767b37f57SJakub Lichman if (!mask[i] && (inShape[i] != 1 || kShape[i] != 1)) 39867b37f57SJakub Lichman return failure(); 3990b17d475SJakub Lichman 4000b17d475SJakub Lichman if (mask[i] && inShape[i] != kShape[i]) 40167b37f57SJakub Lichman return failure(); 40267b37f57SJakub Lichman 4030b17d475SJakub Lichman if (mask[i]) { 40467b37f57SJakub Lichman mapping.push_back(getAffineDimExpr(i, context)); 4050b17d475SJakub Lichman vectorDims.push_back(inShape[i]); 4060b17d475SJakub Lichman } 40767b37f57SJakub Lichman } 40867b37f57SJakub Lichman 40967b37f57SJakub Lichman Value input = op.getInput(0); 41067b37f57SJakub Lichman Value kernel = op.getInput(1); 41167b37f57SJakub Lichman Value output = op.getOutputBuffer(0); 41267b37f57SJakub Lichman 41383d82d1fSJakub Lichman unsigned rank = inShapeType.getRank(); 41483d82d1fSJakub Lichman unsigned numDims = mapping.size(); 41567b37f57SJakub Lichman Type elemType = inShapeType.getElementType(); 41667b37f57SJakub Lichman 41767b37f57SJakub Lichman auto map = AffineMap::get(rank, 0, mapping, context); 41867b37f57SJakub Lichman SmallVector<Value, 4> zeros(rank, std_constant_index(0)); 4190b17d475SJakub Lichman auto vecType = VectorType::get(vectorDims, elemType); 42067b37f57SJakub Lichman 42167b37f57SJakub Lichman auto inputVec = vector_transfer_read(vecType, input, zeros, map); 42267b37f57SJakub Lichman auto kernelVec = vector_transfer_read(vecType, kernel, zeros, map); 42367b37f57SJakub Lichman 42467b37f57SJakub Lichman auto acc = std_constant(elemType, rewriter.getZeroAttr(elemType)); 42567b37f57SJakub Lichman 42667b37f57SJakub Lichman std::array<AffineMap, 3> indexingMaps{ 42767b37f57SJakub Lichman AffineMap::getMultiDimIdentityMap(numDims, context), 42867b37f57SJakub Lichman AffineMap::getMultiDimIdentityMap(numDims, context), 42967b37f57SJakub Lichman AffineMap::get(numDims, 0, {}, context)}; 43067b37f57SJakub Lichman 43167b37f57SJakub Lichman std::vector<StringRef> iteratorTypes(numDims, "reduction"); 43267b37f57SJakub Lichman 43367b37f57SJakub Lichman auto result = rewriter.create<vector::ContractionOp>( 43467b37f57SJakub Lichman loc, inputVec, kernelVec, acc, 43567b37f57SJakub Lichman rewriter.getAffineMapArrayAttr(indexingMaps), 43667b37f57SJakub Lichman rewriter.getStrArrayAttr(iteratorTypes)); 43767b37f57SJakub Lichman 43867b37f57SJakub Lichman rewriter.create<StoreOp>(loc, result, output, ValueRange(zeros)); 43967b37f57SJakub Lichman rewriter.eraseOp(op); 44067b37f57SJakub Lichman return success(); 44167b37f57SJakub Lichman } 44267b37f57SJakub Lichman 443347d59b1SJakub Lichman using ConvOpConst = ConvOpVectorization<ConvWOp, 1>; 444347d59b1SJakub Lichman 445347d59b1SJakub Lichman /// Inserts tiling, promotion and vectorization pattern for ConvOp 446347d59b1SJakub Lichman /// conversion into corresponding pattern lists. 447347d59b1SJakub Lichman template <typename ConvOp, unsigned N> 448347d59b1SJakub Lichman static void 449347d59b1SJakub Lichman populateVectorizationPatterns(OwningRewritePatternList &tilingPatterns, 450347d59b1SJakub Lichman OwningRewritePatternList &promotionPatterns, 451347d59b1SJakub Lichman OwningRewritePatternList &vectorizationPatterns, 452347d59b1SJakub Lichman ArrayRef<int64_t> tileSizes, 453347d59b1SJakub Lichman MLIRContext *context) { 4540b17d475SJakub Lichman if (tileSizes.size() < N) 4550b17d475SJakub Lichman return; 4560b17d475SJakub Lichman 457347d59b1SJakub Lichman constexpr static StringRef kTiledMarker = "TILED"; 458347d59b1SJakub Lichman constexpr static StringRef kPromotedMarker = "PROMOTED"; 459347d59b1SJakub Lichman tilingPatterns.insert<LinalgTilingPattern<ConvOp>>( 460347d59b1SJakub Lichman context, LinalgTilingOptions().setTileSizes(tileSizes), 461347d59b1SJakub Lichman LinalgMarker({}, Identifier::get(kTiledMarker, context))); 462347d59b1SJakub Lichman 463347d59b1SJakub Lichman promotionPatterns.insert<LinalgPromotionPattern<ConvOp>>( 464347d59b1SJakub Lichman context, LinalgPromotionOptions().setUseFullTileBuffersByDefault(true), 465347d59b1SJakub Lichman LinalgMarker(Identifier::get(kTiledMarker, context), 466347d59b1SJakub Lichman Identifier::get(kPromotedMarker, context))); 467347d59b1SJakub Lichman 468347d59b1SJakub Lichman SmallVector<bool, 4> mask(N); 469347d59b1SJakub Lichman int offset = tileSizes.size() - N; 470347d59b1SJakub Lichman std::transform(tileSizes.begin() + offset, tileSizes.end(), mask.begin(), 4710b17d475SJakub Lichman [](int64_t i) -> bool { return i > 1; }); 472347d59b1SJakub Lichman 473347d59b1SJakub Lichman vectorizationPatterns.insert<ConvOpVectorization<ConvOp, N>>(context, mask); 474347d59b1SJakub Lichman } 475347d59b1SJakub Lichman 47667b37f57SJakub Lichman void mlir::linalg::populateConvVectorizationPatterns( 4770b17d475SJakub Lichman MLIRContext *context, SmallVectorImpl<OwningRewritePatternList> &patterns, 4780b17d475SJakub Lichman ArrayRef<int64_t> tileSizes) { 479347d59b1SJakub Lichman OwningRewritePatternList tiling, promotion, vectorization; 4800b17d475SJakub Lichman populateVectorizationPatterns<ConvWOp, 1>(tiling, promotion, vectorization, 4810b17d475SJakub Lichman tileSizes, context); 48267b37f57SJakub Lichman 483347d59b1SJakub Lichman populateVectorizationPatterns<ConvNWCOp, 3>(tiling, promotion, vectorization, 4840b17d475SJakub Lichman tileSizes, context); 48567b37f57SJakub Lichman 486347d59b1SJakub Lichman populateVectorizationPatterns<ConvNCWOp, 3>(tiling, promotion, vectorization, 4870b17d475SJakub Lichman tileSizes, context); 48867b37f57SJakub Lichman 489347d59b1SJakub Lichman populateVectorizationPatterns<ConvHWOp, 2>(tiling, promotion, vectorization, 4900b17d475SJakub Lichman tileSizes, context); 49167b37f57SJakub Lichman 492347d59b1SJakub Lichman populateVectorizationPatterns<ConvNHWCOp, 4>(tiling, promotion, vectorization, 4930b17d475SJakub Lichman tileSizes, context); 49467b37f57SJakub Lichman 495347d59b1SJakub Lichman populateVectorizationPatterns<ConvNCHWOp, 4>(tiling, promotion, vectorization, 4960b17d475SJakub Lichman tileSizes, context); 49767b37f57SJakub Lichman 498347d59b1SJakub Lichman populateVectorizationPatterns<ConvDHWOp, 3>(tiling, promotion, vectorization, 4990b17d475SJakub Lichman tileSizes, context); 50067b37f57SJakub Lichman 501347d59b1SJakub Lichman populateVectorizationPatterns<ConvNDHWCOp, 5>( 5020b17d475SJakub Lichman tiling, promotion, vectorization, tileSizes, context); 503347d59b1SJakub Lichman 504347d59b1SJakub Lichman populateVectorizationPatterns<ConvNCDHWOp, 5>( 5050b17d475SJakub Lichman tiling, promotion, vectorization, tileSizes, context); 506347d59b1SJakub Lichman 507347d59b1SJakub Lichman patterns.push_back(std::move(tiling)); 508347d59b1SJakub Lichman patterns.push_back(std::move(promotion)); 509347d59b1SJakub Lichman patterns.push_back(std::move(vectorization)); 51067b37f57SJakub Lichman } 511