Skip to content

Commit 1cec6be

Browse files
[StaticDataLayout] Reconcile data hotness based on PGO counters and data access profiles (#163325)
This PR enhances the `StaticDataProfileInfo::getConstantSectionPrefix` pass to reconcile data hotness information from both PGO counters and data access profiles. When both profiles are available for a global variable, the pass will now use the "hotter" of the two to determine the variable's section placement. This is a follow-up patch of #162388
1 parent d60d038 commit 1cec6be

File tree

3 files changed

+173
-16
lines changed

3 files changed

+173
-16
lines changed

llvm/include/llvm/Analysis/StaticDataProfileInfo.h

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,18 @@ class StaticDataProfileInfo {
6060
LLVM_ABI StaticDataHotness getConstantHotnessUsingProfileCount(
6161
const Constant *C, const ProfileSummaryInfo *PSI, uint64_t Count) const;
6262

63+
/// Return the hotness based on section prefix \p SectionPrefix.
64+
LLVM_ABI StaticDataHotness getSectionHotnessUsingDataAccessProfile(
65+
std::optional<StringRef> SectionPrefix) const;
66+
6367
/// Return the string representation of the hotness enum \p Hotness.
6468
LLVM_ABI StringRef hotnessToStr(StaticDataHotness Hotness) const;
6569

70+
bool EnableDataAccessProf = false;
71+
6672
public:
67-
StaticDataProfileInfo() = default;
73+
StaticDataProfileInfo(bool EnableDataAccessProf)
74+
: EnableDataAccessProf(EnableDataAccessProf) {}
6875

6976
/// If \p Count is not nullopt, add it to the profile count of the constant \p
7077
/// C in a saturating way, and clamp the count to \p getInstrMaxCountValue if
@@ -73,14 +80,10 @@ class StaticDataProfileInfo {
7380
LLVM_ABI void addConstantProfileCount(const Constant *C,
7481
std::optional<uint64_t> Count);
7582

76-
/// Return a section prefix for the constant \p C based on its profile count.
77-
/// - If a constant doesn't have a counter, return an empty string.
78-
/// - Otherwise,
79-
/// - If it has a hot count, return "hot".
80-
/// - If it is seen by unprofiled function, return an empty string.
81-
/// - If it has a cold count, return "unlikely".
82-
/// - Otherwise (e.g. it's used by lukewarm functions), return an empty
83-
/// string.
83+
/// Given a constant \p C, returns a section prefix.
84+
/// If \p C is a global variable, the section prefix is the bigger one
85+
/// between its existing section prefix and its use profile count. Otherwise,
86+
/// the section prefix is based on its use profile count.
8487
LLVM_ABI StringRef getConstantSectionPrefix(
8588
const Constant *C, const ProfileSummaryInfo *PSI) const;
8689
};

llvm/lib/Analysis/StaticDataProfileInfo.cpp

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
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/Module.h"
57
#include "llvm/InitializePasses.h"
68
#include "llvm/ProfileData/InstrProf.h"
79

10+
#define DEBUG_TYPE "static-data-profile-info"
11+
812
using namespace llvm;
913

1014
namespace llvm {
@@ -79,6 +83,17 @@ StaticDataProfileInfo::getConstantHotnessUsingProfileCount(
7983
return StaticDataHotness::LukewarmOrUnknown;
8084
}
8185

86+
StaticDataProfileInfo::StaticDataHotness
87+
StaticDataProfileInfo::getSectionHotnessUsingDataAccessProfile(
88+
std::optional<StringRef> MaybeSectionPrefix) const {
89+
if (!MaybeSectionPrefix)
90+
return StaticDataHotness::LukewarmOrUnknown;
91+
StringRef Prefix = *MaybeSectionPrefix;
92+
assert((Prefix == "hot" || Prefix == "unlikely") &&
93+
"Expect section_prefix to be one of hot or unlikely");
94+
return Prefix == "hot" ? StaticDataHotness::Hot : StaticDataHotness::Cold;
95+
}
96+
8297
StringRef StaticDataProfileInfo::hotnessToStr(StaticDataHotness Hotness) const {
8398
switch (Hotness) {
8499
case StaticDataHotness::Cold:
@@ -101,13 +116,66 @@ StaticDataProfileInfo::getConstantProfileCount(const Constant *C) const {
101116
StringRef StaticDataProfileInfo::getConstantSectionPrefix(
102117
const Constant *C, const ProfileSummaryInfo *PSI) const {
103118
std::optional<uint64_t> Count = getConstantProfileCount(C);
119+
120+
#ifndef NDEBUG
121+
auto DbgPrintPrefix = [](StringRef Prefix) {
122+
return Prefix.empty() ? "<empty>" : Prefix;
123+
};
124+
#endif
125+
126+
if (EnableDataAccessProf) {
127+
// Module flag `HasDataAccessProf` is 1 -> empty section prefix means
128+
// unknown hotness except for string literals.
129+
if (const GlobalVariable *GV = dyn_cast<GlobalVariable>(C);
130+
GV && llvm::memprof::IsAnnotationOK(*GV) &&
131+
!GV->getName().starts_with(".str")) {
132+
auto HotnessFromDataAccessProf =
133+
getSectionHotnessUsingDataAccessProfile(GV->getSectionPrefix());
134+
135+
if (!Count) {
136+
StringRef Prefix = hotnessToStr(HotnessFromDataAccessProf);
137+
LLVM_DEBUG(dbgs() << GV->getName() << " has section prefix "
138+
<< DbgPrintPrefix(Prefix)
139+
<< ", solely from data access profiles\n");
140+
return Prefix;
141+
}
142+
143+
// Both data access profiles and PGO counters are available. Use the
144+
// hotter one.
145+
auto HotnessFromPGO = getConstantHotnessUsingProfileCount(C, PSI, *Count);
146+
StaticDataHotness GlobalVarHotness = StaticDataHotness::LukewarmOrUnknown;
147+
if (HotnessFromDataAccessProf == StaticDataHotness::Hot ||
148+
HotnessFromPGO == StaticDataHotness::Hot) {
149+
GlobalVarHotness = StaticDataHotness::Hot;
150+
} else if (HotnessFromDataAccessProf ==
151+
StaticDataHotness::LukewarmOrUnknown ||
152+
HotnessFromPGO == StaticDataHotness::LukewarmOrUnknown) {
153+
GlobalVarHotness = StaticDataHotness::LukewarmOrUnknown;
154+
} else {
155+
GlobalVarHotness = StaticDataHotness::Cold;
156+
}
157+
StringRef Prefix = hotnessToStr(GlobalVarHotness);
158+
LLVM_DEBUG(
159+
dbgs() << GV->getName() << " has section prefix "
160+
<< DbgPrintPrefix(Prefix)
161+
<< ", the max from data access profiles as "
162+
<< DbgPrintPrefix(hotnessToStr(HotnessFromDataAccessProf))
163+
<< " and PGO counters as "
164+
<< DbgPrintPrefix(hotnessToStr(HotnessFromPGO)) << "\n");
165+
return Prefix;
166+
}
167+
}
104168
if (!Count)
105169
return "";
106170
return hotnessToStr(getConstantHotnessUsingProfileCount(C, PSI, *Count));
107171
}
108172

109173
bool StaticDataProfileInfoWrapperPass::doInitialization(Module &M) {
110-
Info.reset(new StaticDataProfileInfo());
174+
bool EnableDataAccessProf = false;
175+
if (auto *MD = mdconst::extract_or_null<ConstantInt>(
176+
M.getModuleFlag("EnableDataAccessProf")))
177+
EnableDataAccessProf = MD->getZExtValue();
178+
Info.reset(new StaticDataProfileInfo(EnableDataAccessProf));
111179
return false;
112180
}
113181

llvm/test/CodeGen/X86/global-variable-partition-with-dap.ll

Lines changed: 92 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,101 @@
11
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
22
target triple = "x86_64-unknown-linux-gnu"
33

4-
;; A minimal test case. Subsequent PRs will expand on this test case
5-
;; (e.g., with more functions, variables and profiles) and test the hotness
6-
;; reconcillation implementation.
4+
;; Requires asserts for -debug-only.
5+
; REQUIRES: asserts
6+
7+
; RUN: rm -rf %t && split-file %s %t && cd %t
8+
9+
; RUN: llc -mtriple=x86_64-unknown-linux-gnu -relocation-model=pic \
10+
; RUN: -partition-static-data-sections=true \
11+
; RUN: -debug-only=static-data-profile-info \
12+
; RUN: -data-sections=true -unique-section-names=false \
13+
; RUN: input-with-data-access-prof-on.ll -o - 2>&1 | FileCheck %s --check-prefixes=LOG,IR
14+
715
; RUN: llc -mtriple=x86_64-unknown-linux-gnu -relocation-model=pic \
816
; RUN: -partition-static-data-sections=true \
17+
; RUN: -debug-only=static-data-profile-info \
918
; RUN: -data-sections=true -unique-section-names=false \
10-
; RUN: %s -o - 2>&1 | FileCheck %s --check-prefix=IR
19+
; RUN: input-with-data-access-prof-off.ll -o - 2>&1 | FileCheck %s --check-prefixes=OFF
20+
21+
; LOG: hot_bss has section prefix hot, the max from data access profiles as hot and PGO counters as hot
22+
; LOG: data_unknown_hotness has section prefix <empty>, the max from data access profiles as <empty> and PGO counters as unlikely
23+
; LOG: external_relro_array has section prefix unlikely, solely from data access profiles
24+
25+
; IR: .type hot_bss,@object
26+
; IR-NEXT: .section .bss.hot.,"aw"
27+
; IR: .type data_unknown_hotness,@object
28+
; IR-NEXT: .section .data,"aw"
29+
; IR: .type external_relro_array,@object
30+
; IR-NEXT: .section .data.rel.ro.unlikely.,"aw"
31+
32+
33+
; OFF: .type hot_bss,@object
34+
; OFF-NEXT: .section .bss.hot.,"aw"
35+
; OFF: .type data_unknown_hotness,@object
36+
; OFF-NEXT: .section .data.unlikely.,"aw"
37+
;; Global variable section prefix metadata is not used when
38+
;; module flag `EnableDataAccessProf` is 0, and @external_relro_array has
39+
;; external linkage, so analysis based on PGO counters doesn't apply.
40+
; OFF: .type external_relro_array,@object # @external_relro_array
41+
; OFF-NEXT: .section .data.rel.ro,"aw"
42+
43+
;--- input-with-data-access-prof-on.ll
44+
; Internal vars
45+
@hot_bss = internal global i32 0, !section_prefix !17
46+
@data_unknown_hotness = internal global i32 1
47+
; External vars
48+
@external_relro_array = constant [2 x ptr] [ptr @hot_bss, ptr @data_unknown_hotness], !section_prefix !18
49+
50+
define void @cold_func() !prof !15 {
51+
%9 = load i32, ptr @data_unknown_hotness
52+
%11 = call i32 (...) @func_taking_arbitrary_param(i32 %9)
53+
ret void
54+
}
55+
56+
define void @hot_func() !prof !14 {
57+
%9 = load i32, ptr @hot_bss
58+
%11 = call i32 (...) @func_taking_arbitrary_param(i32 %9)
59+
ret void
60+
}
61+
62+
declare i32 @func_taking_arbitrary_param(...)
1163

12-
; IR: .section .bss.hot.,"aw"
64+
!llvm.module.flags = !{!0, !1}
1365

66+
!0 = !{i32 2, !"EnableDataAccessProf", i32 1}
67+
!1 = !{i32 1, !"ProfileSummary", !2}
68+
!2 = !{!3, !4, !5, !6, !7, !8, !9, !10}
69+
!3 = !{!"ProfileFormat", !"InstrProf"}
70+
!4 = !{!"TotalCount", i64 1460183}
71+
!5 = !{!"MaxCount", i64 849024}
72+
!6 = !{!"MaxInternalCount", i64 32769}
73+
!7 = !{!"MaxFunctionCount", i64 849024}
74+
!8 = !{!"NumCounts", i64 23627}
75+
!9 = !{!"NumFunctions", i64 3271}
76+
!10 = !{!"DetailedSummary", !11}
77+
!11 = !{!12, !13}
78+
!12 = !{i32 990000, i64 166, i32 73}
79+
!13 = !{i32 999999, i64 3, i32 1443}
80+
!14 = !{!"function_entry_count", i64 100000}
81+
!15 = !{!"function_entry_count", i64 1}
82+
!16 = !{!"branch_weights", i32 1, i32 99999}
83+
!17 = !{!"section_prefix", !"hot"}
84+
!18 = !{!"section_prefix", !"unlikely"}
85+
86+
;--- input-with-data-access-prof-off.ll
87+
; Same as file above except that module flag `EnableDataAccessProf` has value 0.
88+
; Internal vars
1489
@hot_bss = internal global i32 0, !section_prefix !17
90+
@data_unknown_hotness = internal global i32 1
91+
; External vars
92+
@external_relro_array = constant [2 x ptr] [ptr @hot_bss, ptr @data_unknown_hotness], !section_prefix !18
93+
94+
define void @cold_func() !prof !15 {
95+
%9 = load i32, ptr @data_unknown_hotness
96+
%11 = call i32 (...) @func_taking_arbitrary_param(i32 %9)
97+
ret void
98+
}
1599

16100
define void @hot_func() !prof !14 {
17101
%9 = load i32, ptr @hot_bss
@@ -21,8 +105,9 @@ define void @hot_func() !prof !14 {
21105

22106
declare i32 @func_taking_arbitrary_param(...)
23107

24-
!llvm.module.flags = !{!1}
108+
!llvm.module.flags = !{!0, !1}
25109

110+
!0 = !{i32 2, !"EnableDataAccessProf", i32 0}
26111
!1 = !{i32 1, !"ProfileSummary", !2}
27112
!2 = !{!3, !4, !5, !6, !7, !8, !9, !10}
28113
!3 = !{!"ProfileFormat", !"InstrProf"}
@@ -40,3 +125,4 @@ declare i32 @func_taking_arbitrary_param(...)
40125
!15 = !{!"function_entry_count", i64 1}
41126
!16 = !{!"branch_weights", i32 1, i32 99999}
42127
!17 = !{!"section_prefix", !"hot"}
128+
!18 = !{!"section_prefix", !"unlikely"}

0 commit comments

Comments
 (0)