38#include "llvm/ADT/STLExtras.h"
39#include "llvm/ADT/Sequence.h"
40#include "llvm/ADT/SmallVector.h"
41#include "llvm/ADT/SmallVectorExtras.h"
42#include "llvm/ADT/TypeSwitch.h"
43#include "llvm/Support/DebugLog.h"
44#include "llvm/Support/InterleavedRange.h"
45#include "llvm/Support/MathExtras.h"
46#include "llvm/Support/raw_ostream.h"
52#define DEBUG_TYPE "linalg-vectorization"
55static FailureOr<Operation *>
59 bool flatten1DDepthwiseConv =
false);
94template <
typename OpType>
97 block.
walk([&](OpType op) {
113 int64_t kwSize,
int strideW,
int dilationW,
114 int64_t wSizeStep,
bool isSingleChanneled) {
116 if (isSingleChanneled) {
121 for (
int64_t kw = 0; kw < kwSize; ++kw) {
122 for (
int64_t w = 0; w < wSize; w += wSizeStep) {
123 result.push_back(vector::ExtractStridedSliceOp::create(
133 for (
int64_t kw = 0; kw < kwSize; ++kw) {
134 for (
int64_t w = 0; w < wSize; w += wSizeStep) {
135 result.push_back(vector::ExtractStridedSliceOp::create(
136 rewriter, loc, input,
153 for (
int64_t kw = 0; kw < kwSize; ++kw) {
154 result.push_back(vector::ExtractOp::create(
165 int64_t wSizeStep,
bool isSingleChanneled) {
167 if (isSingleChanneled) {
171 for (
int64_t w = 0; w < wSize; w += wSizeStep) {
172 result.push_back(vector::ExtractStridedSliceOp::create(
181 for (
int64_t w = 0; w < wSize; w += wSizeStep) {
182 result.push_back(vector::ExtractStridedSliceOp::create(
194 bool isSingleChanneled) {
196 if (isSingleChanneled) {
200 for (
int64_t w = 0; w < wSize; w += wSizeStep) {
201 res = vector::InsertStridedSliceOp::create(
209 for (
int64_t w = 0; w < wSize; w += wSizeStep) {
210 res = vector::InsertStridedSliceOp::create(
211 rewriter, loc, resVals[w], res,
225 LogicalResult initState(
RewriterBase &rewriter, LinalgOp linalgOp,
228 bool assumeDynamicDimsMatchVecSizes =
false);
243 std::optional<AffineMap> dimPermutation = std::nullopt)
const {
246 if (dimPermutation.has_value()) {
252 vectorShape.append(canonicalVecShape.begin(), canonicalVecShape.end());
253 scalableDims.append(scalableVecDims.begin(), scalableVecDims.end());
256 return VectorType::get(
vectorShape, elementType, scalableDims);
265 std::optional<AffineMap> maybeIndexingMap = std::nullopt);
270 void initIterSpaceStaticSizes(LinalgOp linalgOp) {
271 iterSpaceStaticSizes.append(linalgOp.getStaticLoopRanges());
277 LogicalResult precomputeIterSpaceValueSizes(RewriterBase &rewriter,
284 Value getOrCreateMaskFor(RewriterBase &rewriter, Operation *opToMask,
286 std::optional<AffineMap> maybeMaskingMap);
291 bool isValidMaskingMap(AffineMap maskingMap) {
310 AffineMap getMaskingMapFromIndexingMap(AffineMap &indexingMap) {
316 SmallVector<int64_t> iterSpaceStaticSizes;
321 SmallVector<Value> iterSpaceValueSizes;
324 SmallVector<int64_t> canonicalVecShape;
328 SmallVector<bool> scalableVecDims;
336 OpBuilder::InsertionGuard rewriterGuard;
344 bool assumeDynamicDimsMatchVecSizes =
false;
348VectorizationState::precomputeIterSpaceValueSizes(
RewriterBase &rewriter,
351 for (
int vecDim = 0, end = canonicalVecShape.size(); vecDim < end; ++vecDim) {
352 if (ShapedType::isStatic(iterSpaceStaticSizes[vecDim])) {
355 rewriter, linalgOp.getLoc(), iterSpaceStaticSizes[vecDim]));
362 unsigned operandDimPos;
363 if (
failed(linalgOp.mapIterationSpaceDimToOperandDim(vecDim, operand,
368 linalgOp.hasPureTensorSemantics()
369 ? (Value)tensor::DimOp::create(rewriter, linalgOp.getLoc(), operand,
371 : (Value)memref::DimOp::create(rewriter, linalgOp.getLoc(), operand,
373 iterSpaceValueSizes.push_back(dynamicDim);
386 bool assumeDimsMatchVec) {
387 assumeDynamicDimsMatchVecSizes = assumeDimsMatchVec;
391 if (!inputVectorSizes.empty()) {
395 canonicalVecShape.append(inputVectorSizes.begin(), inputVectorSizes.end());
396 scalableVecDims.append(inputScalableVecDims.begin(),
397 inputScalableVecDims.end());
402 canonicalVecShape = linalgOp.getStaticLoopRanges();
403 scalableVecDims.append(linalgOp.getNumLoops(),
false);
406 LDBG() <<
"Canonical vector shape: " << llvm::interleaved(canonicalVecShape);
407 LDBG() <<
"Scalable vector dims: " << llvm::interleaved(scalableVecDims);
409 if (ShapedType::isDynamicShape(canonicalVecShape))
413 initIterSpaceStaticSizes(linalgOp);
418 if (failed(precomputeIterSpaceValueSizes(rewriter, linalgOp)))
428Value VectorizationState::getOrCreateMaskFor(
430 std::optional<AffineMap> maybeMaskingMap) {
432 assert((!maybeMaskingMap || isValidMaskingMap(*maybeMaskingMap)) &&
433 "Ill-formed masking map.");
436 auto maskableOp = dyn_cast<vector::MaskableOpInterface>(opToMask);
440 assert(!maskableOp.isMasked() &&
441 "Masking an operation that is already masked");
444 assert((!maybeMaskingMap || *maybeMaskingMap) &&
445 "Unexpected null mask permutation map");
447 maybeMaskingMap ? *maybeMaskingMap
449 linalgOp.getNumLoops(), rewriter.
getContext());
451 LDBG() <<
"Masking map: " << maskingMap;
455 auto activeMaskIt = activeMaskCache.find(maskingMap);
456 if (activeMaskIt != activeMaskCache.end()) {
457 Value mask = activeMaskIt->second;
458 LDBG() <<
"Reusing mask: " << mask;
468 SmallVector<int64_t> permutedStaticSizes =
470 auto maskType = getCanonicalVecType(rewriter.
getI1Type(), maskingMap);
471 auto maskShape = maskType.getShape();
473 LDBG() <<
"Mask shape: " << llvm::interleaved(maskShape);
475 if (permutedStaticSizes == maskShape) {
476 LDBG() <<
"Masking is not needed for masking map: " << maskingMap;
477 activeMaskCache[maskingMap] = Value();
481 if (assumeDynamicDimsMatchVecSizes) {
485 if (llvm::all_of(llvm::zip(permutedStaticSizes, maskType.getShape()),
487 return std::get<0>(it) == ShapedType::kDynamic
489 : std::get<0>(it) == std::get<1>(it);
492 <<
"Dynamic + static dimensions match vector sizes, masking is not "
494 activeMaskCache[maskingMap] = Value();
500 SmallVector<Value> upperBounds =
502 assert(!maskShape.empty() && !upperBounds.empty() &&
503 "Masked 0-d vectors are not supported yet");
506 Value mask = vector::CreateMaskOp::create(rewriter, linalgOp.getLoc(),
507 maskType, upperBounds);
508 LDBG() <<
"Creating new mask: " << mask;
509 activeMaskCache[maskingMap] = mask;
516 std::optional<AffineMap> maybeIndexingMap) {
517 LDBG() <<
"Trying to mask: " << *opToMask;
519 std::optional<AffineMap> maybeMaskingMap = std::nullopt;
520 if (maybeIndexingMap)
521 maybeMaskingMap = getMaskingMapFromIndexingMap(*maybeIndexingMap);
525 getOrCreateMaskFor(rewriter, opToMask, linalgOp, maybeMaskingMap);
528 LDBG() <<
"No mask required";
529 if (assumeDynamicDimsMatchVecSizes) {
531 .Case<vector::TransferReadOp, vector::TransferWriteOp>(
537 LDBG() <<
"Assuming dynamic dimensions match vector sizes and "
538 "setting their in-bounds to true!";
540 ShapedType xferType = xferOp.getShapedType();
545 for (
unsigned i = 0; i < xferOp.getTransferRank(); i++) {
546 auto dimExpr = dyn_cast<AffineDimExpr>(permMap.
getResult(i));
550 unsigned pos = dimExpr.getPosition();
551 if (xferType.isDynamicDim(pos))
552 inBoundsMap[i] =
true;
555 xferOp.setInBoundsAttr(
567 assert(opToMask &&
"Expected a valid operation to mask");
568 auto maskOp = cast<vector::MaskOp>(
570 Operation *maskOpTerminator = &maskOp.getMaskRegion().front().back();
572 for (
auto [resIdx, resVal] : llvm::enumerate(opToMask->
getResults()))
576 LDBG() <<
"Masked operation: " << *maskOp;
599 "expected projected permutation");
601 assert(res.getNumDims() ==
602 (res.getNumResults() - res.getNumOfZeroResults()) &&
603 "expected reindexed map with same number of dims and results");
639std::optional<vector::CombiningKind>
641 using ::mlir::vector::CombiningKind;
646 .Case<arith::AddIOp, arith::AddFOp>(
647 [&](
auto op) {
return CombiningKind::ADD; })
648 .Case([&](arith::AndIOp op) {
return CombiningKind::AND; })
649 .Case([&](arith::MaxSIOp op) {
return CombiningKind::MAXSI; })
650 .Case([&](arith::MaxUIOp op) {
return CombiningKind::MAXUI; })
651 .Case([&](arith::MaximumFOp op) {
return CombiningKind::MAXIMUMF; })
652 .Case([&](arith::MaxNumFOp op) {
return CombiningKind::MAXNUMF; })
653 .Case([&](arith::MinSIOp op) {
return CombiningKind::MINSI; })
654 .Case([&](arith::MinUIOp op) {
return CombiningKind::MINUI; })
655 .Case([&](arith::MinimumFOp op) {
return CombiningKind::MINIMUMF; })
656 .Case([&](arith::MinNumFOp op) {
return CombiningKind::MINNUMF; })
657 .Case<arith::MulIOp, arith::MulFOp>(
658 [&](
auto op) {
return CombiningKind::MUL; })
659 .Case([&](arith::OrIOp op) {
return CombiningKind::OR; })
660 .Case([&](arith::XOrIOp op) {
return CombiningKind::XOR; })
661 .Default(std::nullopt);
672 auto linalgOp = cast<LinalgOp>(outputOperand->
getOwner());
677 if (!
matchReduction(linalgOp.getRegionOutputArgs(), outputPos, combinerOps) ||
678 combinerOps.size() != 1)
682 return combinerOps[0];
688 auto dstVecType = dyn_cast<VectorType>(dstType);
690 if (dstVecType.getRank() == 0)
695 Location loc =
b.getInsertionPoint()->getLoc();
696 return b.createOrFold<vector::BroadcastOp>(loc, dstVecType, value);
708 assert(maybeKind &&
"Failed precondition: could not get reduction kind");
709 return vector::MultiDimReductionOp::create(
710 b, reduceOp->
getLoc(), valueToReduce,
acc, dimsToMask, *maybeKind);
714 return llvm::map_to_vector(linalgOp.getIteratorTypesArray(),
721 return isa<linalg::ReduceOp>(op) ||
722 (isa<linalg::GenericOp>(op) &&
734 VectorizationState &state) {
736 auto linalgOp = cast<LinalgOp>(outputOperand->
getOwner());
737 AffineMap opOperandMap = linalgOp.getMatchingIndexingMap(outputOperand);
746 return llvm::is_contained(opOperandMap.getResults(), dimExpr);
748 auto vectorType = state.getCanonicalVecType(
755 if (vectorType.getRank() > 0) {
758 assert(value.
getType() == vectorType &&
"Incorrect type");
759 write = vector::TransferWriteOp::create(
760 rewriter, loc, value, outputOperand->
get(),
indices, writeMap);
763 if (!isa<VectorType>(value.
getType()))
764 value = vector::BroadcastOp::create(rewriter, loc, vectorType, value);
765 assert(value.
getType() == vectorType &&
"Incorrect type");
766 write = vector::TransferWriteOp::create(rewriter, loc, value,
770 write = state.maskOperation(rewriter, write, linalgOp, opOperandMap);
774 if (
auto maskOp = dyn_cast<vector::MaskingOpInterface>(write)) {
775 auto maskedWriteOp = cast<vector::TransferWriteOp>(maskOp.getMaskableOp());
780 LDBG() <<
"vectorized op: " << *write;
790 std::function<LogicalResult(
Operation *,
bool)>;
807 const IRMapping &bvm, VectorizationState &state,
809 auto yieldOp = dyn_cast<linalg::YieldOp>(op);
812 for (
const auto &output : llvm::enumerate(yieldOp.getValues())) {
818 linalgOp.getDpsInitOperand(output.index()), state);
820 newResults.push_back(newResult);
831 VectorizationState &state,
834 IndexOp indexOp = dyn_cast<linalg::IndexOp>(op);
837 auto loc = indexOp.getLoc();
840 auto dim = indexOp.getDim();
842 auto indexVectorType =
843 VectorType::get({targetShape[dim]}, rewriter.
getIndexType(),
844 state.getScalableVecDims()[dim]);
845 auto indexSteps = vector::StepOp::create(rewriter, loc, indexVectorType);
849 if (dim == targetShape.size() - 1)
855 llvm::to_vector(llvm::seq<unsigned>(0, targetShape.size()));
856 std::swap(permPattern[dim], permPattern.back());
860 auto broadCastOp = vector::BroadcastOp::create(
862 state.getCanonicalVecType(rewriter.
getIndexType(), permMap), indexSteps);
864 llvm::to_vector<16>(llvm::seq<int64_t>(0, linalgOp.getNumLoops()));
865 std::swap(transposition.back(), transposition[dim]);
867 vector::TransposeOp::create(rewriter, loc, broadCastOp, transposition);
875 tensor::ExtractOp extractOp = dyn_cast<tensor::ExtractOp>(op);
879 if (extractOp.getIndices().size() != 1 && !vectorizeNDExtract)
884 if (not extractOp.getIndices().empty()) {
885 if (!VectorType::isValidElementType(extractOp.getIndices()[0].getType()))
889 if (!llvm::all_of(extractOp->getResultTypes(),
890 VectorType::isValidElementType)) {
908 VectorizationState &state,
909 tensor::ExtractOp extractOp,
912 auto indexVecType = state.getCanonicalVecType(rewriter.
getIndexType());
913 auto loc = extractOp.getLoc();
916 rewriter, bvm.
lookup(extractOp.getIndices()[0]), indexVecType);
918 const size_t numIndices = extractOp.getIndices().size();
919 for (
size_t i = 1; i < numIndices; i++) {
924 tensor::DimOp::create(rewriter, loc, extractOp.getTensor(), dimIdx),
927 offset = arith::MulIOp::create(rewriter, loc, offset, dimSize);
930 rewriter, bvm.
lookup(extractOp.getIndices()[i]), indexVecType);
932 offset = arith::AddIOp::create(rewriter, loc, extractOpIndex, offset);
958 (linalgOp.hasDynamicShape() ||
959 llvm::count_if(loopRanges, [](
int64_t dim) { return dim != 1; }) == 1) &&
960 "For statically shaped Linalg Ops, only one "
961 "non-unit loop dim is expected");
962 assert(!loopRanges.empty() &&
"Empty loops, nothing to analyse.");
964 size_t idx = loopRanges.size() - 1;
965 for (; idx != 0; idx--)
966 if (loopRanges[idx] != 1)
974 VectorType resType) {
976 assert(((llvm::count_if(resType.getShape(),
977 [](
int64_t dimSize) { return dimSize > 1; }) == 1)) &&
978 "n-D vectors are not yet supported");
984 auto *block = linalgOp.getBlock();
985 if (isa<BlockArgument>(val))
986 return !llvm::is_contained(block->getArguments(), val);
989 assert(defOp &&
"This is neither a block argument nor an operation result");
994 if (
auto indexOp = dyn_cast<linalg::IndexOp>(defOp)) {
995 return linalgOp.getStaticLoopRanges()[indexOp.getDim()] == 1;
998 auto *ancestor = block->findAncestorOpInBlock(*defOp);
1005 if (isa<arith::ConstantOp>(ancestor))
1009 for (
auto op : ancestor->getOperands())
1033 bool &foundIndexOp, VectorType resType) {
1035 assert(((llvm::count_if(resType.getShape(),
1036 [](
int64_t dimSize) { return dimSize > 1; }) == 1)) &&
1037 "n-D vectors are not yet supported");
1043 auto *block = linalgOp.getBlock();
1044 if (isa<BlockArgument>(val))
1045 return !llvm::is_contained(block->getArguments(), val);
1048 assert(defOp &&
"This is neither a block argument nor an operation result");
1050 if (
auto indexOp = dyn_cast<linalg::IndexOp>(defOp)) {
1053 foundIndexOp = (indexOp.getDim() == loopDimThatIncrementsByOne);
1057 auto *ancestor = block->findAncestorOpInBlock(*defOp);
1064 if (!isa<arith::AddIOp, arith::ConstantOp, linalg::IndexOp>(ancestor))
1068 for (
auto op : ancestor->getOperands())
1088 LinalgOp &linalgOp, VectorType resType) {
1090 auto inputShape = cast<ShapedType>(extractOp.getTensor().getType());
1093 if (inputShape.getShape().empty())
1098 if (resType.getRank() == 0)
1103 bool isOutput1DVector =
1104 (llvm::count_if(resType.getShape(),
1105 [](
int64_t dimSize) { return dimSize > 1; }) == 1);
1107 if (!isOutput1DVector)
1110 bool leadingIdxsLoopInvariant =
true;
1116 auto indices = extractOp.getIndices();
1117 auto leadIndices =
indices.drop_back(1);
1119 for (
auto [i, indexVal] : llvm::enumerate(leadIndices)) {
1120 if (inputShape.getShape()[i] == 1)
1126 if (!leadingIdxsLoopInvariant) {
1127 LDBG() <<
"Found gather load: " << extractOp;
1135 auto extractOpTrailingIdx =
indices.back();
1139 if (leadingIdxsLoopInvariant &&
1141 LDBG() <<
"Found scalar broadcast load: " << extractOp;
1150 bool foundIndexOp =
false;
1152 foundIndexOp, resType);
1155 bool isRowVector = resType.getShape().back() != 1;
1156 isContiguousLoad &= (foundIndexOp && isRowVector);
1158 if (isContiguousLoad) {
1159 LDBG() <<
"Found contigous load: " << extractOp;
1164 LDBG() <<
"Found gather load: " << extractOp;
1172static VectorizationHookResult
1175 tensor::ExtractOp extractOp = dyn_cast<tensor::ExtractOp>(op);
1178 auto loc = extractOp.getLoc();
1181 auto resultType = state.getCanonicalVecType(extractOp.getResult().getType());
1182 auto maskConstantOp = arith::ConstantOp::create(
1186 auto passThruConstantOp = arith::ConstantOp::create(
1192 extractOp.getIndices().size(),
1203 Operation *gatherOp = vector::GatherOp::create(
1204 rewriter, loc, resultType, extractOp.getTensor(), baseIndices, offset,
1205 maskConstantOp, passThruConstantOp);
1206 gatherOp = state.maskOperation(rewriter, gatherOp, linalgOp);
1208 LDBG() <<
"Vectorised as gather load: " << extractOp;
1231 for (
size_t i = 0; i < extractOp.getIndices().size(); i++) {
1232 Value idx = bvm.
lookup(extractOp.getIndices()[i]);
1234 transferReadIdxs.push_back(idx);
1238 auto indexAs1dVector = vector::ShapeCastOp::create(
1240 VectorType::get(resultType.getShape().back(), rewriter.
getIndexType(),
1241 resultType.getScalableDims().back()),
1243 transferReadIdxs.push_back(
1244 vector::ExtractOp::create(rewriter, loc, indexAs1dVector, 0));
1248 auto dstRank = resultType.getRank();
1249 auto srcRank = extractOp.getTensor().getType().getRank();
1258 auto transferReadOp = vector::TransferReadOp::create(
1259 rewriter, loc, resultType, extractOp.getTensor(), transferReadIdxs,
1260 std::nullopt, permutationMap, inBounds);
1262 Operation *readOrMaskedReadOp = transferReadOp;
1268 auto readMaskType = VectorType::get(readMaskShape, rewriter.
getI1Type());
1269 auto allTrue = vector::ConstantMaskOp::create(
1271 readOrMaskedReadOp =
1275 LDBG() <<
"Vectorised as scalar broadcast load: " << extractOp;
1277 readOrMaskedReadOp};
1282 srcRank, std::min(dstRank, srcRank), rewriter.
getContext());
1284 int32_t rankDiff = dstRank - srcRank;
1292 while (rankDiff > 0) {
1293 permutationMap = permutationMap.insertResult(
1298 auto transferReadOp = vector::TransferReadOp::create(
1299 rewriter, loc, resultType, extractOp.getTensor(), transferReadIdxs,
1300 std::nullopt, permutationMap, inBounds);
1309 int64_t numReadDims = std::min(dstRank, srcRank);
1311 linalgOp.getNumLoops(), numReadDims, rewriter.
getContext());
1313 state.maskOperation(rewriter, transferReadOp, linalgOp, maskingMap);
1315 LDBG() <<
"Vectorised as contiguous load: " << extractOp;
1328 auto reduceType = dyn_cast<VectorType>(reduceVec.
getType());
1329 auto outputType = dyn_cast<VectorType>(outputVec.
getType());
1333 (outputType && reduceType.getShape() == outputType.getShape()))
1358static VectorizationHookResult
1362 LDBG() <<
"vectorize op " << *op;
1365 if (!customVectorizationHooks.empty()) {
1366 for (
auto &customFunc : customVectorizationHooks) {
1376 if (isa<arith::ConstantOp, func::ConstantOp>(op))
1378 rewriter.
clone(*op)};
1387 auto blockArg = dyn_cast<BlockArgument>(operand);
1388 if (!blockArg || blockArg.getOwner() != linalgOp.getBlock() ||
1389 blockArg.getArgNumber() < linalgOp.getNumDpsInputs())
1393 linalgOp.getRegionOutputArgs(),
1394 blockArg.getArgNumber() - linalgOp.getNumDpsInputs(), reductionOps);
1397 reductionOperands.push_back(std::make_pair(reduceValue, operand));
1399 if (!reductionOperands.empty()) {
1400 assert(reductionOperands.size() == 1);
1402 reduceIfNeeded(rewriter, linalgOp, op, reductionOperands[0].first,
1403 reductionOperands[0].second, bvm);
1410 VectorType firstMaxRankedType;
1412 auto vecOperand = bvm.
lookup(operand);
1413 assert(vecOperand &&
"Vector operand couldn't be found");
1415 auto vecType = dyn_cast<VectorType>(vecOperand.getType());
1416 if (vecType && (!firstMaxRankedType ||
1417 firstMaxRankedType.getRank() < vecType.getRank()))
1418 firstMaxRankedType = vecType;
1424 assert(vecOperand &&
"Vector operand couldn't be found");
1426 if (firstMaxRankedType) {
1427 auto vecType = VectorType::get(firstMaxRankedType.getShape(),
1429 firstMaxRankedType.getScalableDims());
1432 vecOperands.push_back(vecOperand);
1438 resultTypes.push_back(
1440 ? VectorType::get(firstMaxRankedType.getShape(), resultType,
1441 firstMaxRankedType.getScalableDims())
1477 LDBG() <<
"Vectorizing operation as linalg generic/n";
1478 Block *block = linalgOp.getBlock();
1485 bvm.
map(valuesSet.getArrayRef(), valuesSet.getArrayRef());
1487 if (linalgOp.getNumDpsInits() == 0)
1493 for (
OpOperand *opOperand : linalgOp.getOpOperandsMatchingBBargs()) {
1494 BlockArgument bbarg = linalgOp.getMatchingBlockArgument(opOperand);
1495 if (linalgOp.isScalar(opOperand)) {
1496 bvm.
map(bbarg, opOperand->get());
1502 AffineMap indexingMap = linalgOp.getMatchingIndexingMap(opOperand);
1505 VectorType readType;
1507 if (linalgOp.isDpsInput(opOperand)) {
1510 readType = state.getCanonicalVecType(elemType);
1517 state.getCanonicalVecType(elemType, readMap.
compose(indexingMap));
1522 Operation *read = vector::TransferReadOp::create(
1523 rewriter, loc, readType, opOperand->get(),
indices,
1524 std::nullopt, readMap);
1525 read = state.maskOperation(rewriter, read, linalgOp, indexingMap);
1530 if (
auto maskOp = dyn_cast<vector::MaskingOpInterface>(read)) {
1532 cast<vector::TransferReadOp>(maskOp.getMaskableOp())
1538 if (readType.getRank() == 0)
1539 readValue = vector::ExtractOp::create(rewriter, loc, readValue,
1542 LDBG() <<
"New vectorized bbarg(" << bbarg.
getArgNumber()
1543 <<
"): " << readValue;
1544 bvm.
map(bbarg, readValue);
1545 bvm.
map(opOperand->get(), readValue);
1554 hooks.push_back(vectorizeYield);
1561 hooks.push_back(vectorizeIndex);
1568 hooks.push_back(vectorizeExtract);
1575 LDBG() <<
"failed to vectorize: " << op;
1580 state.maskOperation(rewriter,
result.newOp, linalgOp);
1581 LDBG() <<
"New vector op: " << *maybeMaskedOp;
1607 assert(type.getNumScalableDims() < 2 &&
1608 "Collapsing more than 1 scalable dim is not supported ATM");
1614 auto shape = type.getShape();
1615 auto scalableFlags = type.getScalableDims();
1619 unsigned currentDim = 0;
1621 unsigned dim = m.getNumResults();
1624 for (
unsigned d = 0; d < dim; ++d) {
1625 size *=
shape[currentDim + d];
1626 flag |= scalableFlags[currentDim + d];
1628 newShape.push_back(size);
1629 newScalableFlags.push_back(flag);
1633 return VectorType::get(newShape, type.getElementType(), newScalableFlags);
1666vectorizeAsTensorPackOp(RewriterBase &rewriter, linalg::PackOp packOp,
1667 ArrayRef<int64_t> inputVectorSizes,
1668 SmallVectorImpl<Value> &newResults) {
1669 if (!inputVectorSizes.empty()) {
1670 assert(inputVectorSizes.size() == packOp.getDestRank() &&
1671 "Invalid number of input vector sizes!");
1675 OpBuilder::InsertionGuard g(rewriter);
1678 Location loc = packOp.getLoc();
1679 std::optional<Value> padValue = packOp.getPaddingValue()
1680 ? std::optional(packOp.getPaddingValue())
1683 SmallVector<int64_t> destShape =
1684 SmallVector<int64_t>(packOp.getDestType().getShape());
1688 ArrayRef<int64_t> &writeVectorSizes = inputVectorSizes;
1692 bool useInBoundsInsteadOfMasking =
false;
1693 if (writeVectorSizes.empty()) {
1694 if (ShapedType::isDynamicShape(destShape))
1696 "unable to infer vector sizes");
1698 writeVectorSizes = destShape;
1699 useInBoundsInsteadOfMasking =
true;
1708 PackingMetadata packMetadata;
1709 SmallVector<int64_t> preTransposeWriteVecSizses(writeVectorSizes);
1712 auto preTransposeWriteVecType =
1713 VectorType::get(preTransposeWriteVecSizses,
1714 packOp.getResult().getType().getElementType());
1720 preTransposeWriteVecType,
1722 rewriter.
getContext(), packMetadata.reassociations)));
1726 rewriter, loc, packOp.getSource(), readVecType, padValue,
1727 useInBoundsInsteadOfMasking);
1730 auto shapeCastOp = vector::ShapeCastOp::create(
1731 rewriter, loc, preTransposeWriteVecType, maskedRead);
1735 auto transposeOp = vector::TransposeOp::create(
1736 rewriter, loc, shapeCastOp.getResult(), destPermutation);
1740 rewriter, loc, transposeOp.getResult(), packOp.getDest());
1741 newResults.push_back(write->
getResult(0));
1775vectorizeAsTensorUnpackOp(RewriterBase &rewriter, linalg::UnPackOp unpackOp,
1776 ArrayRef<int64_t> inputVectorSizes,
1777 ArrayRef<bool> inputScalableVecDims,
1778 SmallVectorImpl<Value> &newResults) {
1779 if (!inputVectorSizes.empty()) {
1780 assert(inputVectorSizes.size() == unpackOp.getSourceRank() &&
1781 "Invalid number of input vector sizes!");
1782 assert(inputVectorSizes.size() == inputScalableVecDims.size() &&
1783 "Incompatible number of vector sizes and vector scalable flags!");
1787 OpBuilder::InsertionGuard g(rewriter);
1790 ShapedType unpackTensorType = unpackOp.getSourceType();
1792 ArrayRef<int64_t> sourceShape = unpackTensorType.getShape();
1793 bool useInBoundsInsteadOfMasking =
false;
1795 Location loc = unpackOp->getLoc();
1798 SmallVector<int64_t> readVectorSizes(inputVectorSizes);
1799 SmallVector<bool> readScalableVectorFlags(inputScalableVecDims);
1802 if (inputVectorSizes.empty()) {
1803 if (ShapedType::isDynamicShape(sourceShape))
1805 "Unable to infer vector sizes!");
1807 readVectorSizes.assign(sourceShape.begin(), sourceShape.end());
1808 useInBoundsInsteadOfMasking =
true;
1812 VectorType readVecType =
1813 VectorType::get(readVectorSizes, unpackTensorType.getElementType(),
1814 readScalableVectorFlags);
1816 rewriter, loc, unpackOp.getSource(), readVecType, std::nullopt,
1817 useInBoundsInsteadOfMasking);
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(),
1837 {}, useInBoundsInsteadOfMasking);
1839 newResults.push_back(write->
getResult(0));
1847vectorizeAsTensorPadOp(RewriterBase &rewriter, tensor::PadOp padOp,
1848 ArrayRef<int64_t> inputVectorSizes,
1849 SmallVectorImpl<Value> &newResults) {
1850 auto padValue = padOp.getConstantPaddingValue();
1851 Location loc = padOp.getLoc();
1854 OpBuilder::InsertionGuard g(rewriter);
1858 LogicalResult status =
1859 cast<ReifyRankedShapedTypeOpInterface>(padOp.getOperation())
1860 .reifyResultShapes(rewriter, reifiedReturnShapes);
1862 assert(succeeded(status) &&
"failed to reify result shapes");
1863 auto readType = VectorType::get(inputVectorSizes, padValue.getType());
1865 rewriter, loc, padOp.getSource(), readType, padValue,
1869 Value dest = tensor::EmptyOp::create(rewriter, loc, reifiedReturnShapes[0],
1870 padOp.getResultType().getElementType());
1873 newResults.push_back(write->
getResult(0));
1879static LogicalResult reductionPreconditions(LinalgOp op) {
1881 LDBG() <<
"reduction precondition failed: no reduction iterator";
1884 for (OpOperand &opOperand : op.getDpsInitsMutable()) {
1885 AffineMap indexingMap = op.getMatchingIndexingMap(&opOperand);
1891 LDBG() <<
"reduction precondition failed: reduction detection failed";
1899vectorizeDynamicConvOpPrecondition(linalg::LinalgOp conv,
1900 bool flatten1DDepthwiseConv) {
1901 if (flatten1DDepthwiseConv) {
1902 LDBG() <<
"Vectorization of flattened convs with dynamic shapes is not "
1908 LDBG() <<
"Not a 1D depth-wise WC conv, dynamic shapes are not supported";
1914 Value
lhs = conv.getDpsInputOperand(0)->get();
1915 ArrayRef<int64_t> lhsShape = cast<ShapedType>(
lhs.getType()).getShape();
1916 auto shapeWithoutCh = lhsShape.drop_back(1);
1917 if (ShapedType::isDynamicShape(shapeWithoutCh)) {
1918 LDBG() <<
"Dynamically-shaped op vectorization precondition failed: only "
1919 "channel dim can be dynamic";
1927vectorizeDynamicLinalgOpPrecondition(linalg::LinalgOp op,
1928 bool flatten1DDepthwiseConv) {
1930 return vectorizeDynamicConvOpPrecondition(op, flatten1DDepthwiseConv);
1933 return reductionPreconditions(op);
1938 !isa<linalg::GenericOp, linalg::CopyOp, linalg::ContractionOpInterface>(
1942 LDBG() <<
"Dynamically-shaped op meets vectorization pre-conditions";
1952vectorizeUnPackOpPrecondition(linalg::UnPackOp unpackOp,
1953 ArrayRef<int64_t> inputVectorSizes) {
1955 if (!unpackOp.hasPureTensorSemantics())
1960 if (inputVectorSizes.empty() && unpackOp.getDestType().hasStaticShape() &&
1961 unpackOp.getSourceType().hasStaticShape())
1966 if (!inputVectorSizes.empty() &&
1967 (inputVectorSizes.size() != unpackOp.getSourceRank())) {
1968 LDBG() <<
"Incorrect number of input vector sizes";
1974 unpackOp.getSourceType().getShape(), inputVectorSizes))) {
1975 LDBG() <<
"Invalid vector sizes for the read operation";
1983vectorizeInsertSliceOpPrecondition(tensor::InsertSliceOp sliceOp,
1984 ArrayRef<int64_t> inputVectorSizes) {
1987 auto sourceType = source.getType();
1988 if (!VectorType::isValidElementType(sourceType.getElementType()))
2004 bool isOutOfBoundsRead =
2005 !sourceType.hasStaticShape() && inputVectorSizes.empty();
2007 if (!padValue && isOutOfBoundsRead) {
2008 LDBG() <<
"Failed to get a pad value for out-of-bounds read access";
2022vectorizeAsLinalgContraction(RewriterBase &rewriter, VectorizationState &state,
2024 SmallVectorImpl<Value> &newResults) {
2025 Location loc = linalgOp.getLoc();
2026 MLIRContext *ctx = linalgOp.getContext();
2031 if (!isa<ContractionOpInterface>(linalgOp.getOperation()))
2034 OpOperand *outOperand = linalgOp.getDpsInitOperand(0);
2038 LDBG() <<
"Failed to determine contraction combining kind.";
2045 AffineMap lhsMap = linalgOp.getIndexingMapsArray()[0];
2046 AffineMap rhsMap = linalgOp.getIndexingMapsArray()[1];
2048 LDBG() <<
"Contractions with broadcasts are not supported.";
2053 SmallVector<Value> vecOperands;
2054 for (OpOperand &opOperand : linalgOp->getOpOperands()) {
2058 AffineMap indexingMap = linalgOp.getMatchingIndexingMap(&opOperand);
2062 VectorType readType =
2063 state.getCanonicalVecType(elemType, readMap.
compose(indexingMap));
2066 rewriter, loc, opOperand.get(), readType,
2067 arith::getZeroConstant(rewriter, loc, elemType),
2069 vecOperands.push_back(read);
2073 SmallVector<Attribute> iterAttrs;
2074 auto iterators = linalgOp.getIteratorTypesArray();
2075 for (utils::IteratorType iter : iterators) {
2076 auto vecIter = iter == utils::IteratorType::parallel
2077 ? vector::IteratorType::parallel
2078 : vector::IteratorType::reduction;
2079 iterAttrs.push_back(vector::IteratorTypeAttr::get(ctx, vecIter));
2083 Operation *contractOp = vector::ContractionOp::create(
2084 rewriter, loc, vecOperands[0],
2085 vecOperands[1], vecOperands[2],
2086 linalgOp.getIndexingMaps(), rewriter.
getArrayAttr(iterAttrs), *maybeKind);
2087 contractOp = state.maskOperation(rewriter, contractOp, linalgOp);
2091 rewriter, loc, contractOp->
getResult(0), outOperand->
get());
2095 newResults.push_back(write->
getResult(0));
2101enum class ConvOperationKind { Conv, Pool };
2104static bool isCastOfBlockArgument(Operation *op) {
2119static std::optional<ConvOperationKind>
2120getConvOperationKind(Operation *reduceOp) {
2121 int numBlockArguments =
2122 llvm::count_if(reduceOp->
getOperands(), llvm::IsaPred<BlockArgument>);
2124 switch (numBlockArguments) {
2130 auto feedValIt = llvm::find_if_not(reduceOp->
getOperands(),
2131 llvm::IsaPred<BlockArgument>);
2133 "Expected a non-block argument operand");
2134 Operation *feedOp = (*feedValIt).getDefiningOp();
2135 if (isCastOfBlockArgument(feedOp)) {
2136 return ConvOperationKind::Pool;
2139 if (!((isa<arith::MulIOp, arith::MulFOp>(feedOp) ||
2140 (isa<arith::AndIOp>(feedOp) &&
2143 if (isa<BlockArgument>(v))
2145 if (Operation *op = v.getDefiningOp())
2146 return isCastOfBlockArgument(op);
2149 return std::nullopt;
2152 return ConvOperationKind::Conv;
2156 return ConvOperationKind::Pool;
2158 return std::nullopt;
2162static bool isSupportedPoolKind(vector::CombiningKind kind) {
2164 case vector::CombiningKind::ADD:
2165 case vector::CombiningKind::MAXNUMF:
2166 case vector::CombiningKind::MAXIMUMF:
2167 case vector::CombiningKind::MAXSI:
2168 case vector::CombiningKind::MAXUI:
2169 case vector::CombiningKind::MINNUMF:
2170 case vector::CombiningKind::MINIMUMF:
2171 case vector::CombiningKind::MINSI:
2172 case vector::CombiningKind::MINUI:
2179static LogicalResult vectorizeConvOpPrecondition(linalg::LinalgOp convOp) {
2180 auto getOperandType = [&](
auto operand) {
2181 return dyn_cast<ShapedType>((operand->get()).getType());
2183 ShapedType lhsShapedType = getOperandType(convOp.getDpsInputOperand(0));
2184 ShapedType rhsShapedType = getOperandType(convOp.getDpsInputOperand(1));
2185 ShapedType resShapedType = getOperandType(convOp.getDpsInitOperand(0));
2189 if ((lhsShapedType.getRank() != 3 || resShapedType.getRank() != 3) &&
2190 (lhsShapedType.getRank() != 1 || resShapedType.getRank() != 1))
2197 auto maybeOper = getConvOperationKind(reduceOp);
2198 if (!maybeOper.has_value())
2205 if (!maybeKind || ((*maybeKind != vector::CombiningKind::ADD &&
2206 *maybeKind != vector::CombiningKind::OR) &&
2207 (*maybeOper != ConvOperationKind::Pool ||
2208 !isSupportedPoolKind(*maybeKind)))) {
2212 auto rhsRank = rhsShapedType.getRank();
2213 if (*maybeOper == ConvOperationKind::Pool) {
2217 if (rhsRank != 1 && rhsRank != 2 && rhsRank != 3)
2224static LogicalResult vectorizeLinalgOpPrecondition(
2225 LinalgOp linalgOp, ArrayRef<int64_t> inputVectorSizes,
2226 bool vectorizeNDExtract,
bool flatten1DDepthwiseConv) {
2228 if (llvm::any_of(linalgOp->getOpOperands(), [&](OpOperand &operand) {
2229 return llvm::is_contained(linalgOp.getShape(&operand), 0);
2233 if (!inputVectorSizes.empty() &&
2238 if (linalgOp.hasDynamicShape() &&
failed(vectorizeDynamicLinalgOpPrecondition(
2239 linalgOp, flatten1DDepthwiseConv))) {
2240 LDBG() <<
"Dynamically-shaped op failed vectorization pre-conditions";
2244 SmallVector<CustomVectorizationPrecondition> customPreconditions;
2250 for (Operation &innerOp : linalgOp->getRegion(0).front()) {
2253 customPreconditions,
2256 customPrecondition(&innerOp, vectorizeNDExtract));
2260 if (!llvm::all_of(innerOp.getOperandTypes(),
2261 VectorType::isValidElementType)) {
2264 if (!llvm::all_of(innerOp.getResultTypes(),
2265 VectorType::isValidElementType)) {
2274 return vectorizeConvOpPrecondition(linalgOp);
2280 LDBG() <<
"precondition failed: not projected permutations";
2283 if (
failed(reductionPreconditions(linalgOp))) {
2284 LDBG() <<
"precondition failed: reduction preconditions";
2291vectorizePackOpPrecondition(linalg::PackOp packOp,
2292 ArrayRef<int64_t> inputVectorSizes) {
2294 if (!packOp.hasPureTensorSemantics())
2297 auto padValue = packOp.getPaddingValue();
2301 LDBG() <<
"pad value is not constant: " << packOp;
2305 ArrayRef<int64_t> resultTensorShape = packOp.getDestType().getShape();
2306 bool satisfyEmptyCond =
true;
2307 if (inputVectorSizes.empty()) {
2308 if (!packOp.getDestType().hasStaticShape() ||
2309 !packOp.getSourceType().hasStaticShape())
2310 satisfyEmptyCond =
false;
2313 if (!satisfyEmptyCond &&
2315 resultTensorShape.take_front(packOp.getSourceRank()),
2319 if (llvm::any_of(packOp.getInnerTiles(), [](OpFoldResult v) {
2320 return !getConstantIntValue(v).has_value();
2322 LDBG() <<
"inner_tiles must be constant: " << packOp;
2330vectorizePadOpPrecondition(tensor::PadOp padOp,
2331 ArrayRef<int64_t> inputVectorSizes) {
2332 auto padValue = padOp.getConstantPaddingValue();
2334 LDBG() <<
"pad value is not constant: " << padOp;
2338 ArrayRef<int64_t> resultTensorShape = padOp.getResultType().getShape();
2354 if (llvm::any_of(llvm::enumerate(padOp.getMixedLowPad()),
2355 [&](
const auto &en) {
2356 OpFoldResult padValue = en.value();
2357 unsigned pos = en.index();
2358 std::optional<int64_t> pad = getConstantIntValue(padValue);
2359 return (!pad.has_value() || pad.value() != 0) &&
2360 resultTensorShape[pos] != 1;
2362 LDBG() <<
"low pad must all be zero for all non unit dims: " << padOp;
2376vectorizeScalableVectorPrecondition(Operation *op,
2377 ArrayRef<int64_t> inputVectorSizes,
2378 ArrayRef<bool> inputScalableVecDims) {
2379 assert(inputVectorSizes.size() == inputScalableVecDims.size() &&
2380 "Number of input vector sizes and scalable dims doesn't match");
2382 size_t numOfScalableDims =
2383 llvm::count_if(inputScalableVecDims, [](
bool flag) {
return flag; });
2385 if (numOfScalableDims == 0)
2388 auto linalgOp = dyn_cast<LinalgOp>(op);
2393 return success(isa<linalg::UnPackOp>(op));
2397 if (numOfScalableDims > 2)
2417 bool seenNonUnitParallel =
false;
2418 auto iterators = linalgOp.getIteratorTypesArray();
2419 SmallVector<bool> scalableFlags(inputScalableVecDims);
2420 int64_t idx = scalableFlags.size() - 1;
2421 while (!scalableFlags[idx]) {
2422 bool isNonUnitDim = (inputVectorSizes[idx] != 1);
2423 seenNonUnitParallel |=
2424 (iterators[idx] == utils::IteratorType::parallel && isNonUnitDim);
2426 iterators.pop_back();
2427 scalableFlags.pop_back();
2432 switch (iterators.back()) {
2433 case utils::IteratorType::reduction: {
2435 if (iterators.size() != inputVectorSizes.size()) {
2436 LDBG() <<
"Non-trailing reduction dim requested for scalable "
2440 if (isa<linalg::MatmulOp>(op)) {
2442 <<
"Scalable vectorization of the reduction dim in Matmul-like ops "
2448 case utils::IteratorType::parallel: {
2450 if (seenNonUnitParallel) {
2451 LDBG() <<
"Inner parallel dim not requested for scalable "
2463 if (numOfScalableDims == 2) {
2467 if (iterators.back() == utils::IteratorType::reduction) {
2468 LDBG() <<
"Higher dim than the trailing reduction dim requested for "
2473 scalableFlags.pop_back();
2474 iterators.pop_back();
2476 if (!scalableFlags.back() ||
2477 (iterators.back() != utils::IteratorType::parallel))
2485 isa<linalg::BatchMatmulOp>(op) ||
2487 isa<linalg::MatvecOp>(op) || isa<linalg::Mmt4DOp>(op) ||
2492 Operation *op, ArrayRef<int64_t> inputVectorSizes,
2493 ArrayRef<bool> inputScalableVecDims,
bool vectorizeNDExtract,
2494 bool flatten1DDepthwiseConv) {
2499 if (
failed(vectorizeScalableVectorPrecondition(op, inputVectorSizes,
2500 inputScalableVecDims)))
2504 .Case([&](linalg::LinalgOp linalgOp) {
2505 return vectorizeLinalgOpPrecondition(linalgOp, inputVectorSizes,
2507 flatten1DDepthwiseConv);
2509 .Case([&](tensor::PadOp padOp) {
2510 return vectorizePadOpPrecondition(padOp, inputVectorSizes);
2512 .Case([&](linalg::PackOp packOp) {
2513 return vectorizePackOpPrecondition(packOp, inputVectorSizes);
2515 .Case([&](linalg::UnPackOp unpackOp) {
2516 return vectorizeUnPackOpPrecondition(unpackOp, inputVectorSizes);
2518 .Case([&](tensor::InsertSliceOp sliceOp) {
2519 return vectorizeInsertSliceOpPrecondition(sliceOp, inputVectorSizes);
2521 .Default(failure());
2525static void convertAffineApply(RewriterBase &rewriter, LinalgOp linalgOp) {
2526 OpBuilder::InsertionGuard g(rewriter);
2527 auto toReplace = linalgOp.getBlock()->getOps<affine::AffineApplyOp>();
2529 for (
auto op : make_early_inc_range(toReplace)) {
2531 auto expanded = affine::expandAffineExpr(
2533 op.
getOperands().take_front(op.getAffineMap().getNumDims()),
2534 op.
getOperands().take_back(op.getAffineMap().getNumSymbols()));
2540 return isa<linalg::LinalgOp, tensor::PadOp, linalg::PackOp, linalg::UnPackOp,
2541 tensor::InsertSliceOp>(op);
2545 RewriterBase &rewriter, Operation *op, ArrayRef<int64_t> inputVectorSizes,
2546 ArrayRef<bool> inputScalableVecDims,
bool vectorizeNDExtract,
2547 bool flatten1DDepthwiseConv,
bool assumeDynamicDimsMatchVecSizes,
2548 bool createNamedContraction) {
2549 LDBG() <<
"Attempting to vectorize: " << *op;
2550 LDBG() <<
"Input vector sizes: " << llvm::interleaved(inputVectorSizes);
2551 LDBG() <<
"Input scalable vector dims: "
2552 << llvm::interleaved(inputScalableVecDims);
2556 flatten1DDepthwiseConv))) {
2557 LDBG() <<
"Vectorization pre-conditions failed";
2562 VectorizationState state(rewriter);
2563 if (
auto linalgOp = dyn_cast<linalg::LinalgOp>(op)) {
2564 if (
failed(state.initState(rewriter, linalgOp, inputVectorSizes,
2565 inputScalableVecDims,
2566 assumeDynamicDimsMatchVecSizes))) {
2567 LDBG() <<
"Vectorization state couldn't be initialized";
2572 SmallVector<Value> results;
2573 auto vectorizeResult =
2575 .Case([&](linalg::LinalgOp linalgOp) {
2579 rewriter, linalgOp, inputVectorSizes, inputScalableVecDims,
2580 flatten1DDepthwiseConv);
2581 if (succeeded(convOr)) {
2582 llvm::append_range(results, (*convOr)->getResults());
2586 LDBG() <<
"Unsupported convolution can't be vectorized.";
2590 if (createNamedContraction &&
2591 isa<ContractionOpInterface>(linalgOp.getOperation()))
2592 return vectorizeAsLinalgContraction(rewriter, state, linalgOp,
2596 <<
"Vectorize generic by broadcasting to the canonical vector "
2600 convertAffineApply(rewriter, linalgOp);
2609 .Case([&](tensor::PadOp padOp) {
2610 return vectorizeAsTensorPadOp(rewriter, padOp, inputVectorSizes,
2613 .Case([&](linalg::PackOp packOp) {
2614 return vectorizeAsTensorPackOp(rewriter, packOp, inputVectorSizes,
2617 .Case([&](linalg::UnPackOp unpackOp) {
2618 return vectorizeAsTensorUnpackOp(rewriter, unpackOp,
2620 inputScalableVecDims, results);
2622 .Case([&](tensor::InsertSliceOp sliceOp) {
2626 .Default(failure());
2628 if (
failed(vectorizeResult)) {
2629 LDBG() <<
"Vectorization failed";
2633 return VectorizationResult{results};
2637 memref::CopyOp copyOp) {
2638 auto srcType = cast<MemRefType>(copyOp.getSource().getType());
2639 auto dstType = cast<MemRefType>(copyOp.getTarget().getType());
2640 if (!srcType.hasStaticShape() || !dstType.hasStaticShape())
2645 if (!VectorType::isValidElementType(srcElementType) ||
2646 !VectorType::isValidElementType(dstElementType))
2649 auto readType = VectorType::get(srcType.getShape(), srcElementType);
2650 auto writeType = VectorType::get(dstType.getShape(), dstElementType);
2652 Location loc = copyOp->getLoc();
2654 SmallVector<Value>
indices(srcType.getRank(), zero);
2656 Value
readValue = vector::TransferReadOp::create(
2657 rewriter, loc, readType, copyOp.getSource(),
indices,
2660 if (cast<VectorType>(
readValue.getType()).getRank() == 0) {
2661 readValue = vector::ExtractOp::create(rewriter, loc, readValue,
2662 ArrayRef<int64_t>());
2664 vector::BroadcastOp::create(rewriter, loc, writeType, readValue);
2666 Operation *writeValue = vector::TransferWriteOp::create(
2667 rewriter, loc, readValue, copyOp.getTarget(),
indices,
2678template <
typename OpTy>
2679struct VectorizePadOpUserPattern :
public OpRewritePattern<tensor::PadOp> {
2680 using OpRewritePattern<tensor::PadOp>::OpRewritePattern;
2682 LogicalResult matchAndRewrite(tensor::PadOp padOp,
2683 PatternRewriter &rewriter)
const final {
2684 bool changed =
false;
2686 for (
auto *user : llvm::to_vector<4>(padOp->getUsers()))
2687 if (
auto op = dyn_cast<OpTy>(user))
2688 changed |= rewriteUser(rewriter, padOp, op).succeeded();
2693 virtual LogicalResult rewriteUser(PatternRewriter &rewriter,
2694 tensor::PadOp padOp, OpTy op)
const = 0;
2716struct PadOpVectorizationWithTransferReadPattern
2717 :
public VectorizePadOpUserPattern<vector::TransferReadOp> {
2718 using VectorizePadOpUserPattern<
2719 vector::TransferReadOp>::VectorizePadOpUserPattern;
2721 LogicalResult rewriteUser(PatternRewriter &rewriter, tensor::PadOp padOp,
2722 vector::TransferReadOp xferOp)
const override {
2724 if (!padOp.hasZeroLowPad())
2727 auto padValue = padOp.getConstantPaddingValue();
2731 if (xferOp.hasOutOfBoundsDim() || xferOp.getMask())
2735 SmallVector<bool> inBounds(xferOp.getVectorType().getRank(),
false);
2736 xferOp->setAttr(xferOp.getInBoundsAttrName(),
2738 xferOp.getBaseMutable().assign(padOp.getSource());
2739 xferOp.getPaddingMutable().assign(padValue);
2778struct PadOpVectorizationWithTransferWritePattern
2779 :
public VectorizePadOpUserPattern<vector::TransferWriteOp> {
2780 using VectorizePadOpUserPattern<
2781 vector::TransferWriteOp>::VectorizePadOpUserPattern;
2783 LogicalResult rewriteUser(PatternRewriter &rewriter, tensor::PadOp padOp,
2784 vector::TransferWriteOp xferOp)
const override {
2786 if (xferOp.getTransferRank() == 0)
2790 if (!padOp.hasZeroLowPad())
2793 auto padValue = padOp.getConstantPaddingValue();
2797 if (!xferOp->hasOneUse())
2799 auto trimPadding = dyn_cast<tensor::ExtractSliceOp>(*xferOp->user_begin());
2803 if (!trimPadding.hasZeroOffset())
2806 if (!hasSameTensorSize(padOp.getSource(), trimPadding))
2812 SmallVector<bool> inBounds(xferOp.getVectorType().getRank(),
false);
2814 xferOp, padOp.getSource().
getType(), xferOp.getVector(),
2815 padOp.getSource(), xferOp.getIndices(), xferOp.getPermutationMapAttr(),
2817 rewriter.
replaceOp(trimPadding, newXferOp->getResult(0));
2832 bool hasSameTensorSize(Value beforePadding,
2833 tensor::ExtractSliceOp afterTrimming)
const {
2836 if (
auto castOp = beforePadding.
getDefiningOp<tensor::CastOp>())
2837 if (hasSameTensorSize(castOp.getSource(), afterTrimming))
2840 auto t1 = dyn_cast<RankedTensorType>(beforePadding.
getType());
2841 auto t2 = dyn_cast<RankedTensorType>(afterTrimming.getType());
2846 if (t1.getRank() != t2.getRank())
2851 for (
unsigned i = 0; i < t1.getRank(); ++i) {
2852 if (t1.isDynamicDim(i) != t2.isDynamicDim(i))
2854 if (!t1.isDynamicDim(i) && t1.getDimSize(i) != t2.getDimSize(i))
2859 if (t1.getNumDynamicDims() == 0)
2867 auto beforeSlice = beforePadding.
getDefiningOp<tensor::ExtractSliceOp>();
2871 assert(
static_cast<size_t>(t1.getRank()) ==
2872 beforeSlice.getMixedSizes().size());
2873 assert(
static_cast<size_t>(t2.getRank()) ==
2874 afterTrimming.getMixedSizes().size());
2876 for (
unsigned i = 0; i < t1.getRank(); ++i) {
2878 if (!t1.isDynamicDim(i))
2880 auto size1 = beforeSlice.getMixedSizes()[i];
2881 auto size2 = afterTrimming.getMixedSizes()[i];
2888 auto v1 = llvm::dyn_cast_if_present<Value>(size1);
2889 auto v2 = llvm::dyn_cast_if_present<Value>(size2);
2895 auto minOp1 = v1.getDefiningOp<affine::AffineMinOp>();
2896 auto minOp2 = v2.getDefiningOp<affine::AffineMinOp>();
2897 if (minOp1 && minOp2 && minOp1.getAffineMap() == minOp2.getAffineMap() &&
2898 minOp1.getOperands() == minOp2.getOperands())
2924 if (
auto bcast = llvm::dyn_cast<vector::BroadcastOp>(op)) {
2925 auto source = bcast.getSource();
2926 if (llvm::dyn_cast<VectorType>(source.getType()))
2934 if (
auto fill = llvm::dyn_cast<linalg::FillOp>(op)) {
2935 return fill.getInputs()[0];
2940 if (
auto generate = llvm::dyn_cast<tensor::GenerateOp>(op)) {
2947 if (
auto xferWrite = llvm::dyn_cast<vector::TransferWriteOp>(op))
2955 if (
auto slice = llvm::dyn_cast<tensor::InsertSliceOp>(op))
2963 ArrayRef<int64_t> inputVectorSizes,
2964 SmallVectorImpl<Value> &newResults) {
2966 OpBuilder::InsertionGuard g(rewriter);
2970 auto sourceType = source.getType();
2971 auto resultType = sliceOp.getResultType();
2976 auto elemType = sourceType.getElementType();
2977 padValue = arith::ConstantOp::create(rewriter, sliceOp.getLoc(), elemType,
2982 SmallVector<int64_t> vecShape;
2983 size_t rankDiff = resultType.getRank() - sourceType.getRank();
2984 for (int64_t i = 0, end = sourceType.getRank(); i < end; ++i) {
2985 if (!inputVectorSizes.empty()) {
2986 vecShape.push_back(inputVectorSizes[i]);
2987 }
else if (!sourceType.isDynamicDim(i)) {
2988 vecShape.push_back(sourceType.getDimSize(i));
2989 }
else if (!resultType.isDynamicDim(i)) {
2995 vecShape.push_back(resultType.getDimSize(rankDiff + i));
3002 auto vecType = VectorType::get(vecShape, sourceType.getElementType());
3005 auto loc = sliceOp.getLoc();
3008 SmallVector<Value> readIndices(
3011 rewriter, loc, source, vecType, padValue,
3012 inputVectorSizes.empty());
3019 writeIndices, inputVectorSizes.empty());
3022 newResults.push_back(write->
getResult(0));
3050struct PadOpVectorizationWithInsertSlicePattern
3051 :
public VectorizePadOpUserPattern<tensor::InsertSliceOp> {
3052 using VectorizePadOpUserPattern<
3053 tensor::InsertSliceOp>::VectorizePadOpUserPattern;
3055 LogicalResult rewriteUser(PatternRewriter &rewriter, tensor::PadOp padOp,
3056 tensor::InsertSliceOp insertOp)
const override {
3058 if (!padOp.hasZeroLowPad())
3061 if (!insertOp.hasUnitStride())
3064 auto padValue = padOp.getConstantPaddingValue();
3068 if (!cast<ShapedType>(padOp.getResult().getType()).hasStaticShape())
3071 if (insertOp.getDest() == padOp.getResult())
3074 auto vecType = VectorType::get(padOp.getType().getShape(),
3075 padOp.getType().getElementType());
3076 unsigned vecRank = vecType.getRank();
3077 unsigned tensorRank = insertOp.getType().getRank();
3081 SmallVector<int64_t> expectedSizes(tensorRank - vecRank, 1);
3082 expectedSizes.append(vecType.getShape().begin(), vecType.getShape().end());
3084 llvm::zip(insertOp.getMixedSizes(), expectedSizes), [](
auto it) {
3085 return getConstantIntValue(std::get<0>(it)) == std::get<1>(it);
3095 SmallVector<Value> readIndices(
3097 auto read = vector::TransferReadOp::create(rewriter, padOp.getLoc(),
3098 vecType, padOp.getSource(),
3099 readIndices, padValue);
3105 rewriter, padOp.getLoc(), insertOp.getMixedOffsets());
3106 SmallVector<bool> inBounds(vecRank,
true);
3108 insertOp, read, insertOp.getDest(), writeIndices,
3109 ArrayRef<bool>{inBounds});
3116 RewritePatternSet &patterns, PatternBenefit baseBenefit) {
3117 patterns.
add<PadOpVectorizationWithTransferReadPattern,
3118 PadOpVectorizationWithTransferWritePattern,
3119 PadOpVectorizationWithInsertSlicePattern>(
3130static bool mayExistInterleavedUses(Operation *firstOp, Operation *secondOp,
3134 LDBG() <<
"interleavedUses precondition failed, firstOp: " << *firstOp
3135 <<
", second op: " << *secondOp;
3138 for (
auto v : values) {
3139 for (
auto &u : v.getUses()) {
3140 Operation *owner = u.getOwner();
3141 if (owner == firstOp || owner == secondOp)
3147 LDBG() <<
" found interleaved op " << *owner <<
", firstOp: " << *firstOp
3148 <<
", second op: " << *secondOp;
3157static memref::SubViewOp getSubViewUseIfUnique(Value v) {
3158 memref::SubViewOp subViewOp;
3160 if (
auto newSubViewOp = dyn_cast<memref::SubViewOp>(u.getOwner())) {
3162 return memref::SubViewOp();
3163 subViewOp = newSubViewOp;
3172 vector::TransferReadOp xferOp, PatternRewriter &rewriter)
const {
3175 if (xferOp.getMask())
3179 Value viewOrAlloc = xferOp.getBase();
3185 memref::SubViewOp subViewOp = getSubViewUseIfUnique(viewOrAlloc);
3188 Value subView = subViewOp.getResult();
3191 memref::CopyOp copyOp;
3192 for (
auto &u : subView.
getUses()) {
3193 if (
auto newCopyOp = dyn_cast<memref::CopyOp>(u.getOwner())) {
3194 assert(isa<MemRefType>(newCopyOp.getTarget().getType()));
3195 if (newCopyOp.getTarget() != subView)
3197 if (mayExistInterleavedUses(newCopyOp, xferOp, {viewOrAlloc, subView}))
3209 for (
auto &u : viewOrAlloc.
getUses()) {
3210 if (
auto newFillOp = dyn_cast<FillOp>(u.getOwner())) {
3211 assert(isa<MemRefType>(newFillOp.output().getType()));
3212 if (newFillOp.output() != viewOrAlloc)
3214 if (mayExistInterleavedUses(newFillOp, copyOp, {viewOrAlloc, subView}))
3216 maybeFillOp = newFillOp;
3221 if (maybeFillOp && xferOp.getPadding() != maybeFillOp.value())
3223 "padding value does not match fill");
3226 Value in = copyOp.getSource();
3232 auto vectorType = xferOp.getVectorType();
3233 Value res = vector::TransferReadOp::create(
3234 rewriter, xferOp.getLoc(), vectorType, in, xferOp.getIndices(),
3235 xferOp.getPermutationMapAttr(), xferOp.getPadding(), xferOp.getMask(),
3237 SmallVector<bool>(vectorType.getRank(),
false)));
3240 rewriter.
eraseOp(maybeFillOp);
3250 vector::TransferWriteOp xferOp, PatternRewriter &rewriter)
const {
3252 if (xferOp.getMask())
3256 Value viewOrAlloc = xferOp.getBase();
3262 memref::SubViewOp subViewOp = getSubViewUseIfUnique(viewOrAlloc);
3265 Value subView = subViewOp.getResult();
3268 memref::CopyOp copyOp;
3269 for (
auto &u : subViewOp.getResult().getUses()) {
3270 if (
auto newCopyOp = dyn_cast<memref::CopyOp>(u.getOwner())) {
3271 if (newCopyOp.getSource() != subView)
3273 if (mayExistInterleavedUses(xferOp, newCopyOp, {viewOrAlloc, subView}))
3283 assert(isa<MemRefType>(copyOp.getTarget().getType()));
3284 Value out = copyOp.getTarget();
3291 auto vector = xferOp.getVector();
3292 vector::TransferWriteOp::create(
3293 rewriter, xferOp.getLoc(), vector, out, xferOp.getIndices(),
3294 xferOp.getPermutationMapAttr(), xferOp.getMask(),
3296 dyn_cast<VectorType>(vector.getType()).getRank(),
false)));
3309static void bindShapeDims(ShapedType shapedType) {}
3311template <
int N,
typename IntTy,
typename... IntTy2>
3312static void bindShapeDims(ShapedType shapedType, IntTy &val, IntTy2 &...vals) {
3313 val = shapedType.getShape()[N];
3314 bindShapeDims<N + 1, IntTy2 &...>(shapedType, vals...);
3318template <
typename... IntTy>
3319static void bindShapeDims(ShapedType shapedType, IntTy &...vals) {
3320 bindShapeDims<0>(shapedType, vals...);
3325static std::optional<DilationsAndStrides> match1DConvPoolOp(LinalgOp op) {
3326#define MATCH_1D_CONV_POOL_OP(ConvOpTy) \
3327 if (auto convParams = matchConvolutionOpOfType<ConvOpTy>(op)) \
3349#undef MATCH_1D_CONV_POOL_OP
3351 return std::nullopt;
3389struct Conv1DGenerator
3390 :
public StructuredGenerator<LinalgOp, utils::IteratorType> {
3393 static FailureOr<Conv1DGenerator> create(RewriterBase &rewriter,
3394 LinalgOp linalgOp) {
3397 std::optional<DilationsAndStrides> convParams = match1DConvPoolOp(linalgOp);
3401 int strideW =
static_cast<int>(convParams->strides.front());
3402 int dilationW =
static_cast<int>(convParams->dilations.front());
3403 return Conv1DGenerator(rewriter, linalgOp, strideW, dilationW);
3407 Conv1DGenerator(RewriterBase &rewriter, LinalgOp linalgOp,
int strideW,
3409 : StructuredGenerator<LinalgOp, utils::IteratorType>(rewriter, linalgOp),
3410 strideW(strideW), dilationW(dilationW) {
3412 lhsShaped = linalgOp.getDpsInputOperand(0)->
get();
3413 rhsShaped = linalgOp.getDpsInputOperand(1)->
get();
3414 resShaped = linalgOp.getDpsInitOperand(0)->
get();
3415 lhsShapedType = dyn_cast<ShapedType>(lhsShaped.getType());
3416 rhsShapedType = dyn_cast<ShapedType>(rhsShaped.getType());
3417 resShapedType = dyn_cast<ShapedType>(resShaped.getType());
3422 setConvOperationKind(reduceOp);
3425 reductionKind = maybeKind.value();
3448 int64_t nSize, wSize, cSize, kwSize, fSize;
3449 SmallVector<int64_t, 3> lhsShape, rhsShape, resShape;
3451 switch (conv1DOpOrder) {
3454 nSize = fSize = cSize = 0;
3456 bindShapeDims(resShapedType, wSize);
3458 bindShapeDims(rhsShapedType, kwSize);
3461 (wSize + kwSize - 1)};
3462 rhsShape = {kwSize};
3467 bindShapeDims(resShapedType, nSize, wSize, fSize);
3469 case ConvOperationKind::Conv:
3471 bindShapeDims(rhsShapedType, kwSize, cSize);
3473 case ConvOperationKind::Pool:
3475 bindShapeDims(rhsShapedType, kwSize);
3483 ((wSize - 1) * strideW + 1) + ((kwSize - 1) * dilationW + 1) -
3487 case ConvOperationKind::Conv:
3488 rhsShape = {kwSize, cSize, fSize};
3490 case ConvOperationKind::Pool:
3491 rhsShape = {kwSize};
3494 resShape = {nSize, wSize, fSize};
3498 bindShapeDims(resShapedType, nSize, fSize, wSize);
3500 case ConvOperationKind::Conv:
3502 bindShapeDims(rhsShapedType, fSize, cSize, kwSize);
3504 case ConvOperationKind::Pool:
3506 bindShapeDims(rhsShapedType, kwSize);
3510 lhsShape = {nSize, cSize,
3514 ((wSize - 1) * strideW + 1) + ((kwSize - 1) * dilationW + 1) -
3517 case ConvOperationKind::Conv:
3518 rhsShape = {fSize, cSize, kwSize};
3520 case ConvOperationKind::Pool:
3521 rhsShape = {kwSize};
3524 resShape = {nSize, fSize, wSize};
3528 vector::TransferWriteOp write;
3534 int64_t wSizeStep = strideW == 1 ? wSize : 1;
3536 Type lhsEltType = lhsShapedType.getElementType();
3537 Type rhsEltType = rhsShapedType.getElementType();
3538 Type resEltType = resShapedType.getElementType();
3539 auto lhsType = VectorType::get(lhsShape, lhsEltType);
3540 auto rhsType = VectorType::get(rhsShape, rhsEltType);
3541 auto resType = VectorType::get(resShape, resEltType);
3543 SmallVector<Value> lhsPadding(lhsShape.size(), zero);
3544 SmallVector<Value> rhsPadding(rhsShape.size(), zero);
3545 SmallVector<Value> resPadding(resShape.size(), zero);
3548 Value
lhs = vector::TransferReadOp::create(
3549 rewriter, loc, lhsType, lhsShaped, lhsPadding,
3550 arith::getZeroConstant(rewriter, loc, lhsEltType));
3552 Value
rhs =
nullptr;
3553 if (oper == ConvOperationKind::Conv)
3554 rhs = vector::TransferReadOp::create(
3555 rewriter, loc, rhsType, rhsShaped, rhsPadding,
3556 arith::getZeroConstant(rewriter, loc, rhsEltType));
3557 Value res = vector::TransferReadOp::create(
3558 rewriter, loc, resType, resShaped, resPadding,
3559 arith::getZeroConstant(rewriter, loc, resEltType));
3564 switch (conv1DOpOrder) {
3572 static constexpr std::array<int64_t, 3> permLhs = {0, 2, 1};
3573 lhs = vector::TransposeOp::create(rewriter, loc,
lhs, permLhs);
3575 static constexpr std::array<int64_t, 3> permRhs = {2, 1, 0};
3578 if (oper == ConvOperationKind::Conv)
3579 rhs = vector::TransposeOp::create(rewriter, loc,
rhs, permRhs);
3581 static constexpr std::array<int64_t, 3> permRes = {0, 2, 1};
3582 res = vector::TransposeOp::create(rewriter, loc, res, permRes);
3591 SmallVector<Value> lhsVals, rhsVals, resVals;
3593 kwSize, strideW, dilationW, wSizeStep,
3596 if (oper == ConvOperationKind::Conv)
3599 wSizeStep, isSingleChanneled);
3601 auto linearIndex = [&](int64_t kw, int64_t w) {
3602 return kw * (wSize / wSizeStep) + w;
3608 for (int64_t kw = 0; kw < kwSize; ++kw) {
3609 for (int64_t w = 0; w < wSize; w += wSizeStep) {
3611 case ConvOperationKind::Conv:
3612 if (isSingleChanneled) {
3613 resVals[w] = conv1dSliceAsOuterProduct(rewriter, loc,
3614 lhsVals[linearIndex(kw, w)],
3615 rhsVals[kw], resVals[w]);
3617 resVals[w] = conv1dSliceAsContraction(rewriter, loc,
3618 lhsVals[linearIndex(kw, w)],
3619 rhsVals[kw], resVals[w]);
3622 case ConvOperationKind::Pool:
3623 resVals[w] = pool1dSlice(rewriter, loc, lhsVals[linearIndex(kw, w)],
3639 switch (conv1DOpOrder) {
3646 static constexpr std::array<int64_t, 3> perm = {0, 2, 1};
3647 res = vector::TransposeOp::create(rewriter, loc, res, perm);
3652 return vector::TransferWriteOp::create(rewriter, loc, res, resShaped,
3658 Value
promote(RewriterBase &rewriter, Location loc, Value val, Type ty) {
3661 assert(isa<IntegerType>(dstElementType) || isa<FloatType>(dstElementType));
3662 if (srcElementType == dstElementType)
3669 if (
auto shapedType = dyn_cast<ShapedType>(val.
getType()))
3670 dstType = shapedType.cloneWith(std::nullopt, dstElementType);
3672 dstType = dstElementType;
3674 if (isa<IntegerType>(srcElementType) && isa<FloatType>(dstElementType)) {
3675 return arith::SIToFPOp::create(rewriter, loc, dstType, val);
3678 if (isa<FloatType>(srcElementType) && isa<FloatType>(dstElementType) &&
3679 srcWidth < dstWidth)
3680 return arith::ExtFOp::create(rewriter, loc, dstType, val);
3682 if (isa<IntegerType>(srcElementType) && isa<IntegerType>(dstElementType) &&
3683 srcWidth < dstWidth)
3684 return arith::ExtSIOp::create(rewriter, loc, dstType, val);
3686 assert(
false &&
"unhandled promotion case");
3691 Value conv1dSliceAsContraction(RewriterBase &rewriter, Location loc,
3692 Value
lhs, Value
rhs, Value res) {
3693 vector::IteratorType par = vector::IteratorType::parallel;
3694 vector::IteratorType red = vector::IteratorType::reduction;
3695 AffineExpr n, w, f, c;
3699 auto contrationOp = vector::ContractionOp::create(
3700 rewriter, loc,
lhs,
rhs, res,
3701 MapList{{n, w, c}, {c, f}, {n, w, f}},
3702 ArrayRef<vector::IteratorType>{par, par, par, red});
3703 contrationOp.setKind(reductionKind);
3704 return contrationOp;
3709 Value conv1dSliceAsOuterProduct(RewriterBase &rewriter, Location loc,
3710 Value
lhs, Value
rhs, Value res) {
3713 return vector::OuterProductOp::create(rewriter, loc, res.
getType(),
lhs,
3714 rhs, res, vector::CombiningKind::ADD);
3718 Value pool1dSlice(RewriterBase &rewriter, Location loc, Value
lhs,
3736 FailureOr<Operation *> depthwiseConv(uint64_t channelDimVecSize,
3737 bool channelDimScalableFlag,
3739 bool scalableChDim =
false;
3740 bool useMasking =
false;
3741 int64_t nSize, wSize, cSize, kwSize;
3743 bindShapeDims(rhsShapedType, kwSize, cSize);
3744 if (ShapedType::isDynamic(cSize)) {
3745 assert(channelDimVecSize != 0 &&
"Channel dim vec size must be > 0");
3746 cSize = channelDimVecSize;
3750 scalableChDim = channelDimScalableFlag;
3754 assert(!(useMasking && flatten) &&
3755 "Unsupported flattened conv with dynamic shapes");
3758 bindShapeDims(resShapedType, nSize, wSize);
3760 vector::TransferWriteOp write;
3766 int64_t wSizeStep = strideW == 1 ? wSize : 1;
3768 Type lhsEltType = lhsShapedType.getElementType();
3769 Type rhsEltType = rhsShapedType.getElementType();
3770 Type resEltType = resShapedType.getElementType();
3771 VectorType lhsType = VectorType::get(
3775 ((wSize - 1) * strideW + 1) + ((kwSize - 1) * dilationW + 1) - 1,
3777 lhsEltType, {
false,
false, scalableChDim});
3778 VectorType rhsType =
3779 VectorType::get({kwSize, cSize}, rhsEltType,
3780 {
false, scalableChDim});
3781 VectorType resType =
3782 VectorType::get({nSize, wSize, cSize}, resEltType,
3783 {
false,
false, scalableChDim});
3787 auto maybeMaskXferOp = [&](ArrayRef<int64_t> maskShape,
3788 ArrayRef<bool> scalableDims,
3789 Operation *opToMask) {
3793 VectorType::get(maskShape, rewriter.
getI1Type(), scalableDims);
3795 SmallVector<bool> inBounds(maskShape.size(),
true);
3796 auto xferOp = cast<VectorTransferOpInterface>(opToMask);
3797 xferOp->setAttr(xferOp.getInBoundsAttrName(),
3801 cast<LinalgOp>(op).hasPureTensorSemantics(), opToMask, rewriter);
3804 vector::CreateMaskOp::create(rewriter, loc, maskType, mixedDims);
3811 Value
lhs = vector::TransferReadOp::create(
3812 rewriter, loc, lhsType, lhsShaped,
ValueRange{zero, zero, zero},
3813 arith::getZeroConstant(rewriter, loc, lhsEltType));
3814 auto *maybeMaskedLhs = maybeMaskXferOp(
3815 lhsType.getShape(), lhsType.getScalableDims(),
lhs.getDefiningOp());
3818 Value
rhs = vector::TransferReadOp::create(
3819 rewriter, loc, rhsType, rhsShaped,
ValueRange{zero, zero},
3820 arith::getZeroConstant(rewriter, loc, rhsEltType));
3821 auto *maybeMaskedRhs = maybeMaskXferOp(
3822 rhsType.getShape(), rhsType.getScalableDims(),
rhs.getDefiningOp());
3825 Value res = vector::TransferReadOp::create(
3826 rewriter, loc, resType, resShaped,
ValueRange{zero, zero, zero},
3827 arith::getZeroConstant(rewriter, loc, resEltType));
3828 auto *maybeMaskedRes = maybeMaskXferOp(
3829 resType.getShape(), resType.getScalableDims(), res.
getDefiningOp());
3835 SmallVector<Value> lhsVals, rhsVals, resVals;
3836 SmallVector<int64_t> inOutSliceSizes = {nSize, wSizeStep, cSize};
3837 SmallVector<int64_t> inOutStrides = {1, 1, 1};
3841 for (int64_t kw = 0; kw < kwSize; ++kw) {
3842 for (int64_t w = 0; w < wSize; w += wSizeStep) {
3843 lhsVals.push_back(vector::ExtractStridedSliceOp::create(
3844 rewriter, loc, maybeMaskedLhs->getResult(0),
3845 ArrayRef<int64_t>{0, w * strideW + kw * dilationW, 0},
3846 inOutSliceSizes, inOutStrides));
3850 for (int64_t kw = 0; kw < kwSize; ++kw) {
3852 vector::ExtractOp::create(rewriter, loc, maybeMaskedRhs->getResult(0),
3853 ArrayRef<int64_t>{kw}));
3856 for (int64_t w = 0; w < wSize; w += wSizeStep) {
3857 resVals.push_back(vector::ExtractStridedSliceOp::create(
3858 rewriter, loc, maybeMaskedRes->getResult(0),
3859 ArrayRef<int64_t>{0, w, 0}, inOutSliceSizes,
3863 auto linearIndex = [&](int64_t kw, int64_t w) {
3864 return kw * (wSize / wSizeStep) + w;
3869 SmallVector<int64_t> inOutFlattenSliceSizes = {nSize, wSizeStep * cSize};
3870 auto lhsTypeAfterFlattening =
3871 VectorType::get(inOutFlattenSliceSizes, lhsEltType);
3872 auto resTypeAfterFlattening =
3873 VectorType::get(inOutFlattenSliceSizes, resEltType);
3876 for (int64_t kw = 0; kw < kwSize; ++kw) {
3877 for (int64_t w = 0; w < wSize; w += wSizeStep) {
3878 Value lhsVal = lhsVals[linearIndex(kw, w)];
3879 Value resVal = resVals[w];
3884 vector::ShapeCastOp::create(rewriter, loc, lhsTypeAfterFlattening,
3885 lhsVals[linearIndex(kw, w)]);
3886 resVal = vector::ShapeCastOp::create(
3887 rewriter, loc, resTypeAfterFlattening, resVals[w]);
3889 resVals[w] = depthwiseConv1dSliceAsMulAcc(rewriter, loc, lhsVal,
3890 rhsVals[kw], resVal, flatten);
3893 resVals[w] = vector::ShapeCastOp::create(
3894 rewriter, loc, VectorType::get(inOutSliceSizes, resEltType),
3901 if (!llvm::all_of(resVals, [](Value v) {
return v; })) {
3903 for (
auto &collection :
3904 {resVals, rhsVals, lhsVals, {res,
rhs,
lhs, zero}})
3905 for (Value v : collection)
3912 for (int64_t w = 0; w < wSize; w += wSizeStep) {
3913 maybeMaskedRes = vector::InsertStridedSliceOp::create(
3914 rewriter, loc, resVals[w], maybeMaskedRes->getResult(0),
3915 ArrayRef<int64_t>{0, w, 0},
3916 ArrayRef<int64_t>{1, 1, 1});
3923 Operation *resOut = vector::TransferWriteOp::create(
3924 rewriter, loc, maybeMaskedRes->getResult(0), resShaped,
3926 return maybeMaskXferOp(resType.getShape(), resType.getScalableDims(),
3934 Value depthwiseConv1dSliceAsMulAcc(RewriterBase &rewriter, Location loc,
3935 Value
lhs, Value
rhs, Value res,
3937 auto rhsTy = cast<ShapedType>(
rhs.getType());
3938 auto resTy = cast<ShapedType>(res.
getType());
3952 auto rhsSize = cast<VectorType>(
rhs.getType()).getShape()[0];
3953 auto resSize = cast<VectorType>(res.
getType()).getShape()[1];
3955 SmallVector<int64_t, 16>
indices;
3956 for (
int i = 0; i < resSize / rhsSize; ++i) {
3957 for (
int j = 0; j < rhsSize; ++j)
3964 rhs = vector::BroadcastOp::create(rewriter, loc,
3965 resTy.clone(rhsTy.getElementType()),
rhs);
3972 if (isa<FloatType>(resTy.getElementType()))
3973 return vector::FMAOp::create(rewriter, loc,
lhs,
rhs, res);
3975 auto mul = arith::MulIOp::create(rewriter, loc,
lhs,
rhs);
3976 return arith::AddIOp::create(rewriter, loc,
mul, res);
3981 FailureOr<Operation *> generateNonChanneledConv() {
3984 if (!iters({Par(), Red()}))
3986 "failed to match conv::W 1-par 1-red");
3989 if (layout({ {w + kw},
3999 FailureOr<Operation *> generateNwcConv() {
4000 AffineExpr n, w, f, kw, c;
4002 if (!iters({Par(), Par(), Par(), Red(), Red()}))
4004 op,
"failed to match conv::Nwc 3-par 2-red");
4007 if (layout({ {n, strideW * w + dilationW * kw, c},
4017 FailureOr<Operation *> generateNcwConv() {
4018 AffineExpr n, w, f, kw, c;
4020 if (!iters({Par(), Par(), Par(), Red(), Red()}))
4022 op,
"failed to match conv::Ncw 3-par 2-red");
4024 if (layout({ {n, c, strideW * w + dilationW * kw},
4034 FailureOr<Operation *> generateNwcPooling() {
4035 AffineExpr n, w, c, kw;
4037 if (!iters({Par(), Par(), Par(), Red()}))
4039 "failed to match pooling 3-par 1-red");
4042 if (layout({ {n, strideW * w + dilationW * kw, c},
4052 FailureOr<Operation *> generateNcwPooling() {
4053 AffineExpr n, w, c, kw;
4055 if (!iters({Par(), Par(), Par(), Red()}))
4057 "failed to match pooling 3-par 1-red");
4059 if (layout({ {n, c, strideW * w + dilationW * kw},
4069 FailureOr<Operation *> generateDilatedConv(uint64_t vecChDimSize = 0,
4070 bool vecChDimScalableFlag =
false,
4071 bool flatten =
false) {
4072 AffineExpr n, w, c, kw;
4074 if (!iters({Par(), Par(), Par(), Red()}))
4076 op,
"failed to match depthwise::Nwc conv 3-par 1-red");
4079 if (layout({ {n, strideW * w + dilationW * kw, c},
4082 return depthwiseConv(vecChDimSize, vecChDimScalableFlag, flatten);
4088 ConvOperationKind oper = ConvOperationKind::Conv;
4090 StringAttr poolExtOp;
4091 bool isPoolExt =
false;
4092 int strideW, dilationW;
4093 Value lhsShaped, rhsShaped, resShaped;
4094 ShapedType lhsShapedType, rhsShapedType, resShapedType;
4095 vector::CombiningKind reductionKind;
4098 void setConvOperationKind(Operation *reduceOp) {
4099 int numBlockArguments =
4100 llvm::count_if(reduceOp->
getOperands(), llvm::IsaPred<BlockArgument>);
4101 if (numBlockArguments == 1) {
4106 auto feedValIt = llvm::find_if_not(reduceOp->
getOperands(),
4107 llvm::IsaPred<BlockArgument>);
4108 Operation *feedOp = (*feedValIt).getDefiningOp();
4109 if (isCastOfBlockArgument(feedOp)) {
4110 oper = ConvOperationKind::Pool;
4115 oper = ConvOperationKind::Conv;
4119 oper = ConvOperationKind::Pool;
4128 RewriterBase &rewriter, LinalgOp op, ArrayRef<int64_t> inputVecSizes,
4129 ArrayRef<bool> inputScalableVecDims,
bool flatten1DDepthwiseConv) {
4130 FailureOr<Conv1DGenerator> conv1dGen = Conv1DGenerator::create(rewriter, op);
4133 auto res = conv1dGen->generateNonChanneledConv();
4136 res = conv1dGen->generateNwcConv();
4139 res = conv1dGen->generateNcwConv();
4142 res = conv1dGen->generateNwcPooling();
4145 res = conv1dGen->generateNcwPooling();
4152 uint64_t vecChDimSize = ShapedType::kDynamic;
4153 bool vecChDimScalableFlag =
false;
4154 if (!inputVecSizes.empty()) {
4159 "Not a 1D depthwise conv!");
4160 size_t chDimIdx = 0;
4166 vecChDimSize = inputVecSizes[chDimIdx];
4167 vecChDimScalableFlag = inputScalableVecDims[chDimIdx];
4169 return conv1dGen->generateDilatedConv(vecChDimSize, vecChDimScalableFlag,
4170 flatten1DDepthwiseConv);
4173struct VectorizeConvolution :
public OpInterfaceRewritePattern<LinalgOp> {
4176 LogicalResult matchAndRewrite(LinalgOp op,
4177 PatternRewriter &rewriter)
const override {
4179 if (
failed(resultOrFail))
4181 Operation *newOp = *resultOrFail;
4183 rewriter.
eraseOp(op.getOperation());
4186 assert(newOp->
getNumResults() == 1 &&
"expected single result");
4193 RewritePatternSet &patterns, PatternBenefit benefit) {
4194 patterns.
add<VectorizeConvolution>(patterns.
getContext(), benefit);
static std::optional< VectorShape > vectorShape(Type type)
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.
static OpType getSingleOpOfType(Block &block)
Return the unique instance of OpType in block if it is indeed unique.
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()
RetT walk(FnT &&callback)
Walk all nested operations, blocks (including this block) or regions, depending on the type of callba...
AffineMap getMultiDimIdentityMap(unsigned rank)
TypedAttr getZeroAttr(Type type)
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.
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.
Value getOperand(unsigned idx)
bool isBeforeInBlock(Operation *other)
Given an operation 'other' that is within the same parent block, return whether the current operation...
ArrayRef< NamedAttribute > getAttrs()
Return all of the attributes on this 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()
operand_iterator operand_end()
OperationName getName()
The name of an operation is the key identifier for it.
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...
unsigned getIntOrFloatBitWidth() const
Return the bit width of an integer or a float type, assert failure on other types.
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 WalkResult advance()
static WalkResult interrupt()
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.
void 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)
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.
VectorizationState(RewriterBase &rewriter)
OpInterfaceRewritePattern(MLIRContext *context, PatternBenefit benefit=1)
LogicalResult matchAndRewrite(vector::TransferReadOp xferOp, PatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(vector::TransferWriteOp xferOp, PatternRewriter &rewriter) const override