Skip to content

Commit 792a7bb

Browse files
authored
[profcheck] Option to inject distinct small weights (llvm#159644)
There are cases where the easiest way to regression-test a profile change is to add `!prof`​ metadata, with small numbers as to simplify manual verification. To ensure coverage, this (the inserting) may become tedious. This patch makes `prof-inject`​ do that for us, if so opted in. The list of weights used is a bunch of primes, used as a circular buffer. Issue llvm#147390
1 parent 3cc56dd commit 792a7bb

File tree

2 files changed

+120
-29
lines changed

2 files changed

+120
-29
lines changed

llvm/lib/Transforms/Utils/ProfileVerify.cpp

Lines changed: 47 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ static cl::opt<bool>
2828
AnnotateSelect("profcheck-annotate-select", cl::init(true),
2929
cl::desc("Also inject (if missing) and verify MD_prof for "
3030
"`select` instructions"));
31+
static cl::opt<bool>
32+
WeightsForTest("profcheck-weights-for-test", cl::init(false),
33+
cl::desc("Generate weights with small values for tests."));
34+
3135
static cl::opt<uint32_t> SelectTrueWeight(
3236
"profcheck-default-select-true-weight", cl::init(2U),
3337
cl::desc("When annotating `select` instructions, this value will be used "
@@ -91,6 +95,10 @@ bool ProfileInjector::inject() {
9195
if (F.getEntryCount(/*AllowSynthetic=*/true)->getCount() == 0)
9296
return false;
9397
bool Changed = false;
98+
// Cycle through the weights list. If we didn't, tests with more than (say)
99+
// one conditional branch would have the same !prof metadata on all of them,
100+
// and numerically that may make for a poor unit test.
101+
uint32_t WeightsForTestOffset = 0;
94102
for (auto &BB : F) {
95103
if (AnnotateSelect) {
96104
for (auto &I : BB) {
@@ -103,38 +111,48 @@ bool ProfileInjector::inject() {
103111
if (!Term || Term->getMetadata(LLVMContext::MD_prof))
104112
continue;
105113
SmallVector<BranchProbability> Probs;
106-
Probs.reserve(Term->getNumSuccessors());
107-
for (auto I = 0U, E = Term->getNumSuccessors(); I < E; ++I)
108-
Probs.emplace_back(BPI.getEdgeProbability(&BB, Term->getSuccessor(I)));
109114

110-
assert(llvm::find_if(Probs,
111-
[](const BranchProbability &P) {
112-
return P.isUnknown();
113-
}) == Probs.end() &&
114-
"All branch probabilities should be valid");
115-
const auto *FirstZeroDenominator =
116-
find_if(Probs, [](const BranchProbability &P) {
117-
return P.getDenominator() == 0;
118-
});
119-
(void)FirstZeroDenominator;
120-
assert(FirstZeroDenominator == Probs.end());
121-
const auto *FirstNonZeroNumerator =
122-
find_if(Probs, [](const BranchProbability &P) { return !P.isZero(); });
123-
assert(FirstNonZeroNumerator != Probs.end());
124-
DynamicAPInt LCM(Probs[0].getDenominator());
125-
DynamicAPInt GCD(FirstNonZeroNumerator->getNumerator());
126-
for (const auto &Prob : drop_begin(Probs)) {
127-
if (!Prob.getNumerator())
128-
continue;
129-
LCM = llvm::lcm(LCM, DynamicAPInt(Prob.getDenominator()));
130-
GCD = llvm::gcd(GCD, DynamicAPInt(Prob.getNumerator()));
131-
}
132115
SmallVector<uint32_t> Weights;
133116
Weights.reserve(Term->getNumSuccessors());
134-
for (const auto &Prob : Probs) {
135-
DynamicAPInt W =
136-
(Prob.getNumerator() * LCM / GCD) / Prob.getDenominator();
137-
Weights.emplace_back(static_cast<uint32_t>((int64_t)W));
117+
if (WeightsForTest) {
118+
static const std::array Primes{3, 5, 7, 11, 13, 17, 19, 23, 29, 31,
119+
37, 41, 43, 47, 53, 59, 61, 67, 71};
120+
for (uint32_t I = 0, E = Term->getNumSuccessors(); I < E; ++I)
121+
Weights.emplace_back(
122+
Primes[(WeightsForTestOffset + I) % Primes.size()]);
123+
++WeightsForTestOffset;
124+
} else {
125+
Probs.reserve(Term->getNumSuccessors());
126+
for (auto I = 0U, E = Term->getNumSuccessors(); I < E; ++I)
127+
Probs.emplace_back(BPI.getEdgeProbability(&BB, Term->getSuccessor(I)));
128+
129+
assert(llvm::find_if(Probs,
130+
[](const BranchProbability &P) {
131+
return P.isUnknown();
132+
}) == Probs.end() &&
133+
"All branch probabilities should be valid");
134+
const auto *FirstZeroDenominator =
135+
find_if(Probs, [](const BranchProbability &P) {
136+
return P.getDenominator() == 0;
137+
});
138+
(void)FirstZeroDenominator;
139+
assert(FirstZeroDenominator == Probs.end());
140+
const auto *FirstNonZeroNumerator = find_if(
141+
Probs, [](const BranchProbability &P) { return !P.isZero(); });
142+
assert(FirstNonZeroNumerator != Probs.end());
143+
DynamicAPInt LCM(Probs[0].getDenominator());
144+
DynamicAPInt GCD(FirstNonZeroNumerator->getNumerator());
145+
for (const auto &Prob : drop_begin(Probs)) {
146+
if (!Prob.getNumerator())
147+
continue;
148+
LCM = llvm::lcm(LCM, DynamicAPInt(Prob.getDenominator()));
149+
GCD = llvm::gcd(GCD, DynamicAPInt(Prob.getNumerator()));
150+
}
151+
for (const auto &Prob : Probs) {
152+
DynamicAPInt W =
153+
(Prob.getNumerator() * LCM / GCD) / Prob.getDenominator();
154+
Weights.emplace_back(static_cast<uint32_t>((int64_t)W));
155+
}
138156
}
139157
setBranchWeights(*Term, Weights, /*IsExpected=*/false);
140158
Changed = true;
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals all --version 6
2+
; RUN: opt -passes=prof-inject -profcheck-weights-for-test %s -S -o - | FileCheck %s --check-prefixes=TEST,CHECK
3+
; RUN: opt -passes=prof-inject %s -S -o - | FileCheck %s --check-prefixes=NORMAL,CHECK
4+
5+
define void @foo(i32 %cond) {
6+
; TEST-LABEL: define void @foo(
7+
; TEST-SAME: i32 [[COND:%.*]]) !prof [[PROF0:![0-9]+]] {
8+
; TEST-NEXT: [[I:%.*]] = icmp eq i32 [[COND]], 0
9+
; TEST-NEXT: br i1 [[I]], label %[[A:.*]], label %[[B:.*]], !prof [[PROF1:![0-9]+]]
10+
; TEST: [[A]]:
11+
; TEST-NEXT: switch i32 [[COND]], label %[[DEFAULT:.*]] [
12+
; TEST-NEXT: i32 10, label %[[C:.*]]
13+
; TEST-NEXT: i32 20, label %[[D:.*]]
14+
; TEST-NEXT: ], !prof [[PROF2:![0-9]+]]
15+
; TEST: [[BB1:.*:]]
16+
; TEST-NEXT: br label %[[B]]
17+
; TEST: [[B]]:
18+
; TEST-NEXT: ret void
19+
; TEST: [[DEFAULT]]:
20+
; TEST-NEXT: ret void
21+
; TEST: [[C]]:
22+
; TEST-NEXT: ret void
23+
; TEST: [[D]]:
24+
; TEST-NEXT: ret void
25+
;
26+
; NORMAL-LABEL: define void @foo(
27+
; NORMAL-SAME: i32 [[COND:%.*]]) !prof [[PROF0:![0-9]+]] {
28+
; NORMAL-NEXT: [[I:%.*]] = icmp eq i32 [[COND]], 0
29+
; NORMAL-NEXT: br i1 [[I]], label %[[A:.*]], label %[[B:.*]], !prof [[PROF1:![0-9]+]]
30+
; NORMAL: [[A]]:
31+
; NORMAL-NEXT: switch i32 [[COND]], label %[[DEFAULT:.*]] [
32+
; NORMAL-NEXT: i32 10, label %[[C:.*]]
33+
; NORMAL-NEXT: i32 20, label %[[D:.*]]
34+
; NORMAL-NEXT: ], !prof [[PROF2:![0-9]+]]
35+
; NORMAL: [[BB1:.*:]]
36+
; NORMAL-NEXT: br label %[[B]]
37+
; NORMAL: [[B]]:
38+
; NORMAL-NEXT: ret void
39+
; NORMAL: [[DEFAULT]]:
40+
; NORMAL-NEXT: ret void
41+
; NORMAL: [[C]]:
42+
; NORMAL-NEXT: ret void
43+
; NORMAL: [[D]]:
44+
; NORMAL-NEXT: ret void
45+
;
46+
%i = icmp eq i32 %cond, 0
47+
br i1 %i, label %a, label %b
48+
a:
49+
switch i32 %cond, label %default [
50+
i32 10, label %c
51+
i32 20, label %d
52+
]
53+
br label %b
54+
b:
55+
ret void
56+
default:
57+
ret void
58+
c:
59+
ret void
60+
d:
61+
ret void
62+
}
63+
;.
64+
; TEST: [[PROF0]] = !{!"function_entry_count", i64 1000}
65+
; TEST: [[PROF1]] = !{!"branch_weights", i32 3, i32 5}
66+
; TEST: [[PROF2]] = !{!"branch_weights", i32 5, i32 7, i32 11}
67+
;.
68+
; NORMAL: [[PROF0]] = !{!"function_entry_count", i64 1000}
69+
; NORMAL: [[PROF1]] = !{!"branch_weights", i32 3, i32 5}
70+
; NORMAL: [[PROF2]] = !{!"branch_weights", i32 1, i32 1, i32 1}
71+
;.
72+
;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
73+
; CHECK: {{.*}}

0 commit comments

Comments
 (0)