Skip to content

Commit f4d5327

Browse files
committed
pass validator
1 parent 9a12037 commit f4d5327

File tree

8 files changed

+229
-0
lines changed

8 files changed

+229
-0
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//===- ProfileVerify.h - Verify profile info for testing ----------*-C++-*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// Inject profile information, as part of tests, to verify passes don't
10+
// accidentally drop it.
11+
//
12+
//===----------------------------------------------------------------------===//
13+
#ifndef LLVM_TRANSFORMS_UTILS_PROFILEVERIFY_H
14+
#define LLVM_TRANSFORMS_UTILS_PROFILEVERIFY_H
15+
16+
#include "llvm/IR/Analysis.h"
17+
#include "llvm/IR/PassManager.h"
18+
19+
namespace llvm {
20+
/// Inject MD_prof metadata where it's missing. Used for testing that passes
21+
/// don't accidentally drop this metadata.
22+
class ProfileInjectorPass : public PassInfoMixin<ProfileInjectorPass> {
23+
public:
24+
PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM);
25+
};
26+
27+
/// Checks that MD_prof is present on every instruction that supports it. Used
28+
/// in conjunction with the ProfileInjectorPass. MD_prof "unknown" is considered
29+
/// valid (i.e. !{!"unknown"})
30+
class ProfileVerifierPass : public PassInfoMixin<ProfileVerifierPass> {
31+
public:
32+
PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM);
33+
};
34+
35+
} // namespace llvm
36+
#endif

llvm/lib/Passes/PassBuilder.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,7 @@
364364
#include "llvm/Transforms/Utils/MoveAutoInit.h"
365365
#include "llvm/Transforms/Utils/NameAnonGlobals.h"
366366
#include "llvm/Transforms/Utils/PredicateInfo.h"
367+
#include "llvm/Transforms/Utils/ProfileVerify.h"
367368
#include "llvm/Transforms/Utils/RelLookupTableConverter.h"
368369
#include "llvm/Transforms/Utils/StripGCRelocates.h"
369370
#include "llvm/Transforms/Utils/StripNonLineTableDebugInfo.h"

llvm/lib/Passes/PassRegistry.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,8 @@ FUNCTION_PASS("print<regions>", RegionInfoPrinterPass(errs()))
520520
FUNCTION_PASS("print<scalar-evolution>", ScalarEvolutionPrinterPass(errs()))
521521
FUNCTION_PASS("print<stack-safety-local>", StackSafetyPrinterPass(errs()))
522522
FUNCTION_PASS("print<uniformity>", UniformityInfoPrinterPass(errs()))
523+
FUNCTION_PASS("prof-inject", ProfileInjectorPass())
524+
FUNCTION_PASS("prof-verify", ProfileVerifierPass())
523525
FUNCTION_PASS("reassociate", ReassociatePass())
524526
FUNCTION_PASS("redundant-dbg-inst-elim", RedundantDbgInstEliminationPass())
525527
FUNCTION_PASS("replace-with-veclib", ReplaceWithVeclib())

llvm/lib/Transforms/Utils/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ add_llvm_component_library(LLVMTransformUtils
6868
MoveAutoInit.cpp
6969
NameAnonGlobals.cpp
7070
PredicateInfo.cpp
71+
ProfileVerify.cpp
7172
PromoteMemoryToRegister.cpp
7273
RelLookupTableConverter.cpp
7374
ScalarEvolutionExpander.cpp
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
//===- ProfileVerify.cpp - Verify profile info for testing ----------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "llvm/Transforms/Utils/ProfileVerify.h"
10+
#include "llvm/ADT/DynamicAPInt.h"
11+
#include "llvm/ADT/PostOrderIterator.h"
12+
#include "llvm/ADT/STLExtras.h"
13+
#include "llvm/Analysis/BranchProbabilityInfo.h"
14+
#include "llvm/Analysis/LoopInfo.h"
15+
#include "llvm/IR/Analysis.h"
16+
#include "llvm/IR/Dominators.h"
17+
#include "llvm/IR/Function.h"
18+
#include "llvm/IR/Instructions.h"
19+
#include "llvm/IR/LLVMContext.h"
20+
#include "llvm/IR/MDBuilder.h"
21+
#include "llvm/IR/ProfDataUtils.h"
22+
#include "llvm/Support/BranchProbability.h"
23+
24+
using namespace llvm;
25+
namespace {
26+
class ProfileInjector {
27+
Function &F;
28+
FunctionAnalysisManager &FAM;
29+
30+
public:
31+
static const Instruction *
32+
getTerminatorBenefitingFromMDProf(const BasicBlock &BB) {
33+
if (succ_size(&BB) < 2)
34+
return nullptr;
35+
auto *Term = BB.getTerminator();
36+
return (isa<BranchInst>(Term) || isa<SwitchInst>(Term) ||
37+
isa<IndirectBrInst>(Term) || isa<CallBrInst>(Term))
38+
? Term
39+
: nullptr;
40+
}
41+
42+
static Instruction *getTerminatorBenefitingFromMDProf(BasicBlock &BB) {
43+
return const_cast<Instruction *>(
44+
getTerminatorBenefitingFromMDProf(const_cast<const BasicBlock &>(BB)));
45+
}
46+
47+
ProfileInjector(Function &F, FunctionAnalysisManager &FAM) : F(F), FAM(FAM) {}
48+
bool inject();
49+
};
50+
} // namespace
51+
52+
// FIXME: currently this injects only for terminators. Select isn't yet
53+
// supported.
54+
bool ProfileInjector::inject() {
55+
// Get whatever branch probability info can be derived from the given IR -
56+
// whether it has or not metadata. The main intention for this pass is to
57+
// ensure that other passes don't drop or "forget" to update MD_prof. We do
58+
// this as a mode in which lit tests would run. We want to avoid changing the
59+
// behavior of those tests. A pass may use BPI (or BFI, which is computed from
60+
// BPI). If no metadata is present, BPI is guesstimated by
61+
// BranchProbabilityAnalysis. The injector (this pass) only persists whatever
62+
// information the analysis provides, in other words, the pass being tested
63+
// will get the same BPI it does if the injector wasn't running.
64+
auto &BPI = FAM.getResult<BranchProbabilityAnalysis>(F);
65+
66+
bool Changed = false;
67+
for (auto &BB : F) {
68+
auto *Term = getTerminatorBenefitingFromMDProf(BB);
69+
if (!Term || Term->getMetadata(LLVMContext::MD_prof))
70+
continue;
71+
SmallVector<BranchProbability> Probs;
72+
Probs.reserve(Term->getNumSuccessors());
73+
for (auto I = 0U, E = Term->getNumSuccessors(); I < E; ++I)
74+
Probs.emplace_back(BPI.getEdgeProbability(&BB, Term->getSuccessor(I)));
75+
76+
assert(llvm::find_if(Probs,
77+
[](const BranchProbability &P) {
78+
return P.isUnknown();
79+
}) == Probs.end() &&
80+
"All branch probabilities should be valid");
81+
const auto *FirstZeroDenominator =
82+
find_if(Probs, [](const BranchProbability &P) {
83+
return P.getDenominator() == 0;
84+
});
85+
(void)FirstZeroDenominator;
86+
assert(FirstZeroDenominator == Probs.end());
87+
const auto *FirstNonZeroNumerator =
88+
find_if(Probs, [](const BranchProbability &P) { return !P.isZero(); });
89+
assert(FirstNonZeroNumerator != Probs.end());
90+
DynamicAPInt LCM(Probs[0].getDenominator());
91+
DynamicAPInt GCD(FirstNonZeroNumerator->getNumerator());
92+
for (const auto &Prob : drop_begin(Probs)) {
93+
if (!Prob.getNumerator())
94+
continue;
95+
LCM = llvm::lcm(LCM, DynamicAPInt(Prob.getDenominator()));
96+
GCD = llvm::gcd(GCD, DynamicAPInt(Prob.getNumerator()));
97+
}
98+
SmallVector<uint32_t> Weights;
99+
Weights.reserve(Term->getNumSuccessors());
100+
for (const auto &Prob : Probs) {
101+
DynamicAPInt W =
102+
(Prob.getNumerator() * LCM / GCD) / Prob.getDenominator();
103+
Weights.emplace_back(static_cast<uint32_t>((int64_t)W));
104+
}
105+
setBranchWeights(*Term, Weights, /*IsExpected=*/false);
106+
Changed = true;
107+
}
108+
return Changed;
109+
}
110+
111+
PreservedAnalyses ProfileInjectorPass::run(Function &F,
112+
FunctionAnalysisManager &FAM) {
113+
ProfileInjector PI(F, FAM);
114+
if (!PI.inject())
115+
return PreservedAnalyses::all();
116+
117+
return PreservedAnalyses::none();
118+
}
119+
120+
PreservedAnalyses ProfileVerifierPass::run(Function &F,
121+
FunctionAnalysisManager &FAM) {
122+
for (const auto &BB : F)
123+
if (const auto *Term =
124+
ProfileInjector::getTerminatorBenefitingFromMDProf(BB))
125+
if (!Term->getMetadata(LLVMContext::MD_prof))
126+
F.getContext().emitError("Profile verification failed");
127+
128+
return PreservedAnalyses::none();
129+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
; Test that prof-inject only injects missing metadata
2+
3+
; RUN: opt -passes=prof-inject %s -S -o - | FileCheck %s
4+
5+
define void @foo(i32 %i) {
6+
%c = icmp eq i32 %i, 0
7+
br i1 %c, label %yes, label %no, !prof !0
8+
yes:
9+
br i1 %c, label %yes2, label %no
10+
yes2:
11+
ret void
12+
no:
13+
ret void
14+
}
15+
16+
!0 = !{!"branch_weights", i32 1, i32 2}
17+
; CHECK: br i1 %c, label %yes, label %no, !prof !0
18+
; CHECK: br i1 %c, label %yes2, label %no, !prof !1
19+
; CHECK: !0 = !{!"branch_weights", i32 1, i32 2}
20+
; CHECK: !1 = !{!"branch_weights", i32 3, i32 5}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
; Test that prof-inject does not modify existing metadata (incl. "unknown")
2+
3+
; RUN: opt -passes=prof-inject %s -S -o - | FileCheck %s
4+
; RUN: opt -passes=prof-verify %s -S --disable-output
5+
6+
define void @foo(i32 %i) {
7+
%c = icmp eq i32 %i, 0
8+
br i1 %c, label %yes, label %no, !prof !0
9+
yes:
10+
br i1 %c, label %yes2, label %no, !prof !1
11+
yes2:
12+
ret void
13+
no:
14+
ret void
15+
}
16+
17+
!0 = !{!"branch_weights", i32 1, i32 2}
18+
!1 = !{!"unknown"}
19+
; CHECK: br i1 %c, label %yes, label %no, !prof !0
20+
; CHECK: !0 = !{!"branch_weights", i32 1, i32 2}
21+
; CHECK: !1 = !{!"unknown"}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
; Test prof-inject and prof-verify
2+
3+
; RUN: opt -passes=prof-inject %s -S -o - | FileCheck %s --check-prefix=INJECT
4+
; RUN: not opt -passes=prof-verify %s -S -o - 2>&1 | FileCheck %s --check-prefix=VERIFY
5+
; RUN: opt -passes=prof-inject,prof-verify %s --disable-output
6+
7+
define void @foo(i32 %i) {
8+
%c = icmp eq i32 %i, 0
9+
br i1 %c, label %yes, label %no
10+
yes:
11+
ret void
12+
no:
13+
ret void
14+
}
15+
16+
; INJECT: br i1 %c, label %yes, label %no, !prof !0
17+
; INJECT: !0 = !{!"branch_weights", i32 3, i32 5}
18+
19+
; VERIFY: Profile verification failed

0 commit comments

Comments
 (0)