Skip to content

Commit 4d46fc3

Browse files
committed
[PGO] Instrument modules with at least a single function definition
It makes sense to instrument modules that contain at least a single function definition only. When a module contains only function declarations and, optionally, global variable definitions (a module for the regular-LTO phase for thin-LTO when LTOUnit splitting is enabled, for example), it makes no sense to instrument such module and moreover, to introduce the `__llvm_profile_raw_version` variable into the module. The patch also introduces the gmock-based unittest infrastructure for PGO Instrumentation and adds some test cases to check whether the instrumentation has taken place. The testing infrastructure for analysis modules was borrowed from the `LoopPassManagerTest` unittest and simplified a bit to handle module analysis passes only. Actually, we are testing whether the result of a trivial analysis pass was invalidated by the `PGOInstrumentGen` one: we exploit the fact the pass invalidates all the analysis results after a module was instrumented.
1 parent cda5790 commit 4d46fc3

File tree

7 files changed

+269
-7
lines changed

7 files changed

+269
-7
lines changed

llvm/lib/Transforms/Instrumentation/PGOInstrumentation.cpp

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -405,9 +405,14 @@ static GlobalVariable *createIRLevelProfileFlagVar(Module &M, bool IsCS) {
405405
ProfileVersion |= VARIANT_MASK_BYTE_COVERAGE;
406406
if (PGOTemporalInstrumentation)
407407
ProfileVersion |= VARIANT_MASK_TEMPORAL_PROF;
408-
auto IRLevelVersionVariable = new GlobalVariable(
408+
assert(!M.global_empty() &&
409+
"If a module was instrumented, there must be defined global variables "
410+
"at least for the counters.");
411+
auto *InsertionPoint = &*M.global_begin();
412+
auto *IRLevelVersionVariable = new GlobalVariable(
409413
M, IntTy64, true, GlobalValue::WeakAnyLinkage,
410-
Constant::getIntegerValue(IntTy64, APInt(64, ProfileVersion)), VarName);
414+
Constant::getIntegerValue(IntTy64, APInt(64, ProfileVersion)), VarName,
415+
InsertionPoint);
411416
IRLevelVersionVariable->setVisibility(GlobalValue::HiddenVisibility);
412417
Triple TT(M.getTargetTriple());
413418
if (TT.supportsCOMDAT()) {
@@ -1829,11 +1834,6 @@ static bool InstrumentAllFunctions(
18291834
Module &M, function_ref<TargetLibraryInfo &(Function &)> LookupTLI,
18301835
function_ref<BranchProbabilityInfo *(Function &)> LookupBPI,
18311836
function_ref<BlockFrequencyInfo *(Function &)> LookupBFI, bool IsCS) {
1832-
// For the context-sensitve instrumentation, we should have a separated pass
1833-
// (before LTO/ThinLTO linking) to create these variables.
1834-
if (!IsCS && !PGOCtxProfLoweringPass::isContextualIRPGOEnabled())
1835-
createIRLevelProfileFlagVar(M, /*IsCS=*/false);
1836-
18371837
Triple TT(M.getTargetTriple());
18381838
LLVMContext &Ctx = M.getContext();
18391839
if (!TT.isOSBinFormatELF() && EnableVTableValueProfiling)
@@ -1845,14 +1845,25 @@ static bool InstrumentAllFunctions(
18451845
std::unordered_multimap<Comdat *, GlobalValue *> ComdatMembers;
18461846
collectComdatMembers(M, ComdatMembers);
18471847

1848+
bool AnythingInstrumented = false;
18481849
for (auto &F : M) {
18491850
if (skipPGOGen(F))
18501851
continue;
18511852
auto &TLI = LookupTLI(F);
18521853
auto *BPI = LookupBPI(F);
18531854
auto *BFI = LookupBFI(F);
18541855
instrumentOneFunc(F, &M, TLI, BPI, BFI, ComdatMembers, IsCS);
1856+
AnythingInstrumented = true;
18551857
}
1858+
1859+
if (!AnythingInstrumented)
1860+
return false;
1861+
1862+
// For the context-sensitve instrumentation, we should have a separated pass
1863+
// (before LTO/ThinLTO linking) to create these variables.
1864+
if (!IsCS && !PGOCtxProfLoweringPass::isContextualIRPGOEnabled())
1865+
createIRLevelProfileFlagVar(M, /*IsCS=*/false);
1866+
18561867
return true;
18571868
}
18581869

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
; RUN: opt < %s -passes=pgo-instr-gen -S | FileCheck %s --implicit-check-not='__llvm_profile_raw_version'
2+
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
3+
target triple = "x86_64-unknown-linux-gnu"
4+
5+
declare i32 @test_1(i32 %i);
6+
7+
declare i32 @test_2(i32 %i);
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
; RUN: opt < %s -passes=pgo-instr-gen -S | FileCheck %s --implicit-check-not='__llvm_profile_raw_version'
2+
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
3+
target triple = "x86_64-unknown-linux-gnu"
4+
5+
@var = internal unnamed_addr global [35 x ptr] zeroinitializer, align 16
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
; RUN: opt < %s -passes=pgo-instr-gen -S | FileCheck %s --check-prefix=GEN-COMDAT
2+
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
3+
target triple = "x86_64-unknown-linux-gnu"
4+
; GEN-COMDAT: $__llvm_profile_raw_version = comdat any
5+
; GEN-COMDAT: @__llvm_profile_raw_version = hidden constant i64 {{[0-9]+}}, comdat
6+
7+
define i32 @test_1(i32 %i) {
8+
entry:
9+
ret i32 0
10+
}
11+
12+
define i32 @test_2(i32 %i) {
13+
entry:
14+
ret i32 1
15+
}

llvm/unittests/Transforms/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
add_subdirectory(Coroutines)
2+
add_subdirectory(Instrumentation)
23
add_subdirectory(IPO)
34
add_subdirectory(Scalar)
45
add_subdirectory(Utils)
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
set(LLVM_LINK_COMPONENTS
2+
Analysis
3+
AsmParser
4+
Core
5+
Instrumentation
6+
Passes
7+
Support
8+
)
9+
10+
add_llvm_unittest(InstrumentationTests
11+
PGOInstrumentationTest.cpp
12+
)
13+
14+
target_link_libraries(InstrumentationTests PRIVATE LLVMTestingSupport)
15+
16+
set_property(TARGET InstrumentationTests PROPERTY FOLDER "Tests/UnitTests/TransformTests")
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
//===- PGOInstrumentationTest.cpp - Instrumentation unit tests ------------===//
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/Instrumentation/PGOInstrumentation.h"
10+
#include "llvm/AsmParser/Parser.h"
11+
#include "llvm/IR/Module.h"
12+
#include "llvm/Passes/PassBuilder.h"
13+
#include "llvm/ProfileData/InstrProf.h"
14+
15+
#include "gmock/gmock.h"
16+
#include "gtest/gtest.h"
17+
18+
namespace {
19+
20+
using namespace llvm;
21+
22+
using ::testing::DoDefault;
23+
using ::testing::Invoke;
24+
using ::testing::NotNull;
25+
using ::testing::IsNull;
26+
using ::testing::Ref;
27+
using ::testing::Return;
28+
using ::testing::Sequence;
29+
using ::testing::Test;
30+
using testing::_;
31+
32+
template <typename Derived> class MockAnalysisHandleBase {
33+
public:
34+
class Analysis : public AnalysisInfoMixin<Analysis> {
35+
public:
36+
class Result {
37+
public:
38+
// Forward invalidation events to the mock handle.
39+
bool invalidate(Module &M, const PreservedAnalyses &PA,
40+
ModuleAnalysisManager::Invalidator &Inv) {
41+
return Handle->invalidate(M, PA, Inv);
42+
}
43+
private:
44+
explicit Result(Derived *Handle) : Handle(Handle) {}
45+
46+
friend MockAnalysisHandleBase;
47+
Derived *Handle;
48+
};
49+
50+
Result run(Module &M, ModuleAnalysisManager &AM) {
51+
return Handle->run(M, AM);
52+
}
53+
54+
private:
55+
friend AnalysisInfoMixin<Analysis>;
56+
friend MockAnalysisHandleBase;
57+
static inline AnalysisKey Key;
58+
59+
Derived *Handle;
60+
61+
explicit Analysis(Derived *Handle) : Handle(Handle) {}
62+
};
63+
64+
Analysis getAnalysis() {
65+
return Analysis(static_cast<Derived *>(this));
66+
}
67+
68+
typename Analysis::Result getResult() {
69+
return typename Analysis::Result(static_cast<Derived *>(this));
70+
}
71+
72+
protected:
73+
void setDefaults() {
74+
ON_CALL(static_cast<Derived &>(*this), run(_, _))
75+
.WillByDefault(Return(this->getResult()));
76+
ON_CALL(static_cast<Derived &>(*this), invalidate(_, _, _))
77+
.WillByDefault(Invoke([](Module &M, const PreservedAnalyses &PA,
78+
ModuleAnalysisManager::Invalidator &) {
79+
auto PAC = PA.template getChecker<Analysis>();
80+
return !PAC.preserved() &&
81+
!PAC.template preservedSet<AllAnalysesOn<Module>>();
82+
}));
83+
}
84+
85+
private:
86+
friend Derived;
87+
MockAnalysisHandleBase() = default;
88+
};
89+
90+
class MockModuleAnalysisHandle
91+
: public MockAnalysisHandleBase<MockModuleAnalysisHandle> {
92+
public:
93+
MockModuleAnalysisHandle() { setDefaults(); }
94+
95+
MOCK_METHOD(typename Analysis::Result, run,
96+
(Module &, ModuleAnalysisManager &));
97+
98+
MOCK_METHOD(bool, invalidate,
99+
(Module &, const PreservedAnalyses &,
100+
ModuleAnalysisManager::Invalidator &));
101+
};
102+
103+
struct PGOInstrumentationGenTest : public Test {
104+
LLVMContext Ctx;
105+
ModulePassManager MPM;
106+
PassBuilder PB;
107+
MockModuleAnalysisHandle MMAHandle;
108+
LoopAnalysisManager LAM;
109+
FunctionAnalysisManager FAM;
110+
CGSCCAnalysisManager CGAM;
111+
ModuleAnalysisManager MAM;
112+
LLVMContext Context;
113+
std::unique_ptr<Module> M;
114+
115+
PGOInstrumentationGenTest() {
116+
MAM.registerPass([&] { return MMAHandle.getAnalysis(); });
117+
PB.registerModuleAnalyses(MAM);
118+
PB.registerCGSCCAnalyses(CGAM);
119+
PB.registerFunctionAnalyses(FAM);
120+
PB.registerLoopAnalyses(LAM);
121+
PB.crossRegisterProxies(LAM, FAM, CGAM, MAM);
122+
MPM.addPass(
123+
RequireAnalysisPass<MockModuleAnalysisHandle::Analysis, Module>());
124+
MPM.addPass(PGOInstrumentationGen());
125+
}
126+
127+
void parseAssembly(const StringRef IR) {
128+
SMDiagnostic Error;
129+
M = parseAssemblyString(IR, Error, Context);
130+
std::string ErrMsg;
131+
raw_string_ostream OS(ErrMsg);
132+
Error.print("", OS);
133+
134+
// A failure here means that the test itself is buggy.
135+
if (!M)
136+
report_fatal_error(OS.str().c_str());
137+
}
138+
};
139+
140+
TEST_F(PGOInstrumentationGenTest, NotInstrumentedDeclarationsOnly) {
141+
const StringRef NotInstrumentableCode = R"(
142+
declare i32 @f(i32);
143+
)";
144+
145+
parseAssembly(NotInstrumentableCode);
146+
147+
ASSERT_THAT(M, NotNull());
148+
149+
EXPECT_CALL(MMAHandle, run(Ref(*M), _)).WillOnce(DoDefault());
150+
EXPECT_CALL(MMAHandle, invalidate(Ref(*M), _, _)).Times(0);
151+
152+
MPM.run(*M, MAM);
153+
154+
const auto *IRInstrVar =
155+
M->getNamedGlobal(INSTR_PROF_QUOTE(INSTR_PROF_RAW_VERSION_VAR));
156+
EXPECT_THAT(IRInstrVar, IsNull());
157+
}
158+
159+
TEST_F(PGOInstrumentationGenTest, NotInstrumentedGlobals) {
160+
const StringRef NotInstrumentableCode = R"(
161+
@foo.table = internal unnamed_addr constant [1 x ptr] [ptr @f]
162+
declare i32 @f(i32);
163+
)";
164+
165+
parseAssembly(NotInstrumentableCode);
166+
167+
ASSERT_THAT(M, NotNull());
168+
169+
EXPECT_CALL(MMAHandle, run(Ref(*M), _)).WillOnce(DoDefault());
170+
EXPECT_CALL(MMAHandle, invalidate(Ref(*M), _, _)).Times(0);
171+
172+
MPM.run(*M, MAM);
173+
174+
const auto *IRInstrVar =
175+
M->getNamedGlobal(INSTR_PROF_QUOTE(INSTR_PROF_RAW_VERSION_VAR));
176+
EXPECT_THAT(IRInstrVar, IsNull());
177+
}
178+
179+
TEST_F(PGOInstrumentationGenTest, Instrumented) {
180+
const StringRef InstrumentableCode = R"(
181+
define i32 @f(i32 %n) {
182+
entry:
183+
ret i32 0
184+
}
185+
)";
186+
187+
parseAssembly(InstrumentableCode);
188+
189+
ASSERT_THAT(M, NotNull());
190+
191+
Sequence PassSequence;
192+
EXPECT_CALL(MMAHandle, run(Ref(*M), _))
193+
.InSequence(PassSequence)
194+
.WillOnce(DoDefault());
195+
EXPECT_CALL(MMAHandle, invalidate(Ref(*M), _, _))
196+
.InSequence(PassSequence)
197+
.WillOnce(DoDefault());
198+
199+
MPM.run(*M, MAM);
200+
201+
const auto *IRInstrVar =
202+
M->getNamedGlobal(INSTR_PROF_QUOTE(INSTR_PROF_RAW_VERSION_VAR));
203+
EXPECT_THAT(IRInstrVar, NotNull());
204+
EXPECT_FALSE(IRInstrVar->isDeclaration());
205+
}
206+
207+
} // end anonymous namespace

0 commit comments

Comments
 (0)