Skip to content

Commit 6f23165

Browse files
committed
[ctx_prof] CtxProfAnalysis: populate module data
Continuing from llvm#102084, which introduced the analysis, we now populate it with info about functions contained in the module. When we will update the profile due to e.g. inlined callsites, we'll ingest the callee's counters and callsites to the caller. We'll move those to the caller's respective index space (counter and callers), so we need to know and maintain where those currently end. We also don't need to keep profiles not pertinent to this module. This patch also introduces an arguably much simpler way to track the GUID of a function from the frontend compilation, through ThinLTO, and into the post-thinlink compilation step, which doesn't rely on keeping names around. A separate RFC and patches will discuss extending this to the current PGO (instrumented and sampled) and other consumers as an infrastructural component.
1 parent fff78a5 commit 6f23165

File tree

10 files changed

+366
-40
lines changed

10 files changed

+366
-40
lines changed

llvm/include/llvm/Analysis/CtxProfAnalysis.h

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,38 @@
99
#ifndef LLVM_ANALYSIS_CTXPROFANALYSIS_H
1010
#define LLVM_ANALYSIS_CTXPROFANALYSIS_H
1111

12+
#include "llvm/ADT/StringMap.h"
1213
#include "llvm/IR/GlobalValue.h"
1314
#include "llvm/IR/PassManager.h"
1415
#include "llvm/ProfileData/PGOCtxProfReader.h"
15-
#include <map>
1616

1717
namespace llvm {
1818

1919
class CtxProfAnalysis;
2020

2121
/// The instrumented contextual profile, produced by the CtxProfAnalysis.
2222
class PGOContextualProfile {
23+
friend class CtxProfAnalysis;
24+
friend class CtxProfAnalysisPrinterPass;
25+
struct FunctionInfo {
26+
uint32_t NextCounterIndex = 0;
27+
uint32_t NextCallsiteIndex = 0;
28+
const std::string Name;
29+
30+
FunctionInfo(StringRef Name) : Name(Name) {}
31+
};
2332
std::optional<PGOCtxProfContext::CallTargetMapTy> Profiles;
33+
// For the GUIDs in this module, associate metadata about each function which
34+
// we'll need when we maintain the profiles during IPO transformations.
35+
DenseMap<GlobalValue::GUID, FunctionInfo> FuncInfo;
2436

25-
public:
26-
explicit PGOContextualProfile(PGOCtxProfContext::CallTargetMapTy &&Profiles)
27-
: Profiles(std::move(Profiles)) {}
37+
GlobalValue::GUID getKnownGUID(const Function &F) const;
38+
39+
// This is meant to be constructed from CtxProfAnalysis, which will also set
40+
// its state piecemeal.
2841
PGOContextualProfile() = default;
42+
43+
public:
2944
PGOContextualProfile(const PGOContextualProfile &) = delete;
3045
PGOContextualProfile(PGOContextualProfile &&) = default;
3146

@@ -35,6 +50,18 @@ class PGOContextualProfile {
3550
return *Profiles;
3651
}
3752

53+
bool isFunctionKnown(const Function &F) const { return getKnownGUID(F) != 0; }
54+
55+
uint32_t allocateNextCounterIndex(const Function &F) {
56+
assert(isFunctionKnown(F));
57+
return FuncInfo.find(getKnownGUID(F))->second.NextCounterIndex++;
58+
}
59+
60+
uint32_t allocateNextCallsiteIndex(const Function &F) {
61+
assert(isFunctionKnown(F));
62+
return FuncInfo.find(getKnownGUID(F))->second.NextCallsiteIndex++;
63+
}
64+
3865
bool invalidate(Module &, const PreservedAnalyses &PA,
3966
ModuleAnalysisManager::Invalidator &) {
4067
// Check whether the analysis has been explicitly invalidated. Otherwise,

llvm/include/llvm/Transforms/Instrumentation/PGOCtxProfLowering.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,26 @@ class PGOCtxProfLoweringPass : public PassInfoMixin<PGOCtxProfLoweringPass> {
2424

2525
PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM);
2626
};
27+
28+
/// Assign a GUID to functions as metadata. GUID calculation takes linkage into
29+
/// account, which may change especially through and after thinlto. By
30+
/// pre-computing and assigning as metadata, this mechanism is resilient to such
31+
/// changes (as well as name changes e.g. suffix ".llvm." additions). It's
32+
/// arguably a much simpler mechanism than PGO's current GV-based one, and can
33+
/// be made available more broadly.
34+
35+
// FIXME(mtrofin): we can generalize this mechanism to calculate a GUID early in
36+
// the pass pipeline, associate it with any Global Value, and then use it for
37+
// PGO and ThinLTO.
38+
// At that point, this should be moved elsewhere.
39+
class AssignUniqueIDPass : public PassInfoMixin<AssignUniqueIDPass> {
40+
public:
41+
explicit AssignUniqueIDPass() = default;
42+
PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM);
43+
static const char *GUIDMetadataName;
44+
// This should become GlobalValue::getGUID
45+
static uint64_t getGUID(const Function &F);
46+
};
47+
2748
} // namespace llvm
2849
#endif

llvm/lib/Analysis/CtxProfAnalysis.cpp

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,14 @@
1414
#include "llvm/Analysis/CtxProfAnalysis.h"
1515
#include "llvm/ADT/STLExtras.h"
1616
#include "llvm/IR/Analysis.h"
17+
#include "llvm/IR/IntrinsicInst.h"
1718
#include "llvm/IR/Module.h"
1819
#include "llvm/IR/PassManager.h"
1920
#include "llvm/ProfileData/PGOCtxProfReader.h"
2021
#include "llvm/Support/CommandLine.h"
2122
#include "llvm/Support/JSON.h"
2223
#include "llvm/Support/MemoryBuffer.h"
24+
#include "llvm/Transforms/Instrumentation/PGOCtxProfLowering.h"
2325

2426
#define DEBUG_TYPE "ctx_prof"
2527

@@ -66,8 +68,8 @@ Value toJSON(const PGOCtxProfContext::CallTargetMapTy &P) {
6668

6769
AnalysisKey CtxProfAnalysis::Key;
6870

69-
CtxProfAnalysis::Result CtxProfAnalysis::run(Module &M,
70-
ModuleAnalysisManager &MAM) {
71+
PGOContextualProfile CtxProfAnalysis::run(Module &M,
72+
ModuleAnalysisManager &MAM) {
7173
ErrorOr<std::unique_ptr<MemoryBuffer>> MB = MemoryBuffer::getFile(Profile);
7274
if (auto EC = MB.getError()) {
7375
M.getContext().emitError("could not open contextual profile file: " +
@@ -81,7 +83,55 @@ CtxProfAnalysis::Result CtxProfAnalysis::run(Module &M,
8183
toString(MaybeCtx.takeError()));
8284
return {};
8385
}
84-
return Result(std::move(*MaybeCtx));
86+
87+
PGOContextualProfile Result;
88+
89+
for (const auto &F : M) {
90+
if (F.isDeclaration())
91+
continue;
92+
auto GUID = AssignUniqueIDPass::getGUID(F);
93+
assert(GUID);
94+
const auto &Entry = F.begin();
95+
uint32_t MaxCounters = 0; // we expect at least a counter.
96+
for (const auto &I : *Entry)
97+
if (auto *C = dyn_cast<InstrProfIncrementInst>(&I)) {
98+
MaxCounters =
99+
static_cast<uint32_t>(C->getNumCounters()->getZExtValue());
100+
break;
101+
}
102+
if (!MaxCounters)
103+
continue;
104+
uint32_t MaxCallsites = 0;
105+
for (const auto &BB : F)
106+
for (const auto &I : BB)
107+
if (auto *C = dyn_cast<InstrProfCallsite>(&I)) {
108+
MaxCallsites =
109+
static_cast<uint32_t>(C->getNumCounters()->getZExtValue());
110+
break;
111+
}
112+
auto [It, Ins] = Result.FuncInfo.insert(
113+
{GUID, PGOContextualProfile::FunctionInfo(F.getName())});
114+
(void)Ins;
115+
assert(Ins);
116+
It->second.NextCallsiteIndex = MaxCallsites;
117+
It->second.NextCounterIndex = MaxCounters;
118+
}
119+
// If we made it this far, the Result is valid - which we mark by setting
120+
// .Profiles.
121+
// Trim first the roots that aren't in this module.
122+
DenseSet<GlobalValue::GUID> ProfiledGUIDs;
123+
for (auto &[RootGuid, Tree] : llvm::make_early_inc_range(*MaybeCtx))
124+
if (!Result.FuncInfo.contains(RootGuid))
125+
MaybeCtx->erase(RootGuid);
126+
Result.Profiles = std::move(*MaybeCtx);
127+
return Result;
128+
}
129+
130+
GlobalValue::GUID PGOContextualProfile::getKnownGUID(const Function &F) const {
131+
if (auto It = FuncInfo.find(AssignUniqueIDPass::getGUID(F));
132+
It != FuncInfo.end())
133+
return It->first;
134+
return 0;
85135
}
86136

87137
PreservedAnalyses CtxProfAnalysisPrinterPass::run(Module &M,
@@ -91,8 +141,16 @@ PreservedAnalyses CtxProfAnalysisPrinterPass::run(Module &M,
91141
M.getContext().emitError("Invalid CtxProfAnalysis");
92142
return PreservedAnalyses::all();
93143
}
144+
145+
OS << "Function Info:\n";
146+
for (const auto &[Guid, FuncInfo] : C.FuncInfo)
147+
OS << Guid << " : " << FuncInfo.Name
148+
<< ". MaxCounterID: " << FuncInfo.NextCounterIndex
149+
<< ". MaxCallsiteID: " << FuncInfo.NextCallsiteIndex << "\n";
150+
94151
const auto JSONed = ::llvm::json::toJSON(C.profiles());
95152

153+
OS << "\nCurrent Profile:\n";
96154
OS << formatv("{0:2}", JSONed);
97155
OS << "\n";
98156
return PreservedAnalyses::all();

llvm/lib/Passes/PassBuilderPipelines.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1196,6 +1196,7 @@ PassBuilder::buildModuleSimplificationPipeline(OptimizationLevel Level,
11961196
// In pre-link, we just want the instrumented IR. We use the contextual
11971197
// profile in the post-thinlink phase.
11981198
// The instrumentation will be removed in post-thinlink after IPO.
1199+
MPM.addPass(AssignUniqueIDPass());
11991200
if (IsCtxProfUse)
12001201
return MPM;
12011202
addPostPGOLoopRotation(MPM, Level);

llvm/lib/Passes/PassRegistry.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ MODULE_PASS("always-inline", AlwaysInlinerPass())
4646
MODULE_PASS("annotation2metadata", Annotation2MetadataPass())
4747
MODULE_PASS("attributor", AttributorPass())
4848
MODULE_PASS("attributor-light", AttributorLightPass())
49+
MODULE_PASS("assign-guid", AssignUniqueIDPass())
4950
MODULE_PASS("called-value-propagation", CalledValuePropagationPass())
5051
MODULE_PASS("canonicalize-aliases", CanonicalizeAliasesPass())
5152
MODULE_PASS("check-debugify", NewPMCheckDebugifyPass())

llvm/lib/Transforms/Instrumentation/PGOCtxProfLowering.cpp

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "llvm/IR/IntrinsicInst.h"
1717
#include "llvm/IR/Module.h"
1818
#include "llvm/IR/PassManager.h"
19+
#include "llvm/ProfileData/InstrProf.h"
1920
#include "llvm/Support/CommandLine.h"
2021
#include <utility>
2122

@@ -223,8 +224,8 @@ bool CtxInstrumentationLowerer::lowerFunction(Function &F) {
223224
assert(Mark->getIndex()->isZero());
224225

225226
IRBuilder<> Builder(Mark);
226-
// FIXME(mtrofin): use InstrProfSymtab::getCanonicalName
227-
Guid = Builder.getInt64(F.getGUID());
227+
228+
Guid = Builder.getInt64(AssignUniqueIDPass::getGUID(F));
228229
// The type of the context of this function is now knowable since we have
229230
// NrCallsites and NrCounters. We delcare it here because it's more
230231
// convenient - we have the Builder.
@@ -349,3 +350,34 @@ bool CtxInstrumentationLowerer::lowerFunction(Function &F) {
349350
F.getName());
350351
return true;
351352
}
353+
354+
const char *AssignUniqueIDPass::GUIDMetadataName = "unique_id";
355+
356+
PreservedAnalyses AssignUniqueIDPass::run(Module &M,
357+
ModuleAnalysisManager &MAM) {
358+
for (auto &F : M.functions()) {
359+
if (F.isDeclaration())
360+
continue;
361+
const GlobalValue::GUID GUID = F.getGUID();
362+
assert(!F.getMetadata(GUIDMetadataName) ||
363+
GUID == AssignUniqueIDPass::getGUID(F));
364+
F.setMetadata(GUIDMetadataName,
365+
MDNode::get(M.getContext(),
366+
{ConstantAsMetadata::get(ConstantInt::get(
367+
Type::getInt64Ty(M.getContext()), GUID))}));
368+
}
369+
return PreservedAnalyses::none();
370+
}
371+
372+
GlobalValue::GUID AssignUniqueIDPass::getGUID(const Function &F) {
373+
if (F.isDeclaration()) {
374+
assert(GlobalValue::isExternalLinkage(F.getLinkage()));
375+
return GlobalValue::getGUID(F.getGlobalIdentifier());
376+
}
377+
auto *MD = F.getMetadata(GUIDMetadataName);
378+
assert(MD);
379+
return cast<ConstantInt>(cast<ConstantAsMetadata>(MD->getOperand(0))
380+
->getValue()
381+
->stripPointerCasts())
382+
->getZExtValue();
383+
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
; REQUIRES: x86_64-linux
2+
3+
; RUN: split-file %s %t
4+
;
5+
; Test that the GUID metadata survives through thinlink.
6+
;
7+
; RUN: llvm-ctxprof-util fromJSON --input=%t/profile.json --output=%t/profile.ctxprofdata
8+
;
9+
; Disable pre-inline to avoid losing the trivial functions to the preinliner.
10+
;
11+
; RUN: opt -module-summary -disable-preinline -passes='thinlto-pre-link<O2>' -use-ctx-profile=%t/profile.ctxprofdata -o %t/m1.bc %t/m1.ll
12+
; RUN: opt -module-summary -disable-preinline -passes='thinlto-pre-link<O2>' -use-ctx-profile=%t/profile.ctxprofdata -o %t/m2.bc %t/m2.ll
13+
;
14+
; RUN: rm -rf %t/postlink
15+
; RUN: mkdir %t/postlink
16+
; RUN: llvm-lto2 run %t/m1.bc %t/m2.bc -o %t/ -thinlto-distributed-indexes \
17+
; RUN: -r %t/m1.bc,f1,plx \
18+
; RUN: -r %t/m2.bc,f1 \
19+
; RUN: -r %t/m2.bc,entrypoint,plx
20+
; RUN: opt --passes='function-import,require<ctx-prof-analysis>,print<ctx-prof-analysis>' \
21+
; RUN: -summary-file=%t/m2.bc.thinlto.bc -use-ctx-profile=%t/profile.ctxprofdata %t/m2.bc \
22+
; RUN: -S -o %t/m2.post.ll 2> %t/profile.txt
23+
; RUN: diff %t/expected.txt %t/profile.txt
24+
;--- m1.ll
25+
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
26+
target triple = "x86_64-pc-linux-gnu"
27+
28+
define private void @f2() {
29+
ret void
30+
}
31+
32+
define void @f1() {
33+
call void @f2()
34+
ret void
35+
}
36+
37+
;--- m2.ll
38+
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
39+
target triple = "x86_64-pc-linux-gnu"
40+
41+
declare void @f1()
42+
43+
define void @entrypoint() {
44+
call void @f1()
45+
ret void
46+
}
47+
;--- profile.json
48+
[
49+
{
50+
"Callsites": [
51+
[
52+
{
53+
"Callsites": [
54+
[
55+
{
56+
"Counters": [
57+
10
58+
],
59+
"Guid": 5967942613276634709
60+
}
61+
]
62+
],
63+
"Counters": [
64+
7
65+
],
66+
"Guid": 2072045998141807037
67+
}
68+
]
69+
],
70+
"Counters": [
71+
1
72+
],
73+
"Guid": 10507721908651011566
74+
}
75+
]
76+
;--- expected.txt
77+
Function Info:
78+
5967942613276634709 : f2.llvm.0. MaxCounterID: 1. MaxCallsiteID: 0
79+
10507721908651011566 : entrypoint. MaxCounterID: 1. MaxCallsiteID: 1
80+
2072045998141807037 : f1. MaxCounterID: 1. MaxCallsiteID: 1
81+
82+
Current Profile:
83+
[
84+
{
85+
"Callsites": [
86+
[
87+
{
88+
"Callsites": [
89+
[
90+
{
91+
"Counters": [
92+
10
93+
],
94+
"Guid": 5967942613276634709
95+
}
96+
]
97+
],
98+
"Counters": [
99+
7
100+
],
101+
"Guid": 2072045998141807037
102+
}
103+
]
104+
],
105+
"Counters": [
106+
1
107+
],
108+
"Guid": 10507721908651011566
109+
}
110+
]

0 commit comments

Comments
 (0)