Skip to content

Commit 7594643

Browse files
committed
pass validator
1 parent 0032148 commit 7594643

File tree

8 files changed

+231
-0
lines changed

8 files changed

+231
-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
@@ -357,6 +357,7 @@
357357
#include "llvm/Transforms/Utils/MoveAutoInit.h"
358358
#include "llvm/Transforms/Utils/NameAnonGlobals.h"
359359
#include "llvm/Transforms/Utils/PredicateInfo.h"
360+
#include "llvm/Transforms/Utils/ProfileVerify.h"
360361
#include "llvm/Transforms/Utils/RelLookupTableConverter.h"
361362
#include "llvm/Transforms/Utils/StripGCRelocates.h"
362363
#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
@@ -517,6 +517,8 @@ FUNCTION_PASS("print<regions>", RegionInfoPrinterPass(errs()))
517517
FUNCTION_PASS("print<scalar-evolution>", ScalarEvolutionPrinterPass(errs()))
518518
FUNCTION_PASS("print<stack-safety-local>", StackSafetyPrinterPass(errs()))
519519
FUNCTION_PASS("print<uniformity>", UniformityInfoPrinterPass(errs()))
520+
FUNCTION_PASS("prof-inject", ProfileInjectorPass())
521+
FUNCTION_PASS("prof-verify", ProfileVerifierPass())
520522
FUNCTION_PASS("reassociate", ReassociatePass())
521523
FUNCTION_PASS("redundant-dbg-inst-elim", RedundantDbgInstEliminationPass())
522524
FUNCTION_PASS("reg2mem", RegToMemPass())

llvm/lib/Transforms/Utils/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ add_llvm_component_library(LLVMTransformUtils
6767
MoveAutoInit.cpp
6868
NameAnonGlobals.cpp
6969
PredicateInfo.cpp
70+
ProfileVerify.cpp
7071
PromoteMemoryToRegister.cpp
7172
RelLookupTableConverter.cpp
7273
ScalarEvolutionExpander.cpp
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
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) {
89+
return !P.isZero();
90+
});
91+
assert(FirstNonZeroNumerator != Probs.end());
92+
DynamicAPInt LCM(Probs[0].getDenominator());
93+
DynamicAPInt GCD(FirstNonZeroNumerator->getNumerator());
94+
for (const auto &Prob : drop_begin(Probs)) {
95+
if (!Prob.getNumerator())
96+
continue;
97+
LCM = llvm::lcm(LCM, DynamicAPInt(Prob.getDenominator()));
98+
GCD = llvm::gcd(GCD, DynamicAPInt(Prob.getNumerator()));
99+
}
100+
SmallVector<uint32_t> Weights;
101+
Weights.reserve(Term->getNumSuccessors());
102+
for (const auto &Prob : Probs) {
103+
DynamicAPInt W =
104+
(Prob.getNumerator() * LCM / GCD) / Prob.getDenominator();
105+
Weights.emplace_back(static_cast<uint32_t>((int64_t)W));
106+
}
107+
setBranchWeights(*Term, Weights, /*IsExpected=*/false);
108+
Changed = true;
109+
}
110+
return Changed;
111+
}
112+
113+
PreservedAnalyses ProfileInjectorPass::run(Function &F,
114+
FunctionAnalysisManager &FAM) {
115+
ProfileInjector PI(F, FAM);
116+
if (!PI.inject())
117+
return PreservedAnalyses::all();
118+
119+
return PreservedAnalyses::none();
120+
}
121+
122+
PreservedAnalyses ProfileVerifierPass::run(Function &F,
123+
FunctionAnalysisManager &FAM) {
124+
for (const auto &BB : F)
125+
if (const auto *Term =
126+
ProfileInjector::getTerminatorBenefitingFromMDProf(BB))
127+
if (!Term->getMetadata(LLVMContext::MD_prof))
128+
F.getContext().emitError("Profile verification failed");
129+
130+
return PreservedAnalyses::none();
131+
}
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)