Skip to content

Commit d4e4d95

Browse files
reconcile hotness
1 parent 4a7ba1d commit d4e4d95

File tree

8 files changed

+138
-72
lines changed

8 files changed

+138
-72
lines changed

llvm/include/llvm/Analysis/StaticDataProfileInfo.h

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@ namespace llvm {
1414
/// profile information and provides methods to operate on them.
1515
class StaticDataProfileInfo {
1616
public:
17-
/// Accummulate the profile count of a constant that will be lowered to static
18-
/// data sections.
17+
/// A constant and its profile count.
18+
/// A constant is tracked if both conditions are met:
19+
/// 1) It has local (i.e., private or internal) linkage.
20+
// 2) Its data kind is one of {.rodata, .data, .bss, .data.rel.ro}.
1921
DenseMap<const Constant *, uint64_t> ConstantProfileCounts;
2022

2123
/// Keeps track of the constants that are seen at least once without profile
@@ -26,11 +28,24 @@ class StaticDataProfileInfo {
2628
LLVM_ABI std::optional<uint64_t>
2729
getConstantProfileCount(const Constant *C) const;
2830

29-
LLVM_ABI std::optional<StringRef>
30-
getDataHotnessBasedOnProfileCount(const Constant *C,
31-
const ProfileSummaryInfo *PSI) const;
31+
enum class StaticDataHotness : uint8_t {
32+
Cold = 0,
33+
LukewarmOrUnknown = 1,
34+
Hot = 2,
35+
};
36+
37+
LLVM_ABI StaticDataHotness getSectionHotnessUsingProfileCount(
38+
const Constant *C, const ProfileSummaryInfo *PSI, uint64_t Count) const;
39+
LLVM_ABI StaticDataHotness
40+
getSectionHotnessUsingDAP(std::optional<StringRef> SectionPrefix) const;
41+
42+
LLVM_ABI StringRef hotnessToStr(StaticDataHotness Hotness) const;
43+
44+
bool HasDataAccessProf = false;
3245

3346
public:
47+
StaticDataProfileInfo(bool HasDataAccessProf)
48+
: HasDataAccessProf(HasDataAccessProf) {}
3449
StaticDataProfileInfo() = default;
3550

3651
/// If \p Count is not nullopt, add it to the profile count of the constant \p
@@ -40,14 +55,10 @@ class StaticDataProfileInfo {
4055
LLVM_ABI void addConstantProfileCount(const Constant *C,
4156
std::optional<uint64_t> Count);
4257

43-
/// Return a section prefix for the constant \p C based on its profile count.
44-
/// - If a constant doesn't have a counter, return an empty string.
45-
/// - Otherwise,
46-
/// - If it has a hot count, return "hot".
47-
/// - If it is seen by unprofiled function, return an empty string.
48-
/// - If it has a cold count, return "unlikely".
49-
/// - Otherwise (e.g. it's used by lukewarm functions), return an empty
50-
/// string.
58+
/// Given a constant \p C, returns a section prefix.
59+
/// If \p C is a global variable, the section prefix is the bigger one
60+
/// between its existing section prefix and its use profile count. Otherwise,
61+
/// the section prefix is based on its use profile count.
5162
LLVM_ABI StringRef getConstantSectionPrefix(
5263
const Constant *C, const ProfileSummaryInfo *PSI) const;
5364
};

llvm/include/llvm/IR/GlobalObject.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,9 +121,13 @@ class GlobalObject : public GlobalValue {
121121
/// appropriate default object file section.
122122
LLVM_ABI void setSection(StringRef S);
123123

124-
/// Set the section prefix for this global object.
124+
/// Set the section prefix for this global object. If \p Prefix is empty,
125+
/// the section prefix metadata will be cleared if it exists.
125126
LLVM_ABI void setSectionPrefix(StringRef Prefix);
126127

128+
/// If \p Prefix is different from existing prefix, update section prefix. Returns true if an update happens and false otherwise.
129+
LLVM_ABI bool updateSectionPrefix(StringRef Prefix);
130+
127131
/// Get the section prefix for this global object.
128132
LLVM_ABI std::optional<StringRef> getSectionPrefix() const;
129133

llvm/lib/Analysis/StaticDataProfileInfo.cpp

Lines changed: 59 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
#include "llvm/Analysis/StaticDataProfileInfo.h"
22
#include "llvm/Analysis/ProfileSummaryInfo.h"
33
#include "llvm/IR/Constant.h"
4+
#include "llvm/IR/Constants.h"
45
#include "llvm/IR/GlobalVariable.h"
6+
#include "llvm/IR/Metadata.h"
7+
#include "llvm/IR/Module.h"
58
#include "llvm/InitializePasses.h"
69
#include "llvm/ProfileData/InstrProf.h"
710

811
using namespace llvm;
12+
13+
extern cl::opt<bool> AnnotateStaticDataSectionPrefix;
14+
915
void StaticDataProfileInfo::addConstantProfileCount(
1016
const Constant *C, std::optional<uint64_t> Count) {
1117
if (!Count) {
@@ -28,65 +34,79 @@ StaticDataProfileInfo::getConstantProfileCount(const Constant *C) const {
2834
return I->second;
2935
}
3036

31-
std::optional<StringRef>
32-
StaticDataProfileInfo::getDataHotnessBasedOnProfileCount(
33-
const Constant *C, const ProfileSummaryInfo *PSI) const {
34-
auto Count = getConstantProfileCount(C);
35-
// The constant `C` doesn't have a profile count. `C` might be a external
36-
// linkage global variable, whose PGO-based counter is not tracked within one
37-
// IR module.
38-
if (!Count)
39-
return std::nullopt;
37+
StaticDataProfileInfo::StaticDataHotness
38+
StaticDataProfileInfo::getSectionHotnessUsingProfileCount(
39+
const Constant *C, const ProfileSummaryInfo *PSI, uint64_t Count) const {
4040
// The accummulated counter shows the constant is hot. Return 'hot' whether
4141
// this variable is seen by unprofiled functions or not.
42-
if (PSI->isHotCount(*Count))
43-
return "hot";
42+
if (PSI->isHotCount(Count))
43+
return StaticDataHotness::Hot;
4444
// The constant is not hot, and seen by unprofiled functions. We don't want to
4545
// assign it to unlikely sections, even if the counter says 'cold'. So return
4646
// an empty prefix before checking whether the counter is cold.
4747
if (ConstantWithoutCounts.count(C))
48-
return std::nullopt;
48+
return StaticDataHotness::LukewarmOrUnknown;
4949
// The accummulated counter shows the constant is cold. Return 'unlikely'.
50-
if (PSI->isColdCount(*Count)) {
51-
return "unlikely";
52-
}
53-
return "";
54-
}
50+
if (PSI->isColdCount(Count))
51+
return StaticDataHotness::Cold;
5552

56-
static StringRef reconcileHotness(StringRef SectionPrefix, StringRef Hotness) {
57-
assert((SectionPrefix == "hot" || SectionPrefix == "unlikely") &&
58-
"Section prefix must be 'hot' or 'unlikely'");
53+
return StaticDataHotness::LukewarmOrUnknown;
54+
}
5955

60-
if (SectionPrefix == "hot" || Hotness == "hot")
56+
StringRef StaticDataProfileInfo::hotnessToStr(
57+
StaticDataProfileInfo::StaticDataHotness Hotness) const {
58+
switch (Hotness) {
59+
case StaticDataProfileInfo::StaticDataHotness::Cold:
60+
return "unlikely";
61+
case StaticDataProfileInfo::StaticDataHotness::Hot:
6162
return "hot";
62-
assert(SectionPrefix == "unlikely" && "Section prefix must be 'unlikely'.");
63-
return Hotness;
63+
default:
64+
return "";
65+
}
6466
}
6567

66-
static StringRef
67-
reconcileOptionalHotness(std::optional<StringRef> SectionPrefix,
68-
std::optional<StringRef> Hotness) {
69-
if (!SectionPrefix)
70-
return Hotness.value_or("");
71-
if (!Hotness)
72-
return SectionPrefix.value_or("");
73-
74-
return reconcileHotness(*SectionPrefix, *Hotness);
68+
StaticDataProfileInfo::StaticDataHotness
69+
StaticDataProfileInfo::getSectionHotnessUsingDAP(
70+
std::optional<StringRef> MaybeSectionPrefix) const {
71+
if (!MaybeSectionPrefix)
72+
return StaticDataProfileInfo::StaticDataHotness::LukewarmOrUnknown;
73+
StringRef Prefix = *MaybeSectionPrefix;
74+
assert((Prefix == "hot" || Prefix == "unlikely") &&
75+
"Expect section_prefix to be one of hot or unlikely");
76+
return Prefix == "hot" ? StaticDataProfileInfo::StaticDataHotness::Hot
77+
: StaticDataProfileInfo::StaticDataHotness::Cold;
7578
}
7679

7780
StringRef StaticDataProfileInfo::getConstantSectionPrefix(
7881
const Constant *C, const ProfileSummaryInfo *PSI) const {
79-
std::optional<StringRef> HotnessBasedOnCount =
80-
getDataHotnessBasedOnProfileCount(C, PSI);
81-
if (const GlobalVariable *GV = dyn_cast<GlobalVariable>(C))
82-
return reconcileOptionalHotness(GV->getSectionPrefix(),
83-
HotnessBasedOnCount);
82+
auto Count = getConstantProfileCount(C);
8483

85-
return HotnessBasedOnCount.value_or("");
84+
if (HasDataAccessProf) {
85+
// Module flag `HasDataAccessProf` is 1 -> empty section prefix means uknown
86+
// hotness except for string literals.
87+
if (const GlobalVariable *GV = dyn_cast<GlobalVariable>(C);
88+
GV && !GV->getName().starts_with(".str")) {
89+
auto HotnessFromDAP = getSectionHotnessUsingDAP(GV->getSectionPrefix());
90+
91+
if (!Count)
92+
return hotnessToStr(HotnessFromDAP);
93+
94+
auto HotnessFromPGO = getSectionHotnessUsingProfileCount(C, PSI, *Count);
95+
return hotnessToStr(std::max(HotnessFromDAP, HotnessFromPGO));
96+
}
97+
}
98+
99+
if (!Count)
100+
return "";
101+
return hotnessToStr(getSectionHotnessUsingProfileCount(C, PSI, *Count));
86102
}
87103

88104
bool StaticDataProfileInfoWrapperPass::doInitialization(Module &M) {
89-
Info.reset(new StaticDataProfileInfo());
105+
bool HasDataAccessProf = false;
106+
if (auto *MD = mdconst::extract_or_null<ConstantInt>(
107+
M.getModuleFlag("HasDataAccessProf")))
108+
HasDataAccessProf = MD->getZExtValue();
109+
Info.reset(new StaticDataProfileInfo(HasDataAccessProf));
90110
return false;
91111
}
92112

llvm/lib/CodeGen/StaticDataAnnotator.cpp

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
#include "llvm/Analysis/StaticDataProfileInfo.h"
3232
#include "llvm/CodeGen/Passes.h"
3333
#include "llvm/IR/Analysis.h"
34+
#include "llvm/IR/Constants.h"
35+
#include "llvm/IR/Metadata.h"
3436
#include "llvm/IR/Module.h"
3537
#include "llvm/IR/PassManager.h"
3638
#include "llvm/InitializePasses.h"
@@ -79,11 +81,7 @@ bool StaticDataAnnotator::runOnModule(Module &M) {
7981
continue;
8082

8183
StringRef SectionPrefix = SDPI->getConstantSectionPrefix(&GV, PSI);
82-
if (SectionPrefix.empty())
83-
continue;
84-
85-
GV.setSectionPrefix(SectionPrefix);
86-
Changed = true;
84+
Changed |= GV.updateSectionPrefix(SectionPrefix);
8785
}
8886

8987
return Changed;

llvm/lib/IR/Globals.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,11 +289,28 @@ void GlobalObject::setSection(StringRef S) {
289289
}
290290

291291
void GlobalObject::setSectionPrefix(StringRef Prefix) {
292+
if (Prefix.empty()) {
293+
setMetadata(LLVMContext::MD_section_prefix, nullptr);
294+
return;
295+
}
292296
MDBuilder MDB(getContext());
293297
setMetadata(LLVMContext::MD_section_prefix,
294298
MDB.createGlobalObjectSectionPrefix(Prefix));
295299
}
296300

301+
bool GlobalObject::updateSectionPrefix(StringRef Prefix) {
302+
auto MD = getMetadata(LLVMContext::MD_section_prefix);
303+
StringRef ExistingPrefix; // Empty by default.
304+
if (MD != nullptr)
305+
ExistingPrefix = cast<MDString>(MD->getOperand(1))->getString();
306+
307+
if (ExistingPrefix != Prefix) {
308+
setSectionPrefix(Prefix);
309+
return true;
310+
}
311+
return false;
312+
}
313+
297314
std::optional<StringRef> GlobalObject::getSectionPrefix() const {
298315
if (MDNode *MD = getMetadata(LLVMContext::MD_section_prefix)) {
299316
[[maybe_unused]] StringRef MDName =

llvm/lib/ProfileData/DataAccessProf.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "llvm/ProfileData/DataAccessProf.h"
22
#include "llvm/ADT/STLExtras.h"
33
#include "llvm/ProfileData/InstrProf.h"
4+
#include "llvm/Support/CommandLine.h"
45
#include "llvm/Support/Compression.h"
56
#include "llvm/Support/Endian.h"
67
#include "llvm/Support/Errc.h"
@@ -9,6 +10,9 @@
910
#include "llvm/Support/raw_ostream.h"
1011

1112
namespace llvm {
13+
cl::opt<bool> AnnotateStaticDataSectionPrefix(
14+
"memprof-annotate-static-data-prefix", cl::init(false), cl::Hidden,
15+
cl::desc("If true, annotate the static data section prefix"));
1216
namespace memprof {
1317

1418
// If `Map` has an entry keyed by `Str`, returns the entry iterator. Otherwise,

llvm/lib/Transforms/Instrumentation/MemProfUse.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ using namespace llvm::memprof;
4141
#define DEBUG_TYPE "memprof"
4242

4343
namespace llvm {
44+
extern cl::opt<bool> AnnotateStaticDataSectionPrefix;
4445
extern cl::opt<bool> PGOWarnMissing;
4546
extern cl::opt<bool> NoPGOWarnMismatch;
4647
extern cl::opt<bool> NoPGOWarnMismatchComdatWeak;
@@ -76,10 +77,6 @@ static cl::opt<unsigned> MinMatchedColdBytePercent(
7677
"memprof-matching-cold-threshold", cl::init(100), cl::Hidden,
7778
cl::desc("Min percent of cold bytes matched to hint allocation cold"));
7879

79-
static cl::opt<bool> AnnotateStaticDataSectionPrefix(
80-
"memprof-annotate-static-data-prefix", cl::init(false), cl::Hidden,
81-
cl::desc("If true, annotate the static data section prefix"));
82-
8380
// Matching statistics
8481
STATISTIC(NumOfMemProfMissing, "Number of functions without memory profile.");
8582
STATISTIC(NumOfMemProfMismatch,
@@ -797,7 +794,11 @@ bool MemProfUsePass::annotateGlobalVariables(
797794
if (!AnnotateStaticDataSectionPrefix || M.globals().empty())
798795
return false;
799796

797+
// The module flag helps codegen passes interpret empty section prefix:
798+
// - 0 : empty section prefix is expected for each GV.
799+
// - 1 : empty section prefix means the GV has unknown hotness.
800800
if (!DataAccessProf) {
801+
M.addModuleFlag(Module::Warning, "HasDataAccessProf", 0U);
801802
M.getContext().diagnose(DiagnosticInfoPGOProfile(
802803
MemoryProfileFileName.data(),
803804
StringRef("Data access profiles not found in memprof. Ignore "
@@ -806,6 +807,8 @@ bool MemProfUsePass::annotateGlobalVariables(
806807
return false;
807808
}
808809

810+
M.addModuleFlag(Module::Warning, "HasDataAccessProf", 1);
811+
809812
bool Changed = false;
810813
// Iterate all global variables in the module and annotate them based on
811814
// data access profiles. Note it's up to the linker to decide how to map input

0 commit comments

Comments
 (0)