38#include "llvm/ADT/STLExtras.h"
39#include "llvm/ADT/Sequence.h"
40#include "llvm/ADT/SmallPtrSet.h"
41#include "llvm/ADT/SmallVector.h"
42#include "llvm/ADT/SmallVectorExtras.h"
43#include "llvm/ADT/TypeSwitch.h"
44#include "llvm/Support/DebugLog.h"
45#include "llvm/Support/InterleavedRange.h"
46#include "llvm/Support/MathExtras.h"
47#include "llvm/Support/raw_ostream.h"
53#define DEBUG_TYPE "linalg-vectorization"
56static FailureOr<Operation *>
60 bool flatten1DDepthwiseConv =
false);
99 int64_t kwSize,
int strideW,
int dilationW,
100 int64_t wSizeStep,
bool isSingleChanneled) {
102 if (isSingleChanneled) {
107 for (
int64_t kw = 0; kw < kwSize; ++kw) {
108 for (
int64_t w = 0; w < wSize; w += wSizeStep) {
109 result.push_back(vector::ExtractStridedSliceOp::create(
119 for (
int64_t kw = 0; kw < kwSize; ++kw) {
120 for (
int64_t w = 0; w < wSize; w += wSizeStep) {
121 result.push_back(vector::ExtractStridedSliceOp::create(
122 rewriter, loc, input,
139 for (
int64_t kw = 0; kw < kwSize; ++kw) {
140 result.push_back(vector::ExtractOp::create(
151 int64_t wSizeStep,
bool isSingleChanneled) {
153 if (isSingleChanneled) {
157 for (
int64_t w = 0; w < wSize; w += wSizeStep) {
158 result.push_back(vector::ExtractStridedSliceOp::create(
167 for (
int64_t w = 0; w < wSize; w += wSizeStep) {
168 result.push_back(vector::ExtractStridedSliceOp::create(
180 bool isSingleChanneled) {
182 if (isSingleChanneled) {
186 for (
int64_t w = 0; w < wSize; w += wSizeStep) {
187 res = vector::InsertStridedSliceOp::create(
195 for (
int64_t w = 0; w < wSize; w += wSizeStep) {
196 res = vector::InsertStridedSliceOp::create(
197 rewriter, loc, resVals[w], res,
211 LogicalResult initState(
RewriterBase &rewriter, LinalgOp linalgOp,
214 bool assumeDynamicDimsMatchVecSizes =
false);
229 std::optional<AffineMap> dimPermutation = std::nullopt)
const {
232 if (dimPermutation.has_value()) {
238 vectorShape.append(canonicalVecShape.begin(), canonicalVecShape.end());
239 scalableDims.append(scalableVecDims.begin(), scalableVecDims.end());
242 return VectorType::get(
vectorShape, elementType, scalableDims);
251 std::optional<AffineMap> maybeIndexingMap = std::nullopt);
258 getOrCreateMaskFor(
RewriterBase &rewriter, LinalgOp linalgOp,
259 std::optional<AffineMap> maybeMaskingMap = std::nullopt);
264 void initIterSpaceStaticSizes(LinalgOp linalgOp) {
265 iterSpaceStaticSizes.append(linalgOp.getStaticLoopRanges());
271 LogicalResult precomputeIterSpaceValueSizes(RewriterBase &rewriter,
277 bool isValidMaskingMap(AffineMap maskingMap) {
296 AffineMap getMaskingMapFromIndexingMap(AffineMap &indexingMap) {
302 SmallVector<int64_t> iterSpaceStaticSizes;
307 SmallVector<Value> iterSpaceValueSizes;
310 SmallVector<int64_t> canonicalVecShape;
314 SmallVector<bool> scalableVecDims;
322 OpBuilder::InsertionGuard rewriterGuard;
330 bool assumeDynamicDimsMatchVecSizes =
false;
334VectorizationState::precomputeIterSpaceValueSizes(
RewriterBase &rewriter,
337 for (
int vecDim = 0, end = canonicalVecShape.size(); vecDim < end; ++vecDim) {
338 if (ShapedType::isStatic(iterSpaceStaticSizes[vecDim])) {
341 rewriter, linalgOp.getLoc(), iterSpaceStaticSizes[vecDim]));
348 unsigned operandDimPos;
349 if (
failed(linalgOp.mapIterationSpaceDimToOperandDim(vecDim, operand,
354 linalgOp.hasPureTensorSemantics()
355 ? (Value)tensor::DimOp::create(rewriter, linalgOp.getLoc(), operand,
357 : (Value)memref::DimOp::create(rewriter, linalgOp.getLoc(), operand,
359 iterSpaceValueSizes.push_back(dynamicDim);
372 bool assumeDimsMatchVec) {
373 assumeDynamicDimsMatchVecSizes = assumeDimsMatchVec;
377 if (!inputVectorSizes.empty()) {
381 canonicalVecShape.append(inputVectorSizes.begin(), inputVectorSizes.end());
382 scalableVecDims.append(inputScalableVecDims.begin(),
383 inputScalableVecDims.end());
388 canonicalVecShape = linalgOp.getStaticLoopRanges();
389 scalableVecDims.append(linalgOp.getNumLoops(),
false);
392 LDBG() <<
"Canonical vector shape: " << llvm::interleaved(canonicalVecShape);
393 LDBG() <<
"Scalable vector dims: " << llvm::interleaved(scalableVecDims);
395 if (ShapedType::isDynamicShape(canonicalVecShape))
399 initIterSpaceStaticSizes(linalgOp);
404 if (failed(precomputeIterSpaceValueSizes(rewriter, linalgOp)))
412 std::optional<AffineMap> maybeMaskingMap) {
414 assert((!maybeMaskingMap || isValidMaskingMap(*maybeMaskingMap)) &&
415 "Ill-formed masking map.");
418 assert((!maybeMaskingMap || *maybeMaskingMap) &&
419 "Unexpected null mask permutation map");
421 maybeMaskingMap ? *maybeMaskingMap
423 linalgOp.getNumLoops(), rewriter.
getContext());
425 LDBG() <<
"Masking map: " << maskingMap;
429 auto activeMaskIt = activeMaskCache.find(maskingMap);
430 if (activeMaskIt != activeMaskCache.end()) {
431 Value mask = activeMaskIt->second;
432 LDBG() <<
"Reusing mask: " << mask;
445 auto maskShape = maskType.getShape();
447 LDBG() <<
"Mask shape: " << llvm::interleaved(maskShape);
449 if (permutedStaticSizes == maskShape) {
450 LDBG() <<
"Masking is not needed for masking map: " << maskingMap;
451 activeMaskCache[maskingMap] =
Value();
455 if (assumeDynamicDimsMatchVecSizes) {
459 if (llvm::all_of(llvm::zip(permutedStaticSizes, maskType.getShape()),
461 return std::get<0>(it) == ShapedType::kDynamic
463 : std::get<0>(it) == std::get<1>(it);
466 <<
"Dynamic + static dimensions match vector sizes, masking is not "
468 activeMaskCache[maskingMap] =
Value();
476 assert(!maskShape.empty() && !upperBounds.empty() &&
477 "Masked 0-d vectors are not supported yet");
480 Value mask = vector::CreateMaskOp::create(rewriter, linalgOp.getLoc(),
481 maskType, upperBounds);
482 LDBG() <<
"Creating new mask: " << mask;
483 activeMaskCache[maskingMap] = mask;
490 std::optional<AffineMap> maybeIndexingMap) {
491 LDBG() <<
"Trying to mask: " << *opToMask;
494 auto maskableOp = dyn_cast<vector::MaskableOpInterface>(opToMask);
496 LDBG() <<
"Operation is not maskable";
499 assert(!maskableOp.isMasked() &&
500 "Masking an operation that is already masked");
502 std::optional<AffineMap> maybeMaskingMap = std::nullopt;
503 if (maybeIndexingMap)
504 maybeMaskingMap = getMaskingMapFromIndexingMap(*maybeIndexingMap);
510 LDBG() <<
"No mask required";
511 if (assumeDynamicDimsMatchVecSizes) {
513 .Case<vector::TransferReadOp, vector::TransferWriteOp>(
519 LDBG() <<
"Assuming dynamic dimensions match vector sizes and "
520 "setting their in-bounds to true!";
522 ShapedType xferType = xferOp.getShapedType();
527 for (
unsigned i = 0; i < xferOp.getTransferRank(); i++) {
528 auto dimExpr = dyn_cast<AffineDimExpr>(permMap.
getResult(i));
532 unsigned pos = dimExpr.getPosition();
533 if (xferType.isDynamicDim(pos))
534 inBoundsMap[i] =
true;
537 xferOp.setInBoundsAttr(
549 assert(opToMask &&
"Expected a valid operation to mask");
550 auto maskOp = cast<vector::MaskOp>(
552 Operation *maskOpTerminator = &maskOp.getMaskRegion().front().back();
554 for (
auto [resIdx, resVal] : llvm::enumerate(opToMask->
getResults()))
558 LDBG() <<
"Masked operation: " << *maskOp;
581 "expected projected permutation");
583 assert(res.getNumDims() ==
584 (res.getNumResults() - res.getNumOfZeroResults()) &&
585 "expected reindexed map with same number of dims and results");
621std::optional<vector::CombiningKind>
623 using ::mlir::vector::CombiningKind;
628 .Case<arith::AddIOp, arith::AddFOp>(
629 [&](
auto op) {
return CombiningKind::ADD; })
630 .Case([&](arith::AndIOp op) {
return CombiningKind::AND; })
631 .Case([&](arith::MaxSIOp op) {
return CombiningKind::MAXSI; })
632 .Case([&](arith::MaxUIOp op) {
return CombiningKind::MAXUI; })
633 .Case([&](arith::MaximumFOp op) {
return CombiningKind::MAXIMUMF; })
634 .Case([&](arith::MaxNumFOp op) {
return CombiningKind::MAXNUMF; })
635 .Case([&](arith::MaximumNumFOp op) {
return CombiningKind::MAXIMUMNUMF; })
636 .Case([&](arith::MinSIOp op) {
return CombiningKind::MINSI; })
637 .Case([&](arith::MinUIOp op) {
return CombiningKind::MINUI; })
638 .Case([&](arith::MinimumFOp op) {
return CombiningKind::MINIMUMF; })
639 .Case([&](arith::MinNumFOp op) {
return CombiningKind::MINNUMF; })
640 .Case([&](arith::MinimumNumFOp op) {
return CombiningKind::MINIMUMNUMF; })
641 .Case<arith::MulIOp, arith::MulFOp>(
642 [&](
auto op) {
return CombiningKind::MUL; })
643 .Case([&](arith::OrIOp op) {
return CombiningKind::OR; })
644 .Case([&](arith::XOrIOp op) {
return CombiningKind::XOR; })
645 .Default(std::nullopt);
656 auto linalgOp = cast<LinalgOp>(outputOperand->
getOwner());
661 if (!
matchReduction(linalgOp.getRegionOutputArgs(), outputPos, combinerOps) ||
662 combinerOps.size() != 1)
666 return combinerOps[0];
672 auto dstVecType = dyn_cast<VectorType>(dstType);
674 if (dstVecType.getRank() == 0)
679 Location loc =
b.getInsertionPoint()->getLoc();
680 return b.createOrFold<vector::BroadcastOp>(loc, dstVecType, value);
692 assert(maybeKind &&
"Failed precondition: could not get reduction kind");
694 arith::FastMathFlags fastMathFlags = arith::FastMathFlags::none;
695 if (
auto fmfOp = dyn_cast<arith::ArithFastMathInterface>(reduceOp))
696 fastMathFlags = fmfOp.getFastMathFlagsAttr().getValue();
697 return vector::MultiDimReductionOp::create(
b, reduceOp->
getLoc(),
698 valueToReduce,
acc, dimsToMask,
699 *maybeKind, fastMathFlags);
703 return llvm::map_to_vector(linalgOp.getIteratorTypesArray(),
710 return isa<linalg::ReduceOp>(op) ||
711 (isa<linalg::GenericOp>(op) &&
723 VectorizationState &state) {
725 auto linalgOp = cast<LinalgOp>(outputOperand->
getOwner());
726 AffineMap opOperandMap = linalgOp.getMatchingIndexingMap(outputOperand);
735 return llvm::is_contained(opOperandMap.getResults(), dimExpr);
737 auto vectorType = state.getCanonicalVecType(
744 if (vectorType.getRank() > 0) {
747 assert(value.
getType() == vectorType &&
"Incorrect type");
748 write = vector::TransferWriteOp::create(
749 rewriter, loc, value, outputOperand->
get(),
indices, writeMap);
752 if (!isa<VectorType>(value.
getType()))
753 value = vector::BroadcastOp::create(rewriter, loc, vectorType, value);
754 assert(value.
getType() == vectorType &&
"Incorrect type");
755 write = vector::TransferWriteOp::create(rewriter, loc, value,
759 write = state.maskOperation(rewriter, write, linalgOp, opOperandMap);
763 if (
auto maskOp = dyn_cast<vector::MaskingOpInterface>(write)) {
764 auto maskedWriteOp = cast<vector::TransferWriteOp>(maskOp.getMaskableOp());
769 LDBG() <<
"vectorized op: " << *write;
779 std::function<LogicalResult(
Operation *,
bool)>;
796 const IRMapping &bvm, VectorizationState &state,
798 auto yieldOp = dyn_cast<linalg::YieldOp>(op);
801 for (
const auto &output : llvm::enumerate(yieldOp.getValues())) {
807 linalgOp.getDpsInitOperand(output.index()), state);
809 newResults.push_back(newResult);
820 VectorizationState &state,
823 IndexOp indexOp = dyn_cast<linalg::IndexOp>(op);
826 auto loc = indexOp.getLoc();
829 auto dim = indexOp.getDim();
831 auto indexVectorType =
832 VectorType::get({targetShape[dim]}, rewriter.
getIndexType(),
833 state.getScalableVecDims()[dim]);
834 auto indexSteps = vector::StepOp::create(rewriter, loc, indexVectorType);
838 if (dim == targetShape.size() - 1)
844 llvm::to_vector(llvm::seq<unsigned>(0, targetShape.size()));
845 std::swap(permPattern[dim], permPattern.back());
849 auto broadCastOp = vector::BroadcastOp::create(
851 state.getCanonicalVecType(rewriter.
getIndexType(), permMap), indexSteps);
853 llvm::to_vector<16>(llvm::seq<int64_t>(0, linalgOp.getNumLoops()));
854 std::swap(transposition.back(), transposition[dim]);
856 vector::TransposeOp::create(rewriter, loc, broadCastOp, transposition);
864 tensor::ExtractOp extractOp = dyn_cast<tensor::ExtractOp>(op);
868 if (extractOp.getIndices().size() != 1 && !vectorizeNDExtract)
873 if (not extractOp.getIndices().empty()) {
874 if (!VectorType::isValidElementType(extractOp.getIndices()[0].getType()))
878 if (!llvm::all_of(extractOp->getResultTypes(),
879 VectorType::isValidElementType)) {
897 VectorizationState &state,
898 tensor::ExtractOp extractOp,
901 auto indexVecType = state.getCanonicalVecType(rewriter.
getIndexType());
902 auto loc = extractOp.getLoc();
905 rewriter, bvm.
lookup(extractOp.getIndices()[0]), indexVecType);
907 const size_t numIndices = extractOp.getIndices().size();
908 for (
size_t i = 1; i < numIndices; i++) {
913 tensor::DimOp::create(rewriter, loc, extractOp.getTensor(), dimIdx),
916 offset = arith::MulIOp::create(rewriter, loc, offset, dimSize);
919 rewriter, bvm.
lookup(extractOp.getIndices()[i]), indexVecType);
921 offset = arith::AddIOp::create(rewriter, loc, extractOpIndex, offset);
947 (linalgOp.hasDynamicShape() ||
948 llvm::count_if(loopRanges, [](
int64_t dim) { return dim != 1; }) == 1) &&
949 "For statically shaped Linalg Ops, only one "
950 "non-unit loop dim is expected");
951 assert(!loopRanges.empty() &&
"Empty loops, nothing to analyse.");
953 size_t idx = loopRanges.size() - 1;
954 for (; idx != 0; idx--)
955 if (loopRanges[idx] != 1)
963 VectorType resType) {
965 assert(((llvm::count_if(resType.getShape(),
966 [](
int64_t dimSize) { return dimSize > 1; }) == 1)) &&
967 "n-D vectors are not yet supported");
969 auto *block = linalgOp.getBlock();
975 while (!worklist.empty()) {
976 Value v = worklist.pop_back_val();
982 if (isa<BlockArgument>(v)) {
983 if (llvm::is_contained(block->getArguments(), v))
989 assert(defOp &&
"This is neither a block argument nor an operation result");
994 if (
auto indexOp = dyn_cast<linalg::IndexOp>(defOp)) {
995 if (linalgOp.getStaticLoopRanges()[indexOp.getDim()] != 1)
1000 auto *ancestor = block->findAncestorOpInBlock(*defOp);
1007 if (isa<arith::ConstantOp>(ancestor))
1010 if (visited.insert(ancestor).second)
1011 llvm::append_range(worklist, ancestor->getOperands());
1035 bool &foundIndexOp, VectorType resType) {
1037 assert(((llvm::count_if(resType.getShape(),
1038 [](
int64_t dimSize) { return dimSize > 1; }) == 1)) &&
1039 "n-D vectors are not yet supported");
1045 auto *block = linalgOp.getBlock();
1046 if (isa<BlockArgument>(val))
1047 return !llvm::is_contained(block->getArguments(), val);
1050 assert(defOp &&
"This is neither a block argument nor an operation result");
1052 if (
auto indexOp = dyn_cast<linalg::IndexOp>(defOp)) {
1055 foundIndexOp = (indexOp.getDim() == loopDimThatIncrementsByOne);
1059 auto *ancestor = block->findAncestorOpInBlock(*defOp);
1066 if (!isa<arith::AddIOp, arith::ConstantOp, linalg::IndexOp>(ancestor))
1070 for (
auto op : ancestor->getOperands())
1090 LinalgOp &linalgOp, VectorType resType) {
1092 auto inputShape = cast<ShapedType>(extractOp.getTensor().getType());
1095 if (inputShape.getShape().empty())
1100 if (resType.getRank() == 0)
1105 bool isOutput1DVector =
1106 (llvm::count_if(resType.getShape(),
1107 [](
int64_t dimSize) { return dimSize > 1; }) == 1);
1109 if (!isOutput1DVector)
1112 bool leadingIdxsLoopInvariant =
true;
1118 auto indices = extractOp.getIndices();
1119 auto leadIndices =
indices.drop_back(1);
1121 for (
auto [i, indexVal] : llvm::enumerate(leadIndices)) {
1122 if (inputShape.getShape()[i] == 1)
1128 if (!leadingIdxsLoopInvariant) {
1129 LDBG() <<
"Found gather load: " << extractOp;
1137 auto extractOpTrailingIdx =
indices.back();
1141 if (leadingIdxsLoopInvariant &&
1143 LDBG() <<
"Found scalar broadcast load: " << extractOp;
1152 bool foundIndexOp =
false;
1154 foundIndexOp, resType);
1157 bool isRowVector = resType.getShape().back() != 1;
1158 isContiguousLoad &= (foundIndexOp && isRowVector);
1160 if (isContiguousLoad) {
1161 LDBG() <<
"Found contigous load: " << extractOp;
1166 LDBG() <<
"Found gather load: " << extractOp;
1174static VectorizationHookResult
1177 tensor::ExtractOp extractOp = dyn_cast<tensor::ExtractOp>(op);
1180 auto loc = extractOp.getLoc();
1183 auto resultType = state.getCanonicalVecType(extractOp.getResult().getType());
1184 auto maskConstantOp = arith::ConstantOp::create(
1188 auto passThruConstantOp = arith::ConstantOp::create(
1194 extractOp.getIndices().size(),
1206 Value mask = state.getOrCreateMaskFor(rewriter, linalgOp);
1208 mask = maskConstantOp;
1211 Operation *gatherOp = vector::GatherOp::create(
1212 rewriter, loc, resultType, extractOp.getTensor(), baseIndices, offset,
1213 mask, passThruConstantOp);
1215 LDBG() <<
"Vectorised as gather load: " << extractOp;
1238 for (
size_t i = 0; i < extractOp.getIndices().size(); i++) {
1239 Value idx = bvm.
lookup(extractOp.getIndices()[i]);
1241 transferReadIdxs.push_back(idx);
1245 auto indexAs1dVector = vector::ShapeCastOp::create(
1247 VectorType::get(resultType.getShape().back(), rewriter.
getIndexType(),
1248 resultType.getScalableDims().back()),
1250 transferReadIdxs.push_back(
1251 vector::ExtractOp::create(rewriter, loc, indexAs1dVector, 0));
1255 auto dstRank = resultType.getRank();
1256 auto srcRank = extractOp.getTensor().getType().getRank();
1265 auto transferReadOp = vector::TransferReadOp::create(
1266 rewriter, loc, resultType, extractOp.getTensor(), transferReadIdxs,
1267 std::nullopt, permutationMap, inBounds);
1269 Operation *readOrMaskedReadOp = transferReadOp;
1275 auto readMaskType = VectorType::get(readMaskShape, rewriter.
getI1Type());
1276 auto allTrue = vector::ConstantMaskOp::create(
1278 readOrMaskedReadOp =
1282 LDBG() <<
"Vectorised as scalar broadcast load: " << extractOp;
1284 readOrMaskedReadOp};
1289 srcRank, std::min(dstRank, srcRank), rewriter.
getContext());
1291 int32_t rankDiff = dstRank - srcRank;
1299 while (rankDiff > 0) {
1300 permutationMap = permutationMap.insertResult(
1305 auto transferReadOp = vector::TransferReadOp::create(
1306 rewriter, loc, resultType, extractOp.getTensor(), transferReadIdxs,
1307 std::nullopt, permutationMap, inBounds);
1316 int64_t numReadDims = std::min(dstRank, srcRank);
1318 linalgOp.getNumLoops(), numReadDims, rewriter.
getContext());
1320 state.maskOperation(rewriter, transferReadOp, linalgOp, maskingMap);
1322 LDBG() <<
"Vectorised as contiguous load: " << extractOp;
1335 auto reduceType = dyn_cast<VectorType>(reduceVec.
getType());
1336 auto outputType = dyn_cast<VectorType>(outputVec.
getType());
1340 (outputType && reduceType.getShape() == outputType.getShape()))
1365static VectorizationHookResult
1369 LDBG() <<
"vectorize op " << *op;
1372 if (!customVectorizationHooks.empty()) {
1373 for (
auto &customFunc : customVectorizationHooks) {
1383 if (isa<arith::ConstantOp, func::ConstantOp>(op))
1385 rewriter.
clone(*op)};
1394 auto blockArg = dyn_cast<BlockArgument>(operand);
1395 if (!blockArg || blockArg.getOwner() != linalgOp.getBlock() ||
1396 blockArg.getArgNumber() < linalgOp.getNumDpsInputs())
1400 linalgOp.getRegionOutputArgs(),
1401 blockArg.getArgNumber() - linalgOp.getNumDpsInputs(), reductionOps);
1404 reductionOperands.push_back(std::make_pair(reduceValue, operand));
1406 if (!reductionOperands.empty()) {
1407 assert(reductionOperands.size() == 1);
1409 reduceIfNeeded(rewriter, linalgOp, op, reductionOperands[0].first,
1410 reductionOperands[0].second, bvm);
1417 VectorType firstMaxRankedType;
1419 auto vecOperand = bvm.
lookup(operand);
1420 assert(vecOperand &&
"Vector operand couldn't be found");
1422 auto vecType = dyn_cast<VectorType>(vecOperand.getType());
1423 if (vecType && (!firstMaxRankedType ||
1424 firstMaxRankedType.getRank() < vecType.getRank()))
1425 firstMaxRankedType = vecType;
1431 assert(vecOperand &&
"Vector operand couldn't be found");
1433 if (firstMaxRankedType) {
1434 auto vecType = VectorType::get(firstMaxRankedType.getShape(),
1436 firstMaxRankedType.getScalableDims());
1439 vecOperands.push_back(vecOperand);
1445 resultTypes.push_back(
1447 ? VectorType::get(firstMaxRankedType.getShape(), resultType,
1448 firstMaxRankedType.getScalableDims())
1486 LDBG() <<
"Vectorizing operation as linalg generic/n";
1487 Block *block = linalgOp.getBlock();
1494 bvm.
map(valuesSet.getArrayRef(), valuesSet.getArrayRef());
1496 if (linalgOp.getNumDpsInits() == 0)
1502 for (
OpOperand *opOperand : linalgOp.getOpOperandsMatchingBBargs()) {
1503 BlockArgument bbarg = linalgOp.getMatchingBlockArgument(opOperand);
1504 if (linalgOp.isScalar(opOperand)) {
1505 bvm.
map(bbarg, opOperand->get());
1511 AffineMap indexingMap = linalgOp.getMatchingIndexingMap(opOperand);
1514 VectorType readType;
1516 if (linalgOp.isDpsInput(opOperand)) {
1519 readType = state.getCanonicalVecType(elemType);
1526 state.getCanonicalVecType(elemType, readMap.
compose(indexingMap));
1531 Operation *read = vector::TransferReadOp::create(
1532 rewriter, loc, readType, opOperand->get(),
indices,
1533 std::nullopt, readMap);
1534 read = state.maskOperation(rewriter, read, linalgOp, indexingMap);
1539 if (
auto maskOp = dyn_cast<vector::MaskingOpInterface>(read)) {
1541 cast<vector::TransferReadOp>(maskOp.getMaskableOp())
1547 if (readType.getRank() == 0)
1548 readValue = vector::ExtractOp::create(rewriter, loc, readValue,
1551 LDBG() <<
"New vectorized bbarg(" << bbarg.
getArgNumber()
1552 <<
"): " << readValue;
1553 bvm.
map(bbarg, readValue);
1554 bvm.
map(opOperand->get(), readValue);
1563 hooks.push_back(vectorizeYield);
1570 hooks.push_back(vectorizeIndex);
1577 hooks.push_back(vectorizeExtract);
1584 LDBG() <<
"failed to vectorize: " << op;
1589 state.maskOperation(rewriter,
result.newOp, linalgOp);
1590 LDBG() <<
"New vector op: " << *maybeMaskedOp;
1616 assert(type.getNumScalableDims() < 2 &&
1617 "Collapsing more than 1 scalable dim is not supported ATM");
1623 auto shape = type.getShape();
1624 auto scalableFlags = type.getScalableDims();
1628 unsigned currentDim = 0;
1630 unsigned dim = m.getNumResults();
1633 for (
unsigned d = 0; d < dim; ++d) {
1634 size *=
shape[currentDim + d];
1635 flag |= scalableFlags[currentDim + d];
1637 newShape.push_back(size);
1638 newScalableFlags.push_back(flag);
1642 return VectorType::get(newShape, type.getElementType(), newScalableFlags);
1674vectorizeAsTensorPackOp(RewriterBase &rewriter, linalg::PackOp packOp,
1675 ArrayRef<int64_t> inputVectorSizes,
1676 SmallVectorImpl<Value> &newResults) {
1677 if (!inputVectorSizes.empty()) {
1678 assert(inputVectorSizes.size() == packOp.getDestRank() &&
1679 "Invalid number of input vector sizes!");
1683 OpBuilder::InsertionGuard g(rewriter);
1686 Location loc = packOp.getLoc();
1687 std::optional<Value> padValue = packOp.getPaddingValue()
1688 ? std::optional(packOp.getPaddingValue())
1691 SmallVector<int64_t> destShape =
1692 SmallVector<int64_t>(packOp.getDestType().getShape());
1696 ArrayRef<int64_t> &writeVectorSizes = inputVectorSizes;
1699 if (writeVectorSizes.empty()) {
1700 if (ShapedType::isDynamicShape(destShape))
1702 "unable to infer vector sizes");
1704 writeVectorSizes = destShape;
1713 PackingMetadata packMetadata;
1714 SmallVector<int64_t> preTransposeWriteVecSizses(writeVectorSizes);
1717 auto preTransposeWriteVecType =
1718 VectorType::get(preTransposeWriteVecSizses,
1719 packOp.getResult().getType().getElementType());
1725 preTransposeWriteVecType,
1727 rewriter.
getContext(), packMetadata.reassociations)));
1731 rewriter, loc, packOp.getSource(), readVecType, padValue);
1734 auto shapeCastOp = vector::ShapeCastOp::create(
1735 rewriter, loc, preTransposeWriteVecType, maskedRead);
1739 auto transposeOp = vector::TransposeOp::create(
1740 rewriter, loc, shapeCastOp.getResult(), destPermutation);
1744 rewriter, loc, transposeOp.getResult(), packOp.getDest());
1745 newResults.push_back(write->
getResult(0));
1778vectorizeAsTensorUnpackOp(RewriterBase &rewriter, linalg::UnPackOp unpackOp,
1779 ArrayRef<int64_t> inputVectorSizes,
1780 ArrayRef<bool> inputScalableVecDims,
1781 SmallVectorImpl<Value> &newResults) {
1782 if (!inputVectorSizes.empty()) {
1783 assert(inputVectorSizes.size() == unpackOp.getSourceRank() &&
1784 "Invalid number of input vector sizes!");
1785 assert(inputVectorSizes.size() == inputScalableVecDims.size() &&
1786 "Incompatible number of vector sizes and vector scalable flags!");
1790 OpBuilder::InsertionGuard g(rewriter);
1793 ShapedType unpackTensorType = unpackOp.getSourceType();
1795 ArrayRef<int64_t> sourceShape = unpackTensorType.getShape();
1797 Location loc = unpackOp->getLoc();
1800 SmallVector<int64_t> readVectorSizes(inputVectorSizes);
1801 SmallVector<bool> readScalableVectorFlags(inputScalableVecDims);
1804 if (inputVectorSizes.empty()) {
1805 if (ShapedType::isDynamicShape(sourceShape))
1807 "Unable to infer vector sizes!");
1809 readVectorSizes.assign(sourceShape.begin(), sourceShape.end());
1813 VectorType readVecType =
1814 VectorType::get(readVectorSizes, unpackTensorType.getElementType(),
1815 readScalableVectorFlags);
1817 rewriter, loc, unpackOp.getSource(), readVecType, std::nullopt);
1820 PackingMetadata packMetadata;
1821 SmallVector<int64_t> lastDimToInsertPosPerm =
1823 vector::TransposeOp transposeOp = vector::TransposeOp::create(
1824 rewriter, loc, readResult, lastDimToInsertPosPerm);
1828 transposeOp.getType(),
1830 rewriter.
getContext(), packMetadata.reassociations)));
1831 vector::ShapeCastOp shapeCastOp = vector::ShapeCastOp::create(
1832 rewriter, loc, collapsedVecType, transposeOp->getResult(0));
1836 rewriter, loc, shapeCastOp.getResult(), unpackOp.getDest());
1838 newResults.push_back(write->
getResult(0));
1846vectorizeAsTensorPadOp(RewriterBase &rewriter, tensor::PadOp padOp,
1847 ArrayRef<int64_t> inputVectorSizes,
1848 SmallVectorImpl<Value> &newResults) {
1849 auto padValue = padOp.getConstantPaddingValue();
1850 Location loc = padOp.getLoc();
1853 OpBuilder::InsertionGuard g(rewriter);
1857 LogicalResult status =
1858 cast<ReifyRankedShapedTypeOpInterface>(padOp.getOperation())
1859 .reifyResultShapes(rewriter, reifiedReturnShapes);
1861 assert(succeeded(status) &&
"failed to reify result shapes");
1862 auto readType = VectorType::get(inputVectorSizes, padValue.getType());
1864 rewriter, loc, padOp.getSource(), readType, padValue,
1868 Value dest = tensor::EmptyOp::create(rewriter, loc, reifiedReturnShapes[0],
1869 padOp.getResultType().getElementType());
1872 newResults.push_back(write->
getResult(0));
1878static LogicalResult reductionPreconditions(LinalgOp op) {
1880 LDBG() <<
"reduction precondition failed: no reduction iterator";
1883 for (OpOperand &opOperand : op.getDpsInitsMutable()) {
1884 AffineMap indexingMap = op.getMatchingIndexingMap(&opOperand);
1890 LDBG() <<
"reduction precondition failed: reduction detection failed";
1898vectorizeDynamicConvOpPrecondition(linalg::LinalgOp conv,
1899 bool flatten1DDepthwiseConv) {
1900 if (flatten1DDepthwiseConv) {
1901 LDBG() <<
"Vectorization of flattened convs with dynamic shapes is not "
1907 LDBG() <<
"Not a 1D depth-wise WC conv, dynamic shapes are not supported";
1913 Value
lhs = conv.getDpsInputOperand(0)->get();
1914 ArrayRef<int64_t> lhsShape = cast<ShapedType>(
lhs.getType()).getShape();
1915 auto shapeWithoutCh = lhsShape.drop_back(1);
1916 if (ShapedType::isDynamicShape(shapeWithoutCh)) {
1917 LDBG() <<
"Dynamically-shaped op vectorization precondition failed: only "
1918 "channel dim can be dynamic";
1926vectorizeDynamicLinalgOpPrecondition(linalg::LinalgOp op,
1927 bool flatten1DDepthwiseConv) {
1929 return vectorizeDynamicConvOpPrecondition(op, flatten1DDepthwiseConv);
1932 return reductionPreconditions(op);
1937 !isa<linalg::GenericOp, linalg::CopyOp, linalg::ContractionOpInterface>(
1941 LDBG() <<
"Dynamically-shaped op meets vectorization pre-conditions";
1951vectorizeUnPackOpPrecondition(linalg::UnPackOp unpackOp,
1952 ArrayRef<int64_t> inputVectorSizes) {
1956 if (!unpackOp.hasPureTensorSemantics())
1961 if (inputVectorSizes.empty() && unpackOp.getDestType().hasStaticShape() &&
1962 unpackOp.getSourceType().hasStaticShape())
1967 if (!inputVectorSizes.empty() &&
1968 (inputVectorSizes.size() != unpackOp.getSourceRank())) {
1969 LDBG() <<
"Incorrect number of input vector sizes";
1975 unpackOp.getSourceType().getShape(), inputVectorSizes))) {
1976 LDBG() <<
"Invalid vector sizes for the read operation";
1984vectorizeInsertSliceOpPrecondition(tensor::InsertSliceOp sliceOp,
1985 ArrayRef<int64_t> inputVectorSizes) {
1988 auto sourceType = source.getType();
1989 if (!VectorType::isValidElementType(sourceType.getElementType()))
1994 if (!sliceOp.hasUnitStride()) {
1995 LDBG() <<
"Only unit strides are supported";
2012 bool isOutOfBoundsRead =
2013 !sourceType.hasStaticShape() && inputVectorSizes.empty();
2015 if (!padValue && isOutOfBoundsRead) {
2016 LDBG() <<
"Failed to get a pad value for out-of-bounds read access";
2030vectorizeAsLinalgContraction(RewriterBase &rewriter, VectorizationState &state,
2032 SmallVectorImpl<Value> &newResults) {
2033 Location loc = linalgOp.getLoc();
2034 MLIRContext *ctx = linalgOp.getContext();
2039 if (!isa<ContractionOpInterface>(linalgOp.getOperation()))
2042 OpOperand *outOperand = linalgOp.getDpsInitOperand(0);
2046 LDBG() <<
"Failed to determine contraction combining kind.";
2053 AffineMap lhsMap = linalgOp.getIndexingMapsArray()[0];
2054 AffineMap rhsMap = linalgOp.getIndexingMapsArray()[1];
2056 LDBG() <<
"Contractions with broadcasts are not supported.";
2061 SmallVector<Value> vecOperands;
2062 for (OpOperand &opOperand : linalgOp->getOpOperands()) {
2066 AffineMap indexingMap = linalgOp.getMatchingIndexingMap(&opOperand);
2070 VectorType readType =
2071 state.getCanonicalVecType(elemType, readMap.
compose(indexingMap));
2074 rewriter, loc, opOperand.get(), readType,
2075 arith::getZeroConstant(rewriter, loc, elemType),
2077 vecOperands.push_back(read);
2083 auto castAttr = linalgOp->getAttrOfType<TypeFnAttr>(
"cast");
2084 bool hasUnsignedCast =
2085 castAttr && castAttr.getValue() == TypeFn::cast_unsigned;
2086 auto accType = dyn_cast<VectorType>(vecOperands[2].
getType());
2087 auto accElementType =
2088 accType ? dyn_cast<IntegerType>(accType.getElementType()) :
nullptr;
2089 if (accElementType && accElementType.isSignless()) {
2090 for (Value &operand : MutableArrayRef(vecOperands).take_front(2)) {
2091 auto operandType = cast<VectorType>(operand.
getType());
2092 Type operandElementType = operandType.getElementType();
2093 VectorType castType = operandType.clone(accElementType);
2095 if (isa<FloatType>(operandElementType)) {
2098 ? arith::FPToUIOp::create(rewriter, loc, castType, operand)
2100 : arith::FPToSIOp::create(rewriter, loc, castType, operand)
2105 auto operandIntegerType = dyn_cast<IntegerType>(operandElementType);
2106 if (!operandIntegerType || !operandIntegerType.isSignless())
2108 if (operandIntegerType.getWidth() >= accElementType.getWidth())
2110 if (!hasUnsignedCast)
2115 operand = arith::ExtUIOp::create(rewriter, loc, castType, operand);
2120 SmallVector<Attribute> iterAttrs;
2121 auto iterators = linalgOp.getIteratorTypesArray();
2122 for (utils::IteratorType iter : iterators) {
2123 auto vecIter = iter == utils::IteratorType::parallel
2124 ? vector::IteratorType::parallel
2125 : vector::IteratorType::reduction;
2126 iterAttrs.push_back(vector::IteratorTypeAttr::get(ctx, vecIter));
2130 Operation *contractOp = vector::ContractionOp::create(
2131 rewriter, loc, vecOperands[0],
2132 vecOperands[1], vecOperands[2],
2133 linalgOp.getIndexingMaps(), rewriter.
getArrayAttr(iterAttrs), *maybeKind);
2134 contractOp = state.maskOperation(rewriter, contractOp, linalgOp);
2138 rewriter, loc, contractOp->
getResult(0), outOperand->
get());
2142 newResults.push_back(write->
getResult(0));
2148enum class ConvOperationKind { Conv, Pool };
2151static bool isCastOfBlockArgument(Operation *op) {
2166static std::optional<ConvOperationKind>
2167getConvOperationKind(Operation *reduceOp) {
2168 int numBlockArguments =
2169 llvm::count_if(reduceOp->
getOperands(), llvm::IsaPred<BlockArgument>);
2171 switch (numBlockArguments) {
2177 auto feedValIt = llvm::find_if_not(reduceOp->
getOperands(),
2178 llvm::IsaPred<BlockArgument>);
2180 "Expected a non-block argument operand");
2181 Operation *feedOp = (*feedValIt).getDefiningOp();
2182 if (isCastOfBlockArgument(feedOp)) {
2183 return ConvOperationKind::Pool;
2186 if (!((isa<arith::MulIOp, arith::MulFOp>(feedOp) ||
2187 (isa<arith::AndIOp>(feedOp) &&
2190 if (isa<BlockArgument>(v))
2192 if (Operation *op = v.getDefiningOp())
2193 return isCastOfBlockArgument(op);
2196 return std::nullopt;
2199 return ConvOperationKind::Conv;
2203 return ConvOperationKind::Pool;
2205 return std::nullopt;
2209static bool isSupportedPoolKind(vector::CombiningKind kind) {
2211 case vector::CombiningKind::ADD:
2212 case vector::CombiningKind::MAXNUMF:
2213 case vector::CombiningKind::MAXIMUMF:
2214 case vector::CombiningKind::MAXIMUMNUMF:
2215 case vector::CombiningKind::MAXSI:
2216 case vector::CombiningKind::MAXUI:
2217 case vector::CombiningKind::MINNUMF:
2218 case vector::CombiningKind::MINIMUMF:
2219 case vector::CombiningKind::MINIMUMNUMF:
2220 case vector::CombiningKind::MINSI:
2221 case vector::CombiningKind::MINUI:
2228static LogicalResult vectorizeConvOpPrecondition(linalg::LinalgOp convOp) {
2229 auto getOperandType = [&](
auto operand) {
2230 return dyn_cast<ShapedType>((operand->get()).getType());
2232 ShapedType lhsShapedType = getOperandType(convOp.getDpsInputOperand(0));
2233 ShapedType rhsShapedType = getOperandType(convOp.getDpsInputOperand(1));
2234 ShapedType resShapedType = getOperandType(convOp.getDpsInitOperand(0));
2238 if ((lhsShapedType.getRank() != 3 || resShapedType.getRank() != 3) &&
2239 (lhsShapedType.getRank() != 1 || resShapedType.getRank() != 1))
2246 auto maybeOper = getConvOperationKind(reduceOp);
2247 if (!maybeOper.has_value())
2254 if (!maybeKind || ((*maybeKind != vector::CombiningKind::ADD &&
2255 *maybeKind != vector::CombiningKind::OR) &&
2256 (*maybeOper != ConvOperationKind::Pool ||
2257 !isSupportedPoolKind(*maybeKind)))) {
2261 auto rhsRank = rhsShapedType.getRank();
2262 if (*maybeOper == ConvOperationKind::Pool) {
2266 if (rhsRank != 1 && rhsRank != 2 && rhsRank != 3)
2273static LogicalResult vectorizeLinalgOpPrecondition(
2274 LinalgOp linalgOp, ArrayRef<int64_t> inputVectorSizes,
2275 bool vectorizeNDExtract,
bool flatten1DDepthwiseConv) {
2277 if (llvm::any_of(linalgOp->getOpOperands(), [&](OpOperand &operand) {
2278 return llvm::is_contained(linalgOp.getShape(&operand), 0);
2282 if (!inputVectorSizes.empty() &&
2287 if (linalgOp.hasDynamicShape() &&
failed(vectorizeDynamicLinalgOpPrecondition(
2288 linalgOp, flatten1DDepthwiseConv))) {
2289 LDBG() <<
"Dynamically-shaped op failed vectorization pre-conditions";
2293 SmallVector<CustomVectorizationPrecondition> customPreconditions;
2299 for (Operation &innerOp : linalgOp->getRegion(0).front()) {
2302 customPreconditions,
2305 customPrecondition(&innerOp, vectorizeNDExtract));
2309 if (!llvm::all_of(innerOp.getOperandTypes(),
2310 VectorType::isValidElementType)) {
2313 if (!llvm::all_of(innerOp.getResultTypes(),
2314 VectorType::isValidElementType)) {
2323 return vectorizeConvOpPrecondition(linalgOp);
2329 LDBG() <<
"precondition failed: not projected permutations";
2332 if (
failed(reductionPreconditions(linalgOp))) {
2333 LDBG() <<
"precondition failed: reduction preconditions";
2340vectorizePackOpPrecondition(linalg::PackOp packOp,
2341 ArrayRef<int64_t> inputVectorSizes) {
2345 if (!packOp.hasPureTensorSemantics())
2348 auto padValue = packOp.getPaddingValue();
2352 LDBG() <<
"pad value is not constant: " << packOp;
2356 ArrayRef<int64_t> resultTensorShape = packOp.getDestType().getShape();
2357 bool satisfyEmptyCond =
true;
2358 if (inputVectorSizes.empty()) {
2359 if (!packOp.getDestType().hasStaticShape() ||
2360 !packOp.getSourceType().hasStaticShape())
2361 satisfyEmptyCond =
false;
2364 if (!satisfyEmptyCond &&
2366 resultTensorShape.take_front(packOp.getSourceRank()),
2370 if (llvm::any_of(packOp.getInnerTiles(), [](OpFoldResult v) {
2371 return !getConstantIntValue(v).has_value();
2373 LDBG() <<
"inner_tiles must be constant: " << packOp;
2381vectorizePadOpPrecondition(tensor::PadOp padOp,
2382 ArrayRef<int64_t> inputVectorSizes) {
2383 auto padValue = padOp.getConstantPaddingValue();
2385 LDBG() <<
"pad value is not constant: " << padOp;
2389 ArrayRef<int64_t> resultTensorShape = padOp.getResultType().getShape();
2405 if (llvm::any_of(llvm::enumerate(padOp.getMixedLowPad()),
2406 [&](
const auto &en) {
2407 OpFoldResult padValue = en.value();
2408 unsigned pos = en.index();
2409 std::optional<int64_t> pad = getConstantIntValue(padValue);
2410 return (!pad.has_value() || pad.value() != 0) &&
2411 resultTensorShape[pos] != 1;
2413 LDBG() <<
"low pad must all be zero for all non unit dims: " << padOp;
2427vectorizeScalableVectorPrecondition(Operation *op,
2428 ArrayRef<int64_t> inputVectorSizes,
2429 ArrayRef<bool> inputScalableVecDims) {
2430 assert(inputVectorSizes.size() == inputScalableVecDims.size() &&
2431 "Number of input vector sizes and scalable dims doesn't match");
2433 size_t numOfScalableDims =
2434 llvm::count_if(inputScalableVecDims, [](
bool flag) {
return flag; });
2436 if (numOfScalableDims == 0)
2439 auto linalgOp = dyn_cast<LinalgOp>(op);
2444 return success(isa<linalg::UnPackOp>(op));
2448 if (numOfScalableDims > 2)
2468 bool seenNonUnitParallel =
false;
2469 auto iterators = linalgOp.getIteratorTypesArray();
2470 SmallVector<bool> scalableFlags(inputScalableVecDims);
2471 int64_t idx = scalableFlags.size() - 1;
2472 while (!scalableFlags[idx]) {
2473 bool isNonUnitDim = (inputVectorSizes[idx] != 1);
2474 seenNonUnitParallel |=
2475 (iterators[idx] == utils::IteratorType::parallel && isNonUnitDim);
2477 iterators.pop_back();
2478 scalableFlags.pop_back();
2483 switch (iterators.back()) {
2484 case utils::IteratorType::reduction: {
2486 if (iterators.size() != inputVectorSizes.size()) {
2487 LDBG() <<
"Non-trailing reduction dim requested for scalable "
2491 if (isa<linalg::MatmulOp>(op)) {
2493 <<
"Scalable vectorization of the reduction dim in Matmul-like ops "
2499 case utils::IteratorType::parallel: {
2501 if (seenNonUnitParallel) {
2502 LDBG() <<
"Inner parallel dim not requested for scalable "
2514 if (numOfScalableDims == 2) {
2518 if (iterators.back() == utils::IteratorType::reduction) {
2519 LDBG() <<
"Higher dim than the trailing reduction dim requested for "
2524 scalableFlags.pop_back();
2525 iterators.pop_back();
2527 if (!scalableFlags.back() ||
2528 (iterators.back() != utils::IteratorType::parallel))
2536 isa<linalg::BatchMatmulOp>(op) ||
2538 isa<linalg::MatvecOp>(op) || isa<linalg::Mmt4DOp>(op) ||
2543 Operation *op, ArrayRef<int64_t> inputVectorSizes,
2544 ArrayRef<bool> inputScalableVecDims,
bool vectorizeNDExtract,
2545 bool flatten1DDepthwiseConv) {
2550 if (
failed(vectorizeScalableVectorPrecondition(op, inputVectorSizes,
2551 inputScalableVecDims)))
2555 .Case([&](linalg::LinalgOp linalgOp) {
2556 return vectorizeLinalgOpPrecondition(linalgOp, inputVectorSizes,
2558 flatten1DDepthwiseConv);
2560 .Case([&](tensor::PadOp padOp) {
2561 return vectorizePadOpPrecondition(padOp, inputVectorSizes);
2563 .Case([&](linalg::PackOp packOp) {
2564 return vectorizePackOpPrecondition(packOp, inputVectorSizes);
2566 .Case([&](linalg::UnPackOp unpackOp) {
2567 return vectorizeUnPackOpPrecondition(unpackOp, inputVectorSizes);
2569 .Case([&](tensor::InsertSliceOp sliceOp) {
2570 return vectorizeInsertSliceOpPrecondition(sliceOp, inputVectorSizes);
2572 .Default(failure());
2576static void convertAffineApply(RewriterBase &rewriter, LinalgOp linalgOp) {
2577 OpBuilder::InsertionGuard g(rewriter);
2578 auto toReplace = linalgOp.getBlock()->getOps<affine::AffineApplyOp>();
2580 for (
auto op : make_early_inc_range(toReplace)) {
2582 auto expanded = affine::expandAffineExpr(
2584 op.
getOperands().take_front(op.getAffineMap().getNumDims()),
2585 op.
getOperands().take_back(op.getAffineMap().getNumSymbols()));
2591 return isa<linalg::LinalgOp, tensor::PadOp, linalg::PackOp, linalg::UnPackOp,
2592 tensor::InsertSliceOp>(op);
2596 RewriterBase &rewriter, Operation *op, ArrayRef<int64_t> inputVectorSizes,
2597 ArrayRef<bool> inputScalableVecDims,
bool vectorizeNDExtract,
2598 bool flatten1DDepthwiseConv,
bool assumeDynamicDimsMatchVecSizes,
2599 bool createNamedContraction) {
2600 LDBG() <<
"Attempting to vectorize: " << *op;
2601 LDBG() <<
"Input vector sizes: " << llvm::interleaved(inputVectorSizes);
2602 LDBG() <<
"Input scalable vector dims: "
2603 << llvm::interleaved(inputScalableVecDims);
2607 flatten1DDepthwiseConv))) {
2608 LDBG() <<
"Vectorization pre-conditions failed";
2613 VectorizationState state(rewriter);
2614 if (
auto linalgOp = dyn_cast<linalg::LinalgOp>(op)) {
2615 if (
failed(state.initState(rewriter, linalgOp, inputVectorSizes,
2616 inputScalableVecDims,
2617 assumeDynamicDimsMatchVecSizes))) {
2618 LDBG() <<
"Vectorization state couldn't be initialized";
2623 SmallVector<Value> results;
2624 auto vectorizeResult =
2626 .Case([&](linalg::LinalgOp linalgOp) {
2630 rewriter, linalgOp, inputVectorSizes, inputScalableVecDims,
2631 flatten1DDepthwiseConv);
2632 if (succeeded(convOr)) {
2633 llvm::append_range(results, (*convOr)->getResults());
2637 LDBG() <<
"Unsupported convolution can't be vectorized.";
2641 if (createNamedContraction &&
2642 isa<ContractionOpInterface>(linalgOp.getOperation()))
2643 return vectorizeAsLinalgContraction(rewriter, state, linalgOp,
2647 <<
"Vectorize generic by broadcasting to the canonical vector "
2651 convertAffineApply(rewriter, linalgOp);
2660 .Case([&](tensor::PadOp padOp) {
2661 return vectorizeAsTensorPadOp(rewriter, padOp, inputVectorSizes,
2664 .Case([&](linalg::PackOp packOp) {
2665 return vectorizeAsTensorPackOp(rewriter, packOp, inputVectorSizes,
2668 .Case([&](linalg::UnPackOp unpackOp) {
2669 return vectorizeAsTensorUnpackOp(rewriter, unpackOp,
2671 inputScalableVecDims, results);
2673 .Case([&](tensor::InsertSliceOp sliceOp) {
2677 .Default(failure());
2679 if (
failed(vectorizeResult)) {
2680 LDBG() <<
"Vectorization failed";
2684 return VectorizationResult{results};
2688 memref::CopyOp copyOp) {
2689 auto srcType = cast<MemRefType>(copyOp.getSource().getType());
2690 auto dstType = cast<MemRefType>(copyOp.getTarget().getType());
2691 if (!srcType.hasStaticShape() || !dstType.hasStaticShape())
2696 if (!VectorType::isValidElementType(srcElementType) ||
2697 !VectorType::isValidElementType(dstElementType))
2700 auto readType = VectorType::get(srcType.getShape(), srcElementType);
2701 auto writeType = VectorType::get(dstType.getShape(), dstElementType);
2703 Location loc = copyOp->getLoc();
2705 SmallVector<Value>
indices(srcType.getRank(), zero);
2707 Value
readValue = vector::TransferReadOp::create(
2708 rewriter, loc, readType, copyOp.getSource(),
indices,
2711 if (cast<VectorType>(
readValue.getType()).getRank() == 0) {
2712 readValue = vector::ExtractOp::create(rewriter, loc, readValue,
2713 ArrayRef<int64_t>());
2715 vector::BroadcastOp::create(rewriter, loc, writeType, readValue);
2717 Operation *writeValue = vector::TransferWriteOp::create(
2718 rewriter, loc, readValue, copyOp.getTarget(),
indices,
2729template <
typename OpTy>
2730struct VectorizePadOpUserPattern :
public OpRewritePattern<tensor::PadOp> {
2731 using OpRewritePattern<tensor::PadOp>::OpRewritePattern;
2733 LogicalResult matchAndRewrite(tensor::PadOp padOp,
2734 PatternRewriter &rewriter)
const final {
2735 bool changed =
false;
2737 for (
auto *user : llvm::to_vector<4>(padOp->getUsers()))
2738 if (
auto op = dyn_cast<OpTy>(user))
2739 changed |= rewriteUser(rewriter, padOp, op).succeeded();
2744 virtual LogicalResult rewriteUser(PatternRewriter &rewriter,
2745 tensor::PadOp padOp, OpTy op)
const = 0;
2767struct PadOpVectorizationWithTransferReadPattern
2768 :
public VectorizePadOpUserPattern<vector::TransferReadOp> {
2769 using VectorizePadOpUserPattern<
2770 vector::TransferReadOp>::VectorizePadOpUserPattern;
2772 LogicalResult rewriteUser(PatternRewriter &rewriter, tensor::PadOp padOp,
2773 vector::TransferReadOp xferOp)
const override {
2775 if (!padOp.hasZeroLowPad())
2778 auto padValue = padOp.getConstantPaddingValue();
2782 if (xferOp.hasOutOfBoundsDim() || xferOp.getMask())
2786 SmallVector<bool> inBounds(xferOp.getVectorType().getRank(),
false);
2787 xferOp->setInherentAttr(xferOp.getInBoundsAttrName(),
2789 xferOp.getBaseMutable().assign(padOp.getSource());
2790 xferOp.getPaddingMutable().assign(padValue);
2829struct PadOpVectorizationWithTransferWritePattern
2830 :
public VectorizePadOpUserPattern<vector::TransferWriteOp> {
2831 using VectorizePadOpUserPattern<
2832 vector::TransferWriteOp>::VectorizePadOpUserPattern;
2834 LogicalResult rewriteUser(PatternRewriter &rewriter, tensor::PadOp padOp,
2835 vector::TransferWriteOp xferOp)
const override {
2837 if (xferOp.getTransferRank() == 0)
2841 if (!padOp.hasZeroLowPad())
2844 auto padValue = padOp.getConstantPaddingValue();
2848 if (!xferOp->hasOneUse())
2850 auto trimPadding = dyn_cast<tensor::ExtractSliceOp>(*xferOp->user_begin());
2854 if (!trimPadding.hasZeroOffset())
2857 if (!hasSameTensorSize(padOp.getSource(), trimPadding))
2863 SmallVector<bool> inBounds(xferOp.getVectorType().getRank(),
false);
2865 xferOp, padOp.getSource().
getType(), xferOp.getVector(),
2866 padOp.getSource(), xferOp.getIndices(), xferOp.getPermutationMapAttr(),
2868 rewriter.
replaceOp(trimPadding, newXferOp->getResult(0));
2883 bool hasSameTensorSize(Value beforePadding,
2884 tensor::ExtractSliceOp afterTrimming)
const {
2887 if (
auto castOp = beforePadding.
getDefiningOp<tensor::CastOp>())
2888 if (hasSameTensorSize(castOp.getSource(), afterTrimming))
2891 auto t1 = dyn_cast<RankedTensorType>(beforePadding.
getType());
2892 auto t2 = dyn_cast<RankedTensorType>(afterTrimming.getType());
2897 if (t1.getRank() != t2.getRank())
2902 for (
unsigned i = 0; i < t1.getRank(); ++i) {
2903 if (t1.isDynamicDim(i) != t2.isDynamicDim(i))
2905 if (!t1.isDynamicDim(i) && t1.getDimSize(i) != t2.getDimSize(i))
2910 if (t1.getNumDynamicDims() == 0)
2918 auto beforeSlice = beforePadding.
getDefiningOp<tensor::ExtractSliceOp>();
2922 assert(
static_cast<size_t>(t1.getRank()) ==
2923 beforeSlice.getMixedSizes().size());
2924 assert(
static_cast<size_t>(t2.getRank()) ==
2925 afterTrimming.getMixedSizes().size());
2927 for (
unsigned i = 0; i < t1.getRank(); ++i) {
2929 if (!t1.isDynamicDim(i))
2931 auto size1 = beforeSlice.getMixedSizes()[i];
2932 auto size2 = afterTrimming.getMixedSizes()[i];
2939 auto v1 = llvm::dyn_cast_if_present<Value>(size1);
2940 auto v2 = llvm::dyn_cast_if_present<Value>(size2);
2946 auto minOp1 = v1.getDefiningOp<affine::AffineMinOp>();
2947 auto minOp2 = v2.getDefiningOp<affine::AffineMinOp>();
2948 if (minOp1 && minOp2 && minOp1.getAffineMap() == minOp2.getAffineMap() &&
2949 minOp1.getOperands() == minOp2.getOperands())
2975 if (
auto bcast = llvm::dyn_cast<vector::BroadcastOp>(op)) {
2976 auto source = bcast.getSource();
2977 if (llvm::dyn_cast<VectorType>(source.getType()))
2985 if (
auto fill = llvm::dyn_cast<linalg::FillOp>(op)) {
2986 return fill.getInputs()[0];
2991 if (
auto generate = llvm::dyn_cast<tensor::GenerateOp>(op)) {
2998 if (
auto xferWrite = llvm::dyn_cast<vector::TransferWriteOp>(op))
3006 if (
auto slice = llvm::dyn_cast<tensor::InsertSliceOp>(op))
3014 ArrayRef<int64_t> inputVectorSizes,
3015 SmallVectorImpl<Value> &newResults) {
3017 OpBuilder::InsertionGuard g(rewriter);
3021 auto sourceType = source.getType();
3022 auto resultType = sliceOp.getResultType();
3027 auto elemType = sourceType.getElementType();
3028 padValue = arith::ConstantOp::create(rewriter, sliceOp.getLoc(), elemType,
3035 llvm::SmallBitVector droppedDims = sliceOp.getDroppedDims();
3036 SmallVector<int64_t> resultDimsForSourceDims;
3037 resultDimsForSourceDims.reserve(sourceType.getRank());
3038 for (int64_t resultDim = 0, end = resultType.getRank(); resultDim < end;
3040 if (!droppedDims[resultDim])
3041 resultDimsForSourceDims.push_back(resultDim);
3042 assert(resultDimsForSourceDims.size() ==
3043 static_cast<size_t>(sourceType.getRank()) &&
3044 "expected one non-dropped result dim per source dim");
3046 SmallVector<int64_t> vecShape;
3047 for (int64_t i = 0, end = sourceType.getRank(); i < end; ++i) {
3048 if (!inputVectorSizes.empty()) {
3049 vecShape.push_back(inputVectorSizes[i]);
3050 }
else if (!sourceType.isDynamicDim(i)) {
3051 vecShape.push_back(sourceType.getDimSize(i));
3052 }
else if (!resultType.isDynamicDim(resultDimsForSourceDims[i])) {
3056 vecShape.push_back(resultType.getDimSize(resultDimsForSourceDims[i]));
3063 auto vecType = VectorType::get(vecShape, sourceType.getElementType());
3066 auto loc = sliceOp.getLoc();
3069 SmallVector<Value> readIndices(
3072 rewriter, loc, source, vecType, padValue,
3073 inputVectorSizes.empty());
3081 if (sourceType.getRank() != resultType.getRank()) {
3082 SmallVector<AffineExpr> exprs;
3083 for (int64_t resultDim : resultDimsForSourceDims)
3090 rewriter, loc, read, sliceOp.getDest(), writeIndices,
3091 inputVectorSizes.empty(), writeMap);
3094 newResults.push_back(write->
getResult(0));
3122struct PadOpVectorizationWithInsertSlicePattern
3123 :
public VectorizePadOpUserPattern<tensor::InsertSliceOp> {
3124 using VectorizePadOpUserPattern<
3125 tensor::InsertSliceOp>::VectorizePadOpUserPattern;
3127 LogicalResult rewriteUser(PatternRewriter &rewriter, tensor::PadOp padOp,
3128 tensor::InsertSliceOp insertOp)
const override {
3130 if (!padOp.hasZeroLowPad())
3133 if (!insertOp.hasUnitStride())
3136 auto padValue = padOp.getConstantPaddingValue();
3140 if (!cast<ShapedType>(padOp.getResult().getType()).hasStaticShape())
3143 if (insertOp.getDest() == padOp.getResult())
3146 auto vecType = VectorType::get(padOp.getType().getShape(),
3147 padOp.getType().getElementType());
3148 unsigned vecRank = vecType.getRank();
3149 unsigned tensorRank = insertOp.getType().getRank();
3153 SmallVector<int64_t> expectedSizes(tensorRank - vecRank, 1);
3154 expectedSizes.append(vecType.getShape().begin(), vecType.getShape().end());
3156 llvm::zip(insertOp.getMixedSizes(), expectedSizes), [](
auto it) {
3157 return getConstantIntValue(std::get<0>(it)) == std::get<1>(it);
3167 SmallVector<Value> readIndices(
3169 auto read = vector::TransferReadOp::create(rewriter, padOp.getLoc(),
3170 vecType, padOp.getSource(),
3171 readIndices, padValue);
3177 rewriter, padOp.getLoc(), insertOp.getMixedOffsets());
3178 SmallVector<bool> inBounds(vecRank,
true);
3180 insertOp, read, insertOp.getDest(), writeIndices,
3181 ArrayRef<bool>{inBounds});
3188 RewritePatternSet &patterns, PatternBenefit baseBenefit) {
3189 patterns.
add<PadOpVectorizationWithTransferReadPattern,
3190 PadOpVectorizationWithTransferWritePattern,
3191 PadOpVectorizationWithInsertSlicePattern>(
3202static bool mayExistInterleavedUses(Operation *firstOp, Operation *secondOp,
3206 LDBG() <<
"interleavedUses precondition failed, firstOp: " << *firstOp
3207 <<
", second op: " << *secondOp;
3210 for (
auto v : values) {
3211 for (
auto &u : v.getUses()) {
3212 Operation *owner = u.getOwner();
3213 if (owner == firstOp || owner == secondOp)
3219 LDBG() <<
" found interleaved op " << *owner <<
", firstOp: " << *firstOp
3220 <<
", second op: " << *secondOp;
3229static memref::SubViewOp getSubViewUseIfUnique(Value v) {
3230 memref::SubViewOp subViewOp;
3232 if (
auto newSubViewOp = dyn_cast<memref::SubViewOp>(u.getOwner())) {
3234 return memref::SubViewOp();
3235 subViewOp = newSubViewOp;
3244 vector::TransferReadOp xferOp, PatternRewriter &rewriter)
const {
3247 if (xferOp.getMask())
3251 Value viewOrAlloc = xferOp.getBase();
3257 memref::SubViewOp subViewOp = getSubViewUseIfUnique(viewOrAlloc);
3260 Value subView = subViewOp.getResult();
3263 memref::CopyOp copyOp;
3264 for (
auto &u : subView.
getUses()) {
3265 if (
auto newCopyOp = dyn_cast<memref::CopyOp>(u.getOwner())) {
3266 assert(isa<MemRefType>(newCopyOp.getTarget().getType()));
3267 if (newCopyOp.getTarget() != subView)
3269 if (mayExistInterleavedUses(newCopyOp, xferOp, {viewOrAlloc, subView}))
3281 for (
auto &u : viewOrAlloc.
getUses()) {
3282 if (
auto newFillOp = dyn_cast<FillOp>(u.getOwner())) {
3283 assert(isa<MemRefType>(newFillOp.output().getType()));
3284 if (newFillOp.output() != viewOrAlloc)
3286 if (mayExistInterleavedUses(newFillOp, copyOp, {viewOrAlloc, subView}))
3288 maybeFillOp = newFillOp;
3293 if (maybeFillOp && xferOp.getPadding() != maybeFillOp.value())
3295 "padding value does not match fill");
3298 Value in = copyOp.getSource();
3304 auto vectorType = xferOp.getVectorType();
3305 Value res = vector::TransferReadOp::create(
3306 rewriter, xferOp.getLoc(), vectorType, in, xferOp.getIndices(),
3307 xferOp.getPermutationMapAttr(), xferOp.getPadding(), xferOp.getMask(),
3309 SmallVector<bool>(vectorType.getRank(),
false)));
3312 rewriter.
eraseOp(maybeFillOp);
3322 vector::TransferWriteOp xferOp, PatternRewriter &rewriter)
const {
3324 if (xferOp.getMask())
3328 Value viewOrAlloc = xferOp.getBase();
3334 memref::SubViewOp subViewOp = getSubViewUseIfUnique(viewOrAlloc);
3337 Value subView = subViewOp.getResult();
3340 memref::CopyOp copyOp;
3341 for (
auto &u : subViewOp.getResult().getUses()) {
3342 if (
auto newCopyOp = dyn_cast<memref::CopyOp>(u.getOwner())) {
3343 if (newCopyOp.getSource() != subView)
3345 if (mayExistInterleavedUses(xferOp, newCopyOp, {viewOrAlloc, subView}))
3355 assert(isa<MemRefType>(copyOp.getTarget().getType()));
3356 Value out = copyOp.getTarget();
3363 auto vector = xferOp.getVector();
3364 vector::TransferWriteOp::create(
3365 rewriter, xferOp.getLoc(), vector, out, xferOp.getIndices(),
3366 xferOp.getPermutationMapAttr(), xferOp.getMask(),
3368 dyn_cast<VectorType>(vector.getType()).getRank(),
false)));
3381static void bindShapeDims(ShapedType shapedType) {}
3383template <
int N,
typename IntTy,
typename... IntTy2>
3384static void bindShapeDims(ShapedType shapedType, IntTy &val, IntTy2 &...vals) {
3385 val = shapedType.getShape()[N];
3386 bindShapeDims<N + 1, IntTy2 &...>(shapedType, vals...);
3390template <
typename... IntTy>
3391static void bindShapeDims(ShapedType shapedType, IntTy &...vals) {
3392 bindShapeDims<0>(shapedType, vals...);
3397static std::optional<DilationsAndStrides> match1DConvPoolOp(LinalgOp op) {
3398#define MATCH_1D_CONV_POOL_OP(ConvOpTy) \
3399 if (auto convParams = matchConvolutionOpOfType<ConvOpTy>(op)) \
3421#undef MATCH_1D_CONV_POOL_OP
3423 return std::nullopt;
3461struct Conv1DGenerator
3462 :
public StructuredGenerator<LinalgOp, utils::IteratorType> {
3465 static FailureOr<Conv1DGenerator> create(RewriterBase &rewriter,
3466 LinalgOp linalgOp) {
3469 std::optional<DilationsAndStrides> convParams = match1DConvPoolOp(linalgOp);
3473 int strideW =
static_cast<int>(convParams->strides.front());
3474 int dilationW =
static_cast<int>(convParams->dilations.front());
3475 return Conv1DGenerator(rewriter, linalgOp, strideW, dilationW);
3479 Conv1DGenerator(RewriterBase &rewriter, LinalgOp linalgOp,
int strideW,
3481 : StructuredGenerator<LinalgOp, utils::IteratorType>(rewriter, linalgOp),
3482 strideW(strideW), dilationW(dilationW) {
3484 lhsShaped = linalgOp.getDpsInputOperand(0)->
get();
3485 rhsShaped = linalgOp.getDpsInputOperand(1)->
get();
3486 resShaped = linalgOp.getDpsInitOperand(0)->
get();
3487 lhsShapedType = dyn_cast<ShapedType>(lhsShaped.getType());
3488 rhsShapedType = dyn_cast<ShapedType>(rhsShaped.getType());
3489 resShapedType = dyn_cast<ShapedType>(resShaped.getType());
3494 setConvOperationKind(reduceOp);
3497 reductionKind = maybeKind.value();
3520 int64_t nSize, wSize, cSize, kwSize, fSize;
3521 SmallVector<int64_t, 3> lhsShape, rhsShape, resShape;
3523 switch (conv1DOpOrder) {
3526 nSize = fSize = cSize = 0;
3528 bindShapeDims(resShapedType, wSize);
3530 bindShapeDims(rhsShapedType, kwSize);
3533 (wSize + kwSize - 1)};
3534 rhsShape = {kwSize};
3539 bindShapeDims(resShapedType, nSize, wSize, fSize);
3541 case ConvOperationKind::Conv:
3543 bindShapeDims(rhsShapedType, kwSize, cSize);
3545 case ConvOperationKind::Pool:
3547 bindShapeDims(rhsShapedType, kwSize);
3555 ((wSize - 1) * strideW + 1) + ((kwSize - 1) * dilationW + 1) -
3559 case ConvOperationKind::Conv:
3560 rhsShape = {kwSize, cSize, fSize};
3562 case ConvOperationKind::Pool:
3563 rhsShape = {kwSize};
3566 resShape = {nSize, wSize, fSize};
3570 bindShapeDims(resShapedType, nSize, fSize, wSize);
3572 case ConvOperationKind::Conv:
3574 bindShapeDims(rhsShapedType, fSize, cSize, kwSize);
3576 case ConvOperationKind::Pool:
3578 bindShapeDims(rhsShapedType, kwSize);
3582 lhsShape = {nSize, cSize,
3586 ((wSize - 1) * strideW + 1) + ((kwSize - 1) * dilationW + 1) -
3589 case ConvOperationKind::Conv:
3590 rhsShape = {fSize, cSize, kwSize};
3592 case ConvOperationKind::Pool:
3593 rhsShape = {kwSize};
3596 resShape = {nSize, fSize, wSize};
3600 vector::TransferWriteOp write;
3606 int64_t wSizeStep = strideW == 1 ? wSize : 1;
3608 Type lhsEltType = lhsShapedType.getElementType();
3609 Type rhsEltType = rhsShapedType.getElementType();
3610 Type resEltType = resShapedType.getElementType();
3611 auto lhsType = VectorType::get(lhsShape, lhsEltType);
3612 auto rhsType = VectorType::get(rhsShape, rhsEltType);
3613 auto resType = VectorType::get(resShape, resEltType);
3615 SmallVector<Value> lhsPadding(lhsShape.size(), zero);
3616 SmallVector<Value> rhsPadding(rhsShape.size(), zero);
3617 SmallVector<Value> resPadding(resShape.size(), zero);
3620 Value
lhs = vector::TransferReadOp::create(
3621 rewriter, loc, lhsType, lhsShaped, lhsPadding,
3622 arith::getZeroConstant(rewriter, loc, lhsEltType));
3624 Value
rhs =
nullptr;
3625 if (oper == ConvOperationKind::Conv)
3626 rhs = vector::TransferReadOp::create(
3627 rewriter, loc, rhsType, rhsShaped, rhsPadding,
3628 arith::getZeroConstant(rewriter, loc, rhsEltType));
3629 Value res = vector::TransferReadOp::create(
3630 rewriter, loc, resType, resShaped, resPadding,
3631 arith::getZeroConstant(rewriter, loc, resEltType));
3636 switch (conv1DOpOrder) {
3644 static constexpr std::array<int64_t, 3> permLhs = {0, 2, 1};
3645 lhs = vector::TransposeOp::create(rewriter, loc,
lhs, permLhs);
3647 static constexpr std::array<int64_t, 3> permRhs = {2, 1, 0};
3650 if (oper == ConvOperationKind::Conv)
3651 rhs = vector::TransposeOp::create(rewriter, loc,
rhs, permRhs);
3653 static constexpr std::array<int64_t, 3> permRes = {0, 2, 1};
3654 res = vector::TransposeOp::create(rewriter, loc, res, permRes);
3663 SmallVector<Value> lhsVals, rhsVals, resVals;
3665 kwSize, strideW, dilationW, wSizeStep,
3668 if (oper == ConvOperationKind::Conv)
3671 wSizeStep, isSingleChanneled);
3673 auto linearIndex = [&](int64_t kw, int64_t w) {
3674 return kw * (wSize / wSizeStep) + w;
3680 for (int64_t kw = 0; kw < kwSize; ++kw) {
3681 for (int64_t w = 0; w < wSize; w += wSizeStep) {
3683 case ConvOperationKind::Conv:
3684 if (isSingleChanneled) {
3685 resVals[w] = conv1dSliceAsOuterProduct(rewriter, loc,
3686 lhsVals[linearIndex(kw, w)],
3687 rhsVals[kw], resVals[w]);
3689 resVals[w] = conv1dSliceAsContraction(rewriter, loc,
3690 lhsVals[linearIndex(kw, w)],
3691 rhsVals[kw], resVals[w]);
3694 case ConvOperationKind::Pool:
3695 resVals[w] = pool1dSlice(rewriter, loc, lhsVals[linearIndex(kw, w)],
3711 switch (conv1DOpOrder) {
3718 static constexpr std::array<int64_t, 3> perm = {0, 2, 1};
3719 res = vector::TransposeOp::create(rewriter, loc, res, perm);
3724 return vector::TransferWriteOp::create(rewriter, loc, res, resShaped,
3730 Value
promote(RewriterBase &rewriter, Location loc, Value val, Type ty,
3731 Operation *castOp) {
3736 assert(castOp &&
"expected a payload cast for promoted operand");
3740 if (
auto shapedType = dyn_cast<ShapedType>(val.
getType()))
3741 dstType = shapedType.cloneWith(std::nullopt, dstElementType);
3743 dstType = dstElementType;
3752 Value conv1dSliceAsContraction(RewriterBase &rewriter, Location loc,
3753 Value
lhs, Value
rhs, Value res) {
3754 vector::IteratorType par = vector::IteratorType::parallel;
3755 vector::IteratorType red = vector::IteratorType::reduction;
3756 AffineExpr n, w, f, c;
3760 auto contrationOp = vector::ContractionOp::create(
3761 rewriter, loc,
lhs,
rhs, res,
3762 MapList{{n, w, c}, {c, f}, {n, w, f}},
3763 ArrayRef<vector::IteratorType>{par, par, par, red});
3764 contrationOp.setKind(reductionKind);
3765 return contrationOp;
3770 Value conv1dSliceAsOuterProduct(RewriterBase &rewriter, Location loc,
3771 Value
lhs, Value
rhs, Value res) {
3774 return vector::OuterProductOp::create(rewriter, loc, res.
getType(),
lhs,
3775 rhs, res, vector::CombiningKind::ADD);
3779 Value pool1dSlice(RewriterBase &rewriter, Location loc, Value
lhs,
3797 FailureOr<Operation *> depthwiseConv(uint64_t channelDimVecSize,
3798 bool channelDimScalableFlag,
3800 bool scalableChDim =
false;
3801 bool useMasking =
false;
3802 int64_t nSize, wSize, cSize, kwSize;
3804 bindShapeDims(rhsShapedType, kwSize, cSize);
3805 if (ShapedType::isDynamic(cSize)) {
3806 assert(channelDimVecSize != 0 &&
"Channel dim vec size must be > 0");
3807 cSize = channelDimVecSize;
3811 scalableChDim = channelDimScalableFlag;
3815 assert(!(useMasking && flatten) &&
3816 "Unsupported flattened conv with dynamic shapes");
3819 bindShapeDims(resShapedType, nSize, wSize);
3821 vector::TransferWriteOp write;
3827 int64_t wSizeStep = strideW == 1 ? wSize : 1;
3829 Type lhsEltType = lhsShapedType.getElementType();
3830 Type rhsEltType = rhsShapedType.getElementType();
3831 Type resEltType = resShapedType.getElementType();
3832 VectorType lhsType = VectorType::get(
3836 ((wSize - 1) * strideW + 1) + ((kwSize - 1) * dilationW + 1) - 1,
3838 lhsEltType, {
false,
false, scalableChDim});
3839 VectorType rhsType =
3840 VectorType::get({kwSize, cSize}, rhsEltType,
3841 {
false, scalableChDim});
3842 VectorType resType =
3843 VectorType::get({nSize, wSize, cSize}, resEltType,
3844 {
false,
false, scalableChDim});
3848 auto maybeMaskXferOp = [&](ArrayRef<int64_t> maskShape,
3849 ArrayRef<bool> scalableDims,
3850 Operation *opToMask) {
3854 VectorType::get(maskShape, rewriter.
getI1Type(), scalableDims);
3856 SmallVector<bool> inBounds(maskShape.size(),
true);
3857 auto xferOp = cast<VectorTransferOpInterface>(opToMask);
3858 xferOp->setInherentAttr(
3863 cast<LinalgOp>(op).hasPureTensorSemantics(), opToMask, rewriter);
3866 vector::CreateMaskOp::create(rewriter, loc, maskType, mixedDims);
3873 Value
lhs = vector::TransferReadOp::create(
3874 rewriter, loc, lhsType, lhsShaped,
ValueRange{zero, zero, zero},
3875 arith::getZeroConstant(rewriter, loc, lhsEltType));
3876 auto *maybeMaskedLhs = maybeMaskXferOp(
3877 lhsType.getShape(), lhsType.getScalableDims(),
lhs.getDefiningOp());
3880 Value
rhs = vector::TransferReadOp::create(
3881 rewriter, loc, rhsType, rhsShaped,
ValueRange{zero, zero},
3882 arith::getZeroConstant(rewriter, loc, rhsEltType));
3883 auto *maybeMaskedRhs = maybeMaskXferOp(
3884 rhsType.getShape(), rhsType.getScalableDims(),
rhs.getDefiningOp());
3887 Value res = vector::TransferReadOp::create(
3888 rewriter, loc, resType, resShaped,
ValueRange{zero, zero, zero},
3889 arith::getZeroConstant(rewriter, loc, resEltType));
3890 auto *maybeMaskedRes = maybeMaskXferOp(
3891 resType.getShape(), resType.getScalableDims(), res.
getDefiningOp());
3897 SmallVector<Value> lhsVals, rhsVals, resVals;
3898 SmallVector<int64_t> inOutSliceSizes = {nSize, wSizeStep, cSize};
3899 SmallVector<int64_t> inOutStrides = {1, 1, 1};
3903 for (int64_t kw = 0; kw < kwSize; ++kw) {
3904 for (int64_t w = 0; w < wSize; w += wSizeStep) {
3905 lhsVals.push_back(vector::ExtractStridedSliceOp::create(
3906 rewriter, loc, maybeMaskedLhs->getResult(0),
3907 ArrayRef<int64_t>{0, w * strideW + kw * dilationW, 0},
3908 inOutSliceSizes, inOutStrides));
3912 for (int64_t kw = 0; kw < kwSize; ++kw) {
3914 vector::ExtractOp::create(rewriter, loc, maybeMaskedRhs->getResult(0),
3915 ArrayRef<int64_t>{kw}));
3918 for (int64_t w = 0; w < wSize; w += wSizeStep) {
3919 resVals.push_back(vector::ExtractStridedSliceOp::create(
3920 rewriter, loc, maybeMaskedRes->getResult(0),
3921 ArrayRef<int64_t>{0, w, 0}, inOutSliceSizes,
3925 auto linearIndex = [&](int64_t kw, int64_t w) {
3926 return kw * (wSize / wSizeStep) + w;
3931 SmallVector<int64_t> inOutFlattenSliceSizes = {nSize, wSizeStep * cSize};
3932 auto lhsTypeAfterFlattening =
3933 VectorType::get(inOutFlattenSliceSizes, lhsEltType);
3934 auto resTypeAfterFlattening =
3935 VectorType::get(inOutFlattenSliceSizes, resEltType);
3938 for (int64_t kw = 0; kw < kwSize; ++kw) {
3939 for (int64_t w = 0; w < wSize; w += wSizeStep) {
3940 Value lhsVal = lhsVals[linearIndex(kw, w)];
3941 Value resVal = resVals[w];
3946 vector::ShapeCastOp::create(rewriter, loc, lhsTypeAfterFlattening,
3947 lhsVals[linearIndex(kw, w)]);
3948 resVal = vector::ShapeCastOp::create(
3949 rewriter, loc, resTypeAfterFlattening, resVals[w]);
3951 resVals[w] = depthwiseConv1dSliceAsMulAcc(rewriter, loc, lhsVal,
3952 rhsVals[kw], resVal, flatten);
3955 resVals[w] = vector::ShapeCastOp::create(
3956 rewriter, loc, VectorType::get(inOutSliceSizes, resEltType),
3963 if (!llvm::all_of(resVals, [](Value v) {
return v; })) {
3965 for (
auto &collection :
3966 {resVals, rhsVals, lhsVals, {res,
rhs,
lhs, zero}})
3967 for (Value v : collection)
3974 for (int64_t w = 0; w < wSize; w += wSizeStep) {
3975 maybeMaskedRes = vector::InsertStridedSliceOp::create(
3976 rewriter, loc, resVals[w], maybeMaskedRes->getResult(0),
3977 ArrayRef<int64_t>{0, w, 0},
3978 ArrayRef<int64_t>{1, 1, 1});
3985 Operation *resOut = vector::TransferWriteOp::create(
3986 rewriter, loc, maybeMaskedRes->getResult(0), resShaped,
3988 return maybeMaskXferOp(resType.getShape(), resType.getScalableDims(),
3996 Value depthwiseConv1dSliceAsMulAcc(RewriterBase &rewriter, Location loc,
3997 Value
lhs, Value
rhs, Value res,
3999 auto rhsTy = cast<ShapedType>(
rhs.getType());
4000 auto resTy = cast<ShapedType>(res.
getType());
4014 auto rhsSize = cast<VectorType>(
rhs.getType()).getShape()[0];
4015 auto resSize = cast<VectorType>(res.
getType()).getShape()[1];
4017 SmallVector<int64_t, 16>
indices;
4018 for (
int i = 0; i < resSize / rhsSize; ++i) {
4019 for (
int j = 0; j < rhsSize; ++j)
4026 rhs = vector::BroadcastOp::create(rewriter, loc,
4027 resTy.clone(rhsTy.getElementType()),
rhs);
4034 if (isa<FloatType>(resTy.getElementType()))
4035 return vector::FMAOp::create(rewriter, loc,
lhs,
rhs, res);
4037 auto mul = arith::MulIOp::create(rewriter, loc,
lhs,
rhs);
4038 return arith::AddIOp::create(rewriter, loc,
mul, res);
4043 FailureOr<Operation *> generateNonChanneledConv() {
4046 if (!iters({Par(), Red()}))
4048 "failed to match conv::W 1-par 1-red");
4051 if (layout({ {w + kw},
4061 FailureOr<Operation *> generateNwcConv() {
4062 AffineExpr n, w, f, kw, c;
4064 if (!iters({Par(), Par(), Par(), Red(), Red()}))
4066 op,
"failed to match conv::Nwc 3-par 2-red");
4069 if (layout({ {n, strideW * w + dilationW * kw, c},
4079 FailureOr<Operation *> generateNcwConv() {
4080 AffineExpr n, w, f, kw, c;
4082 if (!iters({Par(), Par(), Par(), Red(), Red()}))
4084 op,
"failed to match conv::Ncw 3-par 2-red");
4086 if (layout({ {n, c, strideW * w + dilationW * kw},
4096 FailureOr<Operation *> generateNwcPooling() {
4097 AffineExpr n, w, c, kw;
4099 if (!iters({Par(), Par(), Par(), Red()}))
4101 "failed to match pooling 3-par 1-red");
4104 if (layout({ {n, strideW * w + dilationW * kw, c},
4114 FailureOr<Operation *> generateNcwPooling() {
4115 AffineExpr n, w, c, kw;
4117 if (!iters({Par(), Par(), Par(), Red()}))
4119 "failed to match pooling 3-par 1-red");
4121 if (layout({ {n, c, strideW * w + dilationW * kw},
4131 FailureOr<Operation *> generateDilatedConv(uint64_t vecChDimSize = 0,
4132 bool vecChDimScalableFlag =
false,
4133 bool flatten =
false) {
4134 AffineExpr n, w, c, kw;
4136 if (!iters({Par(), Par(), Par(), Red()}))
4138 op,
"failed to match depthwise::Nwc conv 3-par 1-red");
4141 if (layout({ {n, strideW * w + dilationW * kw, c},
4144 return depthwiseConv(vecChDimSize, vecChDimScalableFlag, flatten);
4150 ConvOperationKind oper = ConvOperationKind::Conv;
4152 StringAttr poolExtOp;
4153 bool isPoolExt =
false;
4156 Operation *lhsCastOp =
nullptr;
4157 Operation *rhsCastOp =
nullptr;
4158 int strideW, dilationW;
4159 Value lhsShaped, rhsShaped, resShaped;
4160 ShapedType lhsShapedType, rhsShapedType, resShapedType;
4161 vector::CombiningKind reductionKind;
4165 void setConvOperationKind(Operation *reduceOp) {
4166 int numBlockArguments =
4167 llvm::count_if(reduceOp->
getOperands(), llvm::IsaPred<BlockArgument>);
4168 if (numBlockArguments == 1) {
4173 auto feedValIt = llvm::find_if_not(reduceOp->
getOperands(),
4174 llvm::IsaPred<BlockArgument>);
4175 Operation *feedOp = (*feedValIt).getDefiningOp();
4176 if (isCastOfBlockArgument(feedOp)) {
4177 oper = ConvOperationKind::Pool;
4182 oper = ConvOperationKind::Conv;
4183 setConvCastOps(feedOp);
4187 oper = ConvOperationKind::Pool;
4192 void setConvCastOps(Operation *feedOp) {
4202 RewriterBase &rewriter, LinalgOp op, ArrayRef<int64_t> inputVecSizes,
4203 ArrayRef<bool> inputScalableVecDims,
bool flatten1DDepthwiseConv) {
4204 FailureOr<Conv1DGenerator> conv1dGen = Conv1DGenerator::create(rewriter, op);
4207 auto res = conv1dGen->generateNonChanneledConv();
4210 res = conv1dGen->generateNwcConv();
4213 res = conv1dGen->generateNcwConv();
4216 res = conv1dGen->generateNwcPooling();
4219 res = conv1dGen->generateNcwPooling();
4226 uint64_t vecChDimSize = ShapedType::kDynamic;
4227 bool vecChDimScalableFlag =
false;
4228 if (!inputVecSizes.empty()) {
4233 "Not a 1D depthwise conv!");
4234 size_t chDimIdx = 0;
4240 vecChDimSize = inputVecSizes[chDimIdx];
4241 vecChDimScalableFlag = inputScalableVecDims[chDimIdx];
4243 return conv1dGen->generateDilatedConv(vecChDimSize, vecChDimScalableFlag,
4244 flatten1DDepthwiseConv);
4247struct VectorizeConvolution :
public OpInterfaceRewritePattern<LinalgOp> {
4250 LogicalResult matchAndRewrite(LinalgOp op,
4251 PatternRewriter &rewriter)
const override {
4253 if (
failed(resultOrFail))
4255 Operation *newOp = *resultOrFail;
4257 rewriter.
eraseOp(op.getOperation());
4260 assert(newOp->
getNumResults() == 1 &&
"expected single result");
4267 RewritePatternSet &patterns, PatternBenefit benefit) {
4268 patterns.
add<VectorizeConvolution>(patterns.
getContext(), benefit);
static std::optional< VectorShape > vectorShape(Type type)
static SmallVector< Value > slice(OpBuilder &b, Location loc, Value value, VectorType nativeType)
static bool isLoopInvariantIdx(LinalgOp &linalgOp, Value &val, VectorType resType)
Checks whether val can be used for calculating a loop invariant index.
static Value insertConvResultSlices(RewriterBase &rewriter, Location loc, Value res, int64_t wSize, int64_t wSizeStep, SmallVectorImpl< Value > &resVals, bool isSingleChanneled)
Helper function to insert the computed result slices.
static SmallVector< bool > getDimsToReduce(LinalgOp linalgOp)
static VectorMemoryAccessKind getTensorExtractMemoryAccessPattern(tensor::ExtractOp extractOp, LinalgOp &linalgOp, VectorType resType)
Infer the memory access pattern for the input ExtractOp.
static SmallVector< Value > extractConvInputSlices(RewriterBase &rewriter, Location loc, Value input, int64_t nSize, int64_t wSize, int64_t cSize, int64_t kwSize, int strideW, int dilationW, int64_t wSizeStep, bool isSingleChanneled)
Helper function to extract the input slices after filter is unrolled along kw.
static VectorizationHookResult vectorizeTensorExtract(RewriterBase &rewriter, VectorizationState &state, Operation *op, LinalgOp linalgOp, const IRMapping &bvm)
Helper function to vectorize the tensor.extract operations.
static VectorizationHookResult vectorizeLinalgIndex(RewriterBase &rewriter, VectorizationState &state, Operation *op, LinalgOp linalgOp)
Helper function to vectorize the index operations of a linalgOp.
static LogicalResult vectorizeAsInsertSliceOp(RewriterBase &rewriter, tensor::InsertSliceOp sliceOp, ArrayRef< int64_t > inputVectorSizes, SmallVectorImpl< Value > &newResults)
Vectorize tensor::InsertSliceOp with:
static FailureOr< Operation * > vectorizeConvolution(RewriterBase &rewriter, LinalgOp convOp, ArrayRef< int64_t > inputVecSizes={}, ArrayRef< bool > inputVecScalableFlags={}, bool flatten1DDepthwiseConv=false)
Try to vectorize convOp as a convolution.
static LogicalResult vectorizeAsLinalgGeneric(RewriterBase &rewriter, VectorizationState &state, LinalgOp linalgOp, SmallVectorImpl< Value > &newResults)
Generic vectorization function that rewrites the body of a linalgOp into vector form.
#define MATCH_1D_CONV_POOL_OP(ConvOpTy)
static VectorizationHookResult vectorizeOneOp(RewriterBase &rewriter, VectorizationState &state, LinalgOp linalgOp, Operation *op, const IRMapping &bvm, ArrayRef< CustomVectorizationHook > customVectorizationHooks)
Generic vectorization for a single operation op, given already vectorized operands carried by bvm.
static Operation * matchLinalgReduction(OpOperand *outputOperand)
Check whether outputOperand is a reduction with a single combiner operation.
static Value buildVectorWrite(RewriterBase &rewriter, Value value, OpOperand *outputOperand, VectorizationState &state)
Build a vector.transfer_write of value into outputOperand at indices set to all 0; where outputOperan...
static Value getStaticPadVal(Operation *op)
Returns the effective Pad value for the input op, provided it's a scalar.
static SmallVector< Value > extractConvFilterSlices(RewriterBase &rewriter, Location loc, Value filter, int64_t kwSize)
Helper function to extract the filter slices after filter is unrolled along kw.
static bool hasReductionIterator(LinalgOp &op)
Check if op is a linalg.reduce or a linalg.generic that has at least one reduction iterator.
std::function< LogicalResult(Operation *, bool)> CustomVectorizationPrecondition
static uint64_t getTrailingNonUnitLoopDimIdx(LinalgOp linalgOp)
Find the index of the trailing non-unit dim in linalgOp.
static VectorType getCollapsedVecType(VectorType type, ArrayRef< AffineMap > reassociation)
Given the re-associations, "collapses" the input Vector type.
Conv1DOpOrder
Helper enum to represent conv1d input traversal order.
VectorizationHookStatus
Helper data structure to represent the result of vectorization for a single operation.
@ Failure
Op failed to vectorize.
@ NewOp
Op vectorized into a new Op whose results will replace original Op's results.
@ NoReplace
Op vectorized and custom function took care of replacement logic.
static Operation * reduceIfNeeded(OpBuilder &b, LinalgOp linalgOp, Operation *op, Value reduceValue, Value initialValue, const IRMapping &bvm)
Emit reduction operations if the shapes of the value to reduce is different that the result shape.
std::function< VectorizationHookResult(Operation *, const IRMapping &)> CustomVectorizationHook
static AffineMap reindexIndexingMap(AffineMap map)
Given an indexing map coming from a LinalgOp indexing, restricted to a projectedPermutation,...
static LogicalResult tensorExtractVectorizationPrecondition(Operation *op, bool vectorizeNDExtract)
Helper function to check if the tensor.extract can be vectorized by the custom hook vectorizeTensorEx...
static Value broadcastIfNeeded(OpBuilder &b, Value value, Type dstType)
Broadcast value to a vector of shape if possible.
static Value calculateGatherOffset(RewriterBase &rewriter, VectorizationState &state, tensor::ExtractOp extractOp, const IRMapping &bvm)
Calculates the offsets ($index_vec) for vector.gather operations generated from tensor....
static SmallVector< Value > extractConvResultSlices(RewriterBase &rewriter, Location loc, Value res, int64_t nSize, int64_t wSize, int64_t fSize, int64_t wSizeStep, bool isSingleChanneled)
Helper function to extract the result slices after filter is unrolled along kw.
static bool isContiguousLoadIdx(LinalgOp &linalgOp, Value &val, bool &foundIndexOp, VectorType resType)
Check whether val could be used for calculating the trailing index for a contiguous load operation.
static VectorizationHookResult vectorizeLinalgYield(RewriterBase &rewriter, Operation *op, const IRMapping &bvm, VectorizationState &state, LinalgOp linalgOp, SmallVectorImpl< Value > &newResults)
Helper function to vectorize the terminator of a linalgOp.
static Operation * buildMultiDimReduce(OpBuilder &b, Operation *reduceOp, Value valueToReduce, Value acc, ArrayRef< bool > dimsToMask)
Create MultiDimReductionOp to compute the reduction for reductionOp.
A dimensional identifier appearing in an affine expression.
A multi-dimensional affine map Affine map's are immutable like Type's, and they are uniqued.
static AffineMap getMinorIdentityMap(unsigned dims, unsigned results, MLIRContext *context)
Returns an identity affine map (d0, ..., dn) -> (dp, ..., dn) on the most minor dimensions.
MLIRContext * getContext() const
static AffineMap getMultiDimIdentityMap(unsigned numDims, MLIRContext *context)
Returns an AffineMap with 'numDims' identity result dim exprs.
static AffineMap get(MLIRContext *context)
Returns a zero result affine map with no dimensions or symbols: () -> ().
bool isProjectedPermutation(bool allowZeroInResults=false) const
Returns true if the AffineMap represents a subset (i.e.
unsigned getNumResults() const
unsigned getNumInputs() const
AffineExpr getResult(unsigned idx) const
static AffineMap getFilteredIdentityMap(MLIRContext *ctx, unsigned numDims, llvm::function_ref< bool(AffineDimExpr)> keepDimFilter)
Returns an identity affine map with numDims input dimensions and filtered results using keepDimFilter...
AffineMap dropZeroResults()
Returns the AffineMap resulting from removing "zero" results (constant values == 0) from this map.
static AffineMap getPermutationMap(ArrayRef< unsigned > permutation, MLIRContext *context)
Returns an AffineMap representing a permutation.
SmallVector< unsigned > getBroadcastDims() const
Returns the list of broadcast dimensions (i.e.
AffineMap compose(AffineMap map) const
Returns the AffineMap resulting from composing this with map.
bool isPermutation() const
Returns true if the AffineMap represents a symbol-less permutation map.
This class represents an argument of a Block.
unsigned getArgNumber() const
Returns the number of this argument.
Block represents an ordered list of Operations.
OpListType & getOperations()
AffineMap getMultiDimIdentityMap(unsigned rank)
StringAttr getStringAttr(const Twine &bytes)
TypedAttr getZeroAttr(Type type)
AffineExpr getAffineDimExpr(unsigned position)
ArrayAttr getArrayAttr(ArrayRef< Attribute > value)
MLIRContext * getContext() const
ArrayAttr getBoolArrayAttr(ArrayRef< bool > values)
static DenseIntElementsAttr get(const ShapedType &type, Arg &&arg)
Get an instance of a DenseIntElementsAttr with the given arguments.
This is a utility class for mapping one set of IR entities to another.
auto lookup(T from) const
Lookup a mapped value within the map.
void map(Value from, Value to)
Inserts a new mapping for 'from' to 'to'.
IRValueT get() const
Return the current value being used by this operand.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
MLIRContext is the top-level object for a collection of MLIR operations.
This class helps build Operations.
Operation * clone(Operation &op, IRMapping &mapper)
Creates a deep copy of the specified operation, remapping any operands that use values outside of the...
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
Operation * create(const OperationState &state)
Creates an operation given the fields represented as an OperationState.
Operation * insert(Operation *op)
Insert the given operation at the current insertion point and return it.
This class represents an operand of an operation.
unsigned getOperandNumber() const
Return which operand this is in the OpOperand list of the Operation.
StringAttr getIdentifier() const
Return the name of this operation as a StringAttr.
Operation is the basic unit of execution within MLIR.
PropertyRef getPropertiesStorage()
Return a generic (but typed) reference to the property type storage.
Value getOperand(unsigned idx)
bool isBeforeInBlock(Operation *other)
Given an operation 'other' that is within the same parent block, return whether the current operation...
Block * getBlock()
Returns the operation block that contains this operation.
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Location getLoc()
The source location the operation was defined or derived from.
unsigned getNumOperands()
Attribute getPropertiesAsAttribute()
Return the properties converted to an attribute.
operand_iterator operand_end()
OperationName getName()
The name of an operation is the key identifier for it.
DictionaryAttr getDiscardableAttrDictionary()
Return all of the discardable attributes on this operation as a DictionaryAttr.
static Operation * create(Location location, OperationName name, TypeRange resultTypes, ValueRange operands, NamedAttrList &&attributes, PropertyRef properties, BlockRange successors, unsigned numRegions)
Create a new Operation with the specific fields.
result_type_range getResultTypes()
operand_range getOperands()
Returns an iterator on the underlying Value's.
result_range getResults()
unsigned getNumResults()
Return the number of results held by this operation.
unsigned short getBenefit() const
If the corresponding pattern can match, return its benefit. If the.
MLIRContext * getContext() const
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
This class coordinates the application of a rewrite on a set of IR, providing a way for clients to tr...
virtual void replaceOp(Operation *op, ValueRange newValues)
Replace the results of the given (original) operation with the specified list of values (replacements...
virtual void eraseOp(Operation *op)
This method erases an operation that is known to have no uses.
void replaceAllUsesExcept(Value from, Value to, Operation *exceptedUser)
Find uses of from and replace them with to except if the user is exceptedUser.
std::enable_if_t<!std::is_convertible< CallbackT, Twine >::value, LogicalResult > notifyMatchFailure(Location loc, CallbackT &&reasonCallback)
Used to notify the listener that the IR failed to be rewritten because of a match failure,...
void modifyOpInPlace(Operation *root, CallableT &&callable)
This method is a utility wrapper around an in-place modification of an operation.
OpTy replaceOpWithNewOp(Operation *op, Args &&...args)
Replace the results of the given (original) op with a new op that is created without verification (re...
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Type getType() const
Return the type of this value.
use_range getUses() const
Returns a range of all uses, which is useful for iterating over all uses.
Location getLoc() const
Return the location of this value.
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
static ConstantIndexOp create(OpBuilder &builder, Location location, int64_t value)
Operation * getOwner() const
Return the owner of this operand.
bool hasElementwiseMappableTraits(Operation *op)
Together, Elementwise, Scalarizable, Vectorizable, and Tensorizable provide an easy way for scalar op...
bool hasVectorizationImpl(Operation *)
Return true if there's dedicated logic in the Linalg Vectorizer to vectorize this Op,...
SmallVector< int64_t > getUnPackInverseSrcPerm(linalg::UnPackOp, PackingMetadata &metadata)
Compute inverse permutation for the source tensor (i.e.
bool allIndexingsAreProjectedPermutation(LinalgOp op)
Check if all indexing maps are projected permutations.
FailureOr< VectorizationResult > vectorize(RewriterBase &rewriter, Operation *op, ArrayRef< int64_t > inputVectorSizes={}, ArrayRef< bool > inputScalableVecDims={}, bool vectorizeNDExtract=false, bool flatten1DDepthwiseConv=false, bool assumeDynamicDimsMatchVecSizes=false, bool createNamedContraction=false)
Returns a VectorizationResult containing the results of the vectorized op, or failure if the transfor...
void populatePadOpVectorizationPatterns(RewritePatternSet &patterns, PatternBenefit baseBenefit=1)
Populates patterns with patterns that vectorize tensor.pad.
bool isReductionIterator(utils::IteratorType iteratorType)
Check if iterator type has "reduction" semantics.
bool isaConvolutionOpInterface(LinalgOp linalgOp, bool allowEmptyConvolvedDims=false)
Checks whether linalgOp conforms to ConvolutionOpInterface.
void populateConvolutionVectorizationPatterns(RewritePatternSet &patterns, PatternBenefit benefit=1)
Populate patterns for vectorizing low-D convolution ops.
bool isElementwise(LinalgOp op)
Check if a LinalgOp is an element-wise operation.
LogicalResult vectorizeCopy(RewriterBase &builder, memref::CopyOp copyOp)
Emit a suitable vector form for a Copy op with fully static shape.
LogicalResult vectorizeOpPrecondition(Operation *op, ArrayRef< int64_t > inputVectorSizes={}, ArrayRef< bool > inputScalableVecDims={}, bool vectorizeNDExtract=false, bool flatten1DDepthwiseConv=false)
Return success if the operation can be vectorized.
SmallVector< int64_t > getPackInverseDestPerm(linalg::PackOp packOp, PackingMetadata &metadata)
Compute inverse permutation for the destination tensor (i.e.
bool isaConvolutionOpOfType(LinalgOp op)
Returns true if the linalg op is a convolution op of type ConvOpTy.
std::optional< vector::CombiningKind > getCombinerOpKind(Operation *combinerOp)
Return vector::CombiningKind for the given op.
LogicalResult promote(RewriterBase &rewriter, scf::ForallOp forallOp)
Promotes the loop body of a scf::ForallOp to its containing block.
std::enable_if_t<!is_complex< V >::value, V > readValue(char **linePtr)
Returns an element-value of non-complex type.
Operation * maskOperation(OpBuilder &builder, Operation *maskableOp, Value mask, Value passthru=Value())
Creates a vector.mask operation around a maskable operation.
LogicalResult isValidMaskedInputVector(ArrayRef< int64_t > shape, ArrayRef< int64_t > inputVectorSizes)
Returns success if inputVectorSizes is a valid masking configuraion for given shape,...
BroadcastableToResult isBroadcastableTo(Type srcType, VectorType dstVectorType, std::pair< VectorDim, VectorDim > *mismatchingDims=nullptr)
Return whether srcType can be broadcast to dstVectorType under the semantics of the vector....
Operation * createWriteOrMaskedWrite(OpBuilder &builder, Location loc, Value vecToStore, Value dest, SmallVector< Value > writeIndices={}, bool useInBoundsInsteadOfMasking=false, AffineMap permutationMap=AffineMap())
Create a TransferWriteOp of vecToStore into dest.
Value createReadOrMaskedRead(OpBuilder &builder, Location loc, Value source, const VectorType &vecToReadTy, std::optional< Value > padValue=std::nullopt, bool useInBoundsInsteadOfMasking=false, ArrayRef< Value > indices={}, AffineMap permutationMap=AffineMap())
Creates a TransferReadOp from source.
SmallVector< OpFoldResult > getMixedSizesXfer(bool hasTensorSemantics, Operation *xfer, RewriterBase &rewriter)
A wrapper for getMixedSizes for vector.transfer_read and vector.transfer_write Ops (for source and de...
Include the generated interface declarations.
bool matchPattern(Value value, const Pattern &pattern)
Entry point for matching a pattern over a Value.
bool isEqualConstantIntOrValue(OpFoldResult ofr1, OpFoldResult ofr2)
Return true if ofr1 and ofr2 are the same integer constant attribute values or the same SSA value.
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
AffineMap inverseAndBroadcastProjectedPermutation(AffineMap map)
Return the reverse map of a projected permutation where the projected dimensions are transformed into...
void bindDims(MLIRContext *ctx, AffineExprTy &...exprs)
Bind a list of AffineExpr references to DimExpr at positions: [0 .
AffineMap inversePermutation(AffineMap map)
Returns a map of codomain to domain dimensions such that the first codomain dimension for a particula...
SmallVector< AffineMap, 4 > getSymbolLessAffineMaps(ArrayRef< ReassociationExprs > reassociation)
Constructs affine maps out of Array<Array<AffineExpr>>.
SmallVector< SmallVector< OpFoldResult > > ReifiedRankedShapedTypeDims
Value matchReduction(ArrayRef< BlockArgument > iterCarriedArgs, unsigned redPos, SmallVectorImpl< Operation * > &combinerOps)
Utility to match a generic reduction given a list of iteration-carried arguments, iterCarriedArgs and...
llvm::SetVector< T, Vector, Set, N > SetVector
Type getElementTypeOrSelf(Type type)
Return the element type or return the type itself.
std::conditional_t< std::is_same_v< Ty, mlir::Type >, mlir::Value, detail::TypedValue< Ty > > TypedValue
If Ty is mlir::Type this will select Value instead of having a wrapper around it.
void getUsedValuesDefinedAbove(Region ®ion, Region &limit, SetVector< Value > &values)
Fill values with a list of values defined at the ancestors of the limit region and used within region...
AffineMap compressUnusedDims(AffineMap map)
Drop the dims that are not used.
SmallVector< SmallVector< AffineExpr, 2 >, 2 > convertReassociationIndicesToExprs(MLIRContext *context, ArrayRef< ReassociationIndices > reassociationIndices)
Convert reassociation indices to affine expressions.
bool isReassociationValid(ArrayRef< AffineMap > reassociation, int *invalidIndex=nullptr)
Return true if the reassociation specification is valid, false otherwise.
llvm::TypeSwitch< T, ResultT > TypeSwitch
Value getValueOrCreateConstantIndexOp(OpBuilder &b, Location loc, OpFoldResult ofr)
Converts an OpFoldResult to a Value.
AffineExpr getAffineConstantExpr(int64_t constant, MLIRContext *context)
auto get(MLIRContext *context, Ts &&...params)
Helper method that injects context only if needed, this helps unify some of the attribute constructio...
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
SmallVector< T > applyPermutationMap(AffineMap map, llvm::ArrayRef< T > source)
Apply a permutation from map to source and return the result.
llvm::SmallBitVector getUnusedDimsBitVector(ArrayRef< AffineMap > maps)
detail::constant_op_matcher m_Constant()
Matches a constant foldable operation.
void applyPermutationToVector(SmallVector< T, N > &inVec, ArrayRef< int64_t > permutation)
Apply the permutation defined by permutation to inVec.
SmallVector< int64_t > invertPermutationVector(ArrayRef< int64_t > permutation)
Helper method to apply to inverse a permutation.
VectorizationHookResult contains the vectorized op returned from a CustomVectorizationHook.
enum VectorizationHookStatus status
Return status from vectorizing the current op.
Operation * newOp
New vectorized operation to replace the current op.
ArrayRef< int64_t > getCanonicalVecShape() const
Returns the canonical vector shape used to vectorize the iteration space.
LogicalResult initState(RewriterBase &rewriter, LinalgOp linalgOp, ArrayRef< int64_t > inputVectorSizes, ArrayRef< bool > inputScalableVecDims, bool assumeDynamicDimsMatchVecSizes=false)
Initializes the vectorization state, including the computation of the canonical vector shape for vect...
Operation * maskOperation(RewriterBase &rewriter, Operation *opToMask, LinalgOp linalgOp, std::optional< AffineMap > maybeIndexingMap=std::nullopt)
Masks an operation with the canonical vector mask if the operation needs masking.
VectorType getCanonicalVecType(Type elementType, std::optional< AffineMap > dimPermutation=std::nullopt) const
Returns a vector type of the provided elementType with the canonical vector shape and the correspondi...
ArrayRef< bool > getScalableVecDims() const
Returns the vector dimensions that are scalable in the canonical vector shape.
Value getOrCreateMaskFor(RewriterBase &rewriter, LinalgOp linalgOp, std::optional< AffineMap > maybeMaskingMap=std::nullopt)
Create or retrieve an existing mask value for the canonical vector iteration space.
VectorizationState(RewriterBase &rewriter)
LogicalResult matchAndRewrite(vector::TransferReadOp xferOp, PatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(vector::TransferWriteOp xferOp, PatternRewriter &rewriter) const override