38#include "llvm/ADT/STLExtras.h"
39#include "llvm/ADT/Sequence.h"
40#include "llvm/ADT/SmallPtrSet.h"
41#include "llvm/ADT/SmallVector.h"
42#include "llvm/ADT/SmallVectorExtras.h"
43#include "llvm/ADT/TypeSwitch.h"
44#include "llvm/Support/DebugLog.h"
45#include "llvm/Support/InterleavedRange.h"
46#include "llvm/Support/MathExtras.h"
47#include "llvm/Support/raw_ostream.h"
53#define DEBUG_TYPE "linalg-vectorization"
56static FailureOr<Operation *>
60 bool flatten1DDepthwiseConv =
false);
99 int64_t kwSize,
int strideW,
int dilationW,
100 int64_t wSizeStep,
bool isSingleChanneled) {
102 if (isSingleChanneled) {
107 for (
int64_t kw = 0; kw < kwSize; ++kw) {
108 for (
int64_t w = 0; w < wSize; w += wSizeStep) {
109 result.push_back(vector::ExtractStridedSliceOp::create(
119 for (
int64_t kw = 0; kw < kwSize; ++kw) {
120 for (
int64_t w = 0; w < wSize; w += wSizeStep) {
121 result.push_back(vector::ExtractStridedSliceOp::create(
122 rewriter, loc, input,
139 for (
int64_t kw = 0; kw < kwSize; ++kw) {
140 result.push_back(vector::ExtractOp::create(
151 int64_t wSizeStep,
bool isSingleChanneled) {
153 if (isSingleChanneled) {
157 for (
int64_t w = 0; w < wSize; w += wSizeStep) {
158 result.push_back(vector::ExtractStridedSliceOp::create(
167 for (
int64_t w = 0; w < wSize; w += wSizeStep) {
168 result.push_back(vector::ExtractStridedSliceOp::create(
180 bool isSingleChanneled) {
182 if (isSingleChanneled) {
186 for (
int64_t w = 0; w < wSize; w += wSizeStep) {
187 res = vector::InsertStridedSliceOp::create(
195 for (
int64_t w = 0; w < wSize; w += wSizeStep) {
196 res = vector::InsertStridedSliceOp::create(
197 rewriter, loc, resVals[w], res,
211 LogicalResult initState(
RewriterBase &rewriter, LinalgOp linalgOp,
214 bool assumeDynamicDimsMatchVecSizes =
false);
229 std::optional<AffineMap> dimPermutation = std::nullopt)
const {
232 if (dimPermutation.has_value()) {
238 vectorShape.append(canonicalVecShape.begin(), canonicalVecShape.end());
239 scalableDims.append(scalableVecDims.begin(), scalableVecDims.end());
242 return VectorType::get(
vectorShape, elementType, scalableDims);
251 std::optional<AffineMap> maybeIndexingMap = std::nullopt);
256 void initIterSpaceStaticSizes(LinalgOp linalgOp) {
257 iterSpaceStaticSizes.append(linalgOp.getStaticLoopRanges());
263 LogicalResult precomputeIterSpaceValueSizes(RewriterBase &rewriter,
270 Value getOrCreateMaskFor(RewriterBase &rewriter, Operation *opToMask,
272 std::optional<AffineMap> maybeMaskingMap);
277 bool isValidMaskingMap(AffineMap maskingMap) {
296 AffineMap getMaskingMapFromIndexingMap(AffineMap &indexingMap) {
302 SmallVector<int64_t> iterSpaceStaticSizes;
307 SmallVector<Value> iterSpaceValueSizes;
310 SmallVector<int64_t> canonicalVecShape;
314 SmallVector<bool> scalableVecDims;
322 OpBuilder::InsertionGuard rewriterGuard;
330 bool assumeDynamicDimsMatchVecSizes =
false;
334VectorizationState::precomputeIterSpaceValueSizes(
RewriterBase &rewriter,
337 for (
int vecDim = 0, end = canonicalVecShape.size(); vecDim < end; ++vecDim) {
338 if (ShapedType::isStatic(iterSpaceStaticSizes[vecDim])) {
341 rewriter, linalgOp.getLoc(), iterSpaceStaticSizes[vecDim]));
348 unsigned operandDimPos;
349 if (
failed(linalgOp.mapIterationSpaceDimToOperandDim(vecDim, operand,
354 linalgOp.hasPureTensorSemantics()
355 ? (Value)tensor::DimOp::create(rewriter, linalgOp.getLoc(), operand,
357 : (Value)memref::DimOp::create(rewriter, linalgOp.getLoc(), operand,
359 iterSpaceValueSizes.push_back(dynamicDim);
372 bool assumeDimsMatchVec) {
373 assumeDynamicDimsMatchVecSizes = assumeDimsMatchVec;
377 if (!inputVectorSizes.empty()) {
381 canonicalVecShape.append(inputVectorSizes.begin(), inputVectorSizes.end());
382 scalableVecDims.append(inputScalableVecDims.begin(),
383 inputScalableVecDims.end());
388 canonicalVecShape = linalgOp.getStaticLoopRanges();
389 scalableVecDims.append(linalgOp.getNumLoops(),
false);
392 LDBG() <<
"Canonical vector shape: " << llvm::interleaved(canonicalVecShape);
393 LDBG() <<
"Scalable vector dims: " << llvm::interleaved(scalableVecDims);
395 if (ShapedType::isDynamicShape(canonicalVecShape))
399 initIterSpaceStaticSizes(linalgOp);
404 if (failed(precomputeIterSpaceValueSizes(rewriter, linalgOp)))
414Value VectorizationState::getOrCreateMaskFor(
416 std::optional<AffineMap> maybeMaskingMap) {
418 assert((!maybeMaskingMap || isValidMaskingMap(*maybeMaskingMap)) &&
419 "Ill-formed masking map.");
422 auto maskableOp = dyn_cast<vector::MaskableOpInterface>(opToMask);
426 assert(!maskableOp.isMasked() &&
427 "Masking an operation that is already masked");
430 assert((!maybeMaskingMap || *maybeMaskingMap) &&
431 "Unexpected null mask permutation map");
433 maybeMaskingMap ? *maybeMaskingMap
435 linalgOp.getNumLoops(), rewriter.
getContext());
437 LDBG() <<
"Masking map: " << maskingMap;
441 auto activeMaskIt = activeMaskCache.find(maskingMap);
442 if (activeMaskIt != activeMaskCache.end()) {
443 Value mask = activeMaskIt->second;
444 LDBG() <<
"Reusing mask: " << mask;
454 SmallVector<int64_t> permutedStaticSizes =
456 auto maskType = getCanonicalVecType(rewriter.
getI1Type(), maskingMap);
457 auto maskShape = maskType.getShape();
459 LDBG() <<
"Mask shape: " << llvm::interleaved(maskShape);
461 if (permutedStaticSizes == maskShape) {
462 LDBG() <<
"Masking is not needed for masking map: " << maskingMap;
463 activeMaskCache[maskingMap] = Value();
467 if (assumeDynamicDimsMatchVecSizes) {
471 if (llvm::all_of(llvm::zip(permutedStaticSizes, maskType.getShape()),
473 return std::get<0>(it) == ShapedType::kDynamic
475 : std::get<0>(it) == std::get<1>(it);
478 <<
"Dynamic + static dimensions match vector sizes, masking is not "
480 activeMaskCache[maskingMap] = Value();
486 SmallVector<Value> upperBounds =
488 assert(!maskShape.empty() && !upperBounds.empty() &&
489 "Masked 0-d vectors are not supported yet");
492 Value mask = vector::CreateMaskOp::create(rewriter, linalgOp.getLoc(),
493 maskType, upperBounds);
494 LDBG() <<
"Creating new mask: " << mask;
495 activeMaskCache[maskingMap] = mask;
502 std::optional<AffineMap> maybeIndexingMap) {
503 LDBG() <<
"Trying to mask: " << *opToMask;
505 std::optional<AffineMap> maybeMaskingMap = std::nullopt;
506 if (maybeIndexingMap)
507 maybeMaskingMap = getMaskingMapFromIndexingMap(*maybeIndexingMap);
511 getOrCreateMaskFor(rewriter, opToMask, linalgOp, maybeMaskingMap);
514 LDBG() <<
"No mask required";
515 if (assumeDynamicDimsMatchVecSizes) {
517 .Case<vector::TransferReadOp, vector::TransferWriteOp>(
523 LDBG() <<
"Assuming dynamic dimensions match vector sizes and "
524 "setting their in-bounds to true!";
526 ShapedType xferType = xferOp.getShapedType();
531 for (
unsigned i = 0; i < xferOp.getTransferRank(); i++) {
532 auto dimExpr = dyn_cast<AffineDimExpr>(permMap.
getResult(i));
536 unsigned pos = dimExpr.getPosition();
537 if (xferType.isDynamicDim(pos))
538 inBoundsMap[i] =
true;
541 xferOp.setInBoundsAttr(
553 assert(opToMask &&
"Expected a valid operation to mask");
554 auto maskOp = cast<vector::MaskOp>(
556 Operation *maskOpTerminator = &maskOp.getMaskRegion().front().back();
558 for (
auto [resIdx, resVal] : llvm::enumerate(opToMask->
getResults()))
562 LDBG() <<
"Masked operation: " << *maskOp;
585 "expected projected permutation");
587 assert(res.getNumDims() ==
588 (res.getNumResults() - res.getNumOfZeroResults()) &&
589 "expected reindexed map with same number of dims and results");
625std::optional<vector::CombiningKind>
627 using ::mlir::vector::CombiningKind;
632 .Case<arith::AddIOp, arith::AddFOp>(
633 [&](
auto op) {
return CombiningKind::ADD; })
634 .Case([&](arith::AndIOp op) {
return CombiningKind::AND; })
635 .Case([&](arith::MaxSIOp op) {
return CombiningKind::MAXSI; })
636 .Case([&](arith::MaxUIOp op) {
return CombiningKind::MAXUI; })
637 .Case([&](arith::MaximumFOp op) {
return CombiningKind::MAXIMUMF; })
638 .Case([&](arith::MaxNumFOp op) {
return CombiningKind::MAXNUMF; })
639 .Case([&](arith::MaximumNumFOp op) {
return CombiningKind::MAXIMUMNUMF; })
640 .Case([&](arith::MinSIOp op) {
return CombiningKind::MINSI; })
641 .Case([&](arith::MinUIOp op) {
return CombiningKind::MINUI; })
642 .Case([&](arith::MinimumFOp op) {
return CombiningKind::MINIMUMF; })
643 .Case([&](arith::MinNumFOp op) {
return CombiningKind::MINNUMF; })
644 .Case([&](arith::MinimumNumFOp op) {
return CombiningKind::MINIMUMNUMF; })
645 .Case<arith::MulIOp, arith::MulFOp>(
646 [&](
auto op) {
return CombiningKind::MUL; })
647 .Case([&](arith::OrIOp op) {
return CombiningKind::OR; })
648 .Case([&](arith::XOrIOp op) {
return CombiningKind::XOR; })
649 .Default(std::nullopt);
660 auto linalgOp = cast<LinalgOp>(outputOperand->
getOwner());
665 if (!
matchReduction(linalgOp.getRegionOutputArgs(), outputPos, combinerOps) ||
666 combinerOps.size() != 1)
670 return combinerOps[0];
676 auto dstVecType = dyn_cast<VectorType>(dstType);
678 if (dstVecType.getRank() == 0)
683 Location loc =
b.getInsertionPoint()->getLoc();
684 return b.createOrFold<vector::BroadcastOp>(loc, dstVecType, value);
696 assert(maybeKind &&
"Failed precondition: could not get reduction kind");
697 return vector::MultiDimReductionOp::create(
698 b, reduceOp->
getLoc(), valueToReduce,
acc, dimsToMask, *maybeKind);
702 return llvm::map_to_vector(linalgOp.getIteratorTypesArray(),
709 return isa<linalg::ReduceOp>(op) ||
710 (isa<linalg::GenericOp>(op) &&
722 VectorizationState &state) {
724 auto linalgOp = cast<LinalgOp>(outputOperand->
getOwner());
725 AffineMap opOperandMap = linalgOp.getMatchingIndexingMap(outputOperand);
734 return llvm::is_contained(opOperandMap.getResults(), dimExpr);
736 auto vectorType = state.getCanonicalVecType(
743 if (vectorType.getRank() > 0) {
746 assert(value.
getType() == vectorType &&
"Incorrect type");
747 write = vector::TransferWriteOp::create(
748 rewriter, loc, value, outputOperand->
get(),
indices, writeMap);
751 if (!isa<VectorType>(value.
getType()))
752 value = vector::BroadcastOp::create(rewriter, loc, vectorType, value);
753 assert(value.
getType() == vectorType &&
"Incorrect type");
754 write = vector::TransferWriteOp::create(rewriter, loc, value,
758 write = state.maskOperation(rewriter, write, linalgOp, opOperandMap);
762 if (
auto maskOp = dyn_cast<vector::MaskingOpInterface>(write)) {
763 auto maskedWriteOp = cast<vector::TransferWriteOp>(maskOp.getMaskableOp());
768 LDBG() <<
"vectorized op: " << *write;
778 std::function<LogicalResult(
Operation *,
bool)>;
795 const IRMapping &bvm, VectorizationState &state,
797 auto yieldOp = dyn_cast<linalg::YieldOp>(op);
800 for (
const auto &output : llvm::enumerate(yieldOp.getValues())) {
806 linalgOp.getDpsInitOperand(output.index()), state);
808 newResults.push_back(newResult);
819 VectorizationState &state,
822 IndexOp indexOp = dyn_cast<linalg::IndexOp>(op);
825 auto loc = indexOp.getLoc();
828 auto dim = indexOp.getDim();
830 auto indexVectorType =
831 VectorType::get({targetShape[dim]}, rewriter.
getIndexType(),
832 state.getScalableVecDims()[dim]);
833 auto indexSteps = vector::StepOp::create(rewriter, loc, indexVectorType);
837 if (dim == targetShape.size() - 1)
843 llvm::to_vector(llvm::seq<unsigned>(0, targetShape.size()));
844 std::swap(permPattern[dim], permPattern.back());
848 auto broadCastOp = vector::BroadcastOp::create(
850 state.getCanonicalVecType(rewriter.
getIndexType(), permMap), indexSteps);
852 llvm::to_vector<16>(llvm::seq<int64_t>(0, linalgOp.getNumLoops()));
853 std::swap(transposition.back(), transposition[dim]);
855 vector::TransposeOp::create(rewriter, loc, broadCastOp, transposition);
863 tensor::ExtractOp extractOp = dyn_cast<tensor::ExtractOp>(op);
867 if (extractOp.getIndices().size() != 1 && !vectorizeNDExtract)
872 if (not extractOp.getIndices().empty()) {
873 if (!VectorType::isValidElementType(extractOp.getIndices()[0].getType()))
877 if (!llvm::all_of(extractOp->getResultTypes(),
878 VectorType::isValidElementType)) {
896 VectorizationState &state,
897 tensor::ExtractOp extractOp,
900 auto indexVecType = state.getCanonicalVecType(rewriter.
getIndexType());
901 auto loc = extractOp.getLoc();
904 rewriter, bvm.
lookup(extractOp.getIndices()[0]), indexVecType);
906 const size_t numIndices = extractOp.getIndices().size();
907 for (
size_t i = 1; i < numIndices; i++) {
912 tensor::DimOp::create(rewriter, loc, extractOp.getTensor(), dimIdx),
915 offset = arith::MulIOp::create(rewriter, loc, offset, dimSize);
918 rewriter, bvm.
lookup(extractOp.getIndices()[i]), indexVecType);
920 offset = arith::AddIOp::create(rewriter, loc, extractOpIndex, offset);
946 (linalgOp.hasDynamicShape() ||
947 llvm::count_if(loopRanges, [](
int64_t dim) { return dim != 1; }) == 1) &&
948 "For statically shaped Linalg Ops, only one "
949 "non-unit loop dim is expected");
950 assert(!loopRanges.empty() &&
"Empty loops, nothing to analyse.");
952 size_t idx = loopRanges.size() - 1;
953 for (; idx != 0; idx--)
954 if (loopRanges[idx] != 1)
962 VectorType resType) {
964 assert(((llvm::count_if(resType.getShape(),
965 [](
int64_t dimSize) { return dimSize > 1; }) == 1)) &&
966 "n-D vectors are not yet supported");
968 auto *block = linalgOp.getBlock();
974 while (!worklist.empty()) {
975 Value v = worklist.pop_back_val();
981 if (isa<BlockArgument>(v)) {
982 if (llvm::is_contained(block->getArguments(), v))
988 assert(defOp &&
"This is neither a block argument nor an operation result");
993 if (
auto indexOp = dyn_cast<linalg::IndexOp>(defOp)) {
994 if (linalgOp.getStaticLoopRanges()[indexOp.getDim()] != 1)
999 auto *ancestor = block->findAncestorOpInBlock(*defOp);
1006 if (isa<arith::ConstantOp>(ancestor))
1009 if (visited.insert(ancestor).second)
1010 llvm::append_range(worklist, ancestor->getOperands());
1034 bool &foundIndexOp, VectorType resType) {
1036 assert(((llvm::count_if(resType.getShape(),
1037 [](
int64_t dimSize) { return dimSize > 1; }) == 1)) &&
1038 "n-D vectors are not yet supported");
1044 auto *block = linalgOp.getBlock();
1045 if (isa<BlockArgument>(val))
1046 return !llvm::is_contained(block->getArguments(), val);
1049 assert(defOp &&
"This is neither a block argument nor an operation result");
1051 if (
auto indexOp = dyn_cast<linalg::IndexOp>(defOp)) {
1054 foundIndexOp = (indexOp.getDim() == loopDimThatIncrementsByOne);
1058 auto *ancestor = block->findAncestorOpInBlock(*defOp);
1065 if (!isa<arith::AddIOp, arith::ConstantOp, linalg::IndexOp>(ancestor))
1069 for (
auto op : ancestor->getOperands())
1089 LinalgOp &linalgOp, VectorType resType) {
1091 auto inputShape = cast<ShapedType>(extractOp.getTensor().getType());
1094 if (inputShape.getShape().empty())
1099 if (resType.getRank() == 0)
1104 bool isOutput1DVector =
1105 (llvm::count_if(resType.getShape(),
1106 [](
int64_t dimSize) { return dimSize > 1; }) == 1);
1108 if (!isOutput1DVector)
1111 bool leadingIdxsLoopInvariant =
true;
1117 auto indices = extractOp.getIndices();
1118 auto leadIndices =
indices.drop_back(1);
1120 for (
auto [i, indexVal] : llvm::enumerate(leadIndices)) {
1121 if (inputShape.getShape()[i] == 1)
1127 if (!leadingIdxsLoopInvariant) {
1128 LDBG() <<
"Found gather load: " << extractOp;
1136 auto extractOpTrailingIdx =
indices.back();
1140 if (leadingIdxsLoopInvariant &&
1142 LDBG() <<
"Found scalar broadcast load: " << extractOp;
1151 bool foundIndexOp =
false;
1153 foundIndexOp, resType);
1156 bool isRowVector = resType.getShape().back() != 1;
1157 isContiguousLoad &= (foundIndexOp && isRowVector);
1159 if (isContiguousLoad) {
1160 LDBG() <<
"Found contigous load: " << extractOp;
1165 LDBG() <<
"Found gather load: " << extractOp;
1173static VectorizationHookResult
1176 tensor::ExtractOp extractOp = dyn_cast<tensor::ExtractOp>(op);
1179 auto loc = extractOp.getLoc();
1182 auto resultType = state.getCanonicalVecType(extractOp.getResult().getType());
1183 auto maskConstantOp = arith::ConstantOp::create(
1187 auto passThruConstantOp = arith::ConstantOp::create(
1193 extractOp.getIndices().size(),
1204 Operation *gatherOp = vector::GatherOp::create(
1205 rewriter, loc, resultType, extractOp.getTensor(), baseIndices, offset,
1206 maskConstantOp, passThruConstantOp);
1207 gatherOp = state.maskOperation(rewriter, gatherOp, linalgOp);
1209 LDBG() <<
"Vectorised as gather load: " << extractOp;
1232 for (
size_t i = 0; i < extractOp.getIndices().size(); i++) {
1233 Value idx = bvm.
lookup(extractOp.getIndices()[i]);
1235 transferReadIdxs.push_back(idx);
1239 auto indexAs1dVector = vector::ShapeCastOp::create(
1241 VectorType::get(resultType.getShape().back(), rewriter.
getIndexType(),
1242 resultType.getScalableDims().back()),
1244 transferReadIdxs.push_back(
1245 vector::ExtractOp::create(rewriter, loc, indexAs1dVector, 0));
1249 auto dstRank = resultType.getRank();
1250 auto srcRank = extractOp.getTensor().getType().getRank();
1259 auto transferReadOp = vector::TransferReadOp::create(
1260 rewriter, loc, resultType, extractOp.getTensor(), transferReadIdxs,
1261 std::nullopt, permutationMap, inBounds);
1263 Operation *readOrMaskedReadOp = transferReadOp;
1269 auto readMaskType = VectorType::get(readMaskShape, rewriter.
getI1Type());
1270 auto allTrue = vector::ConstantMaskOp::create(
1272 readOrMaskedReadOp =
1276 LDBG() <<
"Vectorised as scalar broadcast load: " << extractOp;
1278 readOrMaskedReadOp};
1283 srcRank, std::min(dstRank, srcRank), rewriter.
getContext());
1285 int32_t rankDiff = dstRank - srcRank;
1293 while (rankDiff > 0) {
1294 permutationMap = permutationMap.insertResult(
1299 auto transferReadOp = vector::TransferReadOp::create(
1300 rewriter, loc, resultType, extractOp.getTensor(), transferReadIdxs,
1301 std::nullopt, permutationMap, inBounds);
1310 int64_t numReadDims = std::min(dstRank, srcRank);
1312 linalgOp.getNumLoops(), numReadDims, rewriter.
getContext());
1314 state.maskOperation(rewriter, transferReadOp, linalgOp, maskingMap);
1316 LDBG() <<
"Vectorised as contiguous load: " << extractOp;
1329 auto reduceType = dyn_cast<VectorType>(reduceVec.
getType());
1330 auto outputType = dyn_cast<VectorType>(outputVec.
getType());
1334 (outputType && reduceType.getShape() == outputType.getShape()))
1359static VectorizationHookResult
1363 LDBG() <<
"vectorize op " << *op;
1366 if (!customVectorizationHooks.empty()) {
1367 for (
auto &customFunc : customVectorizationHooks) {
1377 if (isa<arith::ConstantOp, func::ConstantOp>(op))
1379 rewriter.
clone(*op)};
1388 auto blockArg = dyn_cast<BlockArgument>(operand);
1389 if (!blockArg || blockArg.getOwner() != linalgOp.getBlock() ||
1390 blockArg.getArgNumber() < linalgOp.getNumDpsInputs())
1394 linalgOp.getRegionOutputArgs(),
1395 blockArg.getArgNumber() - linalgOp.getNumDpsInputs(), reductionOps);
1398 reductionOperands.push_back(std::make_pair(reduceValue, operand));
1400 if (!reductionOperands.empty()) {
1401 assert(reductionOperands.size() == 1);
1403 reduceIfNeeded(rewriter, linalgOp, op, reductionOperands[0].first,
1404 reductionOperands[0].second, bvm);
1411 VectorType firstMaxRankedType;
1413 auto vecOperand = bvm.
lookup(operand);
1414 assert(vecOperand &&
"Vector operand couldn't be found");
1416 auto vecType = dyn_cast<VectorType>(vecOperand.getType());
1417 if (vecType && (!firstMaxRankedType ||
1418 firstMaxRankedType.getRank() < vecType.getRank()))
1419 firstMaxRankedType = vecType;
1425 assert(vecOperand &&
"Vector operand couldn't be found");
1427 if (firstMaxRankedType) {
1428 auto vecType = VectorType::get(firstMaxRankedType.getShape(),
1430 firstMaxRankedType.getScalableDims());
1433 vecOperands.push_back(vecOperand);
1439 resultTypes.push_back(
1441 ? VectorType::get(firstMaxRankedType.getShape(), resultType,
1442 firstMaxRankedType.getScalableDims())
1480 LDBG() <<
"Vectorizing operation as linalg generic/n";
1481 Block *block = linalgOp.getBlock();
1488 bvm.
map(valuesSet.getArrayRef(), valuesSet.getArrayRef());
1490 if (linalgOp.getNumDpsInits() == 0)
1496 for (
OpOperand *opOperand : linalgOp.getOpOperandsMatchingBBargs()) {
1497 BlockArgument bbarg = linalgOp.getMatchingBlockArgument(opOperand);
1498 if (linalgOp.isScalar(opOperand)) {
1499 bvm.
map(bbarg, opOperand->get());
1505 AffineMap indexingMap = linalgOp.getMatchingIndexingMap(opOperand);
1508 VectorType readType;
1510 if (linalgOp.isDpsInput(opOperand)) {
1513 readType = state.getCanonicalVecType(elemType);
1520 state.getCanonicalVecType(elemType, readMap.
compose(indexingMap));
1525 Operation *read = vector::TransferReadOp::create(
1526 rewriter, loc, readType, opOperand->get(),
indices,
1527 std::nullopt, readMap);
1528 read = state.maskOperation(rewriter, read, linalgOp, indexingMap);
1533 if (
auto maskOp = dyn_cast<vector::MaskingOpInterface>(read)) {
1535 cast<vector::TransferReadOp>(maskOp.getMaskableOp())
1541 if (readType.getRank() == 0)
1542 readValue = vector::ExtractOp::create(rewriter, loc, readValue,
1545 LDBG() <<
"New vectorized bbarg(" << bbarg.
getArgNumber()
1546 <<
"): " << readValue;
1547 bvm.
map(bbarg, readValue);
1548 bvm.
map(opOperand->get(), readValue);
1557 hooks.push_back(vectorizeYield);
1564 hooks.push_back(vectorizeIndex);
1571 hooks.push_back(vectorizeExtract);
1578 LDBG() <<
"failed to vectorize: " << op;
1583 state.maskOperation(rewriter,
result.newOp, linalgOp);
1584 LDBG() <<
"New vector op: " << *maybeMaskedOp;
1610 assert(type.getNumScalableDims() < 2 &&
1611 "Collapsing more than 1 scalable dim is not supported ATM");
1617 auto shape = type.getShape();
1618 auto scalableFlags = type.getScalableDims();
1622 unsigned currentDim = 0;
1624 unsigned dim = m.getNumResults();
1627 for (
unsigned d = 0; d < dim; ++d) {
1628 size *=
shape[currentDim + d];
1629 flag |= scalableFlags[currentDim + d];
1631 newShape.push_back(size);
1632 newScalableFlags.push_back(flag);
1636 return VectorType::get(newShape, type.getElementType(), newScalableFlags);
1669vectorizeAsTensorPackOp(RewriterBase &rewriter, linalg::PackOp packOp,
1670 ArrayRef<int64_t> inputVectorSizes,
1671 SmallVectorImpl<Value> &newResults) {
1672 if (!inputVectorSizes.empty()) {
1673 assert(inputVectorSizes.size() == packOp.getDestRank() &&
1674 "Invalid number of input vector sizes!");
1678 OpBuilder::InsertionGuard g(rewriter);
1681 Location loc = packOp.getLoc();
1682 std::optional<Value> padValue = packOp.getPaddingValue()
1683 ? std::optional(packOp.getPaddingValue())
1686 SmallVector<int64_t> destShape =
1687 SmallVector<int64_t>(packOp.getDestType().getShape());
1691 ArrayRef<int64_t> &writeVectorSizes = inputVectorSizes;
1695 bool useInBoundsInsteadOfMasking =
false;
1696 if (writeVectorSizes.empty()) {
1697 if (ShapedType::isDynamicShape(destShape))
1699 "unable to infer vector sizes");
1701 writeVectorSizes = destShape;
1702 useInBoundsInsteadOfMasking =
true;
1711 PackingMetadata packMetadata;
1712 SmallVector<int64_t> preTransposeWriteVecSizses(writeVectorSizes);
1715 auto preTransposeWriteVecType =
1716 VectorType::get(preTransposeWriteVecSizses,
1717 packOp.getResult().getType().getElementType());
1723 preTransposeWriteVecType,
1725 rewriter.
getContext(), packMetadata.reassociations)));
1729 rewriter, loc, packOp.getSource(), readVecType, padValue,
1730 useInBoundsInsteadOfMasking);
1733 auto shapeCastOp = vector::ShapeCastOp::create(
1734 rewriter, loc, preTransposeWriteVecType, maskedRead);
1738 auto transposeOp = vector::TransposeOp::create(
1739 rewriter, loc, shapeCastOp.getResult(), destPermutation);
1743 rewriter, loc, transposeOp.getResult(), packOp.getDest());
1744 newResults.push_back(write->
getResult(0));
1778vectorizeAsTensorUnpackOp(RewriterBase &rewriter, linalg::UnPackOp unpackOp,
1779 ArrayRef<int64_t> inputVectorSizes,
1780 ArrayRef<bool> inputScalableVecDims,
1781 SmallVectorImpl<Value> &newResults) {
1782 if (!inputVectorSizes.empty()) {
1783 assert(inputVectorSizes.size() == unpackOp.getSourceRank() &&
1784 "Invalid number of input vector sizes!");
1785 assert(inputVectorSizes.size() == inputScalableVecDims.size() &&
1786 "Incompatible number of vector sizes and vector scalable flags!");
1790 OpBuilder::InsertionGuard g(rewriter);
1793 ShapedType unpackTensorType = unpackOp.getSourceType();
1795 ArrayRef<int64_t> sourceShape = unpackTensorType.getShape();
1796 bool useInBoundsInsteadOfMasking =
false;
1798 Location loc = unpackOp->getLoc();
1801 SmallVector<int64_t> readVectorSizes(inputVectorSizes);
1802 SmallVector<bool> readScalableVectorFlags(inputScalableVecDims);
1805 if (inputVectorSizes.empty()) {
1806 if (ShapedType::isDynamicShape(sourceShape))
1808 "Unable to infer vector sizes!");
1810 readVectorSizes.assign(sourceShape.begin(), sourceShape.end());
1811 useInBoundsInsteadOfMasking =
true;
1815 VectorType readVecType =
1816 VectorType::get(readVectorSizes, unpackTensorType.getElementType(),
1817 readScalableVectorFlags);
1819 rewriter, loc, unpackOp.getSource(), readVecType, std::nullopt,
1820 useInBoundsInsteadOfMasking);
1823 PackingMetadata packMetadata;
1824 SmallVector<int64_t> lastDimToInsertPosPerm =
1826 vector::TransposeOp transposeOp = vector::TransposeOp::create(
1827 rewriter, loc, readResult, lastDimToInsertPosPerm);
1831 transposeOp.getType(),
1833 rewriter.
getContext(), packMetadata.reassociations)));
1834 vector::ShapeCastOp shapeCastOp = vector::ShapeCastOp::create(
1835 rewriter, loc, collapsedVecType, transposeOp->getResult(0));
1839 rewriter, loc, shapeCastOp.getResult(), unpackOp.getDest(),
1840 {}, useInBoundsInsteadOfMasking);
1842 newResults.push_back(write->
getResult(0));
1850vectorizeAsTensorPadOp(RewriterBase &rewriter, tensor::PadOp padOp,
1851 ArrayRef<int64_t> inputVectorSizes,
1852 SmallVectorImpl<Value> &newResults) {
1853 auto padValue = padOp.getConstantPaddingValue();
1854 Location loc = padOp.getLoc();
1857 OpBuilder::InsertionGuard g(rewriter);
1861 LogicalResult status =
1862 cast<ReifyRankedShapedTypeOpInterface>(padOp.getOperation())
1863 .reifyResultShapes(rewriter, reifiedReturnShapes);
1865 assert(succeeded(status) &&
"failed to reify result shapes");
1866 auto readType = VectorType::get(inputVectorSizes, padValue.getType());
1868 rewriter, loc, padOp.getSource(), readType, padValue,
1872 Value dest = tensor::EmptyOp::create(rewriter, loc, reifiedReturnShapes[0],
1873 padOp.getResultType().getElementType());
1876 newResults.push_back(write->
getResult(0));
1882static LogicalResult reductionPreconditions(LinalgOp op) {
1884 LDBG() <<
"reduction precondition failed: no reduction iterator";
1887 for (OpOperand &opOperand : op.getDpsInitsMutable()) {
1888 AffineMap indexingMap = op.getMatchingIndexingMap(&opOperand);
1894 LDBG() <<
"reduction precondition failed: reduction detection failed";
1902vectorizeDynamicConvOpPrecondition(linalg::LinalgOp conv,
1903 bool flatten1DDepthwiseConv) {
1904 if (flatten1DDepthwiseConv) {
1905 LDBG() <<
"Vectorization of flattened convs with dynamic shapes is not "
1911 LDBG() <<
"Not a 1D depth-wise WC conv, dynamic shapes are not supported";
1917 Value
lhs = conv.getDpsInputOperand(0)->get();
1918 ArrayRef<int64_t> lhsShape = cast<ShapedType>(
lhs.getType()).getShape();
1919 auto shapeWithoutCh = lhsShape.drop_back(1);
1920 if (ShapedType::isDynamicShape(shapeWithoutCh)) {
1921 LDBG() <<
"Dynamically-shaped op vectorization precondition failed: only "
1922 "channel dim can be dynamic";
1930vectorizeDynamicLinalgOpPrecondition(linalg::LinalgOp op,
1931 bool flatten1DDepthwiseConv) {
1933 return vectorizeDynamicConvOpPrecondition(op, flatten1DDepthwiseConv);
1936 return reductionPreconditions(op);
1941 !isa<linalg::GenericOp, linalg::CopyOp, linalg::ContractionOpInterface>(
1945 LDBG() <<
"Dynamically-shaped op meets vectorization pre-conditions";
1955vectorizeUnPackOpPrecondition(linalg::UnPackOp unpackOp,
1956 ArrayRef<int64_t> inputVectorSizes) {
1960 if (!unpackOp.hasPureTensorSemantics())
1965 if (inputVectorSizes.empty() && unpackOp.getDestType().hasStaticShape() &&
1966 unpackOp.getSourceType().hasStaticShape())
1971 if (!inputVectorSizes.empty() &&
1972 (inputVectorSizes.size() != unpackOp.getSourceRank())) {
1973 LDBG() <<
"Incorrect number of input vector sizes";
1979 unpackOp.getSourceType().getShape(), inputVectorSizes))) {
1980 LDBG() <<
"Invalid vector sizes for the read operation";
1988vectorizeInsertSliceOpPrecondition(tensor::InsertSliceOp sliceOp,
1989 ArrayRef<int64_t> inputVectorSizes) {
1992 auto sourceType = source.getType();
1993 if (!VectorType::isValidElementType(sourceType.getElementType()))
1998 if (!sliceOp.hasUnitStride()) {
1999 LDBG() <<
"Only unit strides are supported";
2016 bool isOutOfBoundsRead =
2017 !sourceType.hasStaticShape() && inputVectorSizes.empty();
2019 if (!padValue && isOutOfBoundsRead) {
2020 LDBG() <<
"Failed to get a pad value for out-of-bounds read access";
2034vectorizeAsLinalgContraction(RewriterBase &rewriter, VectorizationState &state,
2036 SmallVectorImpl<Value> &newResults) {
2037 Location loc = linalgOp.getLoc();
2038 MLIRContext *ctx = linalgOp.getContext();
2043 if (!isa<ContractionOpInterface>(linalgOp.getOperation()))
2046 OpOperand *outOperand = linalgOp.getDpsInitOperand(0);
2050 LDBG() <<
"Failed to determine contraction combining kind.";
2057 AffineMap lhsMap = linalgOp.getIndexingMapsArray()[0];
2058 AffineMap rhsMap = linalgOp.getIndexingMapsArray()[1];
2060 LDBG() <<
"Contractions with broadcasts are not supported.";
2065 SmallVector<Value> vecOperands;
2066 for (OpOperand &opOperand : linalgOp->getOpOperands()) {
2070 AffineMap indexingMap = linalgOp.getMatchingIndexingMap(&opOperand);
2074 VectorType readType =
2075 state.getCanonicalVecType(elemType, readMap.
compose(indexingMap));
2078 rewriter, loc, opOperand.get(), readType,
2079 arith::getZeroConstant(rewriter, loc, elemType),
2081 vecOperands.push_back(read);
2087 auto castAttr = linalgOp->getAttrOfType<TypeFnAttr>(
"cast");
2088 bool hasUnsignedCast =
2089 castAttr && castAttr.getValue() == TypeFn::cast_unsigned;
2090 auto accType = dyn_cast<VectorType>(vecOperands[2].
getType());
2091 auto accElementType =
2092 accType ? dyn_cast<IntegerType>(accType.getElementType()) :
nullptr;
2093 if (accElementType && accElementType.isSignless()) {
2094 for (Value &operand : MutableArrayRef(vecOperands).take_front(2)) {
2095 auto operandType = cast<VectorType>(operand.
getType());
2096 Type operandElementType = operandType.getElementType();
2097 VectorType castType = operandType.clone(accElementType);
2099 if (isa<FloatType>(operandElementType)) {
2102 ? arith::FPToUIOp::create(rewriter, loc, castType, operand)
2104 : arith::FPToSIOp::create(rewriter, loc, castType, operand)
2109 auto operandIntegerType = dyn_cast<IntegerType>(operandElementType);
2110 if (!operandIntegerType || !operandIntegerType.isSignless())
2112 if (operandIntegerType.getWidth() >= accElementType.getWidth())
2114 if (!hasUnsignedCast)
2119 operand = arith::ExtUIOp::create(rewriter, loc, castType, operand);
2124 SmallVector<Attribute> iterAttrs;
2125 auto iterators = linalgOp.getIteratorTypesArray();
2126 for (utils::IteratorType iter : iterators) {
2127 auto vecIter = iter == utils::IteratorType::parallel
2128 ? vector::IteratorType::parallel
2129 : vector::IteratorType::reduction;
2130 iterAttrs.push_back(vector::IteratorTypeAttr::get(ctx, vecIter));
2134 Operation *contractOp = vector::ContractionOp::create(
2135 rewriter, loc, vecOperands[0],
2136 vecOperands[1], vecOperands[2],
2137 linalgOp.getIndexingMaps(), rewriter.
getArrayAttr(iterAttrs), *maybeKind);
2138 contractOp = state.maskOperation(rewriter, contractOp, linalgOp);
2142 rewriter, loc, contractOp->
getResult(0), outOperand->
get());
2146 newResults.push_back(write->
getResult(0));
2152enum class ConvOperationKind { Conv, Pool };
2155static bool isCastOfBlockArgument(Operation *op) {
2170static std::optional<ConvOperationKind>
2171getConvOperationKind(Operation *reduceOp) {
2172 int numBlockArguments =
2173 llvm::count_if(reduceOp->
getOperands(), llvm::IsaPred<BlockArgument>);
2175 switch (numBlockArguments) {
2181 auto feedValIt = llvm::find_if_not(reduceOp->
getOperands(),
2182 llvm::IsaPred<BlockArgument>);
2184 "Expected a non-block argument operand");
2185 Operation *feedOp = (*feedValIt).getDefiningOp();
2186 if (isCastOfBlockArgument(feedOp)) {
2187 return ConvOperationKind::Pool;
2190 if (!((isa<arith::MulIOp, arith::MulFOp>(feedOp) ||
2191 (isa<arith::AndIOp>(feedOp) &&
2194 if (isa<BlockArgument>(v))
2196 if (Operation *op = v.getDefiningOp())
2197 return isCastOfBlockArgument(op);
2200 return std::nullopt;
2203 return ConvOperationKind::Conv;
2207 return ConvOperationKind::Pool;
2209 return std::nullopt;
2213static bool isSupportedPoolKind(vector::CombiningKind kind) {
2215 case vector::CombiningKind::ADD:
2216 case vector::CombiningKind::MAXNUMF:
2217 case vector::CombiningKind::MAXIMUMF:
2218 case vector::CombiningKind::MAXIMUMNUMF:
2219 case vector::CombiningKind::MAXSI:
2220 case vector::CombiningKind::MAXUI:
2221 case vector::CombiningKind::MINNUMF:
2222 case vector::CombiningKind::MINIMUMF:
2223 case vector::CombiningKind::MINIMUMNUMF:
2224 case vector::CombiningKind::MINSI:
2225 case vector::CombiningKind::MINUI:
2232static LogicalResult vectorizeConvOpPrecondition(linalg::LinalgOp convOp) {
2233 auto getOperandType = [&](
auto operand) {
2234 return dyn_cast<ShapedType>((operand->get()).getType());
2236 ShapedType lhsShapedType = getOperandType(convOp.getDpsInputOperand(0));
2237 ShapedType rhsShapedType = getOperandType(convOp.getDpsInputOperand(1));
2238 ShapedType resShapedType = getOperandType(convOp.getDpsInitOperand(0));
2242 if ((lhsShapedType.getRank() != 3 || resShapedType.getRank() != 3) &&
2243 (lhsShapedType.getRank() != 1 || resShapedType.getRank() != 1))
2250 auto maybeOper = getConvOperationKind(reduceOp);
2251 if (!maybeOper.has_value())
2258 if (!maybeKind || ((*maybeKind != vector::CombiningKind::ADD &&
2259 *maybeKind != vector::CombiningKind::OR) &&
2260 (*maybeOper != ConvOperationKind::Pool ||
2261 !isSupportedPoolKind(*maybeKind)))) {
2265 auto rhsRank = rhsShapedType.getRank();
2266 if (*maybeOper == ConvOperationKind::Pool) {
2270 if (rhsRank != 1 && rhsRank != 2 && rhsRank != 3)
2277static LogicalResult vectorizeLinalgOpPrecondition(
2278 LinalgOp linalgOp, ArrayRef<int64_t> inputVectorSizes,
2279 bool vectorizeNDExtract,
bool flatten1DDepthwiseConv) {
2281 if (llvm::any_of(linalgOp->getOpOperands(), [&](OpOperand &operand) {
2282 return llvm::is_contained(linalgOp.getShape(&operand), 0);
2286 if (!inputVectorSizes.empty() &&
2291 if (linalgOp.hasDynamicShape() &&
failed(vectorizeDynamicLinalgOpPrecondition(
2292 linalgOp, flatten1DDepthwiseConv))) {
2293 LDBG() <<
"Dynamically-shaped op failed vectorization pre-conditions";
2297 SmallVector<CustomVectorizationPrecondition> customPreconditions;
2303 for (Operation &innerOp : linalgOp->getRegion(0).front()) {
2306 customPreconditions,
2309 customPrecondition(&innerOp, vectorizeNDExtract));
2313 if (!llvm::all_of(innerOp.getOperandTypes(),
2314 VectorType::isValidElementType)) {
2317 if (!llvm::all_of(innerOp.getResultTypes(),
2318 VectorType::isValidElementType)) {
2327 return vectorizeConvOpPrecondition(linalgOp);
2333 LDBG() <<
"precondition failed: not projected permutations";
2336 if (
failed(reductionPreconditions(linalgOp))) {
2337 LDBG() <<
"precondition failed: reduction preconditions";
2344vectorizePackOpPrecondition(linalg::PackOp packOp,
2345 ArrayRef<int64_t> inputVectorSizes) {
2349 if (!packOp.hasPureTensorSemantics())
2352 auto padValue = packOp.getPaddingValue();
2356 LDBG() <<
"pad value is not constant: " << packOp;
2360 ArrayRef<int64_t> resultTensorShape = packOp.getDestType().getShape();
2361 bool satisfyEmptyCond =
true;
2362 if (inputVectorSizes.empty()) {
2363 if (!packOp.getDestType().hasStaticShape() ||
2364 !packOp.getSourceType().hasStaticShape())
2365 satisfyEmptyCond =
false;
2368 if (!satisfyEmptyCond &&
2370 resultTensorShape.take_front(packOp.getSourceRank()),
2374 if (llvm::any_of(packOp.getInnerTiles(), [](OpFoldResult v) {
2375 return !getConstantIntValue(v).has_value();
2377 LDBG() <<
"inner_tiles must be constant: " << packOp;
2385vectorizePadOpPrecondition(tensor::PadOp padOp,
2386 ArrayRef<int64_t> inputVectorSizes) {
2387 auto padValue = padOp.getConstantPaddingValue();
2389 LDBG() <<
"pad value is not constant: " << padOp;
2393 ArrayRef<int64_t> resultTensorShape = padOp.getResultType().getShape();
2409 if (llvm::any_of(llvm::enumerate(padOp.getMixedLowPad()),
2410 [&](
const auto &en) {
2411 OpFoldResult padValue = en.value();
2412 unsigned pos = en.index();
2413 std::optional<int64_t> pad = getConstantIntValue(padValue);
2414 return (!pad.has_value() || pad.value() != 0) &&
2415 resultTensorShape[pos] != 1;
2417 LDBG() <<
"low pad must all be zero for all non unit dims: " << padOp;
2431vectorizeScalableVectorPrecondition(Operation *op,
2432 ArrayRef<int64_t> inputVectorSizes,
2433 ArrayRef<bool> inputScalableVecDims) {
2434 assert(inputVectorSizes.size() == inputScalableVecDims.size() &&
2435 "Number of input vector sizes and scalable dims doesn't match");
2437 size_t numOfScalableDims =
2438 llvm::count_if(inputScalableVecDims, [](
bool flag) {
return flag; });
2440 if (numOfScalableDims == 0)
2443 auto linalgOp = dyn_cast<LinalgOp>(op);
2448 return success(isa<linalg::UnPackOp>(op));
2452 if (numOfScalableDims > 2)
2472 bool seenNonUnitParallel =
false;
2473 auto iterators = linalgOp.getIteratorTypesArray();
2474 SmallVector<bool> scalableFlags(inputScalableVecDims);
2475 int64_t idx = scalableFlags.size() - 1;
2476 while (!scalableFlags[idx]) {
2477 bool isNonUnitDim = (inputVectorSizes[idx] != 1);
2478 seenNonUnitParallel |=
2479 (iterators[idx] == utils::IteratorType::parallel && isNonUnitDim);
2481 iterators.pop_back();
2482 scalableFlags.pop_back();
2487 switch (iterators.back()) {
2488 case utils::IteratorType::reduction: {
2490 if (iterators.size() != inputVectorSizes.size()) {
2491 LDBG() <<
"Non-trailing reduction dim requested for scalable "
2495 if (isa<linalg::MatmulOp>(op)) {
2497 <<
"Scalable vectorization of the reduction dim in Matmul-like ops "
2503 case utils::IteratorType::parallel: {
2505 if (seenNonUnitParallel) {
2506 LDBG() <<
"Inner parallel dim not requested for scalable "
2518 if (numOfScalableDims == 2) {
2522 if (iterators.back() == utils::IteratorType::reduction) {
2523 LDBG() <<
"Higher dim than the trailing reduction dim requested for "
2528 scalableFlags.pop_back();
2529 iterators.pop_back();
2531 if (!scalableFlags.back() ||
2532 (iterators.back() != utils::IteratorType::parallel))
2540 isa<linalg::BatchMatmulOp>(op) ||
2542 isa<linalg::MatvecOp>(op) || isa<linalg::Mmt4DOp>(op) ||
2547 Operation *op, ArrayRef<int64_t> inputVectorSizes,
2548 ArrayRef<bool> inputScalableVecDims,
bool vectorizeNDExtract,
2549 bool flatten1DDepthwiseConv) {
2554 if (
failed(vectorizeScalableVectorPrecondition(op, inputVectorSizes,
2555 inputScalableVecDims)))
2559 .Case([&](linalg::LinalgOp linalgOp) {
2560 return vectorizeLinalgOpPrecondition(linalgOp, inputVectorSizes,
2562 flatten1DDepthwiseConv);
2564 .Case([&](tensor::PadOp padOp) {
2565 return vectorizePadOpPrecondition(padOp, inputVectorSizes);
2567 .Case([&](linalg::PackOp packOp) {
2568 return vectorizePackOpPrecondition(packOp, inputVectorSizes);
2570 .Case([&](linalg::UnPackOp unpackOp) {
2571 return vectorizeUnPackOpPrecondition(unpackOp, inputVectorSizes);
2573 .Case([&](tensor::InsertSliceOp sliceOp) {
2574 return vectorizeInsertSliceOpPrecondition(sliceOp, inputVectorSizes);
2576 .Default(failure());
2580static void convertAffineApply(RewriterBase &rewriter, LinalgOp linalgOp) {
2581 OpBuilder::InsertionGuard g(rewriter);
2582 auto toReplace = linalgOp.getBlock()->getOps<affine::AffineApplyOp>();
2584 for (
auto op : make_early_inc_range(toReplace)) {
2586 auto expanded = affine::expandAffineExpr(
2588 op.
getOperands().take_front(op.getAffineMap().getNumDims()),
2589 op.
getOperands().take_back(op.getAffineMap().getNumSymbols()));
2595 return isa<linalg::LinalgOp, tensor::PadOp, linalg::PackOp, linalg::UnPackOp,
2596 tensor::InsertSliceOp>(op);
2600 RewriterBase &rewriter, Operation *op, ArrayRef<int64_t> inputVectorSizes,
2601 ArrayRef<bool> inputScalableVecDims,
bool vectorizeNDExtract,
2602 bool flatten1DDepthwiseConv,
bool assumeDynamicDimsMatchVecSizes,
2603 bool createNamedContraction) {
2604 LDBG() <<
"Attempting to vectorize: " << *op;
2605 LDBG() <<
"Input vector sizes: " << llvm::interleaved(inputVectorSizes);
2606 LDBG() <<
"Input scalable vector dims: "
2607 << llvm::interleaved(inputScalableVecDims);
2611 flatten1DDepthwiseConv))) {
2612 LDBG() <<
"Vectorization pre-conditions failed";
2617 VectorizationState state(rewriter);
2618 if (
auto linalgOp = dyn_cast<linalg::LinalgOp>(op)) {
2619 if (
failed(state.initState(rewriter, linalgOp, inputVectorSizes,
2620 inputScalableVecDims,
2621 assumeDynamicDimsMatchVecSizes))) {
2622 LDBG() <<
"Vectorization state couldn't be initialized";
2627 SmallVector<Value> results;
2628 auto vectorizeResult =
2630 .Case([&](linalg::LinalgOp linalgOp) {
2634 rewriter, linalgOp, inputVectorSizes, inputScalableVecDims,
2635 flatten1DDepthwiseConv);
2636 if (succeeded(convOr)) {
2637 llvm::append_range(results, (*convOr)->getResults());
2641 LDBG() <<
"Unsupported convolution can't be vectorized.";
2645 if (createNamedContraction &&
2646 isa<ContractionOpInterface>(linalgOp.getOperation()))
2647 return vectorizeAsLinalgContraction(rewriter, state, linalgOp,
2651 <<
"Vectorize generic by broadcasting to the canonical vector "
2655 convertAffineApply(rewriter, linalgOp);
2664 .Case([&](tensor::PadOp padOp) {
2665 return vectorizeAsTensorPadOp(rewriter, padOp, inputVectorSizes,
2668 .Case([&](linalg::PackOp packOp) {
2669 return vectorizeAsTensorPackOp(rewriter, packOp, inputVectorSizes,
2672 .Case([&](linalg::UnPackOp unpackOp) {
2673 return vectorizeAsTensorUnpackOp(rewriter, unpackOp,
2675 inputScalableVecDims, results);
2677 .Case([&](tensor::InsertSliceOp sliceOp) {
2681 .Default(failure());
2683 if (
failed(vectorizeResult)) {
2684 LDBG() <<
"Vectorization failed";
2688 return VectorizationResult{results};
2692 memref::CopyOp copyOp) {
2693 auto srcType = cast<MemRefType>(copyOp.getSource().getType());
2694 auto dstType = cast<MemRefType>(copyOp.getTarget().getType());
2695 if (!srcType.hasStaticShape() || !dstType.hasStaticShape())
2700 if (!VectorType::isValidElementType(srcElementType) ||
2701 !VectorType::isValidElementType(dstElementType))
2704 auto readType = VectorType::get(srcType.getShape(), srcElementType);
2705 auto writeType = VectorType::get(dstType.getShape(), dstElementType);
2707 Location loc = copyOp->getLoc();
2709 SmallVector<Value>
indices(srcType.getRank(), zero);
2711 Value
readValue = vector::TransferReadOp::create(
2712 rewriter, loc, readType, copyOp.getSource(),
indices,
2715 if (cast<VectorType>(
readValue.getType()).getRank() == 0) {
2716 readValue = vector::ExtractOp::create(rewriter, loc, readValue,
2717 ArrayRef<int64_t>());
2719 vector::BroadcastOp::create(rewriter, loc, writeType, readValue);
2721 Operation *writeValue = vector::TransferWriteOp::create(
2722 rewriter, loc, readValue, copyOp.getTarget(),
indices,
2733template <
typename OpTy>
2734struct VectorizePadOpUserPattern :
public OpRewritePattern<tensor::PadOp> {
2735 using OpRewritePattern<tensor::PadOp>::OpRewritePattern;
2737 LogicalResult matchAndRewrite(tensor::PadOp padOp,
2738 PatternRewriter &rewriter)
const final {
2739 bool changed =
false;
2741 for (
auto *user : llvm::to_vector<4>(padOp->getUsers()))
2742 if (
auto op = dyn_cast<OpTy>(user))
2743 changed |= rewriteUser(rewriter, padOp, op).succeeded();
2748 virtual LogicalResult rewriteUser(PatternRewriter &rewriter,
2749 tensor::PadOp padOp, OpTy op)
const = 0;
2771struct PadOpVectorizationWithTransferReadPattern
2772 :
public VectorizePadOpUserPattern<vector::TransferReadOp> {
2773 using VectorizePadOpUserPattern<
2774 vector::TransferReadOp>::VectorizePadOpUserPattern;
2776 LogicalResult rewriteUser(PatternRewriter &rewriter, tensor::PadOp padOp,
2777 vector::TransferReadOp xferOp)
const override {
2779 if (!padOp.hasZeroLowPad())
2782 auto padValue = padOp.getConstantPaddingValue();
2786 if (xferOp.hasOutOfBoundsDim() || xferOp.getMask())
2790 SmallVector<bool> inBounds(xferOp.getVectorType().getRank(),
false);
2791 xferOp->setInherentAttr(xferOp.getInBoundsAttrName(),
2793 xferOp.getBaseMutable().assign(padOp.getSource());
2794 xferOp.getPaddingMutable().assign(padValue);
2833struct PadOpVectorizationWithTransferWritePattern
2834 :
public VectorizePadOpUserPattern<vector::TransferWriteOp> {
2835 using VectorizePadOpUserPattern<
2836 vector::TransferWriteOp>::VectorizePadOpUserPattern;
2838 LogicalResult rewriteUser(PatternRewriter &rewriter, tensor::PadOp padOp,
2839 vector::TransferWriteOp xferOp)
const override {
2841 if (xferOp.getTransferRank() == 0)
2845 if (!padOp.hasZeroLowPad())
2848 auto padValue = padOp.getConstantPaddingValue();
2852 if (!xferOp->hasOneUse())
2854 auto trimPadding = dyn_cast<tensor::ExtractSliceOp>(*xferOp->user_begin());
2858 if (!trimPadding.hasZeroOffset())
2861 if (!hasSameTensorSize(padOp.getSource(), trimPadding))
2867 SmallVector<bool> inBounds(xferOp.getVectorType().getRank(),
false);
2869 xferOp, padOp.getSource().
getType(), xferOp.getVector(),
2870 padOp.getSource(), xferOp.getIndices(), xferOp.getPermutationMapAttr(),
2872 rewriter.
replaceOp(trimPadding, newXferOp->getResult(0));
2887 bool hasSameTensorSize(Value beforePadding,
2888 tensor::ExtractSliceOp afterTrimming)
const {
2891 if (
auto castOp = beforePadding.
getDefiningOp<tensor::CastOp>())
2892 if (hasSameTensorSize(castOp.getSource(), afterTrimming))
2895 auto t1 = dyn_cast<RankedTensorType>(beforePadding.
getType());
2896 auto t2 = dyn_cast<RankedTensorType>(afterTrimming.getType());
2901 if (t1.getRank() != t2.getRank())
2906 for (
unsigned i = 0; i < t1.getRank(); ++i) {
2907 if (t1.isDynamicDim(i) != t2.isDynamicDim(i))
2909 if (!t1.isDynamicDim(i) && t1.getDimSize(i) != t2.getDimSize(i))
2914 if (t1.getNumDynamicDims() == 0)
2922 auto beforeSlice = beforePadding.
getDefiningOp<tensor::ExtractSliceOp>();
2926 assert(
static_cast<size_t>(t1.getRank()) ==
2927 beforeSlice.getMixedSizes().size());
2928 assert(
static_cast<size_t>(t2.getRank()) ==
2929 afterTrimming.getMixedSizes().size());
2931 for (
unsigned i = 0; i < t1.getRank(); ++i) {
2933 if (!t1.isDynamicDim(i))
2935 auto size1 = beforeSlice.getMixedSizes()[i];
2936 auto size2 = afterTrimming.getMixedSizes()[i];
2943 auto v1 = llvm::dyn_cast_if_present<Value>(size1);
2944 auto v2 = llvm::dyn_cast_if_present<Value>(size2);
2950 auto minOp1 = v1.getDefiningOp<affine::AffineMinOp>();
2951 auto minOp2 = v2.getDefiningOp<affine::AffineMinOp>();
2952 if (minOp1 && minOp2 && minOp1.getAffineMap() == minOp2.getAffineMap() &&
2953 minOp1.getOperands() == minOp2.getOperands())
2979 if (
auto bcast = llvm::dyn_cast<vector::BroadcastOp>(op)) {
2980 auto source = bcast.getSource();
2981 if (llvm::dyn_cast<VectorType>(source.getType()))
2989 if (
auto fill = llvm::dyn_cast<linalg::FillOp>(op)) {
2990 return fill.getInputs()[0];
2995 if (
auto generate = llvm::dyn_cast<tensor::GenerateOp>(op)) {
3002 if (
auto xferWrite = llvm::dyn_cast<vector::TransferWriteOp>(op))
3010 if (
auto slice = llvm::dyn_cast<tensor::InsertSliceOp>(op))
3018 ArrayRef<int64_t> inputVectorSizes,
3019 SmallVectorImpl<Value> &newResults) {
3021 OpBuilder::InsertionGuard g(rewriter);
3025 auto sourceType = source.getType();
3026 auto resultType = sliceOp.getResultType();
3031 auto elemType = sourceType.getElementType();
3032 padValue = arith::ConstantOp::create(rewriter, sliceOp.getLoc(), elemType,
3039 llvm::SmallBitVector droppedDims = sliceOp.getDroppedDims();
3040 SmallVector<int64_t> resultDimsForSourceDims;
3041 resultDimsForSourceDims.reserve(sourceType.getRank());
3042 for (int64_t resultDim = 0, end = resultType.getRank(); resultDim < end;
3044 if (!droppedDims[resultDim])
3045 resultDimsForSourceDims.push_back(resultDim);
3046 assert(resultDimsForSourceDims.size() ==
3047 static_cast<size_t>(sourceType.getRank()) &&
3048 "expected one non-dropped result dim per source dim");
3050 SmallVector<int64_t> vecShape;
3051 for (int64_t i = 0, end = sourceType.getRank(); i < end; ++i) {
3052 if (!inputVectorSizes.empty()) {
3053 vecShape.push_back(inputVectorSizes[i]);
3054 }
else if (!sourceType.isDynamicDim(i)) {
3055 vecShape.push_back(sourceType.getDimSize(i));
3056 }
else if (!resultType.isDynamicDim(resultDimsForSourceDims[i])) {
3060 vecShape.push_back(resultType.getDimSize(resultDimsForSourceDims[i]));
3067 auto vecType = VectorType::get(vecShape, sourceType.getElementType());
3070 auto loc = sliceOp.getLoc();
3073 SmallVector<Value> readIndices(
3076 rewriter, loc, source, vecType, padValue,
3077 inputVectorSizes.empty());
3085 if (sourceType.getRank() != resultType.getRank()) {
3086 SmallVector<AffineExpr> exprs;
3087 for (int64_t resultDim : resultDimsForSourceDims)
3094 rewriter, loc, read, sliceOp.getDest(), writeIndices,
3095 inputVectorSizes.empty(), writeMap);
3098 newResults.push_back(write->
getResult(0));
3126struct PadOpVectorizationWithInsertSlicePattern
3127 :
public VectorizePadOpUserPattern<tensor::InsertSliceOp> {
3128 using VectorizePadOpUserPattern<
3129 tensor::InsertSliceOp>::VectorizePadOpUserPattern;
3131 LogicalResult rewriteUser(PatternRewriter &rewriter, tensor::PadOp padOp,
3132 tensor::InsertSliceOp insertOp)
const override {
3134 if (!padOp.hasZeroLowPad())
3137 if (!insertOp.hasUnitStride())
3140 auto padValue = padOp.getConstantPaddingValue();
3144 if (!cast<ShapedType>(padOp.getResult().getType()).hasStaticShape())
3147 if (insertOp.getDest() == padOp.getResult())
3150 auto vecType = VectorType::get(padOp.getType().getShape(),
3151 padOp.getType().getElementType());
3152 unsigned vecRank = vecType.getRank();
3153 unsigned tensorRank = insertOp.getType().getRank();
3157 SmallVector<int64_t> expectedSizes(tensorRank - vecRank, 1);
3158 expectedSizes.append(vecType.getShape().begin(), vecType.getShape().end());
3160 llvm::zip(insertOp.getMixedSizes(), expectedSizes), [](
auto it) {
3161 return getConstantIntValue(std::get<0>(it)) == std::get<1>(it);
3171 SmallVector<Value> readIndices(
3173 auto read = vector::TransferReadOp::create(rewriter, padOp.getLoc(),
3174 vecType, padOp.getSource(),
3175 readIndices, padValue);
3181 rewriter, padOp.getLoc(), insertOp.getMixedOffsets());
3182 SmallVector<bool> inBounds(vecRank,
true);
3184 insertOp, read, insertOp.getDest(), writeIndices,
3185 ArrayRef<bool>{inBounds});
3192 RewritePatternSet &patterns, PatternBenefit baseBenefit) {
3193 patterns.
add<PadOpVectorizationWithTransferReadPattern,
3194 PadOpVectorizationWithTransferWritePattern,
3195 PadOpVectorizationWithInsertSlicePattern>(
3206static bool mayExistInterleavedUses(Operation *firstOp, Operation *secondOp,
3210 LDBG() <<
"interleavedUses precondition failed, firstOp: " << *firstOp
3211 <<
", second op: " << *secondOp;
3214 for (
auto v : values) {
3215 for (
auto &u : v.getUses()) {
3216 Operation *owner = u.getOwner();
3217 if (owner == firstOp || owner == secondOp)
3223 LDBG() <<
" found interleaved op " << *owner <<
", firstOp: " << *firstOp
3224 <<
", second op: " << *secondOp;
3233static memref::SubViewOp getSubViewUseIfUnique(Value v) {
3234 memref::SubViewOp subViewOp;
3236 if (
auto newSubViewOp = dyn_cast<memref::SubViewOp>(u.getOwner())) {
3238 return memref::SubViewOp();
3239 subViewOp = newSubViewOp;
3248 vector::TransferReadOp xferOp, PatternRewriter &rewriter)
const {
3251 if (xferOp.getMask())
3255 Value viewOrAlloc = xferOp.getBase();
3261 memref::SubViewOp subViewOp = getSubViewUseIfUnique(viewOrAlloc);
3264 Value subView = subViewOp.getResult();
3267 memref::CopyOp copyOp;
3268 for (
auto &u : subView.
getUses()) {
3269 if (
auto newCopyOp = dyn_cast<memref::CopyOp>(u.getOwner())) {
3270 assert(isa<MemRefType>(newCopyOp.getTarget().getType()));
3271 if (newCopyOp.getTarget() != subView)
3273 if (mayExistInterleavedUses(newCopyOp, xferOp, {viewOrAlloc, subView}))
3285 for (
auto &u : viewOrAlloc.
getUses()) {
3286 if (
auto newFillOp = dyn_cast<FillOp>(u.getOwner())) {
3287 assert(isa<MemRefType>(newFillOp.output().getType()));
3288 if (newFillOp.output() != viewOrAlloc)
3290 if (mayExistInterleavedUses(newFillOp, copyOp, {viewOrAlloc, subView}))
3292 maybeFillOp = newFillOp;
3297 if (maybeFillOp && xferOp.getPadding() != maybeFillOp.value())
3299 "padding value does not match fill");
3302 Value in = copyOp.getSource();
3308 auto vectorType = xferOp.getVectorType();
3309 Value res = vector::TransferReadOp::create(
3310 rewriter, xferOp.getLoc(), vectorType, in, xferOp.getIndices(),
3311 xferOp.getPermutationMapAttr(), xferOp.getPadding(), xferOp.getMask(),
3313 SmallVector<bool>(vectorType.getRank(),
false)));
3316 rewriter.
eraseOp(maybeFillOp);
3326 vector::TransferWriteOp xferOp, PatternRewriter &rewriter)
const {
3328 if (xferOp.getMask())
3332 Value viewOrAlloc = xferOp.getBase();
3338 memref::SubViewOp subViewOp = getSubViewUseIfUnique(viewOrAlloc);
3341 Value subView = subViewOp.getResult();
3344 memref::CopyOp copyOp;
3345 for (
auto &u : subViewOp.getResult().getUses()) {
3346 if (
auto newCopyOp = dyn_cast<memref::CopyOp>(u.getOwner())) {
3347 if (newCopyOp.getSource() != subView)
3349 if (mayExistInterleavedUses(xferOp, newCopyOp, {viewOrAlloc, subView}))
3359 assert(isa<MemRefType>(copyOp.getTarget().getType()));
3360 Value out = copyOp.getTarget();
3367 auto vector = xferOp.getVector();
3368 vector::TransferWriteOp::create(
3369 rewriter, xferOp.getLoc(), vector, out, xferOp.getIndices(),
3370 xferOp.getPermutationMapAttr(), xferOp.getMask(),
3372 dyn_cast<VectorType>(vector.getType()).getRank(),
false)));
3385static void bindShapeDims(ShapedType shapedType) {}
3387template <
int N,
typename IntTy,
typename... IntTy2>
3388static void bindShapeDims(ShapedType shapedType, IntTy &val, IntTy2 &...vals) {
3389 val = shapedType.getShape()[N];
3390 bindShapeDims<N + 1, IntTy2 &...>(shapedType, vals...);
3394template <
typename... IntTy>
3395static void bindShapeDims(ShapedType shapedType, IntTy &...vals) {
3396 bindShapeDims<0>(shapedType, vals...);
3401static std::optional<DilationsAndStrides> match1DConvPoolOp(LinalgOp op) {
3402#define MATCH_1D_CONV_POOL_OP(ConvOpTy) \
3403 if (auto convParams = matchConvolutionOpOfType<ConvOpTy>(op)) \
3425#undef MATCH_1D_CONV_POOL_OP
3427 return std::nullopt;
3465struct Conv1DGenerator
3466 :
public StructuredGenerator<LinalgOp, utils::IteratorType> {
3469 static FailureOr<Conv1DGenerator> create(RewriterBase &rewriter,
3470 LinalgOp linalgOp) {
3473 std::optional<DilationsAndStrides> convParams = match1DConvPoolOp(linalgOp);
3477 int strideW =
static_cast<int>(convParams->strides.front());
3478 int dilationW =
static_cast<int>(convParams->dilations.front());
3479 return Conv1DGenerator(rewriter, linalgOp, strideW, dilationW);
3483 Conv1DGenerator(RewriterBase &rewriter, LinalgOp linalgOp,
int strideW,
3485 : StructuredGenerator<LinalgOp, utils::IteratorType>(rewriter, linalgOp),
3486 strideW(strideW), dilationW(dilationW) {
3488 lhsShaped = linalgOp.getDpsInputOperand(0)->
get();
3489 rhsShaped = linalgOp.getDpsInputOperand(1)->
get();
3490 resShaped = linalgOp.getDpsInitOperand(0)->
get();
3491 lhsShapedType = dyn_cast<ShapedType>(lhsShaped.getType());
3492 rhsShapedType = dyn_cast<ShapedType>(rhsShaped.getType());
3493 resShapedType = dyn_cast<ShapedType>(resShaped.getType());
3498 setConvOperationKind(reduceOp);
3501 reductionKind = maybeKind.value();
3524 int64_t nSize, wSize, cSize, kwSize, fSize;
3525 SmallVector<int64_t, 3> lhsShape, rhsShape, resShape;
3527 switch (conv1DOpOrder) {
3530 nSize = fSize = cSize = 0;
3532 bindShapeDims(resShapedType, wSize);
3534 bindShapeDims(rhsShapedType, kwSize);
3537 (wSize + kwSize - 1)};
3538 rhsShape = {kwSize};
3543 bindShapeDims(resShapedType, nSize, wSize, fSize);
3545 case ConvOperationKind::Conv:
3547 bindShapeDims(rhsShapedType, kwSize, cSize);
3549 case ConvOperationKind::Pool:
3551 bindShapeDims(rhsShapedType, kwSize);
3559 ((wSize - 1) * strideW + 1) + ((kwSize - 1) * dilationW + 1) -
3563 case ConvOperationKind::Conv:
3564 rhsShape = {kwSize, cSize, fSize};
3566 case ConvOperationKind::Pool:
3567 rhsShape = {kwSize};
3570 resShape = {nSize, wSize, fSize};
3574 bindShapeDims(resShapedType, nSize, fSize, wSize);
3576 case ConvOperationKind::Conv:
3578 bindShapeDims(rhsShapedType, fSize, cSize, kwSize);
3580 case ConvOperationKind::Pool:
3582 bindShapeDims(rhsShapedType, kwSize);
3586 lhsShape = {nSize, cSize,
3590 ((wSize - 1) * strideW + 1) + ((kwSize - 1) * dilationW + 1) -
3593 case ConvOperationKind::Conv:
3594 rhsShape = {fSize, cSize, kwSize};
3596 case ConvOperationKind::Pool:
3597 rhsShape = {kwSize};
3600 resShape = {nSize, fSize, wSize};
3604 vector::TransferWriteOp write;
3610 int64_t wSizeStep = strideW == 1 ? wSize : 1;
3612 Type lhsEltType = lhsShapedType.getElementType();
3613 Type rhsEltType = rhsShapedType.getElementType();
3614 Type resEltType = resShapedType.getElementType();
3615 auto lhsType = VectorType::get(lhsShape, lhsEltType);
3616 auto rhsType = VectorType::get(rhsShape, rhsEltType);
3617 auto resType = VectorType::get(resShape, resEltType);
3619 SmallVector<Value> lhsPadding(lhsShape.size(), zero);
3620 SmallVector<Value> rhsPadding(rhsShape.size(), zero);
3621 SmallVector<Value> resPadding(resShape.size(), zero);
3624 Value
lhs = vector::TransferReadOp::create(
3625 rewriter, loc, lhsType, lhsShaped, lhsPadding,
3626 arith::getZeroConstant(rewriter, loc, lhsEltType));
3628 Value
rhs =
nullptr;
3629 if (oper == ConvOperationKind::Conv)
3630 rhs = vector::TransferReadOp::create(
3631 rewriter, loc, rhsType, rhsShaped, rhsPadding,
3632 arith::getZeroConstant(rewriter, loc, rhsEltType));
3633 Value res = vector::TransferReadOp::create(
3634 rewriter, loc, resType, resShaped, resPadding,
3635 arith::getZeroConstant(rewriter, loc, resEltType));
3640 switch (conv1DOpOrder) {
3648 static constexpr std::array<int64_t, 3> permLhs = {0, 2, 1};
3649 lhs = vector::TransposeOp::create(rewriter, loc,
lhs, permLhs);
3651 static constexpr std::array<int64_t, 3> permRhs = {2, 1, 0};
3654 if (oper == ConvOperationKind::Conv)
3655 rhs = vector::TransposeOp::create(rewriter, loc,
rhs, permRhs);
3657 static constexpr std::array<int64_t, 3> permRes = {0, 2, 1};
3658 res = vector::TransposeOp::create(rewriter, loc, res, permRes);
3667 SmallVector<Value> lhsVals, rhsVals, resVals;
3669 kwSize, strideW, dilationW, wSizeStep,
3672 if (oper == ConvOperationKind::Conv)
3675 wSizeStep, isSingleChanneled);
3677 auto linearIndex = [&](int64_t kw, int64_t w) {
3678 return kw * (wSize / wSizeStep) + w;
3684 for (int64_t kw = 0; kw < kwSize; ++kw) {
3685 for (int64_t w = 0; w < wSize; w += wSizeStep) {
3687 case ConvOperationKind::Conv:
3688 if (isSingleChanneled) {
3689 resVals[w] = conv1dSliceAsOuterProduct(rewriter, loc,
3690 lhsVals[linearIndex(kw, w)],
3691 rhsVals[kw], resVals[w]);
3693 resVals[w] = conv1dSliceAsContraction(rewriter, loc,
3694 lhsVals[linearIndex(kw, w)],
3695 rhsVals[kw], resVals[w]);
3698 case ConvOperationKind::Pool:
3699 resVals[w] = pool1dSlice(rewriter, loc, lhsVals[linearIndex(kw, w)],
3715 switch (conv1DOpOrder) {
3722 static constexpr std::array<int64_t, 3> perm = {0, 2, 1};
3723 res = vector::TransposeOp::create(rewriter, loc, res, perm);
3728 return vector::TransferWriteOp::create(rewriter, loc, res, resShaped,
3734 Value
promote(RewriterBase &rewriter, Location loc, Value val, Type ty,
3735 Operation *castOp) {
3740 assert(castOp &&
"expected a payload cast for promoted operand");
3744 if (
auto shapedType = dyn_cast<ShapedType>(val.
getType()))
3745 dstType = shapedType.cloneWith(std::nullopt, dstElementType);
3747 dstType = dstElementType;
3756 Value conv1dSliceAsContraction(RewriterBase &rewriter, Location loc,
3757 Value
lhs, Value
rhs, Value res) {
3758 vector::IteratorType par = vector::IteratorType::parallel;
3759 vector::IteratorType red = vector::IteratorType::reduction;
3760 AffineExpr n, w, f, c;
3764 auto contrationOp = vector::ContractionOp::create(
3765 rewriter, loc,
lhs,
rhs, res,
3766 MapList{{n, w, c}, {c, f}, {n, w, f}},
3767 ArrayRef<vector::IteratorType>{par, par, par, red});
3768 contrationOp.setKind(reductionKind);
3769 return contrationOp;
3774 Value conv1dSliceAsOuterProduct(RewriterBase &rewriter, Location loc,
3775 Value
lhs, Value
rhs, Value res) {
3778 return vector::OuterProductOp::create(rewriter, loc, res.
getType(),
lhs,
3779 rhs, res, vector::CombiningKind::ADD);
3783 Value pool1dSlice(RewriterBase &rewriter, Location loc, Value
lhs,
3801 FailureOr<Operation *> depthwiseConv(uint64_t channelDimVecSize,
3802 bool channelDimScalableFlag,
3804 bool scalableChDim =
false;
3805 bool useMasking =
false;
3806 int64_t nSize, wSize, cSize, kwSize;
3808 bindShapeDims(rhsShapedType, kwSize, cSize);
3809 if (ShapedType::isDynamic(cSize)) {
3810 assert(channelDimVecSize != 0 &&
"Channel dim vec size must be > 0");
3811 cSize = channelDimVecSize;
3815 scalableChDim = channelDimScalableFlag;
3819 assert(!(useMasking && flatten) &&
3820 "Unsupported flattened conv with dynamic shapes");
3823 bindShapeDims(resShapedType, nSize, wSize);
3825 vector::TransferWriteOp write;
3831 int64_t wSizeStep = strideW == 1 ? wSize : 1;
3833 Type lhsEltType = lhsShapedType.getElementType();
3834 Type rhsEltType = rhsShapedType.getElementType();
3835 Type resEltType = resShapedType.getElementType();
3836 VectorType lhsType = VectorType::get(
3840 ((wSize - 1) * strideW + 1) + ((kwSize - 1) * dilationW + 1) - 1,
3842 lhsEltType, {
false,
false, scalableChDim});
3843 VectorType rhsType =
3844 VectorType::get({kwSize, cSize}, rhsEltType,
3845 {
false, scalableChDim});
3846 VectorType resType =
3847 VectorType::get({nSize, wSize, cSize}, resEltType,
3848 {
false,
false, scalableChDim});
3852 auto maybeMaskXferOp = [&](ArrayRef<int64_t> maskShape,
3853 ArrayRef<bool> scalableDims,
3854 Operation *opToMask) {
3858 VectorType::get(maskShape, rewriter.
getI1Type(), scalableDims);
3860 SmallVector<bool> inBounds(maskShape.size(),
true);
3861 auto xferOp = cast<VectorTransferOpInterface>(opToMask);
3862 xferOp->setInherentAttr(
3867 cast<LinalgOp>(op).hasPureTensorSemantics(), opToMask, rewriter);
3870 vector::CreateMaskOp::create(rewriter, loc, maskType, mixedDims);
3877 Value
lhs = vector::TransferReadOp::create(
3878 rewriter, loc, lhsType, lhsShaped,
ValueRange{zero, zero, zero},
3879 arith::getZeroConstant(rewriter, loc, lhsEltType));
3880 auto *maybeMaskedLhs = maybeMaskXferOp(
3881 lhsType.getShape(), lhsType.getScalableDims(),
lhs.getDefiningOp());
3884 Value
rhs = vector::TransferReadOp::create(
3885 rewriter, loc, rhsType, rhsShaped,
ValueRange{zero, zero},
3886 arith::getZeroConstant(rewriter, loc, rhsEltType));
3887 auto *maybeMaskedRhs = maybeMaskXferOp(
3888 rhsType.getShape(), rhsType.getScalableDims(),
rhs.getDefiningOp());
3891 Value res = vector::TransferReadOp::create(
3892 rewriter, loc, resType, resShaped,
ValueRange{zero, zero, zero},
3893 arith::getZeroConstant(rewriter, loc, resEltType));
3894 auto *maybeMaskedRes = maybeMaskXferOp(
3895 resType.getShape(), resType.getScalableDims(), res.
getDefiningOp());
3901 SmallVector<Value> lhsVals, rhsVals, resVals;
3902 SmallVector<int64_t> inOutSliceSizes = {nSize, wSizeStep, cSize};
3903 SmallVector<int64_t> inOutStrides = {1, 1, 1};
3907 for (int64_t kw = 0; kw < kwSize; ++kw) {
3908 for (int64_t w = 0; w < wSize; w += wSizeStep) {
3909 lhsVals.push_back(vector::ExtractStridedSliceOp::create(
3910 rewriter, loc, maybeMaskedLhs->getResult(0),
3911 ArrayRef<int64_t>{0, w * strideW + kw * dilationW, 0},
3912 inOutSliceSizes, inOutStrides));
3916 for (int64_t kw = 0; kw < kwSize; ++kw) {
3918 vector::ExtractOp::create(rewriter, loc, maybeMaskedRhs->getResult(0),
3919 ArrayRef<int64_t>{kw}));
3922 for (int64_t w = 0; w < wSize; w += wSizeStep) {
3923 resVals.push_back(vector::ExtractStridedSliceOp::create(
3924 rewriter, loc, maybeMaskedRes->getResult(0),
3925 ArrayRef<int64_t>{0, w, 0}, inOutSliceSizes,
3929 auto linearIndex = [&](int64_t kw, int64_t w) {
3930 return kw * (wSize / wSizeStep) + w;
3935 SmallVector<int64_t> inOutFlattenSliceSizes = {nSize, wSizeStep * cSize};
3936 auto lhsTypeAfterFlattening =
3937 VectorType::get(inOutFlattenSliceSizes, lhsEltType);
3938 auto resTypeAfterFlattening =
3939 VectorType::get(inOutFlattenSliceSizes, resEltType);
3942 for (int64_t kw = 0; kw < kwSize; ++kw) {
3943 for (int64_t w = 0; w < wSize; w += wSizeStep) {
3944 Value lhsVal = lhsVals[linearIndex(kw, w)];
3945 Value resVal = resVals[w];
3950 vector::ShapeCastOp::create(rewriter, loc, lhsTypeAfterFlattening,
3951 lhsVals[linearIndex(kw, w)]);
3952 resVal = vector::ShapeCastOp::create(
3953 rewriter, loc, resTypeAfterFlattening, resVals[w]);
3955 resVals[w] = depthwiseConv1dSliceAsMulAcc(rewriter, loc, lhsVal,
3956 rhsVals[kw], resVal, flatten);
3959 resVals[w] = vector::ShapeCastOp::create(
3960 rewriter, loc, VectorType::get(inOutSliceSizes, resEltType),
3967 if (!llvm::all_of(resVals, [](Value v) {
return v; })) {
3969 for (
auto &collection :
3970 {resVals, rhsVals, lhsVals, {res,
rhs,
lhs, zero}})
3971 for (Value v : collection)
3978 for (int64_t w = 0; w < wSize; w += wSizeStep) {
3979 maybeMaskedRes = vector::InsertStridedSliceOp::create(
3980 rewriter, loc, resVals[w], maybeMaskedRes->getResult(0),
3981 ArrayRef<int64_t>{0, w, 0},
3982 ArrayRef<int64_t>{1, 1, 1});
3989 Operation *resOut = vector::TransferWriteOp::create(
3990 rewriter, loc, maybeMaskedRes->getResult(0), resShaped,
3992 return maybeMaskXferOp(resType.getShape(), resType.getScalableDims(),
4000 Value depthwiseConv1dSliceAsMulAcc(RewriterBase &rewriter, Location loc,
4001 Value
lhs, Value
rhs, Value res,
4003 auto rhsTy = cast<ShapedType>(
rhs.getType());
4004 auto resTy = cast<ShapedType>(res.
getType());
4018 auto rhsSize = cast<VectorType>(
rhs.getType()).getShape()[0];
4019 auto resSize = cast<VectorType>(res.
getType()).getShape()[1];
4021 SmallVector<int64_t, 16>
indices;
4022 for (
int i = 0; i < resSize / rhsSize; ++i) {
4023 for (
int j = 0; j < rhsSize; ++j)
4030 rhs = vector::BroadcastOp::create(rewriter, loc,
4031 resTy.clone(rhsTy.getElementType()),
rhs);
4038 if (isa<FloatType>(resTy.getElementType()))
4039 return vector::FMAOp::create(rewriter, loc,
lhs,
rhs, res);
4041 auto mul = arith::MulIOp::create(rewriter, loc,
lhs,
rhs);
4042 return arith::AddIOp::create(rewriter, loc,
mul, res);
4047 FailureOr<Operation *> generateNonChanneledConv() {
4050 if (!iters({Par(), Red()}))
4052 "failed to match conv::W 1-par 1-red");
4055 if (layout({ {w + kw},
4065 FailureOr<Operation *> generateNwcConv() {
4066 AffineExpr n, w, f, kw, c;
4068 if (!iters({Par(), Par(), Par(), Red(), Red()}))
4070 op,
"failed to match conv::Nwc 3-par 2-red");
4073 if (layout({ {n, strideW * w + dilationW * kw, c},
4083 FailureOr<Operation *> generateNcwConv() {
4084 AffineExpr n, w, f, kw, c;
4086 if (!iters({Par(), Par(), Par(), Red(), Red()}))
4088 op,
"failed to match conv::Ncw 3-par 2-red");
4090 if (layout({ {n, c, strideW * w + dilationW * kw},
4100 FailureOr<Operation *> generateNwcPooling() {
4101 AffineExpr n, w, c, kw;
4103 if (!iters({Par(), Par(), Par(), Red()}))
4105 "failed to match pooling 3-par 1-red");
4108 if (layout({ {n, strideW * w + dilationW * kw, c},
4118 FailureOr<Operation *> generateNcwPooling() {
4119 AffineExpr n, w, c, kw;
4121 if (!iters({Par(), Par(), Par(), Red()}))
4123 "failed to match pooling 3-par 1-red");
4125 if (layout({ {n, c, strideW * w + dilationW * kw},
4135 FailureOr<Operation *> generateDilatedConv(uint64_t vecChDimSize = 0,
4136 bool vecChDimScalableFlag =
false,
4137 bool flatten =
false) {
4138 AffineExpr n, w, c, kw;
4140 if (!iters({Par(), Par(), Par(), Red()}))
4142 op,
"failed to match depthwise::Nwc conv 3-par 1-red");
4145 if (layout({ {n, strideW * w + dilationW * kw, c},
4148 return depthwiseConv(vecChDimSize, vecChDimScalableFlag, flatten);
4154 ConvOperationKind oper = ConvOperationKind::Conv;
4156 StringAttr poolExtOp;
4157 bool isPoolExt =
false;
4160 Operation *lhsCastOp =
nullptr;
4161 Operation *rhsCastOp =
nullptr;
4162 int strideW, dilationW;
4163 Value lhsShaped, rhsShaped, resShaped;
4164 ShapedType lhsShapedType, rhsShapedType, resShapedType;
4165 vector::CombiningKind reductionKind;
4169 void setConvOperationKind(Operation *reduceOp) {
4170 int numBlockArguments =
4171 llvm::count_if(reduceOp->
getOperands(), llvm::IsaPred<BlockArgument>);
4172 if (numBlockArguments == 1) {
4177 auto feedValIt = llvm::find_if_not(reduceOp->
getOperands(),
4178 llvm::IsaPred<BlockArgument>);
4179 Operation *feedOp = (*feedValIt).getDefiningOp();
4180 if (isCastOfBlockArgument(feedOp)) {
4181 oper = ConvOperationKind::Pool;
4186 oper = ConvOperationKind::Conv;
4187 setConvCastOps(feedOp);
4191 oper = ConvOperationKind::Pool;
4196 void setConvCastOps(Operation *feedOp) {
4206 RewriterBase &rewriter, LinalgOp op, ArrayRef<int64_t> inputVecSizes,
4207 ArrayRef<bool> inputScalableVecDims,
bool flatten1DDepthwiseConv) {
4208 FailureOr<Conv1DGenerator> conv1dGen = Conv1DGenerator::create(rewriter, op);
4211 auto res = conv1dGen->generateNonChanneledConv();
4214 res = conv1dGen->generateNwcConv();
4217 res = conv1dGen->generateNcwConv();
4220 res = conv1dGen->generateNwcPooling();
4223 res = conv1dGen->generateNcwPooling();
4230 uint64_t vecChDimSize = ShapedType::kDynamic;
4231 bool vecChDimScalableFlag =
false;
4232 if (!inputVecSizes.empty()) {
4237 "Not a 1D depthwise conv!");
4238 size_t chDimIdx = 0;
4244 vecChDimSize = inputVecSizes[chDimIdx];
4245 vecChDimScalableFlag = inputScalableVecDims[chDimIdx];
4247 return conv1dGen->generateDilatedConv(vecChDimSize, vecChDimScalableFlag,
4248 flatten1DDepthwiseConv);
4251struct VectorizeConvolution :
public OpInterfaceRewritePattern<LinalgOp> {
4254 LogicalResult matchAndRewrite(LinalgOp op,
4255 PatternRewriter &rewriter)
const override {
4257 if (
failed(resultOrFail))
4259 Operation *newOp = *resultOrFail;
4261 rewriter.
eraseOp(op.getOperation());
4264 assert(newOp->
getNumResults() == 1 &&
"expected single result");
4271 RewritePatternSet &patterns, PatternBenefit benefit) {
4272 patterns.
add<VectorizeConvolution>(patterns.
getContext(), benefit);
static std::optional< VectorShape > vectorShape(Type type)
static SmallVector< Value > slice(OpBuilder &b, Location loc, Value value, VectorType nativeType)
static bool isLoopInvariantIdx(LinalgOp &linalgOp, Value &val, VectorType resType)
Checks whether val can be used for calculating a loop invariant index.
static Value insertConvResultSlices(RewriterBase &rewriter, Location loc, Value res, int64_t wSize, int64_t wSizeStep, SmallVectorImpl< Value > &resVals, bool isSingleChanneled)
Helper function to insert the computed result slices.
static SmallVector< bool > getDimsToReduce(LinalgOp linalgOp)
static VectorMemoryAccessKind getTensorExtractMemoryAccessPattern(tensor::ExtractOp extractOp, LinalgOp &linalgOp, VectorType resType)
Infer the memory access pattern for the input ExtractOp.
static SmallVector< Value > extractConvInputSlices(RewriterBase &rewriter, Location loc, Value input, int64_t nSize, int64_t wSize, int64_t cSize, int64_t kwSize, int strideW, int dilationW, int64_t wSizeStep, bool isSingleChanneled)
Helper function to extract the input slices after filter is unrolled along kw.
static VectorizationHookResult vectorizeTensorExtract(RewriterBase &rewriter, VectorizationState &state, Operation *op, LinalgOp linalgOp, const IRMapping &bvm)
Helper function to vectorize the tensor.extract operations.
static VectorizationHookResult vectorizeLinalgIndex(RewriterBase &rewriter, VectorizationState &state, Operation *op, LinalgOp linalgOp)
Helper function to vectorize the index operations of a linalgOp.
static LogicalResult vectorizeAsInsertSliceOp(RewriterBase &rewriter, tensor::InsertSliceOp sliceOp, ArrayRef< int64_t > inputVectorSizes, SmallVectorImpl< Value > &newResults)
Vectorize tensor::InsertSliceOp with:
static FailureOr< Operation * > vectorizeConvolution(RewriterBase &rewriter, LinalgOp convOp, ArrayRef< int64_t > inputVecSizes={}, ArrayRef< bool > inputVecScalableFlags={}, bool flatten1DDepthwiseConv=false)
Try to vectorize convOp as a convolution.
static LogicalResult vectorizeAsLinalgGeneric(RewriterBase &rewriter, VectorizationState &state, LinalgOp linalgOp, SmallVectorImpl< Value > &newResults)
Generic vectorization function that rewrites the body of a linalgOp into vector form.
#define MATCH_1D_CONV_POOL_OP(ConvOpTy)
static VectorizationHookResult vectorizeOneOp(RewriterBase &rewriter, VectorizationState &state, LinalgOp linalgOp, Operation *op, const IRMapping &bvm, ArrayRef< CustomVectorizationHook > customVectorizationHooks)
Generic vectorization for a single operation op, given already vectorized operands carried by bvm.
static Operation * matchLinalgReduction(OpOperand *outputOperand)
Check whether outputOperand is a reduction with a single combiner operation.
static Value buildVectorWrite(RewriterBase &rewriter, Value value, OpOperand *outputOperand, VectorizationState &state)
Build a vector.transfer_write of value into outputOperand at indices set to all 0; where outputOperan...
static Value getStaticPadVal(Operation *op)
Returns the effective Pad value for the input op, provided it's a scalar.
static SmallVector< Value > extractConvFilterSlices(RewriterBase &rewriter, Location loc, Value filter, int64_t kwSize)
Helper function to extract the filter slices after filter is unrolled along kw.
static bool hasReductionIterator(LinalgOp &op)
Check if op is a linalg.reduce or a linalg.generic that has at least one reduction iterator.
std::function< LogicalResult(Operation *, bool)> CustomVectorizationPrecondition
static uint64_t getTrailingNonUnitLoopDimIdx(LinalgOp linalgOp)
Find the index of the trailing non-unit dim in linalgOp.
static VectorType getCollapsedVecType(VectorType type, ArrayRef< AffineMap > reassociation)
Given the re-associations, "collapses" the input Vector type.
Conv1DOpOrder
Helper enum to represent conv1d input traversal order.
VectorizationHookStatus
Helper data structure to represent the result of vectorization for a single operation.
@ Failure
Op failed to vectorize.
@ NewOp
Op vectorized into a new Op whose results will replace original Op's results.
@ NoReplace
Op vectorized and custom function took care of replacement logic.
static Operation * reduceIfNeeded(OpBuilder &b, LinalgOp linalgOp, Operation *op, Value reduceValue, Value initialValue, const IRMapping &bvm)
Emit reduction operations if the shapes of the value to reduce is different that the result shape.
std::function< VectorizationHookResult(Operation *, const IRMapping &)> CustomVectorizationHook
static AffineMap reindexIndexingMap(AffineMap map)
Given an indexing map coming from a LinalgOp indexing, restricted to a projectedPermutation,...
static LogicalResult tensorExtractVectorizationPrecondition(Operation *op, bool vectorizeNDExtract)
Helper function to check if the tensor.extract can be vectorized by the custom hook vectorizeTensorEx...
static Value broadcastIfNeeded(OpBuilder &b, Value value, Type dstType)
Broadcast value to a vector of shape if possible.
static Value calculateGatherOffset(RewriterBase &rewriter, VectorizationState &state, tensor::ExtractOp extractOp, const IRMapping &bvm)
Calculates the offsets ($index_vec) for vector.gather operations generated from tensor....
static SmallVector< Value > extractConvResultSlices(RewriterBase &rewriter, Location loc, Value res, int64_t nSize, int64_t wSize, int64_t fSize, int64_t wSizeStep, bool isSingleChanneled)
Helper function to extract the result slices after filter is unrolled along kw.
static bool isContiguousLoadIdx(LinalgOp &linalgOp, Value &val, bool &foundIndexOp, VectorType resType)
Check whether val could be used for calculating the trailing index for a contiguous load operation.
static VectorizationHookResult vectorizeLinalgYield(RewriterBase &rewriter, Operation *op, const IRMapping &bvm, VectorizationState &state, LinalgOp linalgOp, SmallVectorImpl< Value > &newResults)
Helper function to vectorize the terminator of a linalgOp.
static Operation * buildMultiDimReduce(OpBuilder &b, Operation *reduceOp, Value valueToReduce, Value acc, ArrayRef< bool > dimsToMask)
Create MultiDimReductionOp to compute the reduction for reductionOp.
A dimensional identifier appearing in an affine expression.
A multi-dimensional affine map Affine map's are immutable like Type's, and they are uniqued.
static AffineMap getMinorIdentityMap(unsigned dims, unsigned results, MLIRContext *context)
Returns an identity affine map (d0, ..., dn) -> (dp, ..., dn) on the most minor dimensions.
MLIRContext * getContext() const
static AffineMap getMultiDimIdentityMap(unsigned numDims, MLIRContext *context)
Returns an AffineMap with 'numDims' identity result dim exprs.
static AffineMap get(MLIRContext *context)
Returns a zero result affine map with no dimensions or symbols: () -> ().
bool isProjectedPermutation(bool allowZeroInResults=false) const
Returns true if the AffineMap represents a subset (i.e.
unsigned getNumResults() const
unsigned getNumInputs() const
AffineExpr getResult(unsigned idx) const
static AffineMap getFilteredIdentityMap(MLIRContext *ctx, unsigned numDims, llvm::function_ref< bool(AffineDimExpr)> keepDimFilter)
Returns an identity affine map with numDims input dimensions and filtered results using keepDimFilter...
AffineMap dropZeroResults()
Returns the AffineMap resulting from removing "zero" results (constant values == 0) from this map.
static AffineMap getPermutationMap(ArrayRef< unsigned > permutation, MLIRContext *context)
Returns an AffineMap representing a permutation.
SmallVector< unsigned > getBroadcastDims() const
Returns the list of broadcast dimensions (i.e.
AffineMap compose(AffineMap map) const
Returns the AffineMap resulting from composing this with map.
bool isPermutation() const
Returns true if the AffineMap represents a symbol-less permutation map.
This class represents an argument of a Block.
unsigned getArgNumber() const
Returns the number of this argument.
Block represents an ordered list of Operations.
OpListType & getOperations()
AffineMap getMultiDimIdentityMap(unsigned rank)
StringAttr getStringAttr(const Twine &bytes)
TypedAttr getZeroAttr(Type type)
AffineExpr getAffineDimExpr(unsigned position)
ArrayAttr getArrayAttr(ArrayRef< Attribute > value)
MLIRContext * getContext() const
ArrayAttr getBoolArrayAttr(ArrayRef< bool > values)
static DenseIntElementsAttr get(const ShapedType &type, Arg &&arg)
Get an instance of a DenseIntElementsAttr with the given arguments.
This is a utility class for mapping one set of IR entities to another.
auto lookup(T from) const
Lookup a mapped value within the map.
void map(Value from, Value to)
Inserts a new mapping for 'from' to 'to'.
IRValueT get() const
Return the current value being used by this operand.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
MLIRContext is the top-level object for a collection of MLIR operations.
This class helps build Operations.
Operation * clone(Operation &op, IRMapping &mapper)
Creates a deep copy of the specified operation, remapping any operands that use values outside of the...
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
Operation * create(const OperationState &state)
Creates an operation given the fields represented as an OperationState.
Operation * insert(Operation *op)
Insert the given operation at the current insertion point and return it.
This class represents an operand of an operation.
unsigned getOperandNumber() const
Return which operand this is in the OpOperand list of the Operation.
StringAttr getIdentifier() const
Return the name of this operation as a StringAttr.
Operation is the basic unit of execution within MLIR.
PropertyRef getPropertiesStorage()
Return a generic (but typed) reference to the property type storage.
Value getOperand(unsigned idx)
bool isBeforeInBlock(Operation *other)
Given an operation 'other' that is within the same parent block, return whether the current operation...
Block * getBlock()
Returns the operation block that contains this operation.
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Location getLoc()
The source location the operation was defined or derived from.
unsigned getNumOperands()
Attribute getPropertiesAsAttribute()
Return the properties converted to an attribute.
operand_iterator operand_end()
OperationName getName()
The name of an operation is the key identifier for it.
DictionaryAttr getDiscardableAttrDictionary()
Return all of the discardable attributes on this operation as a DictionaryAttr.
static Operation * create(Location location, OperationName name, TypeRange resultTypes, ValueRange operands, NamedAttrList &&attributes, PropertyRef properties, BlockRange successors, unsigned numRegions)
Create a new Operation with the specific fields.
result_type_range getResultTypes()
operand_range getOperands()
Returns an iterator on the underlying Value's.
result_range getResults()
unsigned getNumResults()
Return the number of results held by this operation.
unsigned short getBenefit() const
If the corresponding pattern can match, return its benefit. If the.
MLIRContext * getContext() const
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
This class coordinates the application of a rewrite on a set of IR, providing a way for clients to tr...
virtual void replaceOp(Operation *op, ValueRange newValues)
Replace the results of the given (original) operation with the specified list of values (replacements...
virtual void eraseOp(Operation *op)
This method erases an operation that is known to have no uses.
void replaceAllUsesExcept(Value from, Value to, Operation *exceptedUser)
Find uses of from and replace them with to except if the user is exceptedUser.
std::enable_if_t<!std::is_convertible< CallbackT, Twine >::value, LogicalResult > notifyMatchFailure(Location loc, CallbackT &&reasonCallback)
Used to notify the listener that the IR failed to be rewritten because of a match failure,...
void modifyOpInPlace(Operation *root, CallableT &&callable)
This method is a utility wrapper around an in-place modification of an operation.
OpTy replaceOpWithNewOp(Operation *op, Args &&...args)
Replace the results of the given (original) op with a new op that is created without verification (re...
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Type getType() const
Return the type of this value.
use_range getUses() const
Returns a range of all uses, which is useful for iterating over all uses.
Location getLoc() const
Return the location of this value.
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
static ConstantIndexOp create(OpBuilder &builder, Location location, int64_t value)
Operation * getOwner() const
Return the owner of this operand.
bool hasElementwiseMappableTraits(Operation *op)
Together, Elementwise, Scalarizable, Vectorizable, and Tensorizable provide an easy way for scalar op...
bool hasVectorizationImpl(Operation *)
Return true if there's dedicated logic in the Linalg Vectorizer to vectorize this Op,...
SmallVector< int64_t > getUnPackInverseSrcPerm(linalg::UnPackOp, PackingMetadata &metadata)
Compute inverse permutation for the source tensor (i.e.
bool allIndexingsAreProjectedPermutation(LinalgOp op)
Check if all indexing maps are projected permutations.
FailureOr< VectorizationResult > vectorize(RewriterBase &rewriter, Operation *op, ArrayRef< int64_t > inputVectorSizes={}, ArrayRef< bool > inputScalableVecDims={}, bool vectorizeNDExtract=false, bool flatten1DDepthwiseConv=false, bool assumeDynamicDimsMatchVecSizes=false, bool createNamedContraction=false)
Returns a VectorizationResult containing the results of the vectorized op, or failure if the transfor...
void populatePadOpVectorizationPatterns(RewritePatternSet &patterns, PatternBenefit baseBenefit=1)
Populates patterns with patterns that vectorize tensor.pad.
bool isReductionIterator(utils::IteratorType iteratorType)
Check if iterator type has "reduction" semantics.
bool isaConvolutionOpInterface(LinalgOp linalgOp, bool allowEmptyConvolvedDims=false)
Checks whether linalgOp conforms to ConvolutionOpInterface.
void populateConvolutionVectorizationPatterns(RewritePatternSet &patterns, PatternBenefit benefit=1)
Populate patterns for vectorizing low-D convolution ops.
bool isElementwise(LinalgOp op)
Check if a LinalgOp is an element-wise operation.
LogicalResult vectorizeCopy(RewriterBase &builder, memref::CopyOp copyOp)
Emit a suitable vector form for a Copy op with fully static shape.
LogicalResult vectorizeOpPrecondition(Operation *op, ArrayRef< int64_t > inputVectorSizes={}, ArrayRef< bool > inputScalableVecDims={}, bool vectorizeNDExtract=false, bool flatten1DDepthwiseConv=false)
Return success if the operation can be vectorized.
SmallVector< int64_t > getPackInverseDestPerm(linalg::PackOp packOp, PackingMetadata &metadata)
Compute inverse permutation for the destination tensor (i.e.
bool isaConvolutionOpOfType(LinalgOp op)
Returns true if the linalg op is a convolution op of type ConvOpTy.
std::optional< vector::CombiningKind > getCombinerOpKind(Operation *combinerOp)
Return vector::CombiningKind for the given op.
LogicalResult promote(RewriterBase &rewriter, scf::ForallOp forallOp)
Promotes the loop body of a scf::ForallOp to its containing block.
std::enable_if_t<!is_complex< V >::value, V > readValue(char **linePtr)
Returns an element-value of non-complex type.
Operation * maskOperation(OpBuilder &builder, Operation *maskableOp, Value mask, Value passthru=Value())
Creates a vector.mask operation around a maskable operation.
LogicalResult isValidMaskedInputVector(ArrayRef< int64_t > shape, ArrayRef< int64_t > inputVectorSizes)
Returns success if inputVectorSizes is a valid masking configuraion for given shape,...
BroadcastableToResult isBroadcastableTo(Type srcType, VectorType dstVectorType, std::pair< VectorDim, VectorDim > *mismatchingDims=nullptr)
Return whether srcType can be broadcast to dstVectorType under the semantics of the vector....
Operation * createWriteOrMaskedWrite(OpBuilder &builder, Location loc, Value vecToStore, Value dest, SmallVector< Value > writeIndices={}, bool useInBoundsInsteadOfMasking=false, AffineMap permutationMap=AffineMap())
Create a TransferWriteOp of vecToStore into dest.
Value createReadOrMaskedRead(OpBuilder &builder, Location loc, Value source, const VectorType &vecToReadTy, std::optional< Value > padValue=std::nullopt, bool useInBoundsInsteadOfMasking=false, ArrayRef< Value > indices={}, AffineMap permutationMap=AffineMap())
Creates a TransferReadOp from source.
SmallVector< OpFoldResult > getMixedSizesXfer(bool hasTensorSemantics, Operation *xfer, RewriterBase &rewriter)
A wrapper for getMixedSizes for vector.transfer_read and vector.transfer_write Ops (for source and de...
Include the generated interface declarations.
bool matchPattern(Value value, const Pattern &pattern)
Entry point for matching a pattern over a Value.
bool isEqualConstantIntOrValue(OpFoldResult ofr1, OpFoldResult ofr2)
Return true if ofr1 and ofr2 are the same integer constant attribute values or the same SSA value.
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
AffineMap inverseAndBroadcastProjectedPermutation(AffineMap map)
Return the reverse map of a projected permutation where the projected dimensions are transformed into...
void bindDims(MLIRContext *ctx, AffineExprTy &...exprs)
Bind a list of AffineExpr references to DimExpr at positions: [0 .
AffineMap inversePermutation(AffineMap map)
Returns a map of codomain to domain dimensions such that the first codomain dimension for a particula...
SmallVector< AffineMap, 4 > getSymbolLessAffineMaps(ArrayRef< ReassociationExprs > reassociation)
Constructs affine maps out of Array<Array<AffineExpr>>.
SmallVector< SmallVector< OpFoldResult > > ReifiedRankedShapedTypeDims
Value matchReduction(ArrayRef< BlockArgument > iterCarriedArgs, unsigned redPos, SmallVectorImpl< Operation * > &combinerOps)
Utility to match a generic reduction given a list of iteration-carried arguments, iterCarriedArgs and...
llvm::SetVector< T, Vector, Set, N > SetVector
Type getElementTypeOrSelf(Type type)
Return the element type or return the type itself.
std::conditional_t< std::is_same_v< Ty, mlir::Type >, mlir::Value, detail::TypedValue< Ty > > TypedValue
If Ty is mlir::Type this will select Value instead of having a wrapper around it.
void getUsedValuesDefinedAbove(Region ®ion, Region &limit, SetVector< Value > &values)
Fill values with a list of values defined at the ancestors of the limit region and used within region...
AffineMap compressUnusedDims(AffineMap map)
Drop the dims that are not used.
SmallVector< SmallVector< AffineExpr, 2 >, 2 > convertReassociationIndicesToExprs(MLIRContext *context, ArrayRef< ReassociationIndices > reassociationIndices)
Convert reassociation indices to affine expressions.
bool isReassociationValid(ArrayRef< AffineMap > reassociation, int *invalidIndex=nullptr)
Return true if the reassociation specification is valid, false otherwise.
llvm::TypeSwitch< T, ResultT > TypeSwitch
Value getValueOrCreateConstantIndexOp(OpBuilder &b, Location loc, OpFoldResult ofr)
Converts an OpFoldResult to a Value.
AffineExpr getAffineConstantExpr(int64_t constant, MLIRContext *context)
auto get(MLIRContext *context, Ts &&...params)
Helper method that injects context only if needed, this helps unify some of the attribute constructio...
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
SmallVector< T > applyPermutationMap(AffineMap map, llvm::ArrayRef< T > source)
Apply a permutation from map to source and return the result.
llvm::SmallBitVector getUnusedDimsBitVector(ArrayRef< AffineMap > maps)
detail::constant_op_matcher m_Constant()
Matches a constant foldable operation.
void applyPermutationToVector(SmallVector< T, N > &inVec, ArrayRef< int64_t > permutation)
Apply the permutation defined by permutation to inVec.
SmallVector< int64_t > invertPermutationVector(ArrayRef< int64_t > permutation)
Helper method to apply to inverse a permutation.
VectorizationHookResult contains the vectorized op returned from a CustomVectorizationHook.
enum VectorizationHookStatus status
Return status from vectorizing the current op.
Operation * newOp
New vectorized operation to replace the current op.
ArrayRef< int64_t > getCanonicalVecShape() const
Returns the canonical vector shape used to vectorize the iteration space.
LogicalResult initState(RewriterBase &rewriter, LinalgOp linalgOp, ArrayRef< int64_t > inputVectorSizes, ArrayRef< bool > inputScalableVecDims, bool assumeDynamicDimsMatchVecSizes=false)
Initializes the vectorization state, including the computation of the canonical vector shape for vect...
Operation * maskOperation(RewriterBase &rewriter, Operation *opToMask, LinalgOp linalgOp, std::optional< AffineMap > maybeIndexingMap=std::nullopt)
Masks an operation with the canonical vector mask if the operation needs masking.
VectorType getCanonicalVecType(Type elementType, std::optional< AffineMap > dimPermutation=std::nullopt) const
Returns a vector type of the provided elementType with the canonical vector shape and the correspondi...
ArrayRef< bool > getScalableVecDims() const
Returns the vector dimensions that are scalable in the canonical vector shape.
VectorizationState(RewriterBase &rewriter)
LogicalResult matchAndRewrite(vector::TransferReadOp xferOp, PatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(vector::TransferWriteOp xferOp, PatternRewriter &rewriter) const override