Skip to content

Commit e8219e5

Browse files
authored
[VPlan] Use BlockFrequencyInfo in getPredBlockCostDivisor (#158690)
In 531.deepsjeng_r from SPEC CPU 2017 there's a loop that we unprofitably loop vectorize on RISC-V. The loop looks something like: ```c for (int i = 0; i < n; i++) { if (x0[i] == a) if (x1[i] == b) if (x2[i] == c) // do stuff... } ``` Because it's so deeply nested the actual inner level of the loop rarely gets executed. However we still deem it profitable to vectorize, which due to the if-conversion means we now always execute the body. This stems from the fact that `getPredBlockCostDivisor` currently assumes that blocks have 50% chance of being executed as a heuristic. We can fix this by using BlockFrequencyInfo, which gives a more accurate estimate of the innermost block being executed 12.5% of the time. We can then calculate the probability as `HeaderFrequency / BlockFrequency`. Fixing the cost here gives a 7% speedup for 531.deepsjeng_r on RISC-V. Whilst there's a lot of changes in the in-tree tests, this doesn't affect llvm-test-suite or SPEC CPU 2017 that much: - On armv9-a -flto -O3 there's 0.0%/0.2% more geomean loops vectorized on llvm-test-suite/SPEC CPU 2017. - On x86-64 -flto -O3 **with PGO** there's 0.9%/0% less geomean loops vectorized on llvm-test-suite/SPEC CPU 2017. Overall geomean compile time impact is 0.03% on stage1-ReleaseLTO: https://llvm-compile-time-tracker.com/compare.php?from=9eee396c58d2e24beb93c460141170def328776d&to=32fbff48f965d03b51549fdf9bbc4ca06473b623&stat=instructions%3Au
1 parent dd06214 commit e8219e5

File tree

11 files changed

+463
-42
lines changed

11 files changed

+463
-42
lines changed

llvm/include/llvm/Transforms/Vectorize/LoopVectorizationLegality.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,7 @@ class LoopVectorizationLegality {
364364

365365
/// Return true if the block BB needs to be predicated in order for the loop
366366
/// to be vectorized.
367-
bool blockNeedsPredication(BasicBlock *BB) const;
367+
bool blockNeedsPredication(const BasicBlock *BB) const;
368368

369369
/// Check if this pointer is consecutive when vectorizing. This happens
370370
/// when the last index of the GEP is the induction variable, or that the

llvm/include/llvm/Transforms/Vectorize/LoopVectorize.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ struct LoopVectorizePass : public PassInfoMixin<LoopVectorizePass> {
145145
LoopInfo *LI;
146146
TargetTransformInfo *TTI;
147147
DominatorTree *DT;
148-
BlockFrequencyInfo *BFI;
148+
std::function<BlockFrequencyInfo &()> GetBFI;
149149
TargetLibraryInfo *TLI;
150150
DemandedBits *DB;
151151
AssumptionCache *AC;

llvm/lib/Transforms/Vectorize/LoopVectorizationLegality.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1443,7 +1443,8 @@ bool LoopVectorizationLegality::isFixedOrderRecurrence(
14431443
return FixedOrderRecurrences.count(Phi);
14441444
}
14451445

1446-
bool LoopVectorizationLegality::blockNeedsPredication(BasicBlock *BB) const {
1446+
bool LoopVectorizationLegality::blockNeedsPredication(
1447+
const BasicBlock *BB) const {
14471448
// When vectorizing early exits, create predicates for the latch block only.
14481449
// The early exiting block must be a direct predecessor of the latch at the
14491450
// moment.

llvm/lib/Transforms/Vectorize/LoopVectorize.cpp

Lines changed: 62 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@
146146
#include "llvm/Transforms/Vectorize/LoopVectorizationLegality.h"
147147
#include <algorithm>
148148
#include <cassert>
149+
#include <cmath>
149150
#include <cstdint>
150151
#include <functional>
151152
#include <iterator>
@@ -873,12 +874,14 @@ class LoopVectorizationCostModel {
873874
const TargetTransformInfo &TTI,
874875
const TargetLibraryInfo *TLI, DemandedBits *DB,
875876
AssumptionCache *AC,
876-
OptimizationRemarkEmitter *ORE, const Function *F,
877-
const LoopVectorizeHints *Hints,
877+
OptimizationRemarkEmitter *ORE,
878+
std::function<BlockFrequencyInfo &()> GetBFI,
879+
const Function *F, const LoopVectorizeHints *Hints,
878880
InterleavedAccessInfo &IAI, bool OptForSize)
879881
: ScalarEpilogueStatus(SEL), TheLoop(L), PSE(PSE), LI(LI), Legal(Legal),
880-
TTI(TTI), TLI(TLI), DB(DB), AC(AC), ORE(ORE), TheFunction(F),
881-
Hints(Hints), InterleaveInfo(IAI), OptForSize(OptForSize) {
882+
TTI(TTI), TLI(TLI), DB(DB), AC(AC), ORE(ORE), GetBFI(GetBFI),
883+
TheFunction(F), Hints(Hints), InterleaveInfo(IAI),
884+
OptForSize(OptForSize) {
882885
if (TTI.supportsScalableVectors() || ForceTargetSupportsScalableVectors)
883886
initializeVScaleForTuning();
884887
CostKind = F->hasMinSize() ? TTI::TCK_CodeSize : TTI::TCK_RecipThroughput;
@@ -1219,7 +1222,7 @@ class LoopVectorizationCostModel {
12191222
/// for which our chosen predication strategy is scalarization (i.e. we
12201223
/// don't have an alternate strategy such as masking available).
12211224
/// \p VF is the vectorization factor that will be used to vectorize \p I.
1222-
bool isScalarWithPredication(Instruction *I, ElementCount VF) const;
1225+
bool isScalarWithPredication(Instruction *I, ElementCount VF);
12231226

12241227
/// Returns true if \p I is an instruction that needs to be predicated
12251228
/// at runtime. The result is independent of the predication mechanism.
@@ -1234,29 +1237,19 @@ class LoopVectorizationCostModel {
12341237
/// optimizing for code size it will just be 1 as code size costs don't depend
12351238
/// on execution probabilities.
12361239
///
1237-
/// TODO: We should use actual block probability here, if available.
1238-
/// Currently, we always assume predicated blocks have a 50% chance of
1239-
/// executing, apart from blocks that are only predicated due to tail folding.
1240+
/// Note that if a block wasn't originally predicated but was predicated due
1241+
/// to tail folding, the divisor will still be 1 because it will execute for
1242+
/// every iteration of the loop header.
12401243
inline unsigned
12411244
getPredBlockCostDivisor(TargetTransformInfo::TargetCostKind CostKind,
1242-
BasicBlock *BB) const {
1243-
// If a block wasn't originally predicated but was predicated due to
1244-
// e.g. tail folding, don't divide the cost. Tail folded loops may still be
1245-
// predicated in the final vector loop iteration, but for most loops that
1246-
// don't have low trip counts we can expect their probability to be close to
1247-
// zero.
1248-
if (!Legal->blockNeedsPredication(BB))
1249-
return 1;
1250-
return CostKind == TTI::TCK_CodeSize ? 1 : 2;
1251-
}
1245+
const BasicBlock *BB);
12521246

12531247
/// Return the costs for our two available strategies for lowering a
12541248
/// div/rem operation which requires speculating at least one lane.
12551249
/// First result is for scalarization (will be invalid for scalable
12561250
/// vectors); second is for the safe-divisor strategy.
12571251
std::pair<InstructionCost, InstructionCost>
1258-
getDivRemSpeculationCost(Instruction *I,
1259-
ElementCount VF) const;
1252+
getDivRemSpeculationCost(Instruction *I, ElementCount VF);
12601253

12611254
/// Returns true if \p I is a memory instruction with consecutive memory
12621255
/// access that can be widened.
@@ -1729,6 +1722,20 @@ class LoopVectorizationCostModel {
17291722
/// Interface to emit optimization remarks.
17301723
OptimizationRemarkEmitter *ORE;
17311724

1725+
/// A function to lazily fetch BlockFrequencyInfo. This avoids computing it
1726+
/// unless necessary, e.g. when the loop isn't legal to vectorize or when
1727+
/// there is no predication.
1728+
std::function<BlockFrequencyInfo &()> GetBFI;
1729+
/// The BlockFrequencyInfo returned from GetBFI.
1730+
BlockFrequencyInfo *BFI = nullptr;
1731+
/// Returns the BlockFrequencyInfo for the function if cached, otherwise
1732+
/// fetches it via GetBFI. Avoids an indirect call to the std::function.
1733+
BlockFrequencyInfo &getBFI() {
1734+
if (!BFI)
1735+
BFI = &GetBFI();
1736+
return *BFI;
1737+
}
1738+
17321739
const Function *TheFunction;
17331740

17341741
/// Loop Vectorize Hint.
@@ -2792,8 +2799,8 @@ void LoopVectorizationCostModel::collectLoopScalars(ElementCount VF) {
27922799
Scalars[VF].insert_range(Worklist);
27932800
}
27942801

2795-
bool LoopVectorizationCostModel::isScalarWithPredication(
2796-
Instruction *I, ElementCount VF) const {
2802+
bool LoopVectorizationCostModel::isScalarWithPredication(Instruction *I,
2803+
ElementCount VF) {
27972804
if (!isPredicatedInst(I))
27982805
return false;
27992806

@@ -2886,9 +2893,26 @@ bool LoopVectorizationCostModel::isPredicatedInst(Instruction *I) const {
28862893
}
28872894
}
28882895

2896+
unsigned LoopVectorizationCostModel::getPredBlockCostDivisor(
2897+
TargetTransformInfo::TargetCostKind CostKind, const BasicBlock *BB) {
2898+
if (CostKind == TTI::TCK_CodeSize)
2899+
return 1;
2900+
// If the block wasn't originally predicated then return early to avoid
2901+
// computing BlockFrequencyInfo unnecessarily.
2902+
if (!Legal->blockNeedsPredication(BB))
2903+
return 1;
2904+
2905+
uint64_t HeaderFreq =
2906+
getBFI().getBlockFreq(TheLoop->getHeader()).getFrequency();
2907+
uint64_t BBFreq = getBFI().getBlockFreq(BB).getFrequency();
2908+
assert(HeaderFreq >= BBFreq &&
2909+
"Header has smaller block freq than dominated BB?");
2910+
return std::round((double)HeaderFreq / BBFreq);
2911+
}
2912+
28892913
std::pair<InstructionCost, InstructionCost>
28902914
LoopVectorizationCostModel::getDivRemSpeculationCost(Instruction *I,
2891-
ElementCount VF) const {
2915+
ElementCount VF) {
28922916
assert(I->getOpcode() == Instruction::UDiv ||
28932917
I->getOpcode() == Instruction::SDiv ||
28942918
I->getOpcode() == Instruction::SRem ||
@@ -9182,8 +9206,9 @@ static bool processLoopInVPlanNativePath(
91829206
Loop *L, PredicatedScalarEvolution &PSE, LoopInfo *LI, DominatorTree *DT,
91839207
LoopVectorizationLegality *LVL, TargetTransformInfo *TTI,
91849208
TargetLibraryInfo *TLI, DemandedBits *DB, AssumptionCache *AC,
9185-
OptimizationRemarkEmitter *ORE, bool OptForSize, LoopVectorizeHints &Hints,
9186-
LoopVectorizationRequirements &Requirements) {
9209+
OptimizationRemarkEmitter *ORE,
9210+
std::function<BlockFrequencyInfo &()> GetBFI, bool OptForSize,
9211+
LoopVectorizeHints &Hints, LoopVectorizationRequirements &Requirements) {
91879212

91889213
if (isa<SCEVCouldNotCompute>(PSE.getBackedgeTakenCount())) {
91899214
LLVM_DEBUG(dbgs() << "LV: cannot compute the outer-loop trip count\n");
@@ -9196,8 +9221,8 @@ static bool processLoopInVPlanNativePath(
91969221
ScalarEpilogueLowering SEL =
91979222
getScalarEpilogueLowering(F, L, Hints, OptForSize, TTI, TLI, *LVL, &IAI);
91989223

9199-
LoopVectorizationCostModel CM(SEL, L, PSE, LI, LVL, *TTI, TLI, DB, AC, ORE, F,
9200-
&Hints, IAI, OptForSize);
9224+
LoopVectorizationCostModel CM(SEL, L, PSE, LI, LVL, *TTI, TLI, DB, AC, ORE,
9225+
GetBFI, F, &Hints, IAI, OptForSize);
92019226
// Use the planner for outer loop vectorization.
92029227
// TODO: CM is not used at this point inside the planner. Turn CM into an
92039228
// optional argument if we don't need it in the future.
@@ -9897,8 +9922,10 @@ bool LoopVectorizePass::processLoop(Loop *L) {
98979922

98989923
// Query this against the original loop and save it here because the profile
98999924
// of the original loop header may change as the transformation happens.
9900-
bool OptForSize = llvm::shouldOptimizeForSize(L->getHeader(), PSI, BFI,
9901-
PGSOQueryType::IRPass);
9925+
bool OptForSize = llvm::shouldOptimizeForSize(
9926+
L->getHeader(), PSI,
9927+
PSI && PSI->hasProfileSummary() ? &GetBFI() : nullptr,
9928+
PGSOQueryType::IRPass);
99029929

99039930
// Check if it is legal to vectorize the loop.
99049931
LoopVectorizationRequirements Requirements;
@@ -9932,7 +9959,8 @@ bool LoopVectorizePass::processLoop(Loop *L) {
99329959
// pipeline.
99339960
if (!L->isInnermost())
99349961
return processLoopInVPlanNativePath(L, PSE, LI, DT, &LVL, TTI, TLI, DB, AC,
9935-
ORE, OptForSize, Hints, Requirements);
9962+
ORE, GetBFI, OptForSize, Hints,
9963+
Requirements);
99369964

99379965
assert(L->isInnermost() && "Inner loop expected.");
99389966

@@ -10035,7 +10063,7 @@ bool LoopVectorizePass::processLoop(Loop *L) {
1003510063

1003610064
// Use the cost model.
1003710065
LoopVectorizationCostModel CM(SEL, L, PSE, LI, &LVL, *TTI, TLI, DB, AC, ORE,
10038-
F, &Hints, IAI, OptForSize);
10066+
GetBFI, F, &Hints, IAI, OptForSize);
1003910067
// Use the planner for vectorization.
1004010068
LoopVectorizationPlanner LVP(L, LI, DT, TLI, *TTI, &LVL, CM, IAI, PSE, Hints,
1004110069
ORE);
@@ -10353,9 +10381,9 @@ PreservedAnalyses LoopVectorizePass::run(Function &F,
1035310381

1035410382
auto &MAMProxy = AM.getResult<ModuleAnalysisManagerFunctionProxy>(F);
1035510383
PSI = MAMProxy.getCachedResult<ProfileSummaryAnalysis>(*F.getParent());
10356-
BFI = nullptr;
10357-
if (PSI && PSI->hasProfileSummary())
10358-
BFI = &AM.getResult<BlockFrequencyAnalysis>(F);
10384+
GetBFI = [&AM, &F]() -> BlockFrequencyInfo & {
10385+
return AM.getResult<BlockFrequencyAnalysis>(F);
10386+
};
1035910387
LoopVectorizeResult Result = runImpl(F);
1036010388
if (!Result.MadeAnyChange)
1036110389
return PreservedAnalyses::all();

llvm/test/Transforms/LoopVectorize/AArch64/early_exit_costs.ll

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,8 @@ define i64 @same_exit_block_pre_inc_use1_nosve() {
5757
; CHECK-NEXT: Cost of 48 for VF 16: EMIT vp<{{.*}}> = first-active-lane ir<%cmp3>
5858
; CHECK-NEXT: Cost of 0 for VF 16: EMIT vp<{{.*}}> = add
5959
; CHECK-NEXT: Cost of 0 for VF 16: vp<{{.*}}> = DERIVED-IV
60-
; CHECK: LV: Minimum required TC for runtime checks to be profitable:160
61-
; CHECK-NEXT: LV: Vectorization is not beneficial: expected trip count < minimum profitable VF (64 < 160)
60+
; CHECK: LV: Minimum required TC for runtime checks to be profitable:128
61+
; CHECK-NEXT: LV: Vectorization is not beneficial: expected trip count < minimum profitable VF (64 < 128)
6262
; CHECK-NEXT: LV: Too many memory checks needed.
6363
entry:
6464
%p1 = alloca [1024 x i8]
@@ -105,7 +105,7 @@ loop.header:
105105
%gep.src = getelementptr inbounds i64, ptr %src, i64 %iv
106106
%l = load i64, ptr %gep.src, align 1
107107
%t = trunc i64 %l to i1
108-
br i1 %t, label %exit.0, label %loop.latch
108+
br i1 %t, label %exit.0, label %loop.latch, !prof !0
109109

110110
loop.latch:
111111
%iv.next = add i64 %iv, 1
@@ -120,4 +120,6 @@ exit.1:
120120
ret i64 0
121121
}
122122

123+
!0 = !{!"branch_weights", i32 1, i32 1}
124+
123125
attributes #1 = { "target-features"="+sve" vscale_range(1,16) }

llvm/test/Transforms/LoopVectorize/AArch64/predicated-costs.ll

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,59 @@ attributes #1 = { "target-cpu"="neoverse-v2" }
385385
!1 = !{!"llvm.loop.mustprogress"}
386386
!2 = !{!"llvm.loop.vectorize.predicate.enable", i1 true}
387387
!3 = !{!"llvm.loop.vectorize.enable", i1 true}
388+
389+
; BFI computes if is taken 20 times, and loop 32 times. Make sure we round the
390+
; divisor up to 2 so that we don't vectorize the loop unprofitably.
391+
define void @round_scalar_pred_divisor(ptr %dst, double %x) {
392+
; CHECK-LABEL: define void @round_scalar_pred_divisor(
393+
; CHECK-SAME: ptr [[DST:%.*]], double [[X:%.*]]) {
394+
; CHECK-NEXT: [[ENTRY:.*]]:
395+
; CHECK-NEXT: br label %[[LOOP:.*]]
396+
; CHECK: [[LOOP]]:
397+
; CHECK-NEXT: [[IV:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LATCH:.*]] ]
398+
; CHECK-NEXT: [[C:%.*]] = fcmp une double [[X]], 0.000000e+00
399+
; CHECK-NEXT: br i1 [[C]], label %[[IF:.*]], label %[[LATCH]]
400+
; CHECK: [[IF]]:
401+
; CHECK-NEXT: [[TRUNC:%.*]] = trunc i64 [[IV]] to i32
402+
; CHECK-NEXT: [[UITOFP:%.*]] = uitofp i32 [[TRUNC]] to double
403+
; CHECK-NEXT: [[SIN:%.*]] = tail call double @llvm.sin.f64(double [[UITOFP]])
404+
; CHECK-NEXT: [[FPTRUNC:%.*]] = fptrunc double [[SIN]] to float
405+
; CHECK-NEXT: br label %[[LATCH]]
406+
; CHECK: [[LATCH]]:
407+
; CHECK-NEXT: [[PHI:%.*]] = phi float [ [[FPTRUNC]], %[[IF]] ], [ 0.000000e+00, %[[LOOP]] ]
408+
; CHECK-NEXT: store float [[PHI]], ptr [[DST]], align 4
409+
; CHECK-NEXT: [[IV_NEXT]] = add i64 [[IV]], 1
410+
; CHECK-NEXT: [[EC:%.*]] = icmp eq i64 [[IV]], 1024
411+
; CHECK-NEXT: br i1 [[EC]], label %[[EXIT:.*]], label %[[LOOP]]
412+
; CHECK: [[EXIT]]:
413+
; CHECK-NEXT: ret void
414+
;
415+
entry:
416+
br label %loop
417+
418+
loop:
419+
%iv = phi i64 [ 0, %entry ], [ %iv.next, %latch ]
420+
%c = fcmp une double %x, 0.0
421+
br i1 %c, label %if, label %latch
422+
423+
if:
424+
%trunc = trunc i64 %iv to i32
425+
%uitofp = uitofp i32 %trunc to double
426+
%sin = tail call double @llvm.sin(double %uitofp)
427+
%fptrunc = fptrunc double %sin to float
428+
br label %latch
429+
430+
latch:
431+
%phi = phi float [ %fptrunc, %if ], [ 0.0, %loop ]
432+
store float %phi, ptr %dst
433+
%iv.next = add i64 %iv, 1
434+
%ec = icmp eq i64 %iv, 1024
435+
br i1 %ec, label %exit, label %loop
436+
437+
exit:
438+
ret void
439+
}
440+
388441
;.
389442
; CHECK: [[META0]] = !{[[META1:![0-9]+]]}
390443
; CHECK: [[META1]] = distinct !{[[META1]], [[META2:![0-9]+]]}

llvm/test/Transforms/LoopVectorize/AArch64/simple_early_exit.ll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@ define i32 @diff_exit_block_needs_scev_check(i32 %end) {
386386
; CHECK-NEXT: [[TMP0:%.*]] = trunc i32 [[END]] to i10
387387
; CHECK-NEXT: [[TMP1:%.*]] = zext i10 [[TMP0]] to i64
388388
; CHECK-NEXT: [[UMAX1:%.*]] = call i64 @llvm.umax.i64(i64 [[TMP1]], i64 1)
389-
; CHECK-NEXT: [[MIN_ITERS_CHECK:%.*]] = icmp ult i64 [[UMAX1]], 12
389+
; CHECK-NEXT: [[MIN_ITERS_CHECK:%.*]] = icmp ult i64 [[UMAX1]], 8
390390
; CHECK-NEXT: br i1 [[MIN_ITERS_CHECK]], label [[SCALAR_PH:%.*]], label [[VECTOR_SCEVCHECK:%.*]]
391391
; CHECK: vector.scevcheck:
392392
; CHECK-NEXT: [[UMAX:%.*]] = call i32 @llvm.umax.i32(i32 [[END_CLAMPED]], i32 1)

0 commit comments

Comments
 (0)