Skip to content

Commit adac0aa

Browse files
committed
[LV] Vectorize Epilogues for loops with small VF but high IC
- Consider MainLoopVF * IC when determining whether Epilogue Vectorization is profitable - Allow the same VF for the Epilogue as for the main loop - Use an upper bound for the trip count of the Epilogue when choosing the Epilogue VF
1 parent f4dd1bc commit adac0aa

File tree

8 files changed

+723
-196
lines changed

8 files changed

+723
-196
lines changed

llvm/lib/Transforms/Vectorize/LoopVectorizationPlanner.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,10 @@ class LoopVectorizationPlanner {
471471
bool isMoreProfitable(const VectorizationFactor &A,
472472
const VectorizationFactor &B) const;
473473

474+
bool isMoreProfitable(const VectorizationFactor &A,
475+
const VectorizationFactor &B,
476+
const unsigned MaxTripCount) const;
477+
474478
/// Determines if we have the infrastructure to vectorize the loop and its
475479
/// epilogue, assuming the main loop is vectorized by \p VF.
476480
bool isCandidateForEpilogueVectorization(const ElementCount VF) const;

llvm/lib/Transforms/Vectorize/LoopVectorize.cpp

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1537,7 +1537,8 @@ class LoopVectorizationCostModel {
15371537
/// Returns true if epilogue vectorization is considered profitable, and
15381538
/// false otherwise.
15391539
/// \p VF is the vectorization factor chosen for the original loop.
1540-
bool isEpilogueVectorizationProfitable(const ElementCount VF) const;
1540+
bool isEpilogueVectorizationProfitable(const ElementCount VF,
1541+
const unsigned Multiplier) const;
15411542

15421543
/// Returns the execution time cost of an instruction for a given vector
15431544
/// width. Vector width of one means scalar.
@@ -4282,12 +4283,11 @@ getVScaleForTuning(const Loop *L, const TargetTransformInfo &TTI) {
42824283
}
42834284

42844285
bool LoopVectorizationPlanner::isMoreProfitable(
4285-
const VectorizationFactor &A, const VectorizationFactor &B) const {
4286+
const VectorizationFactor &A, const VectorizationFactor &B,
4287+
const unsigned MaxTripCount) const {
42864288
InstructionCost CostA = A.Cost;
42874289
InstructionCost CostB = B.Cost;
42884290

4289-
unsigned MaxTripCount = PSE.getSE()->getSmallConstantMaxTripCount(OrigLoop);
4290-
42914291
// Improve estimate for the vector width if it is scalable.
42924292
unsigned EstimatedWidthA = A.Width.getKnownMinValue();
42934293
unsigned EstimatedWidthB = B.Width.getKnownMinValue();
@@ -4336,6 +4336,13 @@ bool LoopVectorizationPlanner::isMoreProfitable(
43364336
return CmpFn(RTCostA, RTCostB);
43374337
}
43384338

4339+
bool LoopVectorizationPlanner::isMoreProfitable(
4340+
const VectorizationFactor &A, const VectorizationFactor &B) const {
4341+
const unsigned MaxTripCount =
4342+
PSE.getSE()->getSmallConstantMaxTripCount(OrigLoop);
4343+
return LoopVectorizationPlanner::isMoreProfitable(A, B, MaxTripCount);
4344+
}
4345+
43394346
void LoopVectorizationPlanner::emitInvalidCostRemarks(
43404347
OptimizationRemarkEmitter *ORE) {
43414348
using RecipeVFPair = std::pair<VPRecipeBase *, ElementCount>;
@@ -4648,7 +4655,7 @@ bool LoopVectorizationPlanner::isCandidateForEpilogueVectorization(
46484655
}
46494656

46504657
bool LoopVectorizationCostModel::isEpilogueVectorizationProfitable(
4651-
const ElementCount VF) const {
4658+
const ElementCount VF, const unsigned Multiplier) const {
46524659
// FIXME: We need a much better cost-model to take different parameters such
46534660
// as register pressure, code size increase and cost of extra branches into
46544661
// account. For now we apply a very crude heuristic and only consider loops
@@ -4663,9 +4670,6 @@ bool LoopVectorizationCostModel::isEpilogueVectorizationProfitable(
46634670
if (TTI.getMaxInterleaveFactor(VF) <= 1)
46644671
return false;
46654672

4666-
unsigned Multiplier = 1;
4667-
if (VF.isScalable())
4668-
Multiplier = getVScaleForTuning(TheLoop, TTI).value_or(1);
46694673
if ((Multiplier * VF.getKnownMinValue()) >= EpilogueVectorizationMinVF)
46704674
return true;
46714675
return false;
@@ -4711,7 +4715,11 @@ VectorizationFactor LoopVectorizationPlanner::selectEpilogueVectorizationFactor(
47114715
return Result;
47124716
}
47134717

4714-
if (!CM.isEpilogueVectorizationProfitable(MainLoopVF)) {
4718+
unsigned Multiplier = IC;
4719+
if (MainLoopVF.isScalable())
4720+
Multiplier = getVScaleForTuning(OrigLoop, TTI).value_or(1);
4721+
4722+
if (!CM.isEpilogueVectorizationProfitable(MainLoopVF, Multiplier)) {
47154723
LLVM_DEBUG(dbgs() << "LEV: Epilogue vectorization is not profitable for "
47164724
"this loop\n");
47174725
return Result;
@@ -4730,16 +4738,20 @@ VectorizationFactor LoopVectorizationPlanner::selectEpilogueVectorizationFactor(
47304738
ScalarEvolution &SE = *PSE.getSE();
47314739
Type *TCType = Legal->getWidestInductionType();
47324740
const SCEV *RemainingIterations = nullptr;
4741+
unsigned MaxTripCount = 0;
47334742
for (auto &NextVF : ProfitableVFs) {
47344743
// Skip candidate VFs without a corresponding VPlan.
47354744
if (!hasPlanWithVF(NextVF.Width))
47364745
continue;
47374746

4738-
// Skip candidate VFs with widths >= the estimate runtime VF (scalable
4739-
// vectors) or the VF of the main loop (fixed vectors).
4747+
// Skip candidate VFs with widths >= the (estimated) runtime VF (scalable
4748+
// vectors) or > the VF of the main loop (fixed vectors).
47404749
if ((!NextVF.Width.isScalable() && MainLoopVF.isScalable() &&
47414750
ElementCount::isKnownGE(NextVF.Width, EstimatedRuntimeVF)) ||
4742-
ElementCount::isKnownGE(NextVF.Width, MainLoopVF))
4751+
(NextVF.Width.isScalable() &&
4752+
ElementCount::isKnownGE(NextVF.Width, MainLoopVF)) ||
4753+
(!NextVF.Width.isScalable() && !MainLoopVF.isScalable() &&
4754+
ElementCount::isKnownGT(NextVF.Width, MainLoopVF)))
47434755
continue;
47444756

47454757
// If NextVF is greater than the number of remaining iterations, the
@@ -4750,6 +4762,14 @@ VectorizationFactor LoopVectorizationPlanner::selectEpilogueVectorizationFactor(
47504762
const SCEV *TC = createTripCountSCEV(TCType, PSE, OrigLoop);
47514763
RemainingIterations = SE.getURemExpr(
47524764
TC, SE.getConstant(TCType, MainLoopVF.getKnownMinValue() * IC));
4765+
const APInt MaxRemainingIterations =
4766+
SE.getUnsignedRangeMax(RemainingIterations);
4767+
// Guard against huge trip counts.
4768+
if (MaxRemainingIterations.getActiveBits() <= 32) {
4769+
MaxTripCount = MaxRemainingIterations.getZExtValue();
4770+
LLVM_DEBUG(dbgs() << "LEV: Maximum Trip Count for Epilogue: "
4771+
<< MaxTripCount << "\n");
4772+
}
47534773
}
47544774
if (SE.isKnownPredicate(
47554775
CmpInst::ICMP_UGT,
@@ -4758,7 +4778,8 @@ VectorizationFactor LoopVectorizationPlanner::selectEpilogueVectorizationFactor(
47584778
continue;
47594779
}
47604780

4761-
if (Result.Width.isScalar() || isMoreProfitable(NextVF, Result))
4781+
if (Result.Width.isScalar() ||
4782+
isMoreProfitable(NextVF, Result, MaxTripCount))
47624783
Result = NextVF;
47634784
}
47644785

llvm/test/Transforms/LoopVectorize/AArch64/deterministic-type-shrinkage.ll

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ define void @test_pr25490(i32 %n, ptr noalias nocapture %a, ptr noalias nocaptur
1616
; CHECK-NEXT: br i1 [[CMP_28]], label [[FOR_COND_CLEANUP:%.*]], label [[ITER_CHECK:%.*]]
1717
; CHECK: iter.check:
1818
; CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[N]] to i64
19-
; CHECK-NEXT: [[MIN_ITERS_CHECK:%.*]] = icmp ult i32 [[N]], 8
19+
; CHECK-NEXT: [[MIN_ITERS_CHECK:%.*]] = icmp ult i32 [[N]], 4
2020
; CHECK-NEXT: br i1 [[MIN_ITERS_CHECK]], label [[VEC_EPILOG_SCALAR_PH:%.*]], label [[VECTOR_MAIN_LOOP_ITER_CHECK:%.*]]
2121
; CHECK: vector.main.loop.iter.check:
2222
; CHECK-NEXT: [[MIN_ITERS_CHECK1:%.*]] = icmp ult i32 [[N]], 16
@@ -50,33 +50,33 @@ define void @test_pr25490(i32 %n, ptr noalias nocapture %a, ptr noalias nocaptur
5050
; CHECK-NEXT: [[CMP_N:%.*]] = icmp eq i64 [[N_VEC]], [[TMP0]]
5151
; CHECK-NEXT: br i1 [[CMP_N]], label [[FOR_COND_CLEANUP_LOOPEXIT:%.*]], label [[VEC_EPILOG_ITER_CHECK:%.*]]
5252
; CHECK: vec.epilog.iter.check:
53-
; CHECK-NEXT: [[N_VEC_REMAINING:%.*]] = and i64 [[TMP0]], 8
54-
; CHECK-NEXT: [[MIN_EPILOG_ITERS_CHECK_NOT_NOT:%.*]] = icmp eq i64 [[N_VEC_REMAINING]], 0
55-
; CHECK-NEXT: br i1 [[MIN_EPILOG_ITERS_CHECK_NOT_NOT]], label [[VEC_EPILOG_SCALAR_PH]], label [[VEC_EPILOG_PH]]
53+
; CHECK-NEXT: [[N_VEC_REMAINING:%.*]] = and i64 [[TMP0]], 12
54+
; CHECK-NEXT: [[MIN_EPILOG_ITERS_CHECK:%.*]] = icmp eq i64 [[N_VEC_REMAINING]], 0
55+
; CHECK-NEXT: br i1 [[MIN_EPILOG_ITERS_CHECK]], label [[VEC_EPILOG_SCALAR_PH]], label [[VEC_EPILOG_PH]]
5656
; CHECK: vec.epilog.ph:
5757
; CHECK-NEXT: [[VEC_EPILOG_RESUME_VAL:%.*]] = phi i64 [ [[N_VEC]], [[VEC_EPILOG_ITER_CHECK]] ], [ 0, [[VECTOR_MAIN_LOOP_ITER_CHECK]] ]
58-
; CHECK-NEXT: [[N_VEC5:%.*]] = and i64 [[TMP0]], 4294967288
58+
; CHECK-NEXT: [[N_VEC5:%.*]] = and i64 [[TMP0]], 4294967292
5959
; CHECK-NEXT: br label [[VEC_EPILOG_VECTOR_BODY:%.*]]
6060
; CHECK: vec.epilog.vector.body:
6161
; CHECK-NEXT: [[INDEX7:%.*]] = phi i64 [ [[VEC_EPILOG_RESUME_VAL]], [[VEC_EPILOG_PH]] ], [ [[INDEX_NEXT11:%.*]], [[VEC_EPILOG_VECTOR_BODY]] ]
6262
; CHECK-NEXT: [[TMP14:%.*]] = getelementptr inbounds i8, ptr [[C]], i64 [[INDEX7]]
63-
; CHECK-NEXT: [[WIDE_LOAD8:%.*]] = load <8 x i8>, ptr [[TMP14]], align 1
63+
; CHECK-NEXT: [[WIDE_LOAD8:%.*]] = load <4 x i8>, ptr [[TMP14]], align 1
6464
; CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds i8, ptr [[A]], i64 [[INDEX7]]
65-
; CHECK-NEXT: [[WIDE_LOAD9:%.*]] = load <8 x i8>, ptr [[TMP15]], align 1
66-
; CHECK-NEXT: [[TMP16:%.*]] = zext <8 x i8> [[WIDE_LOAD9]] to <8 x i16>
67-
; CHECK-NEXT: [[TMP17:%.*]] = zext <8 x i8> [[WIDE_LOAD8]] to <8 x i16>
68-
; CHECK-NEXT: [[TMP18:%.*]] = mul nuw <8 x i16> [[TMP16]], [[TMP17]]
69-
; CHECK-NEXT: [[TMP19:%.*]] = lshr <8 x i16> [[TMP18]], <i16 8, i16 8, i16 8, i16 8, i16 8, i16 8, i16 8, i16 8>
70-
; CHECK-NEXT: [[TMP20:%.*]] = trunc nuw <8 x i16> [[TMP19]] to <8 x i8>
71-
; CHECK-NEXT: store <8 x i8> [[TMP20]], ptr [[TMP15]], align 1
65+
; CHECK-NEXT: [[WIDE_LOAD9:%.*]] = load <4 x i8>, ptr [[TMP15]], align 1
66+
; CHECK-NEXT: [[TMP16:%.*]] = zext <4 x i8> [[WIDE_LOAD9]] to <4 x i16>
67+
; CHECK-NEXT: [[TMP17:%.*]] = zext <4 x i8> [[WIDE_LOAD8]] to <4 x i16>
68+
; CHECK-NEXT: [[TMP18:%.*]] = mul nuw <4 x i16> [[TMP16]], [[TMP17]]
69+
; CHECK-NEXT: [[TMP19:%.*]] = lshr <4 x i16> [[TMP18]], <i16 8, i16 8, i16 8, i16 8>
70+
; CHECK-NEXT: [[TMP20:%.*]] = trunc nuw <4 x i16> [[TMP19]] to <4 x i8>
71+
; CHECK-NEXT: store <4 x i8> [[TMP20]], ptr [[TMP15]], align 1
7272
; CHECK-NEXT: [[TMP21:%.*]] = getelementptr inbounds i8, ptr [[B]], i64 [[INDEX7]]
73-
; CHECK-NEXT: [[WIDE_LOAD10:%.*]] = load <8 x i8>, ptr [[TMP21]], align 1
74-
; CHECK-NEXT: [[TMP22:%.*]] = zext <8 x i8> [[WIDE_LOAD10]] to <8 x i16>
75-
; CHECK-NEXT: [[TMP23:%.*]] = mul nuw <8 x i16> [[TMP22]], [[TMP17]]
76-
; CHECK-NEXT: [[TMP24:%.*]] = lshr <8 x i16> [[TMP23]], <i16 8, i16 8, i16 8, i16 8, i16 8, i16 8, i16 8, i16 8>
77-
; CHECK-NEXT: [[TMP25:%.*]] = trunc nuw <8 x i16> [[TMP24]] to <8 x i8>
78-
; CHECK-NEXT: store <8 x i8> [[TMP25]], ptr [[TMP21]], align 1
79-
; CHECK-NEXT: [[INDEX_NEXT11]] = add nuw i64 [[INDEX7]], 8
73+
; CHECK-NEXT: [[WIDE_LOAD10:%.*]] = load <4 x i8>, ptr [[TMP21]], align 1
74+
; CHECK-NEXT: [[TMP22:%.*]] = zext <4 x i8> [[WIDE_LOAD10]] to <4 x i16>
75+
; CHECK-NEXT: [[TMP23:%.*]] = mul nuw <4 x i16> [[TMP22]], [[TMP17]]
76+
; CHECK-NEXT: [[TMP24:%.*]] = lshr <4 x i16> [[TMP23]], <i16 8, i16 8, i16 8, i16 8>
77+
; CHECK-NEXT: [[TMP25:%.*]] = trunc nuw <4 x i16> [[TMP24]] to <4 x i8>
78+
; CHECK-NEXT: store <4 x i8> [[TMP25]], ptr [[TMP21]], align 1
79+
; CHECK-NEXT: [[INDEX_NEXT11]] = add nuw i64 [[INDEX7]], 4
8080
; CHECK-NEXT: [[TMP26:%.*]] = icmp eq i64 [[INDEX_NEXT11]], [[N_VEC5]]
8181
; CHECK-NEXT: br i1 [[TMP26]], label [[VEC_EPILOG_MIDDLE_BLOCK:%.*]], label [[VEC_EPILOG_VECTOR_BODY]], !llvm.loop [[LOOP3:![0-9]+]]
8282
; CHECK: vec.epilog.middle.block:

0 commit comments

Comments
 (0)