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);
97 int64_t kwSize,
int strideW,
int dilationW,
98 int64_t wSizeStep,
bool isSingleChanneled) {
100 if (isSingleChanneled) {
105 for (
int64_t kw = 0; kw < kwSize; ++kw) {
106 for (
int64_t w = 0; w < wSize; w += wSizeStep) {
107 result.push_back(vector::ExtractStridedSliceOp::create(
117 for (
int64_t kw = 0; kw < kwSize; ++kw) {
118 for (
int64_t w = 0; w < wSize; w += wSizeStep) {
119 result.push_back(vector::ExtractStridedSliceOp::create(
120 rewriter, loc, input,
137 for (
int64_t kw = 0; kw < kwSize; ++kw) {
138 result.push_back(vector::ExtractOp::create(
149 int64_t wSizeStep,
bool isSingleChanneled) {
151 if (isSingleChanneled) {
155 for (
int64_t w = 0; w < wSize; w += wSizeStep) {
156 result.push_back(vector::ExtractStridedSliceOp::create(
165 for (
int64_t w = 0; w < wSize; w += wSizeStep) {
166 result.push_back(vector::ExtractStridedSliceOp::create(
178 bool isSingleChanneled) {
180 if (isSingleChanneled) {
184 for (
int64_t w = 0; w < wSize; w += wSizeStep) {
185 res = vector::InsertStridedSliceOp::create(
193 for (
int64_t w = 0; w < wSize; w += wSizeStep) {
194 res = vector::InsertStridedSliceOp::create(
195 rewriter, loc, resVals[w], res,
209 LogicalResult initState(
RewriterBase &rewriter, LinalgOp linalgOp,
212 bool assumeDynamicDimsMatchVecSizes =
false);
227 std::optional<AffineMap> dimPermutation = std::nullopt)
const {
230 if (dimPermutation.has_value()) {
236 vectorShape.append(canonicalVecShape.begin(), canonicalVecShape.end());
237 scalableDims.append(scalableVecDims.begin(), scalableVecDims.end());
240 return VectorType::get(
vectorShape, elementType, scalableDims);
249 std::optional<AffineMap> maybeIndexingMap = std::nullopt);
254 void initIterSpaceStaticSizes(LinalgOp linalgOp) {
255 iterSpaceStaticSizes.append(linalgOp.getStaticLoopRanges());
261 LogicalResult precomputeIterSpaceValueSizes(RewriterBase &rewriter,
268 Value getOrCreateMaskFor(RewriterBase &rewriter, Operation *opToMask,
270 std::optional<AffineMap> maybeMaskingMap);
275 bool isValidMaskingMap(AffineMap maskingMap) {
294 AffineMap getMaskingMapFromIndexingMap(AffineMap &indexingMap) {
300 SmallVector<int64_t> iterSpaceStaticSizes;
305 SmallVector<Value> iterSpaceValueSizes;
308 SmallVector<int64_t> canonicalVecShape;
312 SmallVector<bool> scalableVecDims;
320 OpBuilder::InsertionGuard rewriterGuard;
328 bool assumeDynamicDimsMatchVecSizes =
false;
332VectorizationState::precomputeIterSpaceValueSizes(
RewriterBase &rewriter,
335 for (
int vecDim = 0, end = canonicalVecShape.size(); vecDim < end; ++vecDim) {
336 if (ShapedType::isStatic(iterSpaceStaticSizes[vecDim])) {
339 rewriter, linalgOp.getLoc(), iterSpaceStaticSizes[vecDim]));
346 unsigned operandDimPos;
347 if (
failed(linalgOp.mapIterationSpaceDimToOperandDim(vecDim, operand,
352 linalgOp.hasPureTensorSemantics()
353 ? (Value)tensor::DimOp::create(rewriter, linalgOp.getLoc(), operand,
355 : (Value)memref::DimOp::create(rewriter, linalgOp.getLoc(), operand,
357 iterSpaceValueSizes.push_back(dynamicDim);
370 bool assumeDimsMatchVec) {
371 assumeDynamicDimsMatchVecSizes = assumeDimsMatchVec;
375 if (!inputVectorSizes.empty()) {
379 canonicalVecShape.append(inputVectorSizes.begin(), inputVectorSizes.end());
380 scalableVecDims.append(inputScalableVecDims.begin(),
381 inputScalableVecDims.end());
386 canonicalVecShape = linalgOp.getStaticLoopRanges();
387 scalableVecDims.append(linalgOp.getNumLoops(),
false);
390 LDBG() <<
"Canonical vector shape: " << llvm::interleaved(canonicalVecShape);
391 LDBG() <<
"Scalable vector dims: " << llvm::interleaved(scalableVecDims);
393 if (ShapedType::isDynamicShape(canonicalVecShape))
397 initIterSpaceStaticSizes(linalgOp);
402 if (failed(precomputeIterSpaceValueSizes(rewriter, linalgOp)))
412Value VectorizationState::getOrCreateMaskFor(
414 std::optional<AffineMap> maybeMaskingMap) {
416 assert((!maybeMaskingMap || isValidMaskingMap(*maybeMaskingMap)) &&
417 "Ill-formed masking map.");
420 auto maskableOp = dyn_cast<vector::MaskableOpInterface>(opToMask);
424 assert(!maskableOp.isMasked() &&
425 "Masking an operation that is already masked");
428 assert((!maybeMaskingMap || *maybeMaskingMap) &&
429 "Unexpected null mask permutation map");
431 maybeMaskingMap ? *maybeMaskingMap
433 linalgOp.getNumLoops(), rewriter.
getContext());
435 LDBG() <<
"Masking map: " << maskingMap;
439 auto activeMaskIt = activeMaskCache.find(maskingMap);
440 if (activeMaskIt != activeMaskCache.end()) {
441 Value mask = activeMaskIt->second;
442 LDBG() <<
"Reusing mask: " << mask;
452 SmallVector<int64_t> permutedStaticSizes =
454 auto maskType = getCanonicalVecType(rewriter.
getI1Type(), maskingMap);
455 auto maskShape = maskType.getShape();
457 LDBG() <<
"Mask shape: " << llvm::interleaved(maskShape);
459 if (permutedStaticSizes == maskShape) {
460 LDBG() <<
"Masking is not needed for masking map: " << maskingMap;
461 activeMaskCache[maskingMap] = Value();
465 if (assumeDynamicDimsMatchVecSizes) {
469 if (llvm::all_of(llvm::zip(permutedStaticSizes, maskType.getShape()),
471 return std::get<0>(it) == ShapedType::kDynamic
473 : std::get<0>(it) == std::get<1>(it);
476 <<
"Dynamic + static dimensions match vector sizes, masking is not "
478 activeMaskCache[maskingMap] = Value();
484 SmallVector<Value> upperBounds =
486 assert(!maskShape.empty() && !upperBounds.empty() &&
487 "Masked 0-d vectors are not supported yet");
490 Value mask = vector::CreateMaskOp::create(rewriter, linalgOp.getLoc(),
491 maskType, upperBounds);
492 LDBG() <<
"Creating new mask: " << mask;
493 activeMaskCache[maskingMap] = mask;
500 std::optional<AffineMap> maybeIndexingMap) {
501 LDBG() <<
"Trying to mask: " << *opToMask;
503 std::optional<AffineMap> maybeMaskingMap = std::nullopt;
504 if (maybeIndexingMap)
505 maybeMaskingMap = getMaskingMapFromIndexingMap(*maybeIndexingMap);
509 getOrCreateMaskFor(rewriter, opToMask, linalgOp, maybeMaskingMap);
512 LDBG() <<
"No mask required";
513 if (assumeDynamicDimsMatchVecSizes) {
515 .Case<vector::TransferReadOp, vector::TransferWriteOp>(
521 LDBG() <<
"Assuming dynamic dimensions match vector sizes and "
522 "setting their in-bounds to true!";
524 ShapedType xferType = xferOp.getShapedType();
529 for (
unsigned i = 0; i < xferOp.getTransferRank(); i++) {
530 auto dimExpr = dyn_cast<AffineDimExpr>(permMap.
getResult(i));
534 unsigned pos = dimExpr.getPosition();
535 if (xferType.isDynamicDim(pos))
536 inBoundsMap[i] =
true;
539 xferOp.setInBoundsAttr(
551 assert(opToMask &&
"Expected a valid operation to mask");
552 auto maskOp = cast<vector::MaskOp>(
554 Operation *maskOpTerminator = &maskOp.getMaskRegion().front().back();
556 for (
auto [resIdx, resVal] : llvm::enumerate(opToMask->
getResults()))
560 LDBG() <<
"Masked operation: " << *maskOp;
583 "expected projected permutation");
585 assert(res.getNumDims() ==
586 (res.getNumResults() - res.getNumOfZeroResults()) &&
587 "expected reindexed map with same number of dims and results");
623std::optional<vector::CombiningKind>
625 using ::mlir::vector::CombiningKind;
630 .Case<arith::AddIOp, arith::AddFOp>(
631 [&](
auto op) {
return CombiningKind::ADD; })
632 .Case([&](arith::AndIOp op) {
return CombiningKind::AND; })
633 .Case([&](arith::MaxSIOp op) {
return CombiningKind::MAXSI; })
634 .Case([&](arith::MaxUIOp op) {
return CombiningKind::MAXUI; })
635 .Case([&](arith::MaximumFOp op) {
return CombiningKind::MAXIMUMF; })
636 .Case([&](arith::MaxNumFOp op) {
return CombiningKind::MAXNUMF; })
637 .Case([&](arith::MinSIOp op) {
return CombiningKind::MINSI; })
638 .Case([&](arith::MinUIOp op) {
return CombiningKind::MINUI; })
639 .Case([&](arith::MinimumFOp op) {
return CombiningKind::MINIMUMF; })
640 .Case([&](arith::MinNumFOp op) {
return CombiningKind::MINNUMF; })
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");
693 return vector::MultiDimReductionOp::create(
694 b, reduceOp->
getLoc(), valueToReduce,
acc, dimsToMask, *maybeKind);
698 return llvm::map_to_vector(linalgOp.getIteratorTypesArray(),
705 return isa<linalg::ReduceOp>(op) ||
706 (isa<linalg::GenericOp>(op) &&
718 VectorizationState &state) {
720 auto linalgOp = cast<LinalgOp>(outputOperand->
getOwner());
721 AffineMap opOperandMap = linalgOp.getMatchingIndexingMap(outputOperand);
730 return llvm::is_contained(opOperandMap.getResults(), dimExpr);
732 auto vectorType = state.getCanonicalVecType(
739 if (vectorType.getRank() > 0) {
742 assert(value.
getType() == vectorType &&
"Incorrect type");
743 write = vector::TransferWriteOp::create(
744 rewriter, loc, value, outputOperand->
get(),
indices, writeMap);
747 if (!isa<VectorType>(value.
getType()))
748 value = vector::BroadcastOp::create(rewriter, loc, vectorType, value);
749 assert(value.
getType() == vectorType &&
"Incorrect type");
750 write = vector::TransferWriteOp::create(rewriter, loc, value,
754 write = state.maskOperation(rewriter, write, linalgOp, opOperandMap);
758 if (
auto maskOp = dyn_cast<vector::MaskingOpInterface>(write)) {
759 auto maskedWriteOp = cast<vector::TransferWriteOp>(maskOp.getMaskableOp());
764 LDBG() <<
"vectorized op: " << *write;
774 std::function<LogicalResult(
Operation *,
bool)>;
791 const IRMapping &bvm, VectorizationState &state,
793 auto yieldOp = dyn_cast<linalg::YieldOp>(op);
796 for (
const auto &output : llvm::enumerate(yieldOp.getValues())) {
802 linalgOp.getDpsInitOperand(output.index()), state);
804 newResults.push_back(newResult);
815 VectorizationState &state,
818 IndexOp indexOp = dyn_cast<linalg::IndexOp>(op);
821 auto loc = indexOp.getLoc();
824 auto dim = indexOp.getDim();
826 auto indexVectorType =
827 VectorType::get({targetShape[dim]}, rewriter.
getIndexType(),
828 state.getScalableVecDims()[dim]);
829 auto indexSteps = vector::StepOp::create(rewriter, loc, indexVectorType);
833 if (dim == targetShape.size() - 1)
839 llvm::to_vector(llvm::seq<unsigned>(0, targetShape.size()));
840 std::swap(permPattern[dim], permPattern.back());
844 auto broadCastOp = vector::BroadcastOp::create(
846 state.getCanonicalVecType(rewriter.
getIndexType(), permMap), indexSteps);
848 llvm::to_vector<16>(llvm::seq<int64_t>(0, linalgOp.getNumLoops()));
849 std::swap(transposition.back(), transposition[dim]);
851 vector::TransposeOp::create(rewriter, loc, broadCastOp, transposition);
859 tensor::ExtractOp extractOp = dyn_cast<tensor::ExtractOp>(op);
863 if (extractOp.getIndices().size() != 1 && !vectorizeNDExtract)
868 if (not extractOp.getIndices().empty()) {
869 if (!VectorType::isValidElementType(extractOp.getIndices()[0].getType()))
873 if (!llvm::all_of(extractOp->getResultTypes(),
874 VectorType::isValidElementType)) {
892 VectorizationState &state,
893 tensor::ExtractOp extractOp,
896 auto indexVecType = state.getCanonicalVecType(rewriter.
getIndexType());
897 auto loc = extractOp.getLoc();
900 rewriter, bvm.
lookup(extractOp.getIndices()[0]), indexVecType);
902 const size_t numIndices = extractOp.getIndices().size();
903 for (
size_t i = 1; i < numIndices; i++) {
908 tensor::DimOp::create(rewriter, loc, extractOp.getTensor(), dimIdx),
911 offset = arith::MulIOp::create(rewriter, loc, offset, dimSize);
914 rewriter, bvm.
lookup(extractOp.getIndices()[i]), indexVecType);
916 offset = arith::AddIOp::create(rewriter, loc, extractOpIndex, offset);
942 (linalgOp.hasDynamicShape() ||
943 llvm::count_if(loopRanges, [](
int64_t dim) { return dim != 1; }) == 1) &&
944 "For statically shaped Linalg Ops, only one "
945 "non-unit loop dim is expected");
946 assert(!loopRanges.empty() &&
"Empty loops, nothing to analyse.");
948 size_t idx = loopRanges.size() - 1;
949 for (; idx != 0; idx--)
950 if (loopRanges[idx] != 1)
958 VectorType resType) {
960 assert(((llvm::count_if(resType.getShape(),
961 [](
int64_t dimSize) { return dimSize > 1; }) == 1)) &&
962 "n-D vectors are not yet supported");
968 auto *block = linalgOp.getBlock();
969 if (isa<BlockArgument>(val))
970 return !llvm::is_contained(block->getArguments(), val);
973 assert(defOp &&
"This is neither a block argument nor an operation result");
978 if (
auto indexOp = dyn_cast<linalg::IndexOp>(defOp)) {
979 return linalgOp.getStaticLoopRanges()[indexOp.getDim()] == 1;
982 auto *ancestor = block->findAncestorOpInBlock(*defOp);
989 if (isa<arith::ConstantOp>(ancestor))
993 for (
auto op : ancestor->getOperands())
1017 bool &foundIndexOp, VectorType resType) {
1019 assert(((llvm::count_if(resType.getShape(),
1020 [](
int64_t dimSize) { return dimSize > 1; }) == 1)) &&
1021 "n-D vectors are not yet supported");
1027 auto *block = linalgOp.getBlock();
1028 if (isa<BlockArgument>(val))
1029 return !llvm::is_contained(block->getArguments(), val);
1032 assert(defOp &&
"This is neither a block argument nor an operation result");
1034 if (
auto indexOp = dyn_cast<linalg::IndexOp>(defOp)) {
1037 foundIndexOp = (indexOp.getDim() == loopDimThatIncrementsByOne);
1041 auto *ancestor = block->findAncestorOpInBlock(*defOp);
1048 if (!isa<arith::AddIOp, arith::ConstantOp, linalg::IndexOp>(ancestor))
1052 for (
auto op : ancestor->getOperands())
1072 LinalgOp &linalgOp, VectorType resType) {
1074 auto inputShape = cast<ShapedType>(extractOp.getTensor().getType());
1077 if (inputShape.getShape().empty())
1082 if (resType.getRank() == 0)
1087 bool isOutput1DVector =
1088 (llvm::count_if(resType.getShape(),
1089 [](
int64_t dimSize) { return dimSize > 1; }) == 1);
1091 if (!isOutput1DVector)
1094 bool leadingIdxsLoopInvariant =
true;
1100 auto indices = extractOp.getIndices();
1101 auto leadIndices =
indices.drop_back(1);
1103 for (
auto [i, indexVal] : llvm::enumerate(leadIndices)) {
1104 if (inputShape.getShape()[i] == 1)
1110 if (!leadingIdxsLoopInvariant) {
1111 LDBG() <<
"Found gather load: " << extractOp;
1119 auto extractOpTrailingIdx =
indices.back();
1123 if (leadingIdxsLoopInvariant &&
1125 LDBG() <<
"Found scalar broadcast load: " << extractOp;
1134 bool foundIndexOp =
false;
1136 foundIndexOp, resType);
1139 bool isRowVector = resType.getShape().back() != 1;
1140 isContiguousLoad &= (foundIndexOp && isRowVector);
1142 if (isContiguousLoad) {
1143 LDBG() <<
"Found contigous load: " << extractOp;
1148 LDBG() <<
"Found gather load: " << extractOp;
1156static VectorizationHookResult
1159 tensor::ExtractOp extractOp = dyn_cast<tensor::ExtractOp>(op);
1162 auto loc = extractOp.getLoc();
1165 auto resultType = state.getCanonicalVecType(extractOp.getResult().getType());
1166 auto maskConstantOp = arith::ConstantOp::create(
1170 auto passThruConstantOp = arith::ConstantOp::create(
1176 extractOp.getIndices().size(),
1187 Operation *gatherOp = vector::GatherOp::create(
1188 rewriter, loc, resultType, extractOp.getTensor(), baseIndices, offset,
1189 maskConstantOp, passThruConstantOp);
1190 gatherOp = state.maskOperation(rewriter, gatherOp, linalgOp);
1192 LDBG() <<
"Vectorised as gather load: " << extractOp;
1215 for (
size_t i = 0; i < extractOp.getIndices().size(); i++) {
1216 Value idx = bvm.
lookup(extractOp.getIndices()[i]);
1218 transferReadIdxs.push_back(idx);
1222 auto indexAs1dVector = vector::ShapeCastOp::create(
1224 VectorType::get(resultType.getShape().back(), rewriter.
getIndexType(),
1225 resultType.getScalableDims().back()),
1227 transferReadIdxs.push_back(
1228 vector::ExtractOp::create(rewriter, loc, indexAs1dVector, 0));
1232 auto dstRank = resultType.getRank();
1233 auto srcRank = extractOp.getTensor().getType().getRank();
1242 auto transferReadOp = vector::TransferReadOp::create(
1243 rewriter, loc, resultType, extractOp.getTensor(), transferReadIdxs,
1244 std::nullopt, permutationMap, inBounds);
1246 Operation *readOrMaskedReadOp = transferReadOp;
1252 auto readMaskType = VectorType::get(readMaskShape, rewriter.
getI1Type());
1253 auto allTrue = vector::ConstantMaskOp::create(
1255 readOrMaskedReadOp =
1259 LDBG() <<
"Vectorised as scalar broadcast load: " << extractOp;
1261 readOrMaskedReadOp};
1266 srcRank, std::min(dstRank, srcRank), rewriter.
getContext());
1268 int32_t rankDiff = dstRank - srcRank;
1276 while (rankDiff > 0) {
1277 permutationMap = permutationMap.insertResult(
1282 auto transferReadOp = vector::TransferReadOp::create(
1283 rewriter, loc, resultType, extractOp.getTensor(), transferReadIdxs,
1284 std::nullopt, permutationMap, inBounds);
1293 int64_t numReadDims = std::min(dstRank, srcRank);
1295 linalgOp.getNumLoops(), numReadDims, rewriter.
getContext());
1297 state.maskOperation(rewriter, transferReadOp, linalgOp, maskingMap);
1299 LDBG() <<
"Vectorised as contiguous load: " << extractOp;
1312 auto reduceType = dyn_cast<VectorType>(reduceVec.
getType());
1313 auto outputType = dyn_cast<VectorType>(outputVec.
getType());
1317 (outputType && reduceType.getShape() == outputType.getShape()))
1342static VectorizationHookResult
1346 LDBG() <<
"vectorize op " << *op;
1349 if (!customVectorizationHooks.empty()) {
1350 for (
auto &customFunc : customVectorizationHooks) {
1360 if (isa<arith::ConstantOp, func::ConstantOp>(op))
1362 rewriter.
clone(*op)};
1371 auto blockArg = dyn_cast<BlockArgument>(operand);
1372 if (!blockArg || blockArg.getOwner() != linalgOp.getBlock() ||
1373 blockArg.getArgNumber() < linalgOp.getNumDpsInputs())
1377 linalgOp.getRegionOutputArgs(),
1378 blockArg.getArgNumber() - linalgOp.getNumDpsInputs(), reductionOps);
1381 reductionOperands.push_back(std::make_pair(reduceValue, operand));
1383 if (!reductionOperands.empty()) {
1384 assert(reductionOperands.size() == 1);
1386 reduceIfNeeded(rewriter, linalgOp, op, reductionOperands[0].first,
1387 reductionOperands[0].second, bvm);
1394 VectorType firstMaxRankedType;
1396 auto vecOperand = bvm.
lookup(operand);
1397 assert(vecOperand &&
"Vector operand couldn't be found");
1399 auto vecType = dyn_cast<VectorType>(vecOperand.getType());
1400 if (vecType && (!firstMaxRankedType ||
1401 firstMaxRankedType.getRank() < vecType.getRank()))
1402 firstMaxRankedType = vecType;
1408 assert(vecOperand &&
"Vector operand couldn't be found");
1410 if (firstMaxRankedType) {
1411 auto vecType = VectorType::get(firstMaxRankedType.getShape(),
1413 firstMaxRankedType.getScalableDims());
1416 vecOperands.push_back(vecOperand);
1422 resultTypes.push_back(
1424 ? VectorType::get(firstMaxRankedType.getShape(), resultType,
1425 firstMaxRankedType.getScalableDims())
1463 LDBG() <<
"Vectorizing operation as linalg generic/n";
1464 Block *block = linalgOp.getBlock();
1471 bvm.
map(valuesSet.getArrayRef(), valuesSet.getArrayRef());
1473 if (linalgOp.getNumDpsInits() == 0)
1479 for (
OpOperand *opOperand : linalgOp.getOpOperandsMatchingBBargs()) {
1480 BlockArgument bbarg = linalgOp.getMatchingBlockArgument(opOperand);
1481 if (linalgOp.isScalar(opOperand)) {
1482 bvm.
map(bbarg, opOperand->get());
1488 AffineMap indexingMap = linalgOp.getMatchingIndexingMap(opOperand);
1491 VectorType readType;
1493 if (linalgOp.isDpsInput(opOperand)) {
1496 readType = state.getCanonicalVecType(elemType);
1503 state.getCanonicalVecType(elemType, readMap.
compose(indexingMap));
1508 Operation *read = vector::TransferReadOp::create(
1509 rewriter, loc, readType, opOperand->get(),
indices,
1510 std::nullopt, readMap);
1511 read = state.maskOperation(rewriter, read, linalgOp, indexingMap);
1516 if (
auto maskOp = dyn_cast<vector::MaskingOpInterface>(read)) {
1518 cast<vector::TransferReadOp>(maskOp.getMaskableOp())
1524 if (readType.getRank() == 0)
1525 readValue = vector::ExtractOp::create(rewriter, loc, readValue,
1528 LDBG() <<
"New vectorized bbarg(" << bbarg.
getArgNumber()
1529 <<
"): " << readValue;
1530 bvm.
map(bbarg, readValue);
1531 bvm.
map(opOperand->get(), readValue);
1540 hooks.push_back(vectorizeYield);
1547 hooks.push_back(vectorizeIndex);
1554 hooks.push_back(vectorizeExtract);
1561 LDBG() <<
"failed to vectorize: " << op;
1566 state.maskOperation(rewriter,
result.newOp, linalgOp);
1567 LDBG() <<
"New vector op: " << *maybeMaskedOp;
1593 assert(type.getNumScalableDims() < 2 &&
1594 "Collapsing more than 1 scalable dim is not supported ATM");
1600 auto shape = type.getShape();
1601 auto scalableFlags = type.getScalableDims();
1605 unsigned currentDim = 0;
1607 unsigned dim = m.getNumResults();
1610 for (
unsigned d = 0; d < dim; ++d) {
1611 size *=
shape[currentDim + d];
1612 flag |= scalableFlags[currentDim + d];
1614 newShape.push_back(size);
1615 newScalableFlags.push_back(flag);
1619 return VectorType::get(newShape, type.getElementType(), newScalableFlags);
1652vectorizeAsTensorPackOp(RewriterBase &rewriter, linalg::PackOp packOp,
1653 ArrayRef<int64_t> inputVectorSizes,
1654 SmallVectorImpl<Value> &newResults) {
1655 if (!inputVectorSizes.empty()) {
1656 assert(inputVectorSizes.size() == packOp.getDestRank() &&
1657 "Invalid number of input vector sizes!");
1661 OpBuilder::InsertionGuard g(rewriter);
1664 Location loc = packOp.getLoc();
1665 std::optional<Value> padValue = packOp.getPaddingValue()
1666 ? std::optional(packOp.getPaddingValue())
1669 SmallVector<int64_t> destShape =
1670 SmallVector<int64_t>(packOp.getDestType().getShape());
1674 ArrayRef<int64_t> &writeVectorSizes = inputVectorSizes;
1678 bool useInBoundsInsteadOfMasking =
false;
1679 if (writeVectorSizes.empty()) {
1680 if (ShapedType::isDynamicShape(destShape))
1682 "unable to infer vector sizes");
1684 writeVectorSizes = destShape;
1685 useInBoundsInsteadOfMasking =
true;
1694 PackingMetadata packMetadata;
1695 SmallVector<int64_t> preTransposeWriteVecSizses(writeVectorSizes);
1698 auto preTransposeWriteVecType =
1699 VectorType::get(preTransposeWriteVecSizses,
1700 packOp.getResult().getType().getElementType());
1706 preTransposeWriteVecType,
1708 rewriter.
getContext(), packMetadata.reassociations)));
1712 rewriter, loc, packOp.getSource(), readVecType, padValue,
1713 useInBoundsInsteadOfMasking);
1716 auto shapeCastOp = vector::ShapeCastOp::create(
1717 rewriter, loc, preTransposeWriteVecType, maskedRead);
1721 auto transposeOp = vector::TransposeOp::create(
1722 rewriter, loc, shapeCastOp.getResult(), destPermutation);
1726 rewriter, loc, transposeOp.getResult(), packOp.getDest());
1727 newResults.push_back(write->
getResult(0));
1761vectorizeAsTensorUnpackOp(RewriterBase &rewriter, linalg::UnPackOp unpackOp,
1762 ArrayRef<int64_t> inputVectorSizes,
1763 ArrayRef<bool> inputScalableVecDims,
1764 SmallVectorImpl<Value> &newResults) {
1765 if (!inputVectorSizes.empty()) {
1766 assert(inputVectorSizes.size() == unpackOp.getSourceRank() &&
1767 "Invalid number of input vector sizes!");
1768 assert(inputVectorSizes.size() == inputScalableVecDims.size() &&
1769 "Incompatible number of vector sizes and vector scalable flags!");
1773 OpBuilder::InsertionGuard g(rewriter);
1776 ShapedType unpackTensorType = unpackOp.getSourceType();
1778 ArrayRef<int64_t> sourceShape = unpackTensorType.getShape();
1779 bool useInBoundsInsteadOfMasking =
false;
1781 Location loc = unpackOp->getLoc();
1784 SmallVector<int64_t> readVectorSizes(inputVectorSizes);
1785 SmallVector<bool> readScalableVectorFlags(inputScalableVecDims);
1788 if (inputVectorSizes.empty()) {
1789 if (ShapedType::isDynamicShape(sourceShape))
1791 "Unable to infer vector sizes!");
1793 readVectorSizes.assign(sourceShape.begin(), sourceShape.end());
1794 useInBoundsInsteadOfMasking =
true;
1798 VectorType readVecType =
1799 VectorType::get(readVectorSizes, unpackTensorType.getElementType(),
1800 readScalableVectorFlags);
1802 rewriter, loc, unpackOp.getSource(), readVecType, std::nullopt,
1803 useInBoundsInsteadOfMasking);
1806 PackingMetadata packMetadata;
1807 SmallVector<int64_t> lastDimToInsertPosPerm =
1809 vector::TransposeOp transposeOp = vector::TransposeOp::create(
1810 rewriter, loc, readResult, lastDimToInsertPosPerm);
1814 transposeOp.getType(),
1816 rewriter.
getContext(), packMetadata.reassociations)));
1817 vector::ShapeCastOp shapeCastOp = vector::ShapeCastOp::create(
1818 rewriter, loc, collapsedVecType, transposeOp->getResult(0));
1822 rewriter, loc, shapeCastOp.getResult(), unpackOp.getDest(),
1823 {}, useInBoundsInsteadOfMasking);
1825 newResults.push_back(write->
getResult(0));
1833vectorizeAsTensorPadOp(RewriterBase &rewriter, tensor::PadOp padOp,
1834 ArrayRef<int64_t> inputVectorSizes,
1835 SmallVectorImpl<Value> &newResults) {
1836 auto padValue = padOp.getConstantPaddingValue();
1837 Location loc = padOp.getLoc();
1840 OpBuilder::InsertionGuard g(rewriter);
1844 LogicalResult status =
1845 cast<ReifyRankedShapedTypeOpInterface>(padOp.getOperation())
1846 .reifyResultShapes(rewriter, reifiedReturnShapes);
1848 assert(succeeded(status) &&
"failed to reify result shapes");
1849 auto readType = VectorType::get(inputVectorSizes, padValue.getType());
1851 rewriter, loc, padOp.getSource(), readType, padValue,
1855 Value dest = tensor::EmptyOp::create(rewriter, loc, reifiedReturnShapes[0],
1856 padOp.getResultType().getElementType());
1859 newResults.push_back(write->
getResult(0));
1865static LogicalResult reductionPreconditions(LinalgOp op) {
1867 LDBG() <<
"reduction precondition failed: no reduction iterator";
1870 for (OpOperand &opOperand : op.getDpsInitsMutable()) {
1871 AffineMap indexingMap = op.getMatchingIndexingMap(&opOperand);
1877 LDBG() <<
"reduction precondition failed: reduction detection failed";
1885vectorizeDynamicConvOpPrecondition(linalg::LinalgOp conv,
1886 bool flatten1DDepthwiseConv) {
1887 if (flatten1DDepthwiseConv) {
1888 LDBG() <<
"Vectorization of flattened convs with dynamic shapes is not "
1894 LDBG() <<
"Not a 1D depth-wise WC conv, dynamic shapes are not supported";
1900 Value
lhs = conv.getDpsInputOperand(0)->get();
1901 ArrayRef<int64_t> lhsShape = cast<ShapedType>(
lhs.getType()).getShape();
1902 auto shapeWithoutCh = lhsShape.drop_back(1);
1903 if (ShapedType::isDynamicShape(shapeWithoutCh)) {
1904 LDBG() <<
"Dynamically-shaped op vectorization precondition failed: only "
1905 "channel dim can be dynamic";
1913vectorizeDynamicLinalgOpPrecondition(linalg::LinalgOp op,
1914 bool flatten1DDepthwiseConv) {
1916 return vectorizeDynamicConvOpPrecondition(op, flatten1DDepthwiseConv);
1919 return reductionPreconditions(op);
1924 !isa<linalg::GenericOp, linalg::CopyOp, linalg::ContractionOpInterface>(
1928 LDBG() <<
"Dynamically-shaped op meets vectorization pre-conditions";
1938vectorizeUnPackOpPrecondition(linalg::UnPackOp unpackOp,
1939 ArrayRef<int64_t> inputVectorSizes) {
1941 if (!unpackOp.hasPureTensorSemantics())
1946 if (inputVectorSizes.empty() && unpackOp.getDestType().hasStaticShape() &&
1947 unpackOp.getSourceType().hasStaticShape())
1952 if (!inputVectorSizes.empty() &&
1953 (inputVectorSizes.size() != unpackOp.getSourceRank())) {
1954 LDBG() <<
"Incorrect number of input vector sizes";
1960 unpackOp.getSourceType().getShape(), inputVectorSizes))) {
1961 LDBG() <<
"Invalid vector sizes for the read operation";
1969vectorizeInsertSliceOpPrecondition(tensor::InsertSliceOp sliceOp,
1970 ArrayRef<int64_t> inputVectorSizes) {
1973 auto sourceType = source.getType();
1974 if (!VectorType::isValidElementType(sourceType.getElementType()))
1990 bool isOutOfBoundsRead =
1991 !sourceType.hasStaticShape() && inputVectorSizes.empty();
1993 if (!padValue && isOutOfBoundsRead) {
1994 LDBG() <<
"Failed to get a pad value for out-of-bounds read access";
2008vectorizeAsLinalgContraction(RewriterBase &rewriter, VectorizationState &state,
2010 SmallVectorImpl<Value> &newResults) {
2011 Location loc = linalgOp.getLoc();
2012 MLIRContext *ctx = linalgOp.getContext();
2017 if (!isa<ContractionOpInterface>(linalgOp.getOperation()))
2020 OpOperand *outOperand = linalgOp.getDpsInitOperand(0);
2024 LDBG() <<
"Failed to determine contraction combining kind.";
2031 AffineMap lhsMap = linalgOp.getIndexingMapsArray()[0];
2032 AffineMap rhsMap = linalgOp.getIndexingMapsArray()[1];
2034 LDBG() <<
"Contractions with broadcasts are not supported.";
2039 SmallVector<Value> vecOperands;
2040 for (OpOperand &opOperand : linalgOp->getOpOperands()) {
2044 AffineMap indexingMap = linalgOp.getMatchingIndexingMap(&opOperand);
2048 VectorType readType =
2049 state.getCanonicalVecType(elemType, readMap.
compose(indexingMap));
2052 rewriter, loc, opOperand.get(), readType,
2053 arith::getZeroConstant(rewriter, loc, elemType),
2055 vecOperands.push_back(read);
2061 auto castAttr = linalgOp->getAttrOfType<TypeFnAttr>(
"cast");
2062 bool hasUnsignedCast =
2063 castAttr && castAttr.getValue() == TypeFn::cast_unsigned;
2064 auto accType = dyn_cast<VectorType>(vecOperands[2].
getType());
2065 auto accElementType =
2066 accType ? dyn_cast<IntegerType>(accType.getElementType()) :
nullptr;
2067 if (accElementType && accElementType.isSignless()) {
2068 for (Value &operand : MutableArrayRef(vecOperands).take_front(2)) {
2069 auto operandType = cast<VectorType>(operand.
getType());
2070 Type operandElementType = operandType.getElementType();
2071 VectorType castType = operandType.clone(accElementType);
2073 if (isa<FloatType>(operandElementType)) {
2076 ? arith::FPToUIOp::create(rewriter, loc, castType, operand)
2078 : arith::FPToSIOp::create(rewriter, loc, castType, operand)
2083 auto operandIntegerType = dyn_cast<IntegerType>(operandElementType);
2084 if (!operandIntegerType || !operandIntegerType.isSignless())
2086 if (operandIntegerType.getWidth() >= accElementType.getWidth())
2088 if (!hasUnsignedCast)
2093 operand = arith::ExtUIOp::create(rewriter, loc, castType, operand);
2098 SmallVector<Attribute> iterAttrs;
2099 auto iterators = linalgOp.getIteratorTypesArray();
2100 for (utils::IteratorType iter : iterators) {
2101 auto vecIter = iter == utils::IteratorType::parallel
2102 ? vector::IteratorType::parallel
2103 : vector::IteratorType::reduction;
2104 iterAttrs.push_back(vector::IteratorTypeAttr::get(ctx, vecIter));
2108 Operation *contractOp = vector::ContractionOp::create(
2109 rewriter, loc, vecOperands[0],
2110 vecOperands[1], vecOperands[2],
2111 linalgOp.getIndexingMaps(), rewriter.
getArrayAttr(iterAttrs), *maybeKind);
2112 contractOp = state.maskOperation(rewriter, contractOp, linalgOp);
2116 rewriter, loc, contractOp->
getResult(0), outOperand->
get());
2120 newResults.push_back(write->
getResult(0));
2126enum class ConvOperationKind { Conv, Pool };
2129static bool isCastOfBlockArgument(Operation *op) {
2144static std::optional<ConvOperationKind>
2145getConvOperationKind(Operation *reduceOp) {
2146 int numBlockArguments =
2147 llvm::count_if(reduceOp->
getOperands(), llvm::IsaPred<BlockArgument>);
2149 switch (numBlockArguments) {
2155 auto feedValIt = llvm::find_if_not(reduceOp->
getOperands(),
2156 llvm::IsaPred<BlockArgument>);
2158 "Expected a non-block argument operand");
2159 Operation *feedOp = (*feedValIt).getDefiningOp();
2160 if (isCastOfBlockArgument(feedOp)) {
2161 return ConvOperationKind::Pool;
2164 if (!((isa<arith::MulIOp, arith::MulFOp>(feedOp) ||
2165 (isa<arith::AndIOp>(feedOp) &&
2168 if (isa<BlockArgument>(v))
2170 if (Operation *op = v.getDefiningOp())
2171 return isCastOfBlockArgument(op);
2174 return std::nullopt;
2177 return ConvOperationKind::Conv;
2181 return ConvOperationKind::Pool;
2183 return std::nullopt;
2187static bool isSupportedPoolKind(vector::CombiningKind kind) {
2189 case vector::CombiningKind::ADD:
2190 case vector::CombiningKind::MAXNUMF:
2191 case vector::CombiningKind::MAXIMUMF:
2192 case vector::CombiningKind::MAXSI:
2193 case vector::CombiningKind::MAXUI:
2194 case vector::CombiningKind::MINNUMF:
2195 case vector::CombiningKind::MINIMUMF:
2196 case vector::CombiningKind::MINSI:
2197 case vector::CombiningKind::MINUI:
2204static LogicalResult vectorizeConvOpPrecondition(linalg::LinalgOp convOp) {
2205 auto getOperandType = [&](
auto operand) {
2206 return dyn_cast<ShapedType>((operand->get()).getType());
2208 ShapedType lhsShapedType = getOperandType(convOp.getDpsInputOperand(0));
2209 ShapedType rhsShapedType = getOperandType(convOp.getDpsInputOperand(1));
2210 ShapedType resShapedType = getOperandType(convOp.getDpsInitOperand(0));
2214 if ((lhsShapedType.getRank() != 3 || resShapedType.getRank() != 3) &&
2215 (lhsShapedType.getRank() != 1 || resShapedType.getRank() != 1))
2222 auto maybeOper = getConvOperationKind(reduceOp);
2223 if (!maybeOper.has_value())
2230 if (!maybeKind || ((*maybeKind != vector::CombiningKind::ADD &&
2231 *maybeKind != vector::CombiningKind::OR) &&
2232 (*maybeOper != ConvOperationKind::Pool ||
2233 !isSupportedPoolKind(*maybeKind)))) {
2237 auto rhsRank = rhsShapedType.getRank();
2238 if (*maybeOper == ConvOperationKind::Pool) {
2242 if (rhsRank != 1 && rhsRank != 2 && rhsRank != 3)
2249static LogicalResult vectorizeLinalgOpPrecondition(
2250 LinalgOp linalgOp, ArrayRef<int64_t> inputVectorSizes,
2251 bool vectorizeNDExtract,
bool flatten1DDepthwiseConv) {
2253 if (llvm::any_of(linalgOp->getOpOperands(), [&](OpOperand &operand) {
2254 return llvm::is_contained(linalgOp.getShape(&operand), 0);
2258 if (!inputVectorSizes.empty() &&
2263 if (linalgOp.hasDynamicShape() &&
failed(vectorizeDynamicLinalgOpPrecondition(
2264 linalgOp, flatten1DDepthwiseConv))) {
2265 LDBG() <<
"Dynamically-shaped op failed vectorization pre-conditions";
2269 SmallVector<CustomVectorizationPrecondition> customPreconditions;
2275 for (Operation &innerOp : linalgOp->getRegion(0).front()) {
2278 customPreconditions,
2281 customPrecondition(&innerOp, vectorizeNDExtract));
2285 if (!llvm::all_of(innerOp.getOperandTypes(),
2286 VectorType::isValidElementType)) {
2289 if (!llvm::all_of(innerOp.getResultTypes(),
2290 VectorType::isValidElementType)) {
2299 return vectorizeConvOpPrecondition(linalgOp);
2305 LDBG() <<
"precondition failed: not projected permutations";
2308 if (
failed(reductionPreconditions(linalgOp))) {
2309 LDBG() <<
"precondition failed: reduction preconditions";
2316vectorizePackOpPrecondition(linalg::PackOp packOp,
2317 ArrayRef<int64_t> inputVectorSizes) {
2319 if (!packOp.hasPureTensorSemantics())
2322 auto padValue = packOp.getPaddingValue();
2326 LDBG() <<
"pad value is not constant: " << packOp;
2330 ArrayRef<int64_t> resultTensorShape = packOp.getDestType().getShape();
2331 bool satisfyEmptyCond =
true;
2332 if (inputVectorSizes.empty()) {
2333 if (!packOp.getDestType().hasStaticShape() ||
2334 !packOp.getSourceType().hasStaticShape())
2335 satisfyEmptyCond =
false;
2338 if (!satisfyEmptyCond &&
2340 resultTensorShape.take_front(packOp.getSourceRank()),
2344 if (llvm::any_of(packOp.getInnerTiles(), [](OpFoldResult v) {
2345 return !getConstantIntValue(v).has_value();
2347 LDBG() <<
"inner_tiles must be constant: " << packOp;
2355vectorizePadOpPrecondition(tensor::PadOp padOp,
2356 ArrayRef<int64_t> inputVectorSizes) {
2357 auto padValue = padOp.getConstantPaddingValue();
2359 LDBG() <<
"pad value is not constant: " << padOp;
2363 ArrayRef<int64_t> resultTensorShape = padOp.getResultType().getShape();
2379 if (llvm::any_of(llvm::enumerate(padOp.getMixedLowPad()),
2380 [&](
const auto &en) {
2381 OpFoldResult padValue = en.value();
2382 unsigned pos = en.index();
2383 std::optional<int64_t> pad = getConstantIntValue(padValue);
2384 return (!pad.has_value() || pad.value() != 0) &&
2385 resultTensorShape[pos] != 1;
2387 LDBG() <<
"low pad must all be zero for all non unit dims: " << padOp;
2401vectorizeScalableVectorPrecondition(Operation *op,
2402 ArrayRef<int64_t> inputVectorSizes,
2403 ArrayRef<bool> inputScalableVecDims) {
2404 assert(inputVectorSizes.size() == inputScalableVecDims.size() &&
2405 "Number of input vector sizes and scalable dims doesn't match");
2407 size_t numOfScalableDims =
2408 llvm::count_if(inputScalableVecDims, [](
bool flag) {
return flag; });
2410 if (numOfScalableDims == 0)
2413 auto linalgOp = dyn_cast<LinalgOp>(op);
2418 return success(isa<linalg::UnPackOp>(op));
2422 if (numOfScalableDims > 2)
2442 bool seenNonUnitParallel =
false;
2443 auto iterators = linalgOp.getIteratorTypesArray();
2444 SmallVector<bool> scalableFlags(inputScalableVecDims);
2445 int64_t idx = scalableFlags.size() - 1;
2446 while (!scalableFlags[idx]) {
2447 bool isNonUnitDim = (inputVectorSizes[idx] != 1);
2448 seenNonUnitParallel |=
2449 (iterators[idx] == utils::IteratorType::parallel && isNonUnitDim);
2451 iterators.pop_back();
2452 scalableFlags.pop_back();
2457 switch (iterators.back()) {
2458 case utils::IteratorType::reduction: {
2460 if (iterators.size() != inputVectorSizes.size()) {
2461 LDBG() <<
"Non-trailing reduction dim requested for scalable "
2465 if (isa<linalg::MatmulOp>(op)) {
2467 <<
"Scalable vectorization of the reduction dim in Matmul-like ops "
2473 case utils::IteratorType::parallel: {
2475 if (seenNonUnitParallel) {
2476 LDBG() <<
"Inner parallel dim not requested for scalable "
2488 if (numOfScalableDims == 2) {
2492 if (iterators.back() == utils::IteratorType::reduction) {
2493 LDBG() <<
"Higher dim than the trailing reduction dim requested for "
2498 scalableFlags.pop_back();
2499 iterators.pop_back();
2501 if (!scalableFlags.back() ||
2502 (iterators.back() != utils::IteratorType::parallel))
2510 isa<linalg::BatchMatmulOp>(op) ||
2512 isa<linalg::MatvecOp>(op) || isa<linalg::Mmt4DOp>(op) ||
2517 Operation *op, ArrayRef<int64_t> inputVectorSizes,
2518 ArrayRef<bool> inputScalableVecDims,
bool vectorizeNDExtract,
2519 bool flatten1DDepthwiseConv) {
2524 if (
failed(vectorizeScalableVectorPrecondition(op, inputVectorSizes,
2525 inputScalableVecDims)))
2529 .Case([&](linalg::LinalgOp linalgOp) {
2530 return vectorizeLinalgOpPrecondition(linalgOp, inputVectorSizes,
2532 flatten1DDepthwiseConv);
2534 .Case([&](tensor::PadOp padOp) {
2535 return vectorizePadOpPrecondition(padOp, inputVectorSizes);
2537 .Case([&](linalg::PackOp packOp) {
2538 return vectorizePackOpPrecondition(packOp, inputVectorSizes);
2540 .Case([&](linalg::UnPackOp unpackOp) {
2541 return vectorizeUnPackOpPrecondition(unpackOp, inputVectorSizes);
2543 .Case([&](tensor::InsertSliceOp sliceOp) {
2544 return vectorizeInsertSliceOpPrecondition(sliceOp, inputVectorSizes);
2546 .Default(failure());
2550static void convertAffineApply(RewriterBase &rewriter, LinalgOp linalgOp) {
2551 OpBuilder::InsertionGuard g(rewriter);
2552 auto toReplace = linalgOp.getBlock()->getOps<affine::AffineApplyOp>();
2554 for (
auto op : make_early_inc_range(toReplace)) {
2556 auto expanded = affine::expandAffineExpr(
2558 op.
getOperands().take_front(op.getAffineMap().getNumDims()),
2559 op.
getOperands().take_back(op.getAffineMap().getNumSymbols()));
2565 return isa<linalg::LinalgOp, tensor::PadOp, linalg::PackOp, linalg::UnPackOp,
2566 tensor::InsertSliceOp>(op);
2570 RewriterBase &rewriter, Operation *op, ArrayRef<int64_t> inputVectorSizes,
2571 ArrayRef<bool> inputScalableVecDims,
bool vectorizeNDExtract,
2572 bool flatten1DDepthwiseConv,
bool assumeDynamicDimsMatchVecSizes,
2573 bool createNamedContraction) {
2574 LDBG() <<
"Attempting to vectorize: " << *op;
2575 LDBG() <<
"Input vector sizes: " << llvm::interleaved(inputVectorSizes);
2576 LDBG() <<
"Input scalable vector dims: "
2577 << llvm::interleaved(inputScalableVecDims);
2581 flatten1DDepthwiseConv))) {
2582 LDBG() <<
"Vectorization pre-conditions failed";
2587 VectorizationState state(rewriter);
2588 if (
auto linalgOp = dyn_cast<linalg::LinalgOp>(op)) {
2589 if (
failed(state.initState(rewriter, linalgOp, inputVectorSizes,
2590 inputScalableVecDims,
2591 assumeDynamicDimsMatchVecSizes))) {
2592 LDBG() <<
"Vectorization state couldn't be initialized";
2597 SmallVector<Value> results;
2598 auto vectorizeResult =
2600 .Case([&](linalg::LinalgOp linalgOp) {
2604 rewriter, linalgOp, inputVectorSizes, inputScalableVecDims,
2605 flatten1DDepthwiseConv);
2606 if (succeeded(convOr)) {
2607 llvm::append_range(results, (*convOr)->getResults());
2611 LDBG() <<
"Unsupported convolution can't be vectorized.";
2615 if (createNamedContraction &&
2616 isa<ContractionOpInterface>(linalgOp.getOperation()))
2617 return vectorizeAsLinalgContraction(rewriter, state, linalgOp,
2621 <<
"Vectorize generic by broadcasting to the canonical vector "
2625 convertAffineApply(rewriter, linalgOp);
2634 .Case([&](tensor::PadOp padOp) {
2635 return vectorizeAsTensorPadOp(rewriter, padOp, inputVectorSizes,
2638 .Case([&](linalg::PackOp packOp) {
2639 return vectorizeAsTensorPackOp(rewriter, packOp, inputVectorSizes,
2642 .Case([&](linalg::UnPackOp unpackOp) {
2643 return vectorizeAsTensorUnpackOp(rewriter, unpackOp,
2645 inputScalableVecDims, results);
2647 .Case([&](tensor::InsertSliceOp sliceOp) {
2651 .Default(failure());
2653 if (
failed(vectorizeResult)) {
2654 LDBG() <<
"Vectorization failed";
2658 return VectorizationResult{results};
2662 memref::CopyOp copyOp) {
2663 auto srcType = cast<MemRefType>(copyOp.getSource().getType());
2664 auto dstType = cast<MemRefType>(copyOp.getTarget().getType());
2665 if (!srcType.hasStaticShape() || !dstType.hasStaticShape())
2670 if (!VectorType::isValidElementType(srcElementType) ||
2671 !VectorType::isValidElementType(dstElementType))
2674 auto readType = VectorType::get(srcType.getShape(), srcElementType);
2675 auto writeType = VectorType::get(dstType.getShape(), dstElementType);
2677 Location loc = copyOp->getLoc();
2679 SmallVector<Value>
indices(srcType.getRank(), zero);
2681 Value
readValue = vector::TransferReadOp::create(
2682 rewriter, loc, readType, copyOp.getSource(),
indices,
2685 if (cast<VectorType>(
readValue.getType()).getRank() == 0) {
2686 readValue = vector::ExtractOp::create(rewriter, loc, readValue,
2687 ArrayRef<int64_t>());
2689 vector::BroadcastOp::create(rewriter, loc, writeType, readValue);
2691 Operation *writeValue = vector::TransferWriteOp::create(
2692 rewriter, loc, readValue, copyOp.getTarget(),
indices,
2703template <
typename OpTy>
2704struct VectorizePadOpUserPattern :
public OpRewritePattern<tensor::PadOp> {
2705 using OpRewritePattern<tensor::PadOp>::OpRewritePattern;
2707 LogicalResult matchAndRewrite(tensor::PadOp padOp,
2708 PatternRewriter &rewriter)
const final {
2709 bool changed =
false;
2711 for (
auto *user : llvm::to_vector<4>(padOp->getUsers()))
2712 if (
auto op = dyn_cast<OpTy>(user))
2713 changed |= rewriteUser(rewriter, padOp, op).succeeded();
2718 virtual LogicalResult rewriteUser(PatternRewriter &rewriter,
2719 tensor::PadOp padOp, OpTy op)
const = 0;
2741struct PadOpVectorizationWithTransferReadPattern
2742 :
public VectorizePadOpUserPattern<vector::TransferReadOp> {
2743 using VectorizePadOpUserPattern<
2744 vector::TransferReadOp>::VectorizePadOpUserPattern;
2746 LogicalResult rewriteUser(PatternRewriter &rewriter, tensor::PadOp padOp,
2747 vector::TransferReadOp xferOp)
const override {
2749 if (!padOp.hasZeroLowPad())
2752 auto padValue = padOp.getConstantPaddingValue();
2756 if (xferOp.hasOutOfBoundsDim() || xferOp.getMask())
2760 SmallVector<bool> inBounds(xferOp.getVectorType().getRank(),
false);
2761 xferOp->setInherentAttr(xferOp.getInBoundsAttrName(),
2763 xferOp.getBaseMutable().assign(padOp.getSource());
2764 xferOp.getPaddingMutable().assign(padValue);
2803struct PadOpVectorizationWithTransferWritePattern
2804 :
public VectorizePadOpUserPattern<vector::TransferWriteOp> {
2805 using VectorizePadOpUserPattern<
2806 vector::TransferWriteOp>::VectorizePadOpUserPattern;
2808 LogicalResult rewriteUser(PatternRewriter &rewriter, tensor::PadOp padOp,
2809 vector::TransferWriteOp xferOp)
const override {
2811 if (xferOp.getTransferRank() == 0)
2815 if (!padOp.hasZeroLowPad())
2818 auto padValue = padOp.getConstantPaddingValue();
2822 if (!xferOp->hasOneUse())
2824 auto trimPadding = dyn_cast<tensor::ExtractSliceOp>(*xferOp->user_begin());
2828 if (!trimPadding.hasZeroOffset())
2831 if (!hasSameTensorSize(padOp.getSource(), trimPadding))
2837 SmallVector<bool> inBounds(xferOp.getVectorType().getRank(),
false);
2839 xferOp, padOp.getSource().
getType(), xferOp.getVector(),
2840 padOp.getSource(), xferOp.getIndices(), xferOp.getPermutationMapAttr(),
2842 rewriter.
replaceOp(trimPadding, newXferOp->getResult(0));
2857 bool hasSameTensorSize(Value beforePadding,
2858 tensor::ExtractSliceOp afterTrimming)
const {
2861 if (
auto castOp = beforePadding.
getDefiningOp<tensor::CastOp>())
2862 if (hasSameTensorSize(castOp.getSource(), afterTrimming))
2865 auto t1 = dyn_cast<RankedTensorType>(beforePadding.
getType());
2866 auto t2 = dyn_cast<RankedTensorType>(afterTrimming.getType());
2871 if (t1.getRank() != t2.getRank())
2876 for (
unsigned i = 0; i < t1.getRank(); ++i) {
2877 if (t1.isDynamicDim(i) != t2.isDynamicDim(i))
2879 if (!t1.isDynamicDim(i) && t1.getDimSize(i) != t2.getDimSize(i))
2884 if (t1.getNumDynamicDims() == 0)
2892 auto beforeSlice = beforePadding.
getDefiningOp<tensor::ExtractSliceOp>();
2896 assert(
static_cast<size_t>(t1.getRank()) ==
2897 beforeSlice.getMixedSizes().size());
2898 assert(
static_cast<size_t>(t2.getRank()) ==
2899 afterTrimming.getMixedSizes().size());
2901 for (
unsigned i = 0; i < t1.getRank(); ++i) {
2903 if (!t1.isDynamicDim(i))
2905 auto size1 = beforeSlice.getMixedSizes()[i];
2906 auto size2 = afterTrimming.getMixedSizes()[i];
2913 auto v1 = llvm::dyn_cast_if_present<Value>(size1);
2914 auto v2 = llvm::dyn_cast_if_present<Value>(size2);
2920 auto minOp1 = v1.getDefiningOp<affine::AffineMinOp>();
2921 auto minOp2 = v2.getDefiningOp<affine::AffineMinOp>();
2922 if (minOp1 && minOp2 && minOp1.getAffineMap() == minOp2.getAffineMap() &&
2923 minOp1.getOperands() == minOp2.getOperands())
2949 if (
auto bcast = llvm::dyn_cast<vector::BroadcastOp>(op)) {
2950 auto source = bcast.getSource();
2951 if (llvm::dyn_cast<VectorType>(source.getType()))
2959 if (
auto fill = llvm::dyn_cast<linalg::FillOp>(op)) {
2960 return fill.getInputs()[0];
2965 if (
auto generate = llvm::dyn_cast<tensor::GenerateOp>(op)) {
2972 if (
auto xferWrite = llvm::dyn_cast<vector::TransferWriteOp>(op))
2980 if (
auto slice = llvm::dyn_cast<tensor::InsertSliceOp>(op))
2988 ArrayRef<int64_t> inputVectorSizes,
2989 SmallVectorImpl<Value> &newResults) {
2991 OpBuilder::InsertionGuard g(rewriter);
2995 auto sourceType = source.getType();
2996 auto resultType = sliceOp.getResultType();
3001 auto elemType = sourceType.getElementType();
3002 padValue = arith::ConstantOp::create(rewriter, sliceOp.getLoc(), elemType,
3009 llvm::SmallBitVector droppedDims = sliceOp.getDroppedDims();
3010 SmallVector<int64_t> resultDimsForSourceDims;
3011 resultDimsForSourceDims.reserve(sourceType.getRank());
3012 for (int64_t resultDim = 0, end = resultType.getRank(); resultDim < end;
3014 if (!droppedDims[resultDim])
3015 resultDimsForSourceDims.push_back(resultDim);
3016 assert(resultDimsForSourceDims.size() ==
3017 static_cast<size_t>(sourceType.getRank()) &&
3018 "expected one non-dropped result dim per source dim");
3020 SmallVector<int64_t> vecShape;
3021 for (int64_t i = 0, end = sourceType.getRank(); i < end; ++i) {
3022 if (!inputVectorSizes.empty()) {
3023 vecShape.push_back(inputVectorSizes[i]);
3024 }
else if (!sourceType.isDynamicDim(i)) {
3025 vecShape.push_back(sourceType.getDimSize(i));
3026 }
else if (!resultType.isDynamicDim(resultDimsForSourceDims[i])) {
3030 vecShape.push_back(resultType.getDimSize(resultDimsForSourceDims[i]));
3037 auto vecType = VectorType::get(vecShape, sourceType.getElementType());
3040 auto loc = sliceOp.getLoc();
3043 SmallVector<Value> readIndices(
3046 rewriter, loc, source, vecType, padValue,
3047 inputVectorSizes.empty());
3054 writeIndices, inputVectorSizes.empty());
3057 newResults.push_back(write->
getResult(0));
3085struct PadOpVectorizationWithInsertSlicePattern
3086 :
public VectorizePadOpUserPattern<tensor::InsertSliceOp> {
3087 using VectorizePadOpUserPattern<
3088 tensor::InsertSliceOp>::VectorizePadOpUserPattern;
3090 LogicalResult rewriteUser(PatternRewriter &rewriter, tensor::PadOp padOp,
3091 tensor::InsertSliceOp insertOp)
const override {
3093 if (!padOp.hasZeroLowPad())
3096 if (!insertOp.hasUnitStride())
3099 auto padValue = padOp.getConstantPaddingValue();
3103 if (!cast<ShapedType>(padOp.getResult().getType()).hasStaticShape())
3106 if (insertOp.getDest() == padOp.getResult())
3109 auto vecType = VectorType::get(padOp.getType().getShape(),
3110 padOp.getType().getElementType());
3111 unsigned vecRank = vecType.getRank();
3112 unsigned tensorRank = insertOp.getType().getRank();
3116 SmallVector<int64_t> expectedSizes(tensorRank - vecRank, 1);
3117 expectedSizes.append(vecType.getShape().begin(), vecType.getShape().end());
3119 llvm::zip(insertOp.getMixedSizes(), expectedSizes), [](
auto it) {
3120 return getConstantIntValue(std::get<0>(it)) == std::get<1>(it);
3130 SmallVector<Value> readIndices(
3132 auto read = vector::TransferReadOp::create(rewriter, padOp.getLoc(),
3133 vecType, padOp.getSource(),
3134 readIndices, padValue);
3140 rewriter, padOp.getLoc(), insertOp.getMixedOffsets());
3141 SmallVector<bool> inBounds(vecRank,
true);
3143 insertOp, read, insertOp.getDest(), writeIndices,
3144 ArrayRef<bool>{inBounds});
3151 RewritePatternSet &patterns, PatternBenefit baseBenefit) {
3152 patterns.
add<PadOpVectorizationWithTransferReadPattern,
3153 PadOpVectorizationWithTransferWritePattern,
3154 PadOpVectorizationWithInsertSlicePattern>(
3165static bool mayExistInterleavedUses(Operation *firstOp, Operation *secondOp,
3169 LDBG() <<
"interleavedUses precondition failed, firstOp: " << *firstOp
3170 <<
", second op: " << *secondOp;
3173 for (
auto v : values) {
3174 for (
auto &u : v.getUses()) {
3175 Operation *owner = u.getOwner();
3176 if (owner == firstOp || owner == secondOp)
3182 LDBG() <<
" found interleaved op " << *owner <<
", firstOp: " << *firstOp
3183 <<
", second op: " << *secondOp;
3192static memref::SubViewOp getSubViewUseIfUnique(Value v) {
3193 memref::SubViewOp subViewOp;
3195 if (
auto newSubViewOp = dyn_cast<memref::SubViewOp>(u.getOwner())) {
3197 return memref::SubViewOp();
3198 subViewOp = newSubViewOp;
3207 vector::TransferReadOp xferOp, PatternRewriter &rewriter)
const {
3210 if (xferOp.getMask())
3214 Value viewOrAlloc = xferOp.getBase();
3220 memref::SubViewOp subViewOp = getSubViewUseIfUnique(viewOrAlloc);
3223 Value subView = subViewOp.getResult();
3226 memref::CopyOp copyOp;
3227 for (
auto &u : subView.
getUses()) {
3228 if (
auto newCopyOp = dyn_cast<memref::CopyOp>(u.getOwner())) {
3229 assert(isa<MemRefType>(newCopyOp.getTarget().getType()));
3230 if (newCopyOp.getTarget() != subView)
3232 if (mayExistInterleavedUses(newCopyOp, xferOp, {viewOrAlloc, subView}))
3244 for (
auto &u : viewOrAlloc.
getUses()) {
3245 if (
auto newFillOp = dyn_cast<FillOp>(u.getOwner())) {
3246 assert(isa<MemRefType>(newFillOp.output().getType()));
3247 if (newFillOp.output() != viewOrAlloc)
3249 if (mayExistInterleavedUses(newFillOp, copyOp, {viewOrAlloc, subView}))
3251 maybeFillOp = newFillOp;
3256 if (maybeFillOp && xferOp.getPadding() != maybeFillOp.value())
3258 "padding value does not match fill");
3261 Value in = copyOp.getSource();
3267 auto vectorType = xferOp.getVectorType();
3268 Value res = vector::TransferReadOp::create(
3269 rewriter, xferOp.getLoc(), vectorType, in, xferOp.getIndices(),
3270 xferOp.getPermutationMapAttr(), xferOp.getPadding(), xferOp.getMask(),
3272 SmallVector<bool>(vectorType.getRank(),
false)));
3275 rewriter.
eraseOp(maybeFillOp);
3285 vector::TransferWriteOp xferOp, PatternRewriter &rewriter)
const {
3287 if (xferOp.getMask())
3291 Value viewOrAlloc = xferOp.getBase();
3297 memref::SubViewOp subViewOp = getSubViewUseIfUnique(viewOrAlloc);
3300 Value subView = subViewOp.getResult();
3303 memref::CopyOp copyOp;
3304 for (
auto &u : subViewOp.getResult().getUses()) {
3305 if (
auto newCopyOp = dyn_cast<memref::CopyOp>(u.getOwner())) {
3306 if (newCopyOp.getSource() != subView)
3308 if (mayExistInterleavedUses(xferOp, newCopyOp, {viewOrAlloc, subView}))
3318 assert(isa<MemRefType>(copyOp.getTarget().getType()));
3319 Value out = copyOp.getTarget();
3326 auto vector = xferOp.getVector();
3327 vector::TransferWriteOp::create(
3328 rewriter, xferOp.getLoc(), vector, out, xferOp.getIndices(),
3329 xferOp.getPermutationMapAttr(), xferOp.getMask(),
3331 dyn_cast<VectorType>(vector.getType()).getRank(),
false)));
3344static void bindShapeDims(ShapedType shapedType) {}
3346template <
int N,
typename IntTy,
typename... IntTy2>
3347static void bindShapeDims(ShapedType shapedType, IntTy &val, IntTy2 &...vals) {
3348 val = shapedType.getShape()[N];
3349 bindShapeDims<N + 1, IntTy2 &...>(shapedType, vals...);
3353template <
typename... IntTy>
3354static void bindShapeDims(ShapedType shapedType, IntTy &...vals) {
3355 bindShapeDims<0>(shapedType, vals...);
3360static std::optional<DilationsAndStrides> match1DConvPoolOp(LinalgOp op) {
3361#define MATCH_1D_CONV_POOL_OP(ConvOpTy) \
3362 if (auto convParams = matchConvolutionOpOfType<ConvOpTy>(op)) \
3384#undef MATCH_1D_CONV_POOL_OP
3386 return std::nullopt;
3424struct Conv1DGenerator
3425 :
public StructuredGenerator<LinalgOp, utils::IteratorType> {
3428 static FailureOr<Conv1DGenerator> create(RewriterBase &rewriter,
3429 LinalgOp linalgOp) {
3432 std::optional<DilationsAndStrides> convParams = match1DConvPoolOp(linalgOp);
3436 int strideW =
static_cast<int>(convParams->strides.front());
3437 int dilationW =
static_cast<int>(convParams->dilations.front());
3438 return Conv1DGenerator(rewriter, linalgOp, strideW, dilationW);
3442 Conv1DGenerator(RewriterBase &rewriter, LinalgOp linalgOp,
int strideW,
3444 : StructuredGenerator<LinalgOp, utils::IteratorType>(rewriter, linalgOp),
3445 strideW(strideW), dilationW(dilationW) {
3447 lhsShaped = linalgOp.getDpsInputOperand(0)->
get();
3448 rhsShaped = linalgOp.getDpsInputOperand(1)->
get();
3449 resShaped = linalgOp.getDpsInitOperand(0)->
get();
3450 lhsShapedType = dyn_cast<ShapedType>(lhsShaped.getType());
3451 rhsShapedType = dyn_cast<ShapedType>(rhsShaped.getType());
3452 resShapedType = dyn_cast<ShapedType>(resShaped.getType());
3457 setConvOperationKind(reduceOp);
3460 reductionKind = maybeKind.value();
3483 int64_t nSize, wSize, cSize, kwSize, fSize;
3484 SmallVector<int64_t, 3> lhsShape, rhsShape, resShape;
3486 switch (conv1DOpOrder) {
3489 nSize = fSize = cSize = 0;
3491 bindShapeDims(resShapedType, wSize);
3493 bindShapeDims(rhsShapedType, kwSize);
3496 (wSize + kwSize - 1)};
3497 rhsShape = {kwSize};
3502 bindShapeDims(resShapedType, nSize, wSize, fSize);
3504 case ConvOperationKind::Conv:
3506 bindShapeDims(rhsShapedType, kwSize, cSize);
3508 case ConvOperationKind::Pool:
3510 bindShapeDims(rhsShapedType, kwSize);
3518 ((wSize - 1) * strideW + 1) + ((kwSize - 1) * dilationW + 1) -
3522 case ConvOperationKind::Conv:
3523 rhsShape = {kwSize, cSize, fSize};
3525 case ConvOperationKind::Pool:
3526 rhsShape = {kwSize};
3529 resShape = {nSize, wSize, fSize};
3533 bindShapeDims(resShapedType, nSize, fSize, wSize);
3535 case ConvOperationKind::Conv:
3537 bindShapeDims(rhsShapedType, fSize, cSize, kwSize);
3539 case ConvOperationKind::Pool:
3541 bindShapeDims(rhsShapedType, kwSize);
3545 lhsShape = {nSize, cSize,
3549 ((wSize - 1) * strideW + 1) + ((kwSize - 1) * dilationW + 1) -
3552 case ConvOperationKind::Conv:
3553 rhsShape = {fSize, cSize, kwSize};
3555 case ConvOperationKind::Pool:
3556 rhsShape = {kwSize};
3559 resShape = {nSize, fSize, wSize};
3563 vector::TransferWriteOp write;
3569 int64_t wSizeStep = strideW == 1 ? wSize : 1;
3571 Type lhsEltType = lhsShapedType.getElementType();
3572 Type rhsEltType = rhsShapedType.getElementType();
3573 Type resEltType = resShapedType.getElementType();
3574 auto lhsType = VectorType::get(lhsShape, lhsEltType);
3575 auto rhsType = VectorType::get(rhsShape, rhsEltType);
3576 auto resType = VectorType::get(resShape, resEltType);
3578 SmallVector<Value> lhsPadding(lhsShape.size(), zero);
3579 SmallVector<Value> rhsPadding(rhsShape.size(), zero);
3580 SmallVector<Value> resPadding(resShape.size(), zero);
3583 Value
lhs = vector::TransferReadOp::create(
3584 rewriter, loc, lhsType, lhsShaped, lhsPadding,
3585 arith::getZeroConstant(rewriter, loc, lhsEltType));
3587 Value
rhs =
nullptr;
3588 if (oper == ConvOperationKind::Conv)
3589 rhs = vector::TransferReadOp::create(
3590 rewriter, loc, rhsType, rhsShaped, rhsPadding,
3591 arith::getZeroConstant(rewriter, loc, rhsEltType));
3592 Value res = vector::TransferReadOp::create(
3593 rewriter, loc, resType, resShaped, resPadding,
3594 arith::getZeroConstant(rewriter, loc, resEltType));
3599 switch (conv1DOpOrder) {
3607 static constexpr std::array<int64_t, 3> permLhs = {0, 2, 1};
3608 lhs = vector::TransposeOp::create(rewriter, loc,
lhs, permLhs);
3610 static constexpr std::array<int64_t, 3> permRhs = {2, 1, 0};
3613 if (oper == ConvOperationKind::Conv)
3614 rhs = vector::TransposeOp::create(rewriter, loc,
rhs, permRhs);
3616 static constexpr std::array<int64_t, 3> permRes = {0, 2, 1};
3617 res = vector::TransposeOp::create(rewriter, loc, res, permRes);
3626 SmallVector<Value> lhsVals, rhsVals, resVals;
3628 kwSize, strideW, dilationW, wSizeStep,
3631 if (oper == ConvOperationKind::Conv)
3634 wSizeStep, isSingleChanneled);
3636 auto linearIndex = [&](int64_t kw, int64_t w) {
3637 return kw * (wSize / wSizeStep) + w;
3643 for (int64_t kw = 0; kw < kwSize; ++kw) {
3644 for (int64_t w = 0; w < wSize; w += wSizeStep) {
3646 case ConvOperationKind::Conv:
3647 if (isSingleChanneled) {
3648 resVals[w] = conv1dSliceAsOuterProduct(rewriter, loc,
3649 lhsVals[linearIndex(kw, w)],
3650 rhsVals[kw], resVals[w]);
3652 resVals[w] = conv1dSliceAsContraction(rewriter, loc,
3653 lhsVals[linearIndex(kw, w)],
3654 rhsVals[kw], resVals[w]);
3657 case ConvOperationKind::Pool:
3658 resVals[w] = pool1dSlice(rewriter, loc, lhsVals[linearIndex(kw, w)],
3674 switch (conv1DOpOrder) {
3681 static constexpr std::array<int64_t, 3> perm = {0, 2, 1};
3682 res = vector::TransposeOp::create(rewriter, loc, res, perm);
3687 return vector::TransferWriteOp::create(rewriter, loc, res, resShaped,
3693 Value
promote(RewriterBase &rewriter, Location loc, Value val, Type ty,
3694 Operation *castOp) {
3699 assert(castOp &&
"expected a payload cast for promoted operand");
3703 if (
auto shapedType = dyn_cast<ShapedType>(val.
getType()))
3704 dstType = shapedType.cloneWith(std::nullopt, dstElementType);
3706 dstType = dstElementType;
3715 Value conv1dSliceAsContraction(RewriterBase &rewriter, Location loc,
3716 Value
lhs, Value
rhs, Value res) {
3717 vector::IteratorType par = vector::IteratorType::parallel;
3718 vector::IteratorType red = vector::IteratorType::reduction;
3719 AffineExpr n, w, f, c;
3723 auto contrationOp = vector::ContractionOp::create(
3724 rewriter, loc,
lhs,
rhs, res,
3725 MapList{{n, w, c}, {c, f}, {n, w, f}},
3726 ArrayRef<vector::IteratorType>{par, par, par, red});
3727 contrationOp.setKind(reductionKind);
3728 return contrationOp;
3733 Value conv1dSliceAsOuterProduct(RewriterBase &rewriter, Location loc,
3734 Value
lhs, Value
rhs, Value res) {
3737 return vector::OuterProductOp::create(rewriter, loc, res.
getType(),
lhs,
3738 rhs, res, vector::CombiningKind::ADD);
3742 Value pool1dSlice(RewriterBase &rewriter, Location loc, Value
lhs,
3760 FailureOr<Operation *> depthwiseConv(uint64_t channelDimVecSize,
3761 bool channelDimScalableFlag,
3763 bool scalableChDim =
false;
3764 bool useMasking =
false;
3765 int64_t nSize, wSize, cSize, kwSize;
3767 bindShapeDims(rhsShapedType, kwSize, cSize);
3768 if (ShapedType::isDynamic(cSize)) {
3769 assert(channelDimVecSize != 0 &&
"Channel dim vec size must be > 0");
3770 cSize = channelDimVecSize;
3774 scalableChDim = channelDimScalableFlag;
3778 assert(!(useMasking && flatten) &&
3779 "Unsupported flattened conv with dynamic shapes");
3782 bindShapeDims(resShapedType, nSize, wSize);
3784 vector::TransferWriteOp write;
3790 int64_t wSizeStep = strideW == 1 ? wSize : 1;
3792 Type lhsEltType = lhsShapedType.getElementType();
3793 Type rhsEltType = rhsShapedType.getElementType();
3794 Type resEltType = resShapedType.getElementType();
3795 VectorType lhsType = VectorType::get(
3799 ((wSize - 1) * strideW + 1) + ((kwSize - 1) * dilationW + 1) - 1,
3801 lhsEltType, {
false,
false, scalableChDim});
3802 VectorType rhsType =
3803 VectorType::get({kwSize, cSize}, rhsEltType,
3804 {
false, scalableChDim});
3805 VectorType resType =
3806 VectorType::get({nSize, wSize, cSize}, resEltType,
3807 {
false,
false, scalableChDim});
3811 auto maybeMaskXferOp = [&](ArrayRef<int64_t> maskShape,
3812 ArrayRef<bool> scalableDims,
3813 Operation *opToMask) {
3817 VectorType::get(maskShape, rewriter.
getI1Type(), scalableDims);
3819 SmallVector<bool> inBounds(maskShape.size(),
true);
3820 auto xferOp = cast<VectorTransferOpInterface>(opToMask);
3821 xferOp->setInherentAttr(
3826 cast<LinalgOp>(op).hasPureTensorSemantics(), opToMask, rewriter);
3829 vector::CreateMaskOp::create(rewriter, loc, maskType, mixedDims);
3836 Value
lhs = vector::TransferReadOp::create(
3837 rewriter, loc, lhsType, lhsShaped,
ValueRange{zero, zero, zero},
3838 arith::getZeroConstant(rewriter, loc, lhsEltType));
3839 auto *maybeMaskedLhs = maybeMaskXferOp(
3840 lhsType.getShape(), lhsType.getScalableDims(),
lhs.getDefiningOp());
3843 Value
rhs = vector::TransferReadOp::create(
3844 rewriter, loc, rhsType, rhsShaped,
ValueRange{zero, zero},
3845 arith::getZeroConstant(rewriter, loc, rhsEltType));
3846 auto *maybeMaskedRhs = maybeMaskXferOp(
3847 rhsType.getShape(), rhsType.getScalableDims(),
rhs.getDefiningOp());
3850 Value res = vector::TransferReadOp::create(
3851 rewriter, loc, resType, resShaped,
ValueRange{zero, zero, zero},
3852 arith::getZeroConstant(rewriter, loc, resEltType));
3853 auto *maybeMaskedRes = maybeMaskXferOp(
3854 resType.getShape(), resType.getScalableDims(), res.
getDefiningOp());
3860 SmallVector<Value> lhsVals, rhsVals, resVals;
3861 SmallVector<int64_t> inOutSliceSizes = {nSize, wSizeStep, cSize};
3862 SmallVector<int64_t> inOutStrides = {1, 1, 1};
3866 for (int64_t kw = 0; kw < kwSize; ++kw) {
3867 for (int64_t w = 0; w < wSize; w += wSizeStep) {
3868 lhsVals.push_back(vector::ExtractStridedSliceOp::create(
3869 rewriter, loc, maybeMaskedLhs->getResult(0),
3870 ArrayRef<int64_t>{0, w * strideW + kw * dilationW, 0},
3871 inOutSliceSizes, inOutStrides));
3875 for (int64_t kw = 0; kw < kwSize; ++kw) {
3877 vector::ExtractOp::create(rewriter, loc, maybeMaskedRhs->getResult(0),
3878 ArrayRef<int64_t>{kw}));
3881 for (int64_t w = 0; w < wSize; w += wSizeStep) {
3882 resVals.push_back(vector::ExtractStridedSliceOp::create(
3883 rewriter, loc, maybeMaskedRes->getResult(0),
3884 ArrayRef<int64_t>{0, w, 0}, inOutSliceSizes,
3888 auto linearIndex = [&](int64_t kw, int64_t w) {
3889 return kw * (wSize / wSizeStep) + w;
3894 SmallVector<int64_t> inOutFlattenSliceSizes = {nSize, wSizeStep * cSize};
3895 auto lhsTypeAfterFlattening =
3896 VectorType::get(inOutFlattenSliceSizes, lhsEltType);
3897 auto resTypeAfterFlattening =
3898 VectorType::get(inOutFlattenSliceSizes, resEltType);
3901 for (int64_t kw = 0; kw < kwSize; ++kw) {
3902 for (int64_t w = 0; w < wSize; w += wSizeStep) {
3903 Value lhsVal = lhsVals[linearIndex(kw, w)];
3904 Value resVal = resVals[w];
3909 vector::ShapeCastOp::create(rewriter, loc, lhsTypeAfterFlattening,
3910 lhsVals[linearIndex(kw, w)]);
3911 resVal = vector::ShapeCastOp::create(
3912 rewriter, loc, resTypeAfterFlattening, resVals[w]);
3914 resVals[w] = depthwiseConv1dSliceAsMulAcc(rewriter, loc, lhsVal,
3915 rhsVals[kw], resVal, flatten);
3918 resVals[w] = vector::ShapeCastOp::create(
3919 rewriter, loc, VectorType::get(inOutSliceSizes, resEltType),
3926 if (!llvm::all_of(resVals, [](Value v) {
return v; })) {
3928 for (
auto &collection :
3929 {resVals, rhsVals, lhsVals, {res,
rhs,
lhs, zero}})
3930 for (Value v : collection)
3937 for (int64_t w = 0; w < wSize; w += wSizeStep) {
3938 maybeMaskedRes = vector::InsertStridedSliceOp::create(
3939 rewriter, loc, resVals[w], maybeMaskedRes->getResult(0),
3940 ArrayRef<int64_t>{0, w, 0},
3941 ArrayRef<int64_t>{1, 1, 1});
3948 Operation *resOut = vector::TransferWriteOp::create(
3949 rewriter, loc, maybeMaskedRes->getResult(0), resShaped,
3951 return maybeMaskXferOp(resType.getShape(), resType.getScalableDims(),
3959 Value depthwiseConv1dSliceAsMulAcc(RewriterBase &rewriter, Location loc,
3960 Value
lhs, Value
rhs, Value res,
3962 auto rhsTy = cast<ShapedType>(
rhs.getType());
3963 auto resTy = cast<ShapedType>(res.
getType());
3977 auto rhsSize = cast<VectorType>(
rhs.getType()).getShape()[0];
3978 auto resSize = cast<VectorType>(res.
getType()).getShape()[1];
3980 SmallVector<int64_t, 16>
indices;
3981 for (
int i = 0; i < resSize / rhsSize; ++i) {
3982 for (
int j = 0; j < rhsSize; ++j)
3989 rhs = vector::BroadcastOp::create(rewriter, loc,
3990 resTy.clone(rhsTy.getElementType()),
rhs);
3997 if (isa<FloatType>(resTy.getElementType()))
3998 return vector::FMAOp::create(rewriter, loc,
lhs,
rhs, res);
4000 auto mul = arith::MulIOp::create(rewriter, loc,
lhs,
rhs);
4001 return arith::AddIOp::create(rewriter, loc,
mul, res);
4006 FailureOr<Operation *> generateNonChanneledConv() {
4009 if (!iters({Par(), Red()}))
4011 "failed to match conv::W 1-par 1-red");
4014 if (layout({ {w + kw},
4024 FailureOr<Operation *> generateNwcConv() {
4025 AffineExpr n, w, f, kw, c;
4027 if (!iters({Par(), Par(), Par(), Red(), Red()}))
4029 op,
"failed to match conv::Nwc 3-par 2-red");
4032 if (layout({ {n, strideW * w + dilationW * kw, c},
4042 FailureOr<Operation *> generateNcwConv() {
4043 AffineExpr n, w, f, kw, c;
4045 if (!iters({Par(), Par(), Par(), Red(), Red()}))
4047 op,
"failed to match conv::Ncw 3-par 2-red");
4049 if (layout({ {n, c, strideW * w + dilationW * kw},
4059 FailureOr<Operation *> generateNwcPooling() {
4060 AffineExpr n, w, c, kw;
4062 if (!iters({Par(), Par(), Par(), Red()}))
4064 "failed to match pooling 3-par 1-red");
4067 if (layout({ {n, strideW * w + dilationW * kw, c},
4077 FailureOr<Operation *> generateNcwPooling() {
4078 AffineExpr n, w, c, kw;
4080 if (!iters({Par(), Par(), Par(), Red()}))
4082 "failed to match pooling 3-par 1-red");
4084 if (layout({ {n, c, strideW * w + dilationW * kw},
4094 FailureOr<Operation *> generateDilatedConv(uint64_t vecChDimSize = 0,
4095 bool vecChDimScalableFlag =
false,
4096 bool flatten =
false) {
4097 AffineExpr n, w, c, kw;
4099 if (!iters({Par(), Par(), Par(), Red()}))
4101 op,
"failed to match depthwise::Nwc conv 3-par 1-red");
4104 if (layout({ {n, strideW * w + dilationW * kw, c},
4107 return depthwiseConv(vecChDimSize, vecChDimScalableFlag, flatten);
4113 ConvOperationKind oper = ConvOperationKind::Conv;
4115 StringAttr poolExtOp;
4116 bool isPoolExt =
false;
4119 Operation *lhsCastOp =
nullptr;
4120 Operation *rhsCastOp =
nullptr;
4121 int strideW, dilationW;
4122 Value lhsShaped, rhsShaped, resShaped;
4123 ShapedType lhsShapedType, rhsShapedType, resShapedType;
4124 vector::CombiningKind reductionKind;
4128 void setConvOperationKind(Operation *reduceOp) {
4129 int numBlockArguments =
4130 llvm::count_if(reduceOp->
getOperands(), llvm::IsaPred<BlockArgument>);
4131 if (numBlockArguments == 1) {
4136 auto feedValIt = llvm::find_if_not(reduceOp->
getOperands(),
4137 llvm::IsaPred<BlockArgument>);
4138 Operation *feedOp = (*feedValIt).getDefiningOp();
4139 if (isCastOfBlockArgument(feedOp)) {
4140 oper = ConvOperationKind::Pool;
4145 oper = ConvOperationKind::Conv;
4146 setConvCastOps(feedOp);
4150 oper = ConvOperationKind::Pool;
4155 void setConvCastOps(Operation *feedOp) {
4165 RewriterBase &rewriter, LinalgOp op, ArrayRef<int64_t> inputVecSizes,
4166 ArrayRef<bool> inputScalableVecDims,
bool flatten1DDepthwiseConv) {
4167 FailureOr<Conv1DGenerator> conv1dGen = Conv1DGenerator::create(rewriter, op);
4170 auto res = conv1dGen->generateNonChanneledConv();
4173 res = conv1dGen->generateNwcConv();
4176 res = conv1dGen->generateNcwConv();
4179 res = conv1dGen->generateNwcPooling();
4182 res = conv1dGen->generateNcwPooling();
4189 uint64_t vecChDimSize = ShapedType::kDynamic;
4190 bool vecChDimScalableFlag =
false;
4191 if (!inputVecSizes.empty()) {
4196 "Not a 1D depthwise conv!");
4197 size_t chDimIdx = 0;
4203 vecChDimSize = inputVecSizes[chDimIdx];
4204 vecChDimScalableFlag = inputScalableVecDims[chDimIdx];
4206 return conv1dGen->generateDilatedConv(vecChDimSize, vecChDimScalableFlag,
4207 flatten1DDepthwiseConv);
4210struct VectorizeConvolution :
public OpInterfaceRewritePattern<LinalgOp> {
4213 LogicalResult matchAndRewrite(LinalgOp op,
4214 PatternRewriter &rewriter)
const override {
4216 if (
failed(resultOrFail))
4218 Operation *newOp = *resultOrFail;
4220 rewriter.
eraseOp(op.getOperation());
4223 assert(newOp->
getNumResults() == 1 &&
"expected single result");
4230 RewritePatternSet &patterns, PatternBenefit benefit) {
4231 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.
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)
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.
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)
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.
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