Skip to content

Commit c8a70f4

Browse files
[CodeGen][StaticDataPartitioning]Place local-linkage global variables in hot or unlikely prefixed sections based on profile information (#125756)
In this PR, static-data-splitter pass finds out the local-linkage global variables in {`.rodata`, `.data.rel.ro`, `bss`, `.data`} sections by analyzing machine instruction operands, and aggregates their accesses from code across functions. A follow-up item is to analyze global variable initializers and count for access from data. * This limitation is demonstrated by `bss2` and `data3` in `llvm/test/CodeGen/X86/global-variable-partition.ll`. Some stats of static-data-splitter with this patch: **section**|**bss**|**rodata**|**data** :-----:|:-----:|:-----:|:-----: hot-prefixed section coverage|99.75%|97.71%|91.30% unlikely-prefixed section size percentage|67.94%|39.37%|63.10% 1. The coverage is defined as `#perf-sample-in-hot-prefixed <data> section / #perf-sample in <data.*> section` for each <data> section. * The perf command samples `MEM_INST_RETIRED.ALL_LOADS:u:pinned:precise=2` events at a high frequency (`perf -c 2251`) for 30 seconds. The profiled binary is built as non-PIE so `data.rel.ro` coverage data is not available. 2. The unlikely-prefixed `<data>` section size percentage is defined as `unlikely <data> section size / the sum size of <data>.* sections` for each `<data>` section
1 parent 1e00bb1 commit c8a70f4

File tree

12 files changed

+587
-10
lines changed

12 files changed

+587
-10
lines changed
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
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/Analysis/ProfileSummaryInfo.h"
7+
#include "llvm/IR/Constant.h"
8+
#include "llvm/Pass.h"
9+
10+
namespace llvm {
11+
12+
/// A class that holds the constants that represent static data and their
13+
/// profile information and provides methods to operate on them.
14+
class StaticDataProfileInfo {
15+
public:
16+
/// Accummulate the profile count of a constant that will be lowered to static
17+
/// data sections.
18+
DenseMap<const Constant *, uint64_t> ConstantProfileCounts;
19+
20+
/// Keeps track of the constants that are seen at least once without profile
21+
/// counts.
22+
DenseSet<const Constant *> ConstantWithoutCounts;
23+
24+
/// If \p C has a count, return it. Otherwise, return std::nullopt.
25+
std::optional<uint64_t> getConstantProfileCount(const Constant *C) const;
26+
27+
public:
28+
StaticDataProfileInfo() = default;
29+
30+
/// If \p Count is not nullopt, add it to the profile count of the constant \p
31+
/// C in a saturating way, and clamp the count to \p getInstrMaxCountValue if
32+
/// the result exceeds it. Otherwise, mark the constant as having no profile
33+
/// count.
34+
void addConstantProfileCount(const Constant *C,
35+
std::optional<uint64_t> Count);
36+
37+
/// Return a section prefix for the constant \p C based on its profile count.
38+
/// - If a constant doesn't have a counter, return an empty string.
39+
/// - Otherwise,
40+
/// - If it has a hot count, return "hot".
41+
/// - If it is seen by unprofiled function, return an empty string.
42+
/// - If it has a cold count, return "unlikely".
43+
/// - Otherwise (e.g. it's used by lukewarm functions), return an empty
44+
/// string.
45+
StringRef getConstantSectionPrefix(const Constant *C,
46+
const ProfileSummaryInfo *PSI) const;
47+
};
48+
49+
/// This wraps the StaticDataProfileInfo object as an immutable pass, for a
50+
/// backend pass to operate on.
51+
class StaticDataProfileInfoWrapperPass : public ImmutablePass {
52+
public:
53+
static char ID;
54+
StaticDataProfileInfoWrapperPass();
55+
bool doInitialization(Module &M) override;
56+
bool doFinalization(Module &M) override;
57+
58+
StaticDataProfileInfo &getStaticDataProfileInfo() { return *Info; }
59+
const StaticDataProfileInfo &getStaticDataProfileInfo() const {
60+
return *Info;
61+
}
62+
63+
/// This pass provides StaticDataProfileInfo for reads/writes but does not
64+
/// modify \p M or other analysis. All analysis are preserved.
65+
void getAnalysisUsage(AnalysisUsage &AU) const override {
66+
AU.setPreservesAll();
67+
}
68+
69+
private:
70+
std::unique_ptr<StaticDataProfileInfo> Info;
71+
};
72+
73+
} // namespace llvm
74+
75+
#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
@@ -203,6 +203,8 @@ void initializeMachineLoopInfoWrapperPassPass(PassRegistry &);
203203
void initializeMachineModuleInfoWrapperPassPass(PassRegistry &);
204204
void initializeMachineOptimizationRemarkEmitterPassPass(PassRegistry &);
205205
void initializeMachineOutlinerPass(PassRegistry &);
206+
void initializeStaticDataProfileInfoWrapperPassPass(PassRegistry &);
207+
void initializeStaticDataAnnotatorPass(PassRegistry &);
206208
void initializeMachinePipelinerPass(PassRegistry &);
207209
void initializeMachinePostDominatorTreeWrapperPassPass(PassRegistry &);
208210
void initializeMachineRegionInfoPassPass(PassRegistry &);

llvm/include/llvm/Passes/MachinePassRegistry.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,7 @@ MACHINE_FUNCTION_PASS_WITH_PARAMS(
263263
#define DUMMY_MACHINE_MODULE_PASS(NAME, PASS_NAME)
264264
#endif
265265
DUMMY_MACHINE_MODULE_PASS("machine-outliner", MachineOutlinerPass)
266+
DUMMY_MACHINE_MODULE_PASS("static-data-annotator", StaticDataAnnotator)
266267
DUMMY_MACHINE_MODULE_PASS("pseudo-probe-inserter", PseudoProbeInserterPass)
267268
DUMMY_MACHINE_MODULE_PASS("mir-debugify", DebugifyMachineModule)
268269
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
@@ -127,6 +127,7 @@ add_llvm_component_library(LLVMAnalysis
127127
ScalarEvolutionAliasAnalysis.cpp
128128
ScalarEvolutionDivision.cpp
129129
ScalarEvolutionNormalization.cpp
130+
StaticDataProfileInfo.cpp
130131
StackLifetime.cpp
131132
StackSafetyAnalysis.cpp
132133
StructuralHash.cpp
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#include "llvm/Analysis/StaticDataProfileInfo.h"
2+
#include "llvm/Analysis/ProfileSummaryInfo.h"
3+
#include "llvm/IR/Constant.h"
4+
#include "llvm/IR/GlobalVariable.h"
5+
#include "llvm/InitializePasses.h"
6+
#include "llvm/ProfileData/InstrProf.h"
7+
#include <sys/types.h>
8+
9+
using namespace llvm;
10+
void StaticDataProfileInfo::addConstantProfileCount(
11+
const Constant *C, std::optional<uint64_t> Count) {
12+
if (!Count) {
13+
ConstantWithoutCounts.insert(C);
14+
return;
15+
}
16+
uint64_t &OriginalCount = ConstantProfileCounts[C];
17+
OriginalCount = llvm::SaturatingAdd(*Count, OriginalCount);
18+
// Clamp the count to getInstrMaxCountValue. InstrFDO reserves a few
19+
// large values for special use.
20+
if (OriginalCount > getInstrMaxCountValue())
21+
OriginalCount = getInstrMaxCountValue();
22+
}
23+
24+
std::optional<uint64_t>
25+
StaticDataProfileInfo::getConstantProfileCount(const Constant *C) const {
26+
auto I = ConstantProfileCounts.find(C);
27+
if (I == ConstantProfileCounts.end())
28+
return std::nullopt;
29+
return I->second;
30+
}
31+
32+
StringRef StaticDataProfileInfo::getConstantSectionPrefix(
33+
const Constant *C, const ProfileSummaryInfo *PSI) const {
34+
auto Count = getConstantProfileCount(C);
35+
if (!Count)
36+
return "";
37+
// The accummulated counter shows the constant is hot. Return 'hot' whether
38+
// this variable is seen by unprofiled functions or not.
39+
if (PSI->isHotCount(*Count))
40+
return "hot";
41+
// The constant is not hot, and seen by unprofiled functions. We don't want to
42+
// assign it to unlikely sections, even if the counter says 'cold'. So return
43+
// an empty prefix before checking whether the counter is cold.
44+
if (ConstantWithoutCounts.count(C))
45+
return "";
46+
// The accummulated counter shows the constant is cold. Return 'unlikely'.
47+
if (PSI->isColdCount(*Count))
48+
return "unlikely";
49+
// The counter says lukewarm. Return an empty prefix.
50+
return "";
51+
}
52+
53+
bool StaticDataProfileInfoWrapperPass::doInitialization(Module &M) {
54+
Info.reset(new StaticDataProfileInfo());
55+
return false;
56+
}
57+
58+
bool StaticDataProfileInfoWrapperPass::doFinalization(Module &M) {
59+
Info.reset();
60+
return false;
61+
}
62+
63+
INITIALIZE_PASS(StaticDataProfileInfoWrapperPass, "static-data-profile-info",
64+
"Static Data Profile Info", false, true)
65+
66+
StaticDataProfileInfoWrapperPass::StaticDataProfileInfoWrapperPass()
67+
: ImmutablePass(ID) {
68+
initializeStaticDataProfileInfoWrapperPassPass(
69+
*PassRegistry::getPassRegistry());
70+
}
71+
72+
char StaticDataProfileInfoWrapperPass::ID = 0;

llvm/lib/CodeGen/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ add_llvm_component_library(LLVMCodeGen
228228
StackProtector.cpp
229229
StackSlotColoring.cpp
230230
StaticDataSplitter.cpp
231+
StaticDataAnnotator.cpp
231232
SwiftErrorValueTracking.cpp
232233
SwitchLoweringUtils.cpp
233234
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: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
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 StaticDataProfileInfo via its
15+
// wrapper pass StaticDataProfileInfoWrapper.
16+
// The StaticDataProfileInfoWrapper sits in the middle between the
17+
// StaticDataSplitter and StaticDataAnnotator passes.
18+
// The StaticDataAnnotator pass is a module pass. It iterates global variables
19+
// in the module, looks up counters from StaticDataProfileInfo and sets the
20+
// section prefix based on profiles.
21+
//
22+
// The three-pass structure is implemented for practical reasons, to work around
23+
// the limitation that a module pass based on legacy pass manager cannot make
24+
// use of MachineBlockFrequencyInfo analysis. In the future, we can consider
25+
// porting the StaticDataSplitter pass to a module-pass using the new pass
26+
// manager framework. That way, analysis are lazily computed as opposed to
27+
// eagerly scheduled, and a module pass can use MachineBlockFrequencyInfo.
28+
//===----------------------------------------------------------------------===//
29+
30+
#include "llvm/Analysis/ProfileSummaryInfo.h"
31+
#include "llvm/Analysis/StaticDataProfileInfo.h"
32+
#include "llvm/CodeGen/Passes.h"
33+
#include "llvm/IR/Analysis.h"
34+
#include "llvm/IR/Module.h"
35+
#include "llvm/IR/PassManager.h"
36+
#include "llvm/InitializePasses.h"
37+
#include "llvm/Pass.h"
38+
#include "llvm/Support/raw_ostream.h"
39+
40+
#define DEBUG_TYPE "static-data-annotator"
41+
42+
using namespace llvm;
43+
44+
/// A module pass which iterates global variables in the module and annotates
45+
/// their section prefixes based on profile-driven analysis.
46+
class StaticDataAnnotator : public ModulePass {
47+
public:
48+
static char ID;
49+
50+
StaticDataProfileInfo *SDPI = nullptr;
51+
const ProfileSummaryInfo *PSI = nullptr;
52+
53+
StaticDataAnnotator() : ModulePass(ID) {
54+
initializeStaticDataAnnotatorPass(*PassRegistry::getPassRegistry());
55+
}
56+
57+
void getAnalysisUsage(AnalysisUsage &AU) const override {
58+
AU.addRequired<StaticDataProfileInfoWrapperPass>();
59+
AU.addRequired<ProfileSummaryInfoWrapperPass>();
60+
AU.setPreservesAll();
61+
ModulePass::getAnalysisUsage(AU);
62+
}
63+
64+
StringRef getPassName() const override { return "Static Data Annotator"; }
65+
66+
bool runOnModule(Module &M) override;
67+
};
68+
69+
bool StaticDataAnnotator::runOnModule(Module &M) {
70+
SDPI = &getAnalysis<StaticDataProfileInfoWrapperPass>()
71+
.getStaticDataProfileInfo();
72+
PSI = &getAnalysis<ProfileSummaryInfoWrapperPass>().getPSI();
73+
74+
if (!PSI->hasProfileSummary())
75+
return false;
76+
77+
bool Changed = false;
78+
for (auto &GV : M.globals()) {
79+
if (GV.isDeclarationForLinker())
80+
continue;
81+
82+
// The implementation below assumes prior passes don't set section prefixes,
83+
// and specifically do 'assign' rather than 'update'. So report error if a
84+
// section prefix is already set.
85+
if (auto maybeSectionPrefix = GV.getSectionPrefix();
86+
maybeSectionPrefix && !maybeSectionPrefix->empty())
87+
llvm::report_fatal_error("Global variable " + GV.getName() +
88+
" already has a section prefix " +
89+
*maybeSectionPrefix);
90+
91+
StringRef SectionPrefix = SDPI->getConstantSectionPrefix(&GV, PSI);
92+
if (SectionPrefix.empty())
93+
continue;
94+
95+
GV.setSectionPrefix(SectionPrefix);
96+
Changed = true;
97+
}
98+
99+
return Changed;
100+
}
101+
102+
char StaticDataAnnotator::ID = 0;
103+
104+
INITIALIZE_PASS(StaticDataAnnotator, DEBUG_TYPE, "Static Data Annotator", false,
105+
false)
106+
107+
ModulePass *llvm::createStaticDataAnnotatorPass() {
108+
return new StaticDataAnnotator();
109+
}

0 commit comments

Comments
 (0)