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