29#include "llvm/ADT/STLExtras.h"
30#include "llvm/Support/Debug.h"
35#define GEN_PASS_DEF_AFFINEVECTORIZE
36#include "mlir/Dialect/Affine/Transforms/Passes.h.inc"
575#define DEBUG_TYPE "early-vect"
582 int fastestVaryingMemRefDimension);
588static std::optional<NestedPattern>
592 int64_t d0 = fastestVaryingPattern.empty() ? -1 : fastestVaryingPattern[0];
593 int64_t d1 = fastestVaryingPattern.size() < 2 ? -1 : fastestVaryingPattern[1];
594 int64_t d2 = fastestVaryingPattern.size() < 3 ? -1 : fastestVaryingPattern[2];
595 switch (vectorRank) {
613 llvm::IsaPred<vector::TransferReadOp, vector::TransferWriteOp>);
624 void runOnOperation()
override;
630 unsigned patternDepth,
631 VectorizationStrategy *strategy) {
632 assert(patternDepth > depthInPattern &&
633 "patternDepth is greater than depthInPattern");
634 if (patternDepth - depthInPattern > strategy->vectorSizes.size()) {
638 strategy->loopToVectorDim[loop] =
639 strategy->vectorSizes.size() - (patternDepth - depthInPattern);
658 unsigned depthInPattern,
659 unsigned patternDepth,
660 VectorizationStrategy *strategy) {
661 for (
auto m : matches) {
663 patternDepth, strategy))) {
667 patternDepth, strategy);
676struct VectorizationState {
689 void registerOpVectorReplacement(Operation *replaced, Operation *
replacement);
702 void registerValueVectorReplacement(Value replaced, Operation *
replacement);
709 void registerBlockArgVectorReplacement(BlockArgument replaced,
722 void registerValueScalarReplacement(Value replaced, Value
replacement);
734 void registerLoopResultScalarReplacement(Value replaced, Value
replacement);
738 void getScalarValueReplacementsFor(
ValueRange inputVals,
739 SmallVectorImpl<Value> &replacedVals);
742 void finishVectorizationPattern(AffineForOp rootLoop);
751 IRMapping valueVectorReplacement;
754 IRMapping valueScalarReplacement;
756 DenseMap<Value, Value> loopResultScalarReplacement;
766 const VectorizationStrategy *strategy =
nullptr;
771 void registerValueVectorReplacementImpl(Value replaced, Value
replacement);
785void VectorizationState::registerOpVectorReplacement(
Operation *replaced,
787 LLVM_DEBUG(dbgs() <<
"\n[early-vect]+++++ commit vectorized op:\n");
788 LLVM_DEBUG(dbgs() << *replaced <<
"\n");
789 LLVM_DEBUG(dbgs() <<
"into\n");
793 "Unexpected replaced and replacement results");
794 assert(opVectorReplacement.count(replaced) == 0 &&
"already registered");
797 for (
auto resultTuple :
799 registerValueVectorReplacementImpl(std::get<0>(resultTuple),
800 std::get<1>(resultTuple));
814void VectorizationState::registerValueVectorReplacement(
817 "Expected single-result replacement");
818 registerValueVectorReplacementImpl(replaced,
replacement->getResult(0));
826void VectorizationState::registerBlockArgVectorReplacement(
827 BlockArgument replaced, BlockArgument
replacement) {
828 registerValueVectorReplacementImpl(replaced,
replacement);
831void VectorizationState::registerValueVectorReplacementImpl(Value replaced,
833 assert(!valueVectorReplacement.
contains(replaced) &&
834 "Vector replacement already registered");
836 "Expected vector type in vector replacement");
850void VectorizationState::registerValueScalarReplacement(Value replaced,
852 assert(!valueScalarReplacement.
contains(replaced) &&
853 "Scalar value replacement already registered");
855 "Expected scalar type in scalar replacement");
868void VectorizationState::registerLoopResultScalarReplacement(
871 assert(loopResultScalarReplacement.count(replaced) == 0 &&
872 "already registered");
873 LLVM_DEBUG(dbgs() <<
"\n[early-vect]+++++ will replace a result of the loop "
876 loopResultScalarReplacement[replaced] =
replacement;
880void VectorizationState::getScalarValueReplacementsFor(
881 ValueRange inputVals, SmallVectorImpl<Value> &replacedVals) {
882 for (Value inputVal : inputVals)
883 replacedVals.push_back(valueScalarReplacement.
lookupOrDefault(inputVal));
888 LLVM_DEBUG(dbgs() <<
"[early-vect]+++++ erasing:\n" << forOp <<
"\n");
897 LLVM_DEBUG(dbgs() <<
"[early-vect]+++++ erasing user:\n" << *user <<
"\n");
903void VectorizationState::finishVectorizationPattern(AffineForOp rootLoop) {
904 LLVM_DEBUG(dbgs() <<
"\n[early-vect] Finalizing vectorization\n");
911 VectorizationState &state,
916 auto afOp = AffineApplyOp::create(state.builder, op->
getLoc(), singleResMap,
918 results.push_back(afOp);
927 int fastestVaryingMemRefDimension) {
928 return [¶llelLoops, fastestVaryingMemRefDimension](
Operation &forOp) {
929 auto loop = cast<AffineForOp>(forOp);
930 if (!parallelLoops.contains(loop))
933 auto vectorizableBody =
935 if (!vectorizableBody)
937 return memRefDim == -1 || fastestVaryingMemRefDimension == -1 ||
938 memRefDim == fastestVaryingMemRefDimension;
945 const VectorizationStrategy *strategy) {
946 assert(!isa<VectorType>(scalarTy) &&
"Expected scalar type");
947 return VectorType::get(strategy->vectorSizes, scalarTy);
954 VectorizationState &state) {
955 Type scalarTy = constOp.getType();
956 if (!VectorType::isValidElementType(scalarTy))
965 while (parentOp && !state.vecLoopToVecDim.count(parentOp))
967 assert(parentOp && state.vecLoopToVecDim.count(parentOp) &&
968 isa<AffineForOp>(parentOp) &&
"Expected a vectorized for op");
969 auto vecForOp = cast<AffineForOp>(parentOp);
972 arith::ConstantOp::create(state.builder, constOp.getLoc(), vecAttr);
975 state.registerOpVectorReplacement(constOp, newConstOp);
982 if (isa<IndexType>(scalarTy)) {
983 auto scalarConstOp = arith::ConstantOp::create(
984 state.builder, constOp.getLoc(), constOp.getValue());
985 state.registerValueScalarReplacement(constOp.getResult(),
986 scalarConstOp.getResult());
995 VectorizationState &state) {
997 for (
Value operand : applyOp.getOperands()) {
998 if (state.valueVectorReplacement.
contains(operand)) {
1000 dbgs() <<
"\n[early-vect]+++++ affine.apply on vector operand\n");
1004 if (!updatedOperand)
1005 updatedOperand = operand;
1006 updatedOperands.push_back(updatedOperand);
1009 auto newApplyOp = AffineApplyOp::create(
1010 state.builder, applyOp.getLoc(), applyOp.getAffineMap(), updatedOperands);
1013 state.registerValueScalarReplacement(applyOp.getResult(),
1014 newApplyOp.getResult());
1025 if (!VectorType::isValidElementType(scalarTy))
1029 reductionKind, scalarTy, state.builder, oldOperand.
getLoc());
1033 arith::ConstantOp::create(state.builder, oldOperand.
getLoc(), vecAttr);
1046 assert(state.strategy->vectorSizes.size() == 1 &&
1047 "Creating a mask non-1-D vectors is not supported.");
1048 assert(vecForOp.getStep() == state.strategy->vectorSizes[0] &&
1049 "Creating a mask for loops with non-unit original step size is not "
1053 if (
Value mask = state.vecLoopToMask.lookup(vecForOp))
1058 if (vecForOp.hasConstantBounds()) {
1060 vecForOp.getConstantUpperBound() - vecForOp.getConstantLowerBound();
1061 if (originalTripCount % vecForOp.getStepAsInt() == 0)
1066 state.builder.setInsertionPointToStart(vecForOp.getBody());
1082 AffineMap ubMap = vecForOp.getUpperBoundMap();
1085 ub = AffineApplyOp::create(state.builder, loc, vecForOp.getUpperBoundMap(),
1086 vecForOp.getUpperBoundOperands());
1088 ub = AffineMinOp::create(state.builder, loc, vecForOp.getUpperBoundMap(),
1089 vecForOp.getUpperBoundOperands());
1092 state.builder.getAffineDimExpr(0) - state.builder.getAffineDimExpr(1);
1095 {ub, vecForOp.getInductionVar()});
1098 ub.getDefiningOp()->erase();
1100 Type maskTy = VectorType::get(state.strategy->vectorSizes,
1101 state.builder.getIntegerType(1));
1103 vector::CreateMaskOp::create(state.builder, loc, maskTy, itersLeft);
1105 LLVM_DEBUG(dbgs() <<
"\n[early-vect]+++++ creating a mask:\n"
1106 << itersLeft <<
"\n"
1109 state.vecLoopToMask[vecForOp] = mask;
1119 const VectorizationStrategy *strategy) {
1121 if (forOp && strategy->loopToVectorDim.count(forOp) == 0)
1124 for (
auto loopToDim : strategy->loopToVectorDim) {
1125 auto loop = cast<AffineForOp>(loopToDim.first);
1126 if (!loop.isDefinedOutsideOfLoop(value))
1136 VectorizationState &state) {
1138 Value uniformScalarRepl =
1143 auto bcastOp = BroadcastOp::create(state.builder, uniformVal.
getLoc(),
1144 vectorTy, uniformScalarRepl);
1145 state.registerValueVectorReplacement(uniformVal, bcastOp);
1167 LLVM_DEBUG(dbgs() <<
"\n[early-vect]+++++ vectorize operand: " << operand);
1170 LLVM_DEBUG(dbgs() <<
" -> already vectorized: " << vecRepl);
1177 assert(!isa<VectorType>(operand.
getType()) &&
1178 "Vector op not found in replacement map");
1181 if (
auto constOp = operand.
getDefiningOp<arith::ConstantOp>()) {
1183 LLVM_DEBUG(dbgs() <<
"-> constant: " << vecConstant);
1184 return vecConstant.getResult();
1190 LLVM_DEBUG(dbgs() <<
"-> uniform: " << *vecUniform);
1197 LLVM_DEBUG(dbgs() <<
"-> unsupported block argument\n");
1200 LLVM_DEBUG(dbgs() <<
"-> non-vectorizable\n");
1209 for (
auto &kvp : loopToVectorDim) {
1210 AffineForOp forOp = cast<AffineForOp>(kvp.first);
1215 unsigned nonInvariant = 0;
1217 if (invariants.count(idx))
1220 if (++nonInvariant > 1) {
1221 LLVM_DEBUG(dbgs() <<
"[early‑vect] Bail out: IV "
1222 << forOp.getInductionVar() <<
" drives "
1223 << nonInvariant <<
" indices\n");
1238 VectorizationState &state) {
1239 MemRefType memRefType = loadOp.getMemRefType();
1240 Type elementType = memRefType.getElementType();
1241 auto vectorType = VectorType::get(state.strategy->vectorSizes, elementType);
1245 state.getScalarValueReplacementsFor(loadOp.getMapOperands(), mapOperands);
1249 indices.reserve(memRefType.getRank());
1250 if (loadOp.getAffineMap() !=
1253 for (
auto op : mapOperands) {
1254 if (op.getDefiningOp<AffineApplyOp>())
1260 indices.append(mapOperands.begin(), mapOperands.end());
1268 indices, state.vecLoopToVecDim);
1269 if (!permutationMap) {
1270 LLVM_DEBUG(dbgs() <<
"\n[early-vect]+++++ can't compute permutationMap\n");
1273 LLVM_DEBUG(dbgs() <<
"\n[early-vect]+++++ permutationMap: ");
1274 LLVM_DEBUG(permutationMap.print(dbgs()));
1277 state.builder, loadOp.getLoc(), loadOp.getMemRef(), vectorType,
1283 state.registerOpVectorReplacement(loadOp, transfer);
1294 VectorizationState &state) {
1295 MemRefType memRefType = storeOp.getMemRefType();
1302 state.getScalarValueReplacementsFor(storeOp.getMapOperands(), mapOperands);
1306 indices.reserve(memRefType.getRank());
1307 if (storeOp.getAffineMap() !=
1312 indices.append(mapOperands.begin(), mapOperands.end());
1319 indices, state.vecLoopToVecDim);
1320 if (!permutationMap)
1322 LLVM_DEBUG(dbgs() <<
"\n[early-vect]+++++ permutationMap: ");
1323 LLVM_DEBUG(permutationMap.print(dbgs()));
1327 if (llvm::any_of(permutationMap.getResults(),
1328 llvm::IsaPred<AffineConstantExpr>)) {
1329 LLVM_DEBUG(dbgs() <<
"\n[early-vect]+++++ store permutation map has "
1330 "broadcast dims, bailing out\n");
1335 state.builder, storeOp.getLoc(), vectorValue, storeOp.getMemRef(),
1337 true, permutationMap);
1338 LLVM_DEBUG(dbgs() <<
"\n[early-vect]+++++ vectorized store: " << *transfer);
1341 state.registerOpVectorReplacement(storeOp, transfer);
1348 Value value, VectorizationState &state) {
1350 if (!VectorType::isValidElementType(scalarTy))
1352 Attribute valueAttr = getIdentityValueAttr(reductionKind, scalarTy,
1353 state.builder, value.
getLoc());
1354 if (
auto constOp = value.
getDefiningOp<arith::ConstantOp>())
1355 return constOp.getValue() == valueAttr;
1366 VectorizationState &state) {
1367 const VectorizationStrategy &strategy = *state.strategy;
1368 auto loopToVecDimIt = strategy.loopToVectorDim.find(forOp);
1369 bool isLoopVecDim = loopToVecDimIt != strategy.loopToVectorDim.end();
1372 if (isLoopVecDim && forOp.getNumIterOperands() > 0 && forOp.getStep() != 1) {
1375 <<
"\n[early-vect]+++++ unsupported step size for reduction loop: "
1376 << forOp.getStep() <<
"\n");
1385 unsigned vectorDim = loopToVecDimIt->second;
1386 assert(vectorDim < strategy.vectorSizes.size() &&
"vector dim overflow");
1387 int64_t forOpVecFactor = strategy.vectorSizes[vectorDim];
1388 newStep = forOp.getStepAsInt() * forOpVecFactor;
1390 newStep = forOp.getStepAsInt();
1395 if (isLoopVecDim && forOp.getNumIterOperands() > 0) {
1396 auto it = strategy.reductionLoops.find(forOp);
1397 assert(it != strategy.reductionLoops.end() &&
1398 "Reduction descriptors not found when vectorizing a reduction loop");
1399 reductions = it->second;
1400 assert(reductions.size() == forOp.getNumIterOperands() &&
1401 "The size of reductions array must match the number of iter_args");
1406 if (!isLoopVecDim) {
1407 for (
auto operand : forOp.getInits())
1413 for (
auto redAndOperand : llvm::zip(reductions, forOp.getInits())) {
1415 std::get<0>(redAndOperand).kind, std::get<1>(redAndOperand), state));
1423 state.getScalarValueReplacementsFor(forOp.getLowerBoundOperands(),
1425 state.getScalarValueReplacementsFor(forOp.getUpperBoundOperands(),
1427 auto vecForOp = AffineForOp::create(
1428 state.builder, forOp.getLoc(), lbOperands, forOp.getLowerBoundMap(),
1429 ubOperands, forOp.getUpperBoundMap(), newStep, vecIterOperands,
1448 state.registerOpVectorReplacement(forOp, vecForOp);
1449 state.registerValueScalarReplacement(forOp.getInductionVar(),
1450 vecForOp.getInductionVar());
1451 for (
auto iterTuple :
1452 llvm ::zip(forOp.getRegionIterArgs(), vecForOp.getRegionIterArgs()))
1453 state.registerBlockArgVectorReplacement(std::get<0>(iterTuple),
1454 std::get<1>(iterTuple));
1457 for (
unsigned i = 0; i < vecForOp.getNumIterOperands(); ++i) {
1461 vecForOp.getLoc(), vecForOp.getResult(i));
1462 LLVM_DEBUG(dbgs() <<
"\n[early-vect]+++++ creating a vector reduction: "
1466 Value origInit = forOp.getOperand(forOp.getNumControlOperands() + i);
1467 Value finalRes = reducedRes;
1471 reducedRes.
getLoc(), reducedRes, origInit);
1472 state.registerLoopResultScalarReplacement(forOp.getResult(i), finalRes);
1477 state.vecLoopToVecDim[vecForOp] = loopToVecDimIt->second;
1485 if (isLoopVecDim && forOp.getNumIterOperands() > 0)
1497 vectorTypes.push_back(
1498 VectorType::get(state.strategy->vectorSizes,
result.getType()));
1504 LLVM_DEBUG(dbgs() <<
"\n[early-vect]+++++ an operand failed vectorize\n");
1507 vectorOperands.push_back(vecOperand);
1520 state.registerOpVectorReplacement(op, vecOp);
1529 VectorizationState &state) {
1542 if (
Value mask = state.vecLoopToMask.lookup(newParentOp)) {
1547 cast<AffineForOp>(newParentOp).getRegionIterArgs(), i, combinerOps);
1548 assert(reducedVal &&
"expect non-null value for parallel reduction loop");
1549 assert(combinerOps.size() == 1 &&
"expect only one combiner op");
1551 Value neutralVal = cast<AffineForOp>(newParentOp).getInits()[i];
1553 Value maskedReducedVal = arith::SelectOp::create(
1554 state.builder, reducedVal.
getLoc(), mask, reducedVal, neutralVal);
1556 dbgs() <<
"\n[early-vect]+++++ masking an input to a binary op that"
1557 "produces value for a yield Op: "
1558 << maskedReducedVal);
1559 combinerOps.back()->replaceUsesOfWith(reducedVal, maskedReducedVal);
1577 VectorizationState &state) {
1579 assert(!isa<vector::TransferReadOp>(op) &&
1580 "vector.transfer_read cannot be further vectorized");
1581 assert(!isa<vector::TransferWriteOp>(op) &&
1582 "vector.transfer_write cannot be further vectorized");
1584 if (
auto loadOp = dyn_cast<AffineLoadOp>(op))
1586 if (
auto storeOp = dyn_cast<AffineStoreOp>(op))
1588 if (
auto forOp = dyn_cast<AffineForOp>(op))
1590 if (
auto yieldOp = dyn_cast<AffineYieldOp>(op))
1592 if (
auto constant = dyn_cast<arith::ConstantOp>(op))
1594 if (
auto applyOp = dyn_cast<AffineApplyOp>(op))
1612 assert(currentLevel <= loops.size() &&
"Unexpected currentLevel");
1613 if (currentLevel == loops.size())
1614 loops.emplace_back();
1638 const VectorizationStrategy &strategy) {
1639 assert(loops[0].size() == 1 &&
"Expected single root loop");
1640 AffineForOp rootLoop = loops[0][0];
1641 VectorizationState state(rootLoop.getContext());
1643 state.strategy = &strategy;
1653 LLVM_DEBUG(dbgs() <<
"\n[early-vect]+++++ loop is not vectorizable");
1666 LLVM_DEBUG(dbgs() <<
"[early-vect]+++++ Vectorizing: " << *op);
1670 dbgs() <<
"[early-vect]+++++ failed vectorizing the operation: "
1678 if (opVecResult.wasInterrupted()) {
1679 LLVM_DEBUG(dbgs() <<
"[early-vect]+++++ failed vectorization for: "
1680 << rootLoop <<
"\n");
1682 auto vecRootLoopIt = state.opVectorReplacement.find(rootLoop);
1683 if (vecRootLoopIt != state.opVectorReplacement.end()) {
1684 auto vecRootLoop = cast<AffineForOp>(vecRootLoopIt->second);
1694 for (
auto resPair : state.loopResultScalarReplacement)
1695 resPair.first.replaceAllUsesWith(resPair.second);
1697 assert(state.opVectorReplacement.count(rootLoop) == 1 &&
1698 "Expected vector replacement for loop nest");
1699 LLVM_DEBUG(dbgs() <<
"\n[early-vect]+++++ success vectorizing pattern");
1700 LLVM_DEBUG(dbgs() <<
"\n[early-vect]+++++ vectorization result:\n"
1701 << *state.opVectorReplacement[rootLoop]);
1704 state.finishVectorizationPattern(rootLoop);
1712 const VectorizationStrategy &strategy) {
1713 std::vector<SmallVector<AffineForOp, 2>> loopsToVectorize;
1725 assert(intersectionBuckets.empty() &&
"Expected empty output");
1730 AffineForOp matchRoot = cast<AffineForOp>(match.getMatchedOperation());
1731 bool intersects =
false;
1732 for (
int i = 0, end = intersectionBuckets.size(); i < end; ++i) {
1733 AffineForOp bucketRoot = bucketRoots[i];
1735 if (bucketRoot->isAncestor(matchRoot)) {
1736 intersectionBuckets[i].push_back(match);
1742 if (matchRoot->isAncestor(bucketRoot)) {
1743 bucketRoots[i] = matchRoot;
1744 intersectionBuckets[i].push_back(match);
1753 bucketRoots.push_back(matchRoot);
1754 intersectionBuckets.emplace_back();
1755 intersectionBuckets.back().push_back(match);
1770 assert((reductionLoops.empty() || vectorSizes.size() == 1) &&
1771 "Vectorizing reductions is supported only for 1-D vectors");
1774 std::optional<NestedPattern> pattern =
1775 makePattern(loops, vectorSizes.size(), fastestVaryingPattern);
1777 LLVM_DEBUG(dbgs() <<
"\n[early-vect] pattern couldn't be computed\n");
1781 LLVM_DEBUG(dbgs() <<
"\n******************************************");
1782 LLVM_DEBUG(dbgs() <<
"\n******************************************");
1783 LLVM_DEBUG(dbgs() <<
"\n[early-vect] new pattern on parent op\n");
1784 LLVM_DEBUG(dbgs() << *parentOp <<
"\n");
1786 unsigned patternDepth = pattern->getDepth();
1791 pattern->match(parentOp, &allMatches);
1792 std::vector<SmallVector<NestedMatch, 8>> intersectionBuckets;
1798 for (
auto &intersectingMatches : intersectionBuckets) {
1800 VectorizationStrategy strategy;
1802 strategy.vectorSizes.assign(vectorSizes.begin(), vectorSizes.end());
1803 strategy.reductionLoops = reductionLoops;
1805 patternDepth, &strategy))) {
1819 LLVM_DEBUG(dbgs() <<
"\n");
1822void affine::vectorizeChildAffineLoops(
1823 Operation *parentOp,
bool vectorizeReductions,
1824 ArrayRef<int64_t> vectorSizes, ArrayRef<int64_t> fastestVaryingPattern) {
1830 if (vectorizeReductions) {
1831 parentOp->
walk([¶llelLoops, &reductionLoops](AffineForOp loop) {
1832 SmallVector<LoopReduction, 2> reductions;
1833 if (isLoopParallel(loop, &reductions)) {
1834 parallelLoops.insert(loop);
1836 if (!reductions.empty())
1837 reductionLoops[loop] = reductions;
1841 parentOp->
walk([¶llelLoops](AffineForOp loop) {
1842 if (isLoopParallel(loop))
1843 parallelLoops.insert(loop);
1848 NestedPatternContext mlContext;
1849 vectorizeLoops(parentOp, parallelLoops, vectorSizes, fastestVaryingPattern,
1855void Vectorize::runOnOperation() {
1856 func::FuncOp f = getOperation();
1857 if (vectorSizes.empty()) {
1858 f.emitError(
"The 'virtual-vector-size' option must be specified.");
1859 return signalPassFailure();
1862 if (llvm::any_of(vectorSizes, [](int64_t size) {
return size <= 0; })) {
1864 "The 'virtual-vector-size' option must contain only positive values.");
1865 return signalPassFailure();
1868 if (!fastestVaryingPattern.empty() &&
1869 fastestVaryingPattern.size() != vectorSizes.size()) {
1870 f.emitRemark(
"Fastest varying pattern specified with different size than "
1871 "the vector size.");
1872 return signalPassFailure();
1875 if (vectorizeReductions && vectorSizes.size() != 1) {
1876 f.emitError(
"Vectorizing reductions is supported only for 1-D vectors.");
1877 return signalPassFailure();
1880 vectorizeChildAffineLoops(f, vectorizeReductions, vectorSizes,
1881 fastestVaryingPattern);
1897 if (loops[0].size() != 1)
1901 for (
int i = 1, end = loops.size(); i < end; ++i) {
1902 for (AffineForOp loop : loops[i]) {
1905 if (none_of(loops[i - 1], [&](AffineForOp maybeParent) {
1906 return maybeParent->isProperAncestor(loop);
1912 for (AffineForOp sibling : loops[i]) {
1913 if (sibling->isProperAncestor(loop))
1930void mlir::affine::vectorizeAffineLoops(
1932 ArrayRef<int64_t> vectorSizes, ArrayRef<int64_t> fastestVaryingPattern,
1935 NestedPatternContext mlContext;
1936 vectorizeLoops(parentOp, loops, vectorSizes, fastestVaryingPattern,
1975LogicalResult mlir::affine::vectorizeAffineLoopNest(
1976 std::vector<SmallVector<AffineForOp, 2>> &loops,
1977 const VectorizationStrategy &strategy) {
1979 NestedPatternContext mlContext;
*if copies could not be generated due to yet unimplemented cases *copyInPlacementStart and copyOutPlacementStart in copyPlacementBlock *specify the insertion points where the incoming copies and outgoing should be the output argument nBegin is set to its * replacement(set to `begin` if no invalidation happens). Since outgoing *copies could have been inserted at `end`
static Operation * vectorizeUniform(Value uniformVal, VectorizationState &state)
Generates a broadcast op for the provided uniform value using the vectorization strategy in 'state'.
static std::optional< NestedPattern > makePattern(const DenseSet< Operation * > ¶llelLoops, int vectorRank, ArrayRef< int64_t > fastestVaryingPattern)
Creates a vectorization pattern from the command line arguments.
static LogicalResult vectorizeRootMatch(NestedMatch m, const VectorizationStrategy &strategy)
Extracts the matched loops and vectorizes them following a topological order.
static void vectorizeLoopIfProfitable(Operation *loop, unsigned depthInPattern, unsigned patternDepth, VectorizationStrategy *strategy)
static LogicalResult verifyLoopNesting(const std::vector< SmallVector< AffineForOp, 2 > > &loops)
Verify that affine loops in 'loops' meet the nesting criteria expected by SuperVectorizer:
static Operation * vectorizeOneOperation(Operation *op, VectorizationState &state)
Encodes Operation-specific behavior for vectorization.
static bool isNeutralElementConst(arith::AtomicRMWKind reductionKind, Value value, VectorizationState &state)
Returns true if value is a constant equal to the neutral element of the given vectorizable reduction.
static void eraseUsers(Operation *op)
Recursively erases the users of the results of 'op'.
static LogicalResult vectorizeLoopNest(std::vector< SmallVector< AffineForOp, 2 > > &loops, const VectorizationStrategy &strategy)
Internal implementation to vectorize affine loops from a single loop nest using an n-D vectorization ...
static Operation * vectorizeAffineLoad(AffineLoadOp loadOp, VectorizationState &state)
Vectorizes an affine load with the vectorization strategy in 'state' by generating a 'vector....
static Operation * vectorizeAffineForOp(AffineForOp forOp, VectorizationState &state)
Vectorizes a loop with the vectorization strategy in 'state'.
static Operation * vectorizeAffineApplyOp(AffineApplyOp applyOp, VectorizationState &state)
We have no need to vectorize affine.apply.
static LogicalResult analyzeProfitability(ArrayRef< NestedMatch > matches, unsigned depthInPattern, unsigned patternDepth, VectorizationStrategy *strategy)
Implements a simple strawman strategy for vectorization.
static FilterFunctionType isVectorizableLoopPtrFactory(const DenseSet< Operation * > ¶llelLoops, int fastestVaryingMemRefDimension)
Forward declaration.
static bool isIVMappedToMultipleIndices(ArrayRef< Value > indices, const DenseMap< Operation *, unsigned > &loopToVectorDim)
Returns true if any vectorized loop IV drives more than one index.
static arith::ConstantOp vectorizeConstant(arith::ConstantOp constOp, VectorizationState &state)
Tries to transform a scalar constant into a vector constant.
static bool isUniformDefinition(Value value, const VectorizationStrategy *strategy)
Returns true if the provided value is vector uniform given the vectorization strategy.
static void eraseLoopNest(AffineForOp forOp)
Erases a loop nest, including all its nested operations.
static VectorType getVectorType(Type scalarTy, const VectorizationStrategy *strategy)
Returns the vector type resulting from applying the provided vectorization strategy on the scalar typ...
static void getMatchedAffineLoops(NestedMatch match, std::vector< SmallVector< AffineForOp, 2 > > &loops)
Converts all the nested loops in 'match' to a 2D vector container that preserves the relative nesting...
static Value vectorizeOperand(Value operand, VectorizationState &state)
Tries to vectorize a given operand by applying the following logic:
static void getMatchedAffineLoopsRec(NestedMatch match, unsigned currentLevel, std::vector< SmallVector< AffineForOp, 2 > > &loops)
Recursive implementation to convert all the nested loops in 'match' to a 2D vector container that pre...
static Operation * vectorizeAffineYieldOp(AffineYieldOp yieldOp, VectorizationState &state)
Vectorizes a yield operation by widening its types.
static arith::ConstantOp createInitialVector(arith::AtomicRMWKind reductionKind, Value oldOperand, VectorizationState &state)
Creates a constant vector filled with the neutral elements of the given reduction.
static Operation * widenOp(Operation *op, VectorizationState &state)
Vectorizes arbitrary operation by plain widening.
static Operation * vectorizeAffineStore(AffineStoreOp storeOp, VectorizationState &state)
Vectorizes an affine store with the vectorization strategy in 'state' by generating a 'vector....
static NestedPattern & vectorTransferPattern()
static void vectorizeLoops(Operation *parentOp, DenseSet< Operation * > &loops, ArrayRef< int64_t > vectorSizes, ArrayRef< int64_t > fastestVaryingPattern, const ReductionLoopMap &reductionLoops)
Internal implementation to vectorize affine loops in 'loops' using the n-D vectorization factors in '...
static void computeMemoryOpIndices(Operation *op, AffineMap map, ValueRange mapOperands, VectorizationState &state, SmallVectorImpl< Value > &results)
static void computeIntersectionBuckets(ArrayRef< NestedMatch > matches, std::vector< SmallVector< NestedMatch, 8 > > &intersectionBuckets)
Traverses all the loop matches and classifies them into intersection buckets.
static Value createMask(AffineForOp vecForOp, VectorizationState &state)
Creates a mask used to filter out garbage elements in the last iteration of unaligned loops.
static AffineMap makePermutationMap(ArrayRef< Value > indices, const DenseMap< Operation *, unsigned > &enclosingLoopToVectorDim)
Constructs a permutation map from memref indices to vector dimension.
Base type for affine expression.
A multi-dimensional affine map Affine map's are immutable like Type's, and they are uniqued.
static AffineMap get(MLIRContext *context)
Returns a zero result affine map with no dimensions or symbols: () -> ().
unsigned getNumSymbols() const
unsigned getNumDims() const
ArrayRef< AffineExpr > getResults() const
unsigned getNumResults() const
Attributes are known-constant values of operations.
Operation * getParentOp()
Returns the closest surrounding operation that contains this block.
AffineMap getMultiDimIdentityMap(unsigned rank)
static DenseElementsAttr get(ShapedType type, ArrayRef< Attribute > values)
Constructs a dense elements attribute from an array of element values.
auto lookupOrDefault(T from) const
Lookup a mapped value within the map.
void map(Value from, Value to)
Inserts a new mapping for 'from' to 'to'.
bool contains(T from) const
Checks to see if a mapping for 'from' exists.
auto lookupOrNull(T from) const
Lookup a mapped value within the map.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
RAII guard to reset the insertion point of the builder when destroyed.
This class helps build Operations.
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
Block * getInsertionBlock() const
Return the block the current insertion point belongs to.
void setInsertionPointAfterValue(Value val)
Sets the insertion point to the node after the specified value.
Operation * create(const OperationState &state)
Creates an operation given the fields represented as an OperationState.
void setInsertionPointAfter(Operation *op)
Sets the insertion point to the node after the specified operation, which will cause subsequent inser...
Operation is the basic unit of execution within MLIR.
bool use_empty()
Returns true if this operation has no uses.
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
unsigned getNumRegions()
Returns the number of regions held by this operation.
Location getLoc()
The source location the operation was defined or derived from.
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
unsigned getNumOperands()
Attribute getPropertiesAsAttribute()
Return the properties converted to an attribute.
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.
operand_range getOperands()
Returns an iterator on the underlying Value's.
std::enable_if_t< llvm::function_traits< std::decay_t< FnT > >::num_args==1, RetT > walk(FnT &&callback)
Walk the operation by calling the callback for each nested operation (including this one),...
result_range getResults()
user_iterator user_begin()
void erase()
Remove this operation from its parent block and delete it.
unsigned getNumResults()
Return the number of results held by this operation.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
bool isIntOrIndexOrFloat() const
Return true if this is an integer (of any signedness), index, or float type.
This class provides an abstraction over the different types of ranges over Values.
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.
Location getLoc() const
Return the location of this value.
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
static WalkResult advance()
static WalkResult interrupt()
An NestedPattern captures nested patterns in the IR.
ArrayRef< NestedMatch > getMatchedChildren()
Operation * getMatchedOperation() const
NestedPattern For(const NestedPattern &child)
NestedPattern Op(FilterFunctionType filter=defaultFilterFunction)
AffineApplyOp makeComposedAffineApply(OpBuilder &b, Location loc, AffineMap map, ArrayRef< OpFoldResult > operands, bool composeAffineMin=false)
Returns a composed AffineApplyOp by composing map and operands with other AffineApplyOps supplying th...
DenseMap< Operation *, SmallVector< LoopReduction, 2 > > ReductionLoopMap
bool isVectorizableLoopBody(AffineForOp loop, NestedPattern &vectorTransferMatcher)
Checks whether the loop is structurally vectorizable; i.e.:
DenseSet< Value, DenseMapInfo< Value > > getInvariantAccesses(Value iv, ArrayRef< Value > indices)
Given an induction variable iv of type AffineForOp and indices of type IndexType, returns the set of ...
AffineForOp getForInductionVarOwner(Value val)
Returns the loop parent of an induction variable.
std::function< bool(Operation &)> FilterFunctionType
A NestedPattern is a nested operation walker that:
Value getReductionOp(AtomicRMWKind op, OpBuilder &builder, Location loc, Value lhs, Value rhs)
Returns the value obtained by applying the reduction operation kind associated with a binary AtomicRM...
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.
Value getVectorReductionOp(arith::AtomicRMWKind op, OpBuilder &builder, Location loc, Value vector)
Returns the value obtained by reducing the vector into a scalar using the operation kind associated w...
Include the generated interface declarations.
llvm::DenseSet< ValueT, ValueInfoT > DenseSet
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::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
Contains the vectorization state and related methods used across the vectorization process of a given...
VectorizationState(RewriterBase &rewriter)
This represents an operation in an abstracted form, suitable for use with the builder APIs.
Attribute propertiesAttr
This Attribute is used to opaquely construct the properties of the operation.