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 87*c503dc1bSThomas Raoux static bool hasOnlyScalarElementwiseOp(Region &r) { 88*c503dc1bSThomas Raoux if (!llvm::hasSingleElement(r)) 89*c503dc1bSThomas Raoux return false; 90*c503dc1bSThomas Raoux for (Operation &op : r.front()) { 91*c503dc1bSThomas Raoux if (!(isa<ConstantOp, linalg::YieldOp>(op) || 92*c503dc1bSThomas Raoux op.hasTrait<OpTrait::ElementwiseMappable>()) || 93*c503dc1bSThomas Raoux llvm::any_of(op.getResultTypes(), 94*c503dc1bSThomas Raoux [](Type type) { return !type.isIntOrIndexOrFloat(); })) 95*c503dc1bSThomas Raoux return false; 96*c503dc1bSThomas Raoux } 97*c503dc1bSThomas Raoux return true; 98*c503dc1bSThomas Raoux } 99*c503dc1bSThomas Raoux 100*c503dc1bSThomas Raoux // Return true if the op is an element-wise linalg op. 101*c503dc1bSThomas Raoux static bool isElementwise(Operation *op) { 102*c503dc1bSThomas Raoux auto genericOp = dyn_cast<linalg::GenericOp>(op); 103*c503dc1bSThomas Raoux if (!genericOp) 104*c503dc1bSThomas Raoux return false; 105*c503dc1bSThomas Raoux if (genericOp.getNumLoops() != genericOp.getNumParallelLoops()) 106*c503dc1bSThomas Raoux return false; 107*c503dc1bSThomas Raoux // TODO: relax the restrictions on indexing map. 108*c503dc1bSThomas Raoux for (unsigned i = 0, e = genericOp.getNumOutputs(); i < e; i++) { 109*c503dc1bSThomas Raoux if (!genericOp.getOutputIndexingMap(i).isIdentity()) 110*c503dc1bSThomas Raoux return false; 111*c503dc1bSThomas Raoux } 112*c503dc1bSThomas Raoux // Currently limit the input indexing map to minor identity as other 113*c503dc1bSThomas Raoux // permutations might require adding transpose ops to convert the vector read 114*c503dc1bSThomas Raoux // to the right shape. 115*c503dc1bSThomas Raoux for (unsigned i = 0, e = genericOp.getNumInputs(); i < e; i++) { 116*c503dc1bSThomas Raoux if (!genericOp.getInputIndexingMap(i).isMinorIdentity()) 117*c503dc1bSThomas Raoux return false; 118*c503dc1bSThomas Raoux } 119*c503dc1bSThomas Raoux return hasOnlyScalarElementwiseOp(genericOp.getRegion()); 120*c503dc1bSThomas Raoux } 121*c503dc1bSThomas Raoux 122*c503dc1bSThomas Raoux static VectorType extractVectorTypeFromScalarView(Value v) { 123*c503dc1bSThomas Raoux MemRefType mt = v.getType().cast<MemRefType>(); 124*c503dc1bSThomas Raoux return mt.getShape().empty() 125*c503dc1bSThomas Raoux ? VectorType() 126*c503dc1bSThomas Raoux : VectorType::get(mt.getShape(), mt.getElementType()); 127*c503dc1bSThomas Raoux } 128*c503dc1bSThomas Raoux 129*c503dc1bSThomas Raoux static Value transferReadVector(OpBuilder &builder, Value memref) { 130*c503dc1bSThomas Raoux edsc::ScopedContext scope(builder); 131*c503dc1bSThomas Raoux auto memrefType = memref.getType().cast<MemRefType>(); 132*c503dc1bSThomas Raoux if (VectorType vectorType = extractVectorTypeFromScalarView(memref)) { 133*c503dc1bSThomas Raoux SmallVector<Value, 4> indices(memrefType.getRank(), std_constant_index(0)); 134*c503dc1bSThomas Raoux return vector_transfer_read(vectorType, memref, indices); 135*c503dc1bSThomas Raoux } 136*c503dc1bSThomas Raoux return std_load(memref); 137*c503dc1bSThomas Raoux } 138*c503dc1bSThomas Raoux 139*c503dc1bSThomas Raoux static void transferWriteVector(OpBuilder &builder, Value value, Value memref) { 140*c503dc1bSThomas Raoux edsc::ScopedContext scope(builder); 141*c503dc1bSThomas Raoux auto memrefType = memref.getType().cast<MemRefType>(); 142*c503dc1bSThomas Raoux if (VectorType vectorType = extractVectorTypeFromScalarView(memref)) { 143*c503dc1bSThomas Raoux SmallVector<Value, 4> indices(memrefType.getRank(), std_constant_index(0)); 144*c503dc1bSThomas Raoux if (vectorType != value.getType()) 145*c503dc1bSThomas Raoux value = vector_broadcast(vectorType, value); 146*c503dc1bSThomas Raoux vector_transfer_write(value, memref, indices); 147*c503dc1bSThomas Raoux } else { 148*c503dc1bSThomas Raoux std_store(value, memref); 149*c503dc1bSThomas Raoux } 150*c503dc1bSThomas Raoux } 151*c503dc1bSThomas Raoux 152*c503dc1bSThomas Raoux namespace { 153*c503dc1bSThomas Raoux // Transforms scalar operations into their vectorized counterparts, 154*c503dc1bSThomas Raoux // while using the provided generic op to map: 155*c503dc1bSThomas Raoux // * Its arguments to transfer reads from the views of the generic op. 156*c503dc1bSThomas Raoux // * linalg.yield ops to transfer writes to the views of the generic op. 157*c503dc1bSThomas Raoux class GenericVectorizer { 158*c503dc1bSThomas Raoux public: 159*c503dc1bSThomas Raoux GenericVectorizer(OpBuilder &builder, linalg::GenericOp generic) 160*c503dc1bSThomas Raoux : builder(builder), generic(generic) {} 161*c503dc1bSThomas Raoux 162*c503dc1bSThomas Raoux // Takes a scalar operation and builds its vectorized counterpart or 163*c503dc1bSThomas Raoux // counterparts using the underlying builder. 164*c503dc1bSThomas Raoux // If operands of the scalar operation are referring to previously vectorized 165*c503dc1bSThomas Raoux // operations, then in their vectorized form these operands will be referring 166*c503dc1bSThomas Raoux // to previous vectorization results. 167*c503dc1bSThomas Raoux void vectorize(Operation &scalarOp) { 168*c503dc1bSThomas Raoux auto yieldOp = dyn_cast<linalg::YieldOp>(scalarOp); 169*c503dc1bSThomas Raoux if (yieldOp) { 170*c503dc1bSThomas Raoux for (auto outputAndMemref : 171*c503dc1bSThomas Raoux llvm::zip(yieldOp.values(), generic.getOutputBuffers())) { 172*c503dc1bSThomas Raoux Value vectorValue = vectorize(std::get<0>(outputAndMemref)); 173*c503dc1bSThomas Raoux transferWriteVector(builder, vectorValue, std::get<1>(outputAndMemref)); 174*c503dc1bSThomas Raoux } 175*c503dc1bSThomas Raoux return; 176*c503dc1bSThomas Raoux } 177*c503dc1bSThomas Raoux Operation *vectorOp = uncachedVectorize(scalarOp); 178*c503dc1bSThomas Raoux assert(scalarOp.getNumResults() == vectorOp->getNumResults()); 179*c503dc1bSThomas Raoux for (auto result : 180*c503dc1bSThomas Raoux llvm::zip(scalarOp.getResults(), vectorOp->getResults())) { 181*c503dc1bSThomas Raoux valueCache[std::get<0>(result)] = std::get<1>(result); 182*c503dc1bSThomas Raoux } 183*c503dc1bSThomas Raoux } 184*c503dc1bSThomas Raoux 185*c503dc1bSThomas Raoux private: 186*c503dc1bSThomas Raoux // Transforms a scalar value into its vectorized counterpart, recursively 187*c503dc1bSThomas Raoux // vectorizing operations as necessary using the underlying builder. 188*c503dc1bSThomas Raoux // Keeps track of previously vectorized values and reuses vectorization 189*c503dc1bSThomas Raoux // results if these values come up again. 190*c503dc1bSThomas Raoux Value vectorize(Value scalarValue) { 191*c503dc1bSThomas Raoux // Don't vectorize values coming from outside the region. 192*c503dc1bSThomas Raoux if (scalarValue.getParentRegion() != &generic.region()) 193*c503dc1bSThomas Raoux return scalarValue; 194*c503dc1bSThomas Raoux auto vectorValueIt = valueCache.find(scalarValue); 195*c503dc1bSThomas Raoux if (vectorValueIt != valueCache.end()) 196*c503dc1bSThomas Raoux return vectorValueIt->second; 197*c503dc1bSThomas Raoux 198*c503dc1bSThomas Raoux // If the value is from the region but not in the cache it means it is a 199*c503dc1bSThomas Raoux // block argument. 200*c503dc1bSThomas Raoux auto scalarArg = scalarValue.cast<BlockArgument>(); 201*c503dc1bSThomas Raoux assert(scalarArg.getOwner() == &generic.region().front()); 202*c503dc1bSThomas Raoux Value vector_arg = 203*c503dc1bSThomas Raoux generic.getInputsAndOutputBuffers()[scalarArg.getArgNumber()]; 204*c503dc1bSThomas Raoux Value vectorResult = transferReadVector(builder, vector_arg); 205*c503dc1bSThomas Raoux valueCache[scalarArg] = vectorResult; 206*c503dc1bSThomas Raoux return vectorResult; 207*c503dc1bSThomas Raoux } 208*c503dc1bSThomas Raoux 209*c503dc1bSThomas Raoux // Return the largest shape of all the given values. Return an empty 210*c503dc1bSThomas Raoux // SmallVector if there are no vector value. 211*c503dc1bSThomas Raoux static SmallVector<int64_t, 4> getLargestShape(ArrayRef<Value> values) { 212*c503dc1bSThomas Raoux SmallVector<int64_t, 4> largestShape; 213*c503dc1bSThomas Raoux int64_t maxSize = 1; 214*c503dc1bSThomas Raoux for (Value value : values) { 215*c503dc1bSThomas Raoux auto vecType = value.getType().dyn_cast<VectorType>(); 216*c503dc1bSThomas Raoux if (!vecType) 217*c503dc1bSThomas Raoux continue; 218*c503dc1bSThomas Raoux if (maxSize < vecType.getNumElements()) { 219*c503dc1bSThomas Raoux largestShape.assign(vecType.getShape().begin(), 220*c503dc1bSThomas Raoux vecType.getShape().end()); 221*c503dc1bSThomas Raoux } 222*c503dc1bSThomas Raoux } 223*c503dc1bSThomas Raoux return largestShape; 224*c503dc1bSThomas Raoux } 225*c503dc1bSThomas Raoux 226*c503dc1bSThomas Raoux // If the value's type doesn't have the given shape broadcast it. 227*c503dc1bSThomas Raoux Value broadcastIfNeeded(Value value, ArrayRef<int64_t> shape) { 228*c503dc1bSThomas Raoux auto vecType = value.getType().dyn_cast<VectorType>(); 229*c503dc1bSThomas Raoux if (shape.empty() || (vecType != nullptr && vecType.getShape() == shape)) 230*c503dc1bSThomas Raoux return value; 231*c503dc1bSThomas Raoux auto newVecType = VectorType::get(shape, vecType ? vecType.getElementType() 232*c503dc1bSThomas Raoux : value.getType()); 233*c503dc1bSThomas Raoux return builder.create<vector::BroadcastOp>( 234*c503dc1bSThomas Raoux builder.getInsertionPoint()->getLoc(), newVecType, value); 235*c503dc1bSThomas Raoux } 236*c503dc1bSThomas Raoux 237*c503dc1bSThomas Raoux // Takes a scalar operation and builds its vectorized counterpart or 238*c503dc1bSThomas Raoux // counterparts using underlying builder without involving any caches. 239*c503dc1bSThomas Raoux Operation *uncachedVectorize(Operation &base_scalarOp) { 240*c503dc1bSThomas Raoux SmallVector<Value, 4> vectorizedOperands; 241*c503dc1bSThomas Raoux for (Value operand : base_scalarOp.getOperands()) { 242*c503dc1bSThomas Raoux vectorizedOperands.push_back(vectorize(operand)); 243*c503dc1bSThomas Raoux } 244*c503dc1bSThomas Raoux SmallVector<int64_t, 4> shape = getLargestShape(vectorizedOperands); 245*c503dc1bSThomas Raoux for (Value &operand : vectorizedOperands) 246*c503dc1bSThomas Raoux operand = broadcastIfNeeded(operand, shape); 247*c503dc1bSThomas Raoux OperationState state(base_scalarOp.getLoc(), base_scalarOp.getName()); 248*c503dc1bSThomas Raoux state.addAttributes(base_scalarOp.getAttrs()); 249*c503dc1bSThomas Raoux state.addOperands(vectorizedOperands); 250*c503dc1bSThomas Raoux if (shape.empty()) { 251*c503dc1bSThomas Raoux state.addTypes(base_scalarOp.getResultTypes()); 252*c503dc1bSThomas Raoux } else { 253*c503dc1bSThomas Raoux SmallVector<VectorType, 4> vectorizedTypes; 254*c503dc1bSThomas Raoux for (auto Type : base_scalarOp.getResultTypes()) 255*c503dc1bSThomas Raoux vectorizedTypes.push_back(VectorType::get(shape, Type)); 256*c503dc1bSThomas Raoux state.addTypes(vectorizedTypes); 257*c503dc1bSThomas Raoux } 258*c503dc1bSThomas Raoux return builder.createOperation(state); 259*c503dc1bSThomas Raoux } 260*c503dc1bSThomas Raoux 261*c503dc1bSThomas Raoux OpBuilder &builder; 262*c503dc1bSThomas Raoux linalg::GenericOp generic; 263*c503dc1bSThomas Raoux llvm::DenseMap<Value, Value> valueCache; 264*c503dc1bSThomas Raoux }; 265*c503dc1bSThomas Raoux } // namespace 266*c503dc1bSThomas Raoux 267*c503dc1bSThomas Raoux // Replaces elementwise linalg.generic ops with their bodies with scalar 268*c503dc1bSThomas Raoux // operations from these bodies promoted to vector operations. 269*c503dc1bSThomas Raoux static void vectorizeElementwise(linalg::GenericOp op, OpBuilder &builder) { 270*c503dc1bSThomas Raoux GenericVectorizer vectorizer(builder, op); 271*c503dc1bSThomas Raoux for (Operation &scalarOp : op.region().front()) { 272*c503dc1bSThomas Raoux vectorizer.vectorize(scalarOp); 273*c503dc1bSThomas Raoux } 274*c503dc1bSThomas Raoux } 275*c503dc1bSThomas Raoux 276307cfdf5SNicolas Vasilache LogicalResult mlir::linalg::vectorizeLinalgOpPrecondition(Operation *op) { 277307cfdf5SNicolas Vasilache auto linalgOp = cast<linalg::LinalgOp>(op); 278307cfdf5SNicolas Vasilache // All types must be static shape to go to vector. 279307cfdf5SNicolas Vasilache for (Value operand : linalgOp.getInputsAndOutputBuffers()) 280307cfdf5SNicolas Vasilache if (!operand.getType().cast<ShapedType>().hasStaticShape()) 281307cfdf5SNicolas Vasilache return failure(); 282307cfdf5SNicolas Vasilache for (Type outputTensorType : linalgOp.getOutputTensorTypes()) 283307cfdf5SNicolas Vasilache if (!outputTensorType.cast<ShapedType>().hasStaticShape()) 284307cfdf5SNicolas Vasilache return failure(); 28556c638b5SNicolas Vasilache 286a1b9fb22SThomas Raoux if (isa<linalg::FillOp, linalg::CopyOp>(op)) 287307cfdf5SNicolas Vasilache return success(); 288*c503dc1bSThomas Raoux if (isElementwise(op)) 289*c503dc1bSThomas Raoux return success(); 29056c638b5SNicolas Vasilache return isContraction(op); 291307cfdf5SNicolas Vasilache } 292307cfdf5SNicolas Vasilache 293307cfdf5SNicolas Vasilache void mlir::linalg::vectorizeLinalgOp(OpBuilder &builder, Operation *op) { 294307cfdf5SNicolas Vasilache assert(succeeded(vectorizeLinalgOpPrecondition(op))); 295307cfdf5SNicolas Vasilache 2961ee11432SNicolas Vasilache StringRef dbgPref = "\n[" DEBUG_TYPE "]: "; 2971ee11432SNicolas Vasilache (void)dbgPref; 298307cfdf5SNicolas Vasilache edsc::ScopedContext scope(builder, op->getLoc()); 29956c638b5SNicolas Vasilache // In the case of 0-D memrefs, return null and special case to scalar load or 30056c638b5SNicolas Vasilache // store later. 30129d1fba7SThomas Raoux if (auto fillOp = dyn_cast<linalg::FillOp>(op)) { 30229d1fba7SThomas Raoux // Vectorize fill as a vector.broadcast. 30329d1fba7SThomas Raoux LLVM_DEBUG(dbgs() << dbgPref 30429d1fba7SThomas Raoux << "Rewrite linalg.fill as vector.broadcast: " << *op); 305*c503dc1bSThomas Raoux transferWriteVector(builder, fillOp.value(), fillOp.output()); 30629d1fba7SThomas Raoux return; 30729d1fba7SThomas Raoux } 308a1b9fb22SThomas Raoux if (auto copyOp = dyn_cast<linalg::CopyOp>(op)) { 309a1b9fb22SThomas Raoux // Vectorize copy as a vector.transfer_read+vector.transfer_write. 310a1b9fb22SThomas Raoux LLVM_DEBUG(dbgs() << dbgPref 311a1b9fb22SThomas Raoux << "Rewrite linalg.copy as vector.transfer_read + " 312a1b9fb22SThomas Raoux "vector.transfer_write: " 313a1b9fb22SThomas Raoux << *op); 314*c503dc1bSThomas Raoux Value vector = transferReadVector(builder, copyOp.input()); 315*c503dc1bSThomas Raoux transferWriteVector(builder, vector, copyOp.output()); 316a1b9fb22SThomas Raoux return; 317a1b9fb22SThomas Raoux } 318a1b9fb22SThomas Raoux 319*c503dc1bSThomas Raoux if (isElementwise(op)) { 320*c503dc1bSThomas Raoux LLVM_DEBUG(dbgs() << dbgPref 321*c503dc1bSThomas Raoux << "Rewrite linalg op as vector.transfer_read + " 322*c503dc1bSThomas Raoux "vector_op + vector.transfer_write: " 323*c503dc1bSThomas Raoux << *op); 324*c503dc1bSThomas Raoux return vectorizeElementwise(cast<linalg::GenericOp>(op), builder); 325*c503dc1bSThomas Raoux } 326*c503dc1bSThomas Raoux 327a1b9fb22SThomas Raoux assert(succeeded(isContraction(op)) && "Expected contraction"); 328a1b9fb22SThomas Raoux 329a1b9fb22SThomas Raoux // Vectorize other ops as vector contraction. 330a1b9fb22SThomas Raoux // TODO: interface. 331a1b9fb22SThomas Raoux LLVM_DEBUG(dbgs() << dbgPref 332a1b9fb22SThomas Raoux << "Rewrite linalg op as vector.contract: " << *op); 333307cfdf5SNicolas Vasilache auto linalgOp = cast<linalg::LinalgOp>(op); 3349534192cSNicolas Vasilache Value viewA = linalgOp.getInput(0); 3359534192cSNicolas Vasilache Value viewB = linalgOp.getInput(1); 3369534192cSNicolas Vasilache Value viewC = linalgOp.getOutputBuffer(0); 33756c638b5SNicolas Vasilache VectorType vtA = extractVectorTypeFromScalarView(viewA); 33856c638b5SNicolas Vasilache VectorType vtB = extractVectorTypeFromScalarView(viewB); 33956c638b5SNicolas Vasilache VectorType vtC = extractVectorTypeFromScalarView(viewC); 3409534192cSNicolas Vasilache Value zero = std_constant_index(0); 34156c638b5SNicolas Vasilache SmallVector<Value, 4> indicesA, indicesB, indicesC; 34256c638b5SNicolas Vasilache if (vtA) 34356c638b5SNicolas Vasilache indicesA = SmallVector<Value, 4>(vtA.getRank(), zero); 34456c638b5SNicolas Vasilache if (vtB) 34556c638b5SNicolas Vasilache indicesB = SmallVector<Value, 4>(vtB.getRank(), zero); 34656c638b5SNicolas Vasilache if (vtC) 34756c638b5SNicolas Vasilache indicesC = SmallVector<Value, 4>(vtC.getRank(), zero); 34856c638b5SNicolas Vasilache Value a = vtA ? vector_transfer_read(vtA, viewA, indicesA).value 34956c638b5SNicolas Vasilache : std_load(viewA, indicesA).value; 35056c638b5SNicolas Vasilache Value b = vtB ? vector_transfer_read(vtB, viewB, indicesB).value 35156c638b5SNicolas Vasilache : std_load(viewB, indicesB).value; 35256c638b5SNicolas Vasilache Value c = vtC ? vector_transfer_read(vtC, viewC, indicesC).value 35356c638b5SNicolas Vasilache : std_load(viewC, indicesC).value; 354307cfdf5SNicolas Vasilache Value res = vector_contract(a, b, c, linalgOp.indexing_maps(), 355307cfdf5SNicolas Vasilache linalgOp.iterator_types()); 35656c638b5SNicolas Vasilache if (vtC) 3579534192cSNicolas Vasilache vector_transfer_write(res, viewC, indicesC); 35856c638b5SNicolas Vasilache else 35956c638b5SNicolas Vasilache std_store(res, viewC, indicesC); 360307cfdf5SNicolas Vasilache } 3611ee11432SNicolas Vasilache 3621ee11432SNicolas Vasilache /// Check whether there is any interleaved use of any `values` between `firstOp` 3631ee11432SNicolas Vasilache /// and `secondOp`. Conservatively return `true` if any op or value is in a 3641ee11432SNicolas Vasilache /// different block. 3651ee11432SNicolas Vasilache static bool mayExistInterleavedUses(Operation *firstOp, Operation *secondOp, 3661ee11432SNicolas Vasilache ValueRange values) { 3671ee11432SNicolas Vasilache StringRef dbgPref = "\n[" DEBUG_TYPE "]: "; 3681ee11432SNicolas Vasilache (void)dbgPref; 3691ee11432SNicolas Vasilache if (firstOp->getBlock() != secondOp->getBlock() || 3701ee11432SNicolas Vasilache !firstOp->isBeforeInBlock(secondOp)) { 3711ee11432SNicolas Vasilache LLVM_DEBUG(llvm::dbgs() 3721ee11432SNicolas Vasilache << dbgPref << "interleavedUses precondition failed, firstOp: " 3731ee11432SNicolas Vasilache << *firstOp << ", second op: " << *secondOp); 3741ee11432SNicolas Vasilache return true; 3751ee11432SNicolas Vasilache } 3761ee11432SNicolas Vasilache for (auto v : values) { 3771ee11432SNicolas Vasilache for (auto &u : v.getUses()) { 3781ee11432SNicolas Vasilache Operation *owner = u.getOwner(); 3791ee11432SNicolas Vasilache if (owner == firstOp || owner == secondOp) 3801ee11432SNicolas Vasilache continue; 3811ee11432SNicolas Vasilache // TODO: this is too conservative, use dominance info in the future. 3821ee11432SNicolas Vasilache if (owner->getBlock() == firstOp->getBlock() && 3831ee11432SNicolas Vasilache (owner->isBeforeInBlock(firstOp) || secondOp->isBeforeInBlock(owner))) 3841ee11432SNicolas Vasilache continue; 3851ee11432SNicolas Vasilache LLVM_DEBUG(llvm::dbgs() 3861ee11432SNicolas Vasilache << dbgPref << " found interleaved op " << *owner 3871ee11432SNicolas Vasilache << ", firstOp: " << *firstOp << ", second op: " << *secondOp); 3881ee11432SNicolas Vasilache return true; 3891ee11432SNicolas Vasilache } 3901ee11432SNicolas Vasilache } 3911ee11432SNicolas Vasilache return false; 3921ee11432SNicolas Vasilache } 3931ee11432SNicolas Vasilache 3941ee11432SNicolas Vasilache /// Return the unique subview use of `v` if it is indeed unique, null otherwise. 3951ee11432SNicolas Vasilache static SubViewOp getSubViewUseIfUnique(Value v) { 3961ee11432SNicolas Vasilache SubViewOp subViewOp; 3971ee11432SNicolas Vasilache for (auto &u : v.getUses()) { 3981ee11432SNicolas Vasilache if (auto newSubViewOp = dyn_cast<SubViewOp>(u.getOwner())) { 3991ee11432SNicolas Vasilache if (subViewOp) 4001ee11432SNicolas Vasilache return SubViewOp(); 4011ee11432SNicolas Vasilache subViewOp = newSubViewOp; 4021ee11432SNicolas Vasilache } 4031ee11432SNicolas Vasilache } 4041ee11432SNicolas Vasilache return subViewOp; 4051ee11432SNicolas Vasilache } 4061ee11432SNicolas Vasilache 4071ee11432SNicolas Vasilache /// TODO: use interfaces, side-effects and aliasing analysis as appropriate, 4081ee11432SNicolas Vasilache /// when available. 4091ee11432SNicolas Vasilache LogicalResult LinalgCopyVTRForwardingPattern::matchAndRewrite( 4101ee11432SNicolas Vasilache vector::TransferReadOp xferOp, PatternRewriter &rewriter) const { 4111ee11432SNicolas Vasilache 4121ee11432SNicolas Vasilache // Transfer into `view`. 4131ee11432SNicolas Vasilache Value viewOrAlloc = xferOp.memref(); 4141ee11432SNicolas Vasilache if (!viewOrAlloc.getDefiningOp<ViewOp>() && 4151ee11432SNicolas Vasilache !viewOrAlloc.getDefiningOp<AllocOp>()) 4161ee11432SNicolas Vasilache return failure(); 4171ee11432SNicolas Vasilache 4181ee11432SNicolas Vasilache StringRef dbgPref = "\n[" DEBUG_TYPE "]: VTRForwarding: "; 4191ee11432SNicolas Vasilache (void)dbgPref; 4201ee11432SNicolas Vasilache LLVM_DEBUG(llvm::dbgs() << dbgPref << viewOrAlloc); 4211ee11432SNicolas Vasilache 4221ee11432SNicolas Vasilache // Ensure there is exactly one subview of `viewOrAlloc` defining `subView`. 4231ee11432SNicolas Vasilache SubViewOp subViewOp = getSubViewUseIfUnique(viewOrAlloc); 4241ee11432SNicolas Vasilache if (!subViewOp) 4251ee11432SNicolas Vasilache return failure(); 4261ee11432SNicolas Vasilache Value subView = subViewOp.getResult(); 4271ee11432SNicolas Vasilache LLVM_DEBUG(llvm::dbgs() << dbgPref << "with subView " << subView); 4281ee11432SNicolas Vasilache 4291ee11432SNicolas Vasilache // Find the copy into `subView` without interleaved uses. 4301ee11432SNicolas Vasilache CopyOp copyOp; 4311ee11432SNicolas Vasilache for (auto &u : subView.getUses()) { 4321ee11432SNicolas Vasilache if (auto newCopyOp = dyn_cast<CopyOp>(u.getOwner())) { 4331ee11432SNicolas Vasilache if (newCopyOp.getOutputBuffer(0) != subView) 4341ee11432SNicolas Vasilache continue; 4351ee11432SNicolas Vasilache LLVM_DEBUG(llvm::dbgs() << dbgPref << "copy candidate " << *newCopyOp); 4361ee11432SNicolas Vasilache if (mayExistInterleavedUses(newCopyOp, xferOp, {viewOrAlloc, subView})) 4371ee11432SNicolas Vasilache continue; 4381ee11432SNicolas Vasilache copyOp = newCopyOp; 4391ee11432SNicolas Vasilache break; 4401ee11432SNicolas Vasilache } 4411ee11432SNicolas Vasilache } 4421ee11432SNicolas Vasilache if (!copyOp) 4431ee11432SNicolas Vasilache return failure(); 4441ee11432SNicolas Vasilache LLVM_DEBUG(llvm::dbgs() << dbgPref << "with copy " << *copyOp); 4451ee11432SNicolas Vasilache 4461ee11432SNicolas Vasilache // Find the fill into `viewOrAlloc` without interleaved uses before the copy. 4471ee11432SNicolas Vasilache FillOp maybeFillOp; 4481ee11432SNicolas Vasilache for (auto &u : viewOrAlloc.getUses()) { 4491ee11432SNicolas Vasilache if (auto newFillOp = dyn_cast<FillOp>(u.getOwner())) { 4501ee11432SNicolas Vasilache if (newFillOp.getOutputBuffer(0) != viewOrAlloc) 4511ee11432SNicolas Vasilache continue; 4521ee11432SNicolas Vasilache LLVM_DEBUG(llvm::dbgs() << dbgPref << "fill candidate " << *newFillOp); 4531ee11432SNicolas Vasilache if (mayExistInterleavedUses(newFillOp, copyOp, {viewOrAlloc, subView})) 4541ee11432SNicolas Vasilache continue; 4551ee11432SNicolas Vasilache maybeFillOp = newFillOp; 4561ee11432SNicolas Vasilache break; 4571ee11432SNicolas Vasilache } 4581ee11432SNicolas Vasilache } 4591ee11432SNicolas Vasilache // Ensure padding matches. 4601ee11432SNicolas Vasilache if (maybeFillOp && xferOp.padding() != maybeFillOp.value()) 4611ee11432SNicolas Vasilache return failure(); 4621ee11432SNicolas Vasilache if (maybeFillOp) 4631ee11432SNicolas Vasilache LLVM_DEBUG(llvm::dbgs() << dbgPref << "with maybeFillOp " << *maybeFillOp); 4641ee11432SNicolas Vasilache 4651ee11432SNicolas Vasilache // `in` is the subview that linalg.copy reads. Replace it. 4661ee11432SNicolas Vasilache Value in = copyOp.getInput(0); 4671ee11432SNicolas Vasilache 468512da70bSNicolas Vasilache // linalg.copy + linalg.fill can be used to create a padded local buffer. 469512da70bSNicolas Vasilache // The `masked` attribute is only valid on this padded buffer. 470512da70bSNicolas Vasilache // When forwarding to vector.transfer_read, the attribute must be reset 471512da70bSNicolas Vasilache // conservatively. 4721ee11432SNicolas Vasilache Value res = rewriter.create<vector::TransferReadOp>( 4731ee11432SNicolas Vasilache xferOp.getLoc(), xferOp.getVectorType(), in, xferOp.indices(), 474512da70bSNicolas Vasilache xferOp.permutation_map(), xferOp.padding(), ArrayAttr()); 4751ee11432SNicolas Vasilache 4761ee11432SNicolas Vasilache if (maybeFillOp) 4771ee11432SNicolas Vasilache rewriter.eraseOp(maybeFillOp); 4781ee11432SNicolas Vasilache rewriter.eraseOp(copyOp); 4791ee11432SNicolas Vasilache rewriter.replaceOp(xferOp, res); 4801ee11432SNicolas Vasilache 4811ee11432SNicolas Vasilache return success(); 4821ee11432SNicolas Vasilache } 4831ee11432SNicolas Vasilache 4841ee11432SNicolas Vasilache /// TODO: use interfaces, side-effects and aliasing analysis as appropriate, 4851ee11432SNicolas Vasilache /// when available. 4861ee11432SNicolas Vasilache LogicalResult LinalgCopyVTWForwardingPattern::matchAndRewrite( 4871ee11432SNicolas Vasilache vector::TransferWriteOp xferOp, PatternRewriter &rewriter) const { 4881ee11432SNicolas Vasilache // Transfer into `viewOrAlloc`. 4891ee11432SNicolas Vasilache Value viewOrAlloc = xferOp.memref(); 4901ee11432SNicolas Vasilache if (!viewOrAlloc.getDefiningOp<ViewOp>() && 4911ee11432SNicolas Vasilache !viewOrAlloc.getDefiningOp<AllocOp>()) 4921ee11432SNicolas Vasilache return failure(); 4931ee11432SNicolas Vasilache 4941ee11432SNicolas Vasilache // Ensure there is exactly one subview of `viewOrAlloc` defining `subView`. 4951ee11432SNicolas Vasilache SubViewOp subViewOp = getSubViewUseIfUnique(viewOrAlloc); 4961ee11432SNicolas Vasilache if (!subViewOp) 4971ee11432SNicolas Vasilache return failure(); 4981ee11432SNicolas Vasilache Value subView = subViewOp.getResult(); 4991ee11432SNicolas Vasilache 5001ee11432SNicolas Vasilache // Find the copy from `subView` without interleaved uses. 5011ee11432SNicolas Vasilache CopyOp copyOp; 5021ee11432SNicolas Vasilache for (auto &u : subViewOp.getResult().getUses()) { 5031ee11432SNicolas Vasilache if (auto newCopyOp = dyn_cast<CopyOp>(u.getOwner())) { 5041ee11432SNicolas Vasilache if (newCopyOp.getInput(0) != subView) 5051ee11432SNicolas Vasilache continue; 5061ee11432SNicolas Vasilache if (mayExistInterleavedUses(xferOp, newCopyOp, {viewOrAlloc, subView})) 5071ee11432SNicolas Vasilache continue; 5081ee11432SNicolas Vasilache copyOp = newCopyOp; 5091ee11432SNicolas Vasilache break; 5101ee11432SNicolas Vasilache } 5111ee11432SNicolas Vasilache } 5121ee11432SNicolas Vasilache if (!copyOp) 5131ee11432SNicolas Vasilache return failure(); 5141ee11432SNicolas Vasilache 5151ee11432SNicolas Vasilache // `out` is the subview copied into that we replace. 5161ee11432SNicolas Vasilache Value out = copyOp.getOutputBuffer(0); 5171ee11432SNicolas Vasilache 5181ee11432SNicolas Vasilache // Forward vector.transfer into copy. 519512da70bSNicolas Vasilache // linalg.copy + linalg.fill can be used to create a padded local buffer. 520512da70bSNicolas Vasilache // The `masked` attribute is only valid on this padded buffer. 521512da70bSNicolas Vasilache // When forwarding to vector.transfer_write, the attribute must be reset 522512da70bSNicolas Vasilache // conservatively. 5231ee11432SNicolas Vasilache rewriter.create<vector::TransferWriteOp>( 5241ee11432SNicolas Vasilache xferOp.getLoc(), xferOp.vector(), out, xferOp.indices(), 525512da70bSNicolas Vasilache xferOp.permutation_map(), ArrayAttr()); 5261ee11432SNicolas Vasilache 5271ee11432SNicolas Vasilache rewriter.eraseOp(copyOp); 5281ee11432SNicolas Vasilache rewriter.eraseOp(xferOp); 5291ee11432SNicolas Vasilache 5301ee11432SNicolas Vasilache return success(); 5311ee11432SNicolas Vasilache } 53267b37f57SJakub Lichman 53367b37f57SJakub Lichman template <class ConvOp, int N> 53467b37f57SJakub Lichman LogicalResult ConvOpVectorization<ConvOp, N>::matchAndRewrite( 53567b37f57SJakub Lichman ConvOp op, PatternRewriter &rewriter) const { 53667b37f57SJakub Lichman Location loc = op.getLoc(); 53767b37f57SJakub Lichman MLIRContext *context = op.getContext(); 53867b37f57SJakub Lichman edsc::ScopedContext scope(rewriter, loc); 53967b37f57SJakub Lichman 54067b37f57SJakub Lichman ShapedType inShapeType = op.getInputShapedType(0); 54167b37f57SJakub Lichman ShapedType kShapeType = op.getInputShapedType(1); 54267b37f57SJakub Lichman 54367b37f57SJakub Lichman ArrayRef<int64_t> inShape = inShapeType.getShape(); 54467b37f57SJakub Lichman ArrayRef<int64_t> kShape = kShapeType.getShape(); 54567b37f57SJakub Lichman 54667b37f57SJakub Lichman if (!inShapeType.hasStaticShape() || !kShapeType.hasStaticShape()) 54767b37f57SJakub Lichman return failure(); 54867b37f57SJakub Lichman 54967b37f57SJakub Lichman SmallVector<AffineExpr, 4> mapping; 5500b17d475SJakub Lichman SmallVector<int64_t, 4> vectorDims; 5510b17d475SJakub Lichman // Fail to apply when the size of not vectorized dimension is not 1. 55267b37f57SJakub Lichman for (unsigned i = 0; i < N; i++) { 55367b37f57SJakub Lichman if (!mask[i] && (inShape[i] != 1 || kShape[i] != 1)) 55467b37f57SJakub Lichman return failure(); 5550b17d475SJakub Lichman 5560b17d475SJakub Lichman if (mask[i] && inShape[i] != kShape[i]) 55767b37f57SJakub Lichman return failure(); 55867b37f57SJakub Lichman 5590b17d475SJakub Lichman if (mask[i]) { 56067b37f57SJakub Lichman mapping.push_back(getAffineDimExpr(i, context)); 5610b17d475SJakub Lichman vectorDims.push_back(inShape[i]); 5620b17d475SJakub Lichman } 56367b37f57SJakub Lichman } 56467b37f57SJakub Lichman 56567b37f57SJakub Lichman Value input = op.getInput(0); 56667b37f57SJakub Lichman Value kernel = op.getInput(1); 56767b37f57SJakub Lichman Value output = op.getOutputBuffer(0); 56867b37f57SJakub Lichman 56983d82d1fSJakub Lichman unsigned rank = inShapeType.getRank(); 57083d82d1fSJakub Lichman unsigned numDims = mapping.size(); 57167b37f57SJakub Lichman Type elemType = inShapeType.getElementType(); 57267b37f57SJakub Lichman 57367b37f57SJakub Lichman auto map = AffineMap::get(rank, 0, mapping, context); 57467b37f57SJakub Lichman SmallVector<Value, 4> zeros(rank, std_constant_index(0)); 5750b17d475SJakub Lichman auto vecType = VectorType::get(vectorDims, elemType); 57667b37f57SJakub Lichman 57767b37f57SJakub Lichman auto inputVec = vector_transfer_read(vecType, input, zeros, map); 57867b37f57SJakub Lichman auto kernelVec = vector_transfer_read(vecType, kernel, zeros, map); 57967b37f57SJakub Lichman 58067b37f57SJakub Lichman auto acc = std_constant(elemType, rewriter.getZeroAttr(elemType)); 58167b37f57SJakub Lichman 58267b37f57SJakub Lichman std::array<AffineMap, 3> indexingMaps{ 58367b37f57SJakub Lichman AffineMap::getMultiDimIdentityMap(numDims, context), 58467b37f57SJakub Lichman AffineMap::getMultiDimIdentityMap(numDims, context), 58567b37f57SJakub Lichman AffineMap::get(numDims, 0, {}, context)}; 58667b37f57SJakub Lichman 58767b37f57SJakub Lichman std::vector<StringRef> iteratorTypes(numDims, "reduction"); 58867b37f57SJakub Lichman 58967b37f57SJakub Lichman auto result = rewriter.create<vector::ContractionOp>( 59067b37f57SJakub Lichman loc, inputVec, kernelVec, acc, 59167b37f57SJakub Lichman rewriter.getAffineMapArrayAttr(indexingMaps), 59267b37f57SJakub Lichman rewriter.getStrArrayAttr(iteratorTypes)); 59367b37f57SJakub Lichman 59467b37f57SJakub Lichman rewriter.create<StoreOp>(loc, result, output, ValueRange(zeros)); 59567b37f57SJakub Lichman rewriter.eraseOp(op); 59667b37f57SJakub Lichman return success(); 59767b37f57SJakub Lichman } 59867b37f57SJakub Lichman 599347d59b1SJakub Lichman using ConvOpConst = ConvOpVectorization<ConvWOp, 1>; 600347d59b1SJakub Lichman 601347d59b1SJakub Lichman /// Inserts tiling, promotion and vectorization pattern for ConvOp 602347d59b1SJakub Lichman /// conversion into corresponding pattern lists. 603347d59b1SJakub Lichman template <typename ConvOp, unsigned N> 604347d59b1SJakub Lichman static void 605347d59b1SJakub Lichman populateVectorizationPatterns(OwningRewritePatternList &tilingPatterns, 606347d59b1SJakub Lichman OwningRewritePatternList &promotionPatterns, 607347d59b1SJakub Lichman OwningRewritePatternList &vectorizationPatterns, 608347d59b1SJakub Lichman ArrayRef<int64_t> tileSizes, 609347d59b1SJakub Lichman MLIRContext *context) { 6100b17d475SJakub Lichman if (tileSizes.size() < N) 6110b17d475SJakub Lichman return; 6120b17d475SJakub Lichman 613347d59b1SJakub Lichman constexpr static StringRef kTiledMarker = "TILED"; 614347d59b1SJakub Lichman constexpr static StringRef kPromotedMarker = "PROMOTED"; 615347d59b1SJakub Lichman tilingPatterns.insert<LinalgTilingPattern<ConvOp>>( 616347d59b1SJakub Lichman context, LinalgTilingOptions().setTileSizes(tileSizes), 617347d59b1SJakub Lichman LinalgMarker({}, Identifier::get(kTiledMarker, context))); 618347d59b1SJakub Lichman 619347d59b1SJakub Lichman promotionPatterns.insert<LinalgPromotionPattern<ConvOp>>( 620347d59b1SJakub Lichman context, LinalgPromotionOptions().setUseFullTileBuffersByDefault(true), 621347d59b1SJakub Lichman LinalgMarker(Identifier::get(kTiledMarker, context), 622347d59b1SJakub Lichman Identifier::get(kPromotedMarker, context))); 623347d59b1SJakub Lichman 624347d59b1SJakub Lichman SmallVector<bool, 4> mask(N); 625347d59b1SJakub Lichman int offset = tileSizes.size() - N; 626347d59b1SJakub Lichman std::transform(tileSizes.begin() + offset, tileSizes.end(), mask.begin(), 6270b17d475SJakub Lichman [](int64_t i) -> bool { return i > 1; }); 628347d59b1SJakub Lichman 629347d59b1SJakub Lichman vectorizationPatterns.insert<ConvOpVectorization<ConvOp, N>>(context, mask); 630347d59b1SJakub Lichman } 631347d59b1SJakub Lichman 63267b37f57SJakub Lichman void mlir::linalg::populateConvVectorizationPatterns( 6330b17d475SJakub Lichman MLIRContext *context, SmallVectorImpl<OwningRewritePatternList> &patterns, 6340b17d475SJakub Lichman ArrayRef<int64_t> tileSizes) { 635347d59b1SJakub Lichman OwningRewritePatternList tiling, promotion, vectorization; 6360b17d475SJakub Lichman populateVectorizationPatterns<ConvWOp, 1>(tiling, promotion, vectorization, 6370b17d475SJakub Lichman tileSizes, context); 63867b37f57SJakub Lichman 639347d59b1SJakub Lichman populateVectorizationPatterns<ConvNWCOp, 3>(tiling, promotion, vectorization, 6400b17d475SJakub Lichman tileSizes, context); 64167b37f57SJakub Lichman 642347d59b1SJakub Lichman populateVectorizationPatterns<ConvNCWOp, 3>(tiling, promotion, vectorization, 6430b17d475SJakub Lichman tileSizes, context); 64467b37f57SJakub Lichman 645347d59b1SJakub Lichman populateVectorizationPatterns<ConvHWOp, 2>(tiling, promotion, vectorization, 6460b17d475SJakub Lichman tileSizes, context); 64767b37f57SJakub Lichman 648347d59b1SJakub Lichman populateVectorizationPatterns<ConvNHWCOp, 4>(tiling, promotion, vectorization, 6490b17d475SJakub Lichman tileSizes, context); 65067b37f57SJakub Lichman 651347d59b1SJakub Lichman populateVectorizationPatterns<ConvNCHWOp, 4>(tiling, promotion, vectorization, 6520b17d475SJakub Lichman tileSizes, context); 65367b37f57SJakub Lichman 654347d59b1SJakub Lichman populateVectorizationPatterns<ConvDHWOp, 3>(tiling, promotion, vectorization, 6550b17d475SJakub Lichman tileSizes, context); 65667b37f57SJakub Lichman 657347d59b1SJakub Lichman populateVectorizationPatterns<ConvNDHWCOp, 5>( 6580b17d475SJakub Lichman tiling, promotion, vectorization, tileSizes, context); 659347d59b1SJakub Lichman 660347d59b1SJakub Lichman populateVectorizationPatterns<ConvNCDHWOp, 5>( 6610b17d475SJakub Lichman tiling, promotion, vectorization, tileSizes, context); 662347d59b1SJakub Lichman 663347d59b1SJakub Lichman patterns.push_back(std::move(tiling)); 664347d59b1SJakub Lichman patterns.push_back(std::move(promotion)); 665347d59b1SJakub Lichman patterns.push_back(std::move(vectorization)); 66667b37f57SJakub Lichman } 667