Skip to content

Commit 1f50494

Browse files
Implement module-wide analysis of global variable hotness.
* In StaticDataProfileInfo.h/cpp, add an immutable pass to keep track of constants and their profile information across functions in a module. * Add a module pass, StaticDataAnnotator, to set global variable's section prefix based on module-wide hotness.
1 parent 4a2a881 commit 1f50494

File tree

12 files changed

+392
-143
lines changed

12 files changed

+392
-143
lines changed
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#ifndef LLVM_ANALYSIS_STATICDATAPROFILEINFO_H
2+
#define LLVM_ANALYSIS_STATICDATAPROFILEINFO_H
3+
4+
#include "llvm/ADT/DenseMap.h"
5+
#include "llvm/ADT/DenseSet.h"
6+
#include "llvm/IR/Constant.h"
7+
#include "llvm/Pass.h"
8+
9+
namespace llvm {
10+
11+
/// A class that holds the constants that represent static data and their
12+
/// profile information and provides methods to operate on them.
13+
class StaticDataProfileInfo {
14+
public:
15+
/// Accummulate the profile count of a constant that will be lowered to static
16+
/// data sections.
17+
DenseMap<const Constant *, uint64_t> ConstantProfileCounts;
18+
19+
/// Keeps track of the constants that are seen at least once without profile
20+
/// counts.
21+
DenseSet<const Constant *> ConstantWithoutCounts;
22+
23+
public:
24+
StaticDataProfileInfo() = default;
25+
26+
/// If \p Count is not nullopt, add it to the profile count of the constant \p
27+
/// C in a saturating way, and clamp the count to \p getInstrMaxCountValue if
28+
/// the result exceeds it. Otherwise, mark the constant as having no profile
29+
/// count.
30+
void addConstantProfileCount(const Constant *C,
31+
std::optional<uint64_t> Count);
32+
33+
/// If \p C has a count, return it. Otherwise, return std::nullopt.
34+
std::optional<uint64_t> getConstantProfileCount(const Constant *C) const;
35+
36+
/// Return true if the constant \p C is seen at least once without profiles.
37+
bool hasUnknownCount(const Constant *C) const {
38+
return ConstantWithoutCounts.count(C);
39+
}
40+
};
41+
42+
/// This wraps the StaticDataProfileInfo object as an immutable pass, for a
43+
/// backend pass to operate on.
44+
class StaticDataProfileInfoWrapperPass : public ImmutablePass {
45+
public:
46+
static char ID;
47+
StaticDataProfileInfoWrapperPass();
48+
bool doInitialization(Module &M) override;
49+
bool doFinalization(Module &M) override;
50+
51+
StaticDataProfileInfo &getStaticDataProfileInfo() { return *Info; }
52+
const StaticDataProfileInfo &getStaticDataProfileInfo() const {
53+
return *Info;
54+
}
55+
56+
/// This pass provides StaticDataProfileInfo for reads/writes but does not
57+
/// modify \p M or other analysis. All analysis are preserved.
58+
void getAnalysisUsage(AnalysisUsage &AU) const override {
59+
AU.setPreservesAll();
60+
}
61+
62+
private:
63+
std::unique_ptr<StaticDataProfileInfo> Info;
64+
};
65+
66+
} // namespace llvm
67+
68+
#endif // LLVM_ANALYSIS_STATICDATAPROFILEINFO_H

llvm/include/llvm/CodeGen/Passes.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,15 @@ namespace llvm {
7171
/// using profile information.
7272
MachineFunctionPass *createMachineFunctionSplitterPass();
7373

74-
/// createStaticDataSplitterPass - This pass partitions a static data section
75-
/// into a hot and cold section using profile information.
74+
/// createStaticDataSplitterPass - This is a machine-function pass that
75+
/// categorizes static data hotness using profile information.
7676
MachineFunctionPass *createStaticDataSplitterPass();
7777

78+
/// createStaticDataAnnotatorPASS - This is a module pass that reads from
79+
/// StaticDataProfileInfoWrapperPass and annotates the section prefix of
80+
/// global variables.
81+
ModulePass *createStaticDataAnnotatorPass();
82+
7883
/// MachineFunctionPrinter pass - This pass prints out the machine function to
7984
/// the given stream as a debugging tool.
8085
MachineFunctionPass *

llvm/include/llvm/InitializePasses.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,8 @@ void initializeMachineLoopInfoWrapperPassPass(PassRegistry &);
205205
void initializeMachineModuleInfoWrapperPassPass(PassRegistry &);
206206
void initializeMachineOptimizationRemarkEmitterPassPass(PassRegistry &);
207207
void initializeMachineOutlinerPass(PassRegistry &);
208+
void initializeStaticDataProfileInfoWrapperPassPass(PassRegistry &);
209+
void initializeStaticDataAnnotatorPass(PassRegistry &);
208210
void initializeMachinePipelinerPass(PassRegistry &);
209211
void initializeMachinePostDominatorTreeWrapperPassPass(PassRegistry &);
210212
void initializeMachineRegionInfoPassPass(PassRegistry &);

llvm/include/llvm/Passes/MachinePassRegistry.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ MACHINE_FUNCTION_PASS_WITH_PARAMS(
207207
#define DUMMY_MACHINE_MODULE_PASS(NAME, PASS_NAME)
208208
#endif
209209
DUMMY_MACHINE_MODULE_PASS("machine-outliner", MachineOutlinerPass)
210+
DUMMY_MACHINE_MODULE_PASS("static-data-annotator", StaticDataAnnotator)
210211
DUMMY_MACHINE_MODULE_PASS("pseudo-probe-inserter", PseudoProbeInserterPass)
211212
DUMMY_MACHINE_MODULE_PASS("mir-debugify", DebugifyMachineModule)
212213
DUMMY_MACHINE_MODULE_PASS("mir-check-debugify", CheckDebugMachineModulePass)

llvm/lib/Analysis/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ add_llvm_component_library(LLVMAnalysis
126126
ScalarEvolutionAliasAnalysis.cpp
127127
ScalarEvolutionDivision.cpp
128128
ScalarEvolutionNormalization.cpp
129+
StaticDataProfileInfo.cpp
129130
StackLifetime.cpp
130131
StackSafetyAnalysis.cpp
131132
StructuralHash.cpp
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#include "llvm/Analysis/StaticDataProfileInfo.h"
2+
#include "llvm/IR/Constant.h"
3+
#include "llvm/IR/GlobalVariable.h"
4+
#include "llvm/InitializePasses.h"
5+
#include "llvm/ProfileData/InstrProf.h"
6+
#include <sys/types.h>
7+
8+
using namespace llvm;
9+
void StaticDataProfileInfo::addConstantProfileCount(
10+
const Constant *C, std::optional<uint64_t> Count) {
11+
if (!Count) {
12+
ConstantWithoutCounts.insert(C);
13+
return;
14+
}
15+
uint64_t &OriginalCount = ConstantProfileCounts[C];
16+
OriginalCount += llvm::SaturatingAdd(*Count, OriginalCount);
17+
// Clamp the count to getInstrMaxCountValue. InstrFDO reserves a few
18+
// large values for special use.
19+
if (OriginalCount > getInstrMaxCountValue())
20+
OriginalCount = getInstrMaxCountValue();
21+
}
22+
23+
std::optional<uint64_t>
24+
StaticDataProfileInfo::getConstantProfileCount(const Constant *C) const {
25+
auto I = ConstantProfileCounts.find(C);
26+
if (I == ConstantProfileCounts.end())
27+
return std::nullopt;
28+
return I->second;
29+
}
30+
31+
bool StaticDataProfileInfoWrapperPass::doInitialization(Module &M) {
32+
Info.reset(new StaticDataProfileInfo());
33+
return false;
34+
}
35+
36+
bool StaticDataProfileInfoWrapperPass::doFinalization(Module &M) {
37+
Info.reset();
38+
return false;
39+
}
40+
41+
INITIALIZE_PASS(StaticDataProfileInfoWrapperPass, "static-data-profile-info",
42+
"Static Data Profile Info", false, true)
43+
44+
StaticDataProfileInfoWrapperPass::StaticDataProfileInfoWrapperPass()
45+
: ImmutablePass(ID) {
46+
initializeStaticDataProfileInfoWrapperPassPass(
47+
*PassRegistry::getPassRegistry());
48+
}
49+
50+
char StaticDataProfileInfoWrapperPass::ID = 0;

llvm/lib/CodeGen/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ add_llvm_component_library(LLVMCodeGen
227227
StackProtector.cpp
228228
StackSlotColoring.cpp
229229
StaticDataSplitter.cpp
230+
StaticDataAnnotator.cpp
230231
SwiftErrorValueTracking.cpp
231232
SwitchLoweringUtils.cpp
232233
TailDuplication.cpp

llvm/lib/CodeGen/CodeGen.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ void llvm::initializeCodeGen(PassRegistry &Registry) {
132132
initializeStackProtectorPass(Registry);
133133
initializeStackSlotColoringLegacyPass(Registry);
134134
initializeStaticDataSplitterPass(Registry);
135+
initializeStaticDataAnnotatorPass(Registry);
135136
initializeStripDebugMachineModulePass(Registry);
136137
initializeTailDuplicateLegacyPass(Registry);
137138
initializeTargetPassConfigPass(Registry);
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
//===- StaticDataAnnotator - Annotate static data's section prefix --------===//
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+
// To reason about module-wide data hotness in a module granularity, this file
10+
// implements a module pass StaticDataAnnotator to work coordinately with the
11+
// StaticDataSplitter pass.
12+
//
13+
// The StaticDataSplitter pass is a machine function pass. It analyzes data
14+
// hotness based on code and adds counters in the StaticDataProfileInfo.
15+
// The StaticDataAnnotator pass is a module pass. It iterates global variables
16+
// in the module, looks up counters from StaticDataProfileInfo and sets the
17+
// section prefix based on profiles.
18+
//
19+
// The three-pass structure is implemented for practical reasons, to work around
20+
// the limitation that a module pass based on legacy pass manager cannot make
21+
// use of MachineBlockFrequencyInfo analysis. In the future, we can consider
22+
// porting the StaticDataSplitter pass to a module-pass using the new pass
23+
// manager framework. That way, analysis are lazily computed as opposed to
24+
// eagerly scheduled, and a module pass can use MachineBlockFrequencyInfo.
25+
//===----------------------------------------------------------------------===//
26+
27+
#include "llvm/Analysis/ProfileSummaryInfo.h"
28+
#include "llvm/Analysis/StaticDataProfileInfo.h"
29+
#include "llvm/CodeGen/Passes.h"
30+
#include "llvm/IR/Analysis.h"
31+
#include "llvm/IR/Module.h"
32+
#include "llvm/IR/PassManager.h"
33+
#include "llvm/InitializePasses.h"
34+
#include "llvm/Pass.h"
35+
#include "llvm/Support/raw_ostream.h"
36+
37+
#define DEBUG_TYPE "static-data-annotator"
38+
39+
using namespace llvm;
40+
41+
class StaticDataAnnotator : public ModulePass {
42+
public:
43+
static char ID;
44+
45+
StaticDataProfileInfo *SDPI = nullptr;
46+
const ProfileSummaryInfo *PSI = nullptr;
47+
48+
StaticDataAnnotator() : ModulePass(ID) {
49+
initializeStaticDataAnnotatorPass(*PassRegistry::getPassRegistry());
50+
}
51+
52+
void getAnalysisUsage(AnalysisUsage &AU) const override {
53+
AU.addRequired<StaticDataProfileInfoWrapperPass>();
54+
AU.addRequired<ProfileSummaryInfoWrapperPass>();
55+
AU.setPreservesAll();
56+
ModulePass::getAnalysisUsage(AU);
57+
}
58+
59+
StringRef getPassName() const override { return "Static Data Annotator"; }
60+
61+
bool runOnModule(Module &M) override;
62+
};
63+
64+
// Returns true if the global variable already has a section prefix that is the
65+
// same as `Prefix`.
66+
static bool alreadyHasSectionPrefix(const GlobalVariable &GV,
67+
StringRef Prefix) {
68+
std::optional<StringRef> SectionPrefix = GV.getSectionPrefix();
69+
return SectionPrefix && (*SectionPrefix == Prefix);
70+
}
71+
72+
bool StaticDataAnnotator::runOnModule(Module &M) {
73+
SDPI = &getAnalysis<StaticDataProfileInfoWrapperPass>()
74+
.getStaticDataProfileInfo();
75+
PSI = &getAnalysis<ProfileSummaryInfoWrapperPass>().getPSI();
76+
77+
if (!PSI->hasProfileSummary())
78+
return false;
79+
80+
bool Changed = false;
81+
for (auto &GV : M.globals()) {
82+
if (GV.isDeclarationForLinker())
83+
continue;
84+
85+
// Skip global variables without profile counts. The module may not be
86+
// profiled or instrumented.
87+
auto Count = SDPI->getConstantProfileCount(&GV);
88+
if (!Count)
89+
continue;
90+
91+
if (PSI->isHotCount(*Count) && !alreadyHasSectionPrefix(GV, "hot")) {
92+
// The variable counter is hot, set 'hot' section prefix if the section
93+
// prefix isn't hot already.
94+
GV.setSectionPrefix("hot");
95+
Changed = true;
96+
} else if (PSI->isColdCount(*Count) && !SDPI->hasUnknownCount(&GV) &&
97+
!alreadyHasSectionPrefix(GV, "unlikely")) {
98+
// The variable counter is cold, set 'unlikely' section prefix when
99+
// 1) the section prefix isn't unlikely already, and
100+
// 2) the variable is not seen without profile counts. The reason is that
101+
// a variable without profile counts doesn't have all its uses profiled,
102+
// for example when a function is not instrumented, or not sampled (new
103+
// code paths).
104+
GV.setSectionPrefix("unlikely");
105+
Changed = true;
106+
}
107+
}
108+
109+
return Changed;
110+
}
111+
112+
char StaticDataAnnotator::ID = 0;
113+
114+
INITIALIZE_PASS(StaticDataAnnotator, DEBUG_TYPE, "Static Data Annotator", false,
115+
false)
116+
117+
ModulePass *llvm::createStaticDataAnnotatorPass() {
118+
return new StaticDataAnnotator();
119+
}

0 commit comments

Comments
 (0)