1 | //===- StaticDataSplitter.cpp ---------------------------------------------===// |
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 | // The pass uses branch profile data to assign hotness based section qualifiers |
10 | // for the following types of static data: |
11 | // - Jump tables |
12 | // - Module-internal global variables |
13 | // - Constant pools |
14 | // |
15 | // For the original RFC of this pass please see |
16 | // https://discourse.llvm.org/t/rfc-profile-guided-static-data-partitioning/83744 |
17 | |
18 | #include "llvm/ADT/Statistic.h" |
19 | #include "llvm/Analysis/ProfileSummaryInfo.h" |
20 | #include "llvm/Analysis/StaticDataProfileInfo.h" |
21 | #include "llvm/CodeGen/MBFIWrapper.h" |
22 | #include "llvm/CodeGen/MachineBasicBlock.h" |
23 | #include "llvm/CodeGen/MachineBlockFrequencyInfo.h" |
24 | #include "llvm/CodeGen/MachineBranchProbabilityInfo.h" |
25 | #include "llvm/CodeGen/MachineConstantPool.h" |
26 | #include "llvm/CodeGen/MachineFunction.h" |
27 | #include "llvm/CodeGen/MachineFunctionPass.h" |
28 | #include "llvm/CodeGen/MachineJumpTableInfo.h" |
29 | #include "llvm/CodeGen/Passes.h" |
30 | #include "llvm/IR/GlobalVariable.h" |
31 | #include "llvm/InitializePasses.h" |
32 | #include "llvm/Pass.h" |
33 | #include "llvm/Target/TargetLoweringObjectFile.h" |
34 | |
35 | using namespace llvm; |
36 | |
37 | #define DEBUG_TYPE "static-data-splitter" |
38 | |
39 | STATISTIC(NumHotJumpTables, "Number of hot jump tables seen." ); |
40 | STATISTIC(NumColdJumpTables, "Number of cold jump tables seen." ); |
41 | STATISTIC(NumUnknownJumpTables, |
42 | "Number of jump tables with unknown hotness. They are from functions " |
43 | "without profile information." ); |
44 | |
45 | class StaticDataSplitter : public MachineFunctionPass { |
46 | const MachineBranchProbabilityInfo *MBPI = nullptr; |
47 | const MachineBlockFrequencyInfo *MBFI = nullptr; |
48 | const ProfileSummaryInfo *PSI = nullptr; |
49 | StaticDataProfileInfo *SDPI = nullptr; |
50 | |
51 | // If the global value is a local linkage global variable, return it. |
52 | // Otherwise, return nullptr. |
53 | const GlobalVariable *getLocalLinkageGlobalVariable(const GlobalValue *GV); |
54 | |
55 | // Returns true if the global variable is in one of {.rodata, .bss, .data, |
56 | // .data.rel.ro} sections. |
57 | bool inStaticDataSection(const GlobalVariable &GV, const TargetMachine &TM); |
58 | |
59 | // Returns the constant if the operand refers to a global variable or constant |
60 | // that gets lowered to static data sections. Otherwise, return nullptr. |
61 | const Constant *getConstant(const MachineOperand &Op, const TargetMachine &TM, |
62 | const MachineConstantPool *MCP); |
63 | |
64 | // Use profiles to partition static data. |
65 | bool partitionStaticDataWithProfiles(MachineFunction &MF); |
66 | |
67 | // Update LLVM statistics for a machine function with profiles. |
68 | void updateStatsWithProfiles(const MachineFunction &MF); |
69 | |
70 | // Update LLVM statistics for a machine function without profiles. |
71 | void updateStatsWithoutProfiles(const MachineFunction &MF); |
72 | |
73 | void annotateStaticDataWithoutProfiles(const MachineFunction &MF); |
74 | |
75 | public: |
76 | static char ID; |
77 | |
78 | StaticDataSplitter() : MachineFunctionPass(ID) { |
79 | initializeStaticDataSplitterPass(*PassRegistry::getPassRegistry()); |
80 | } |
81 | |
82 | StringRef getPassName() const override { return "Static Data Splitter" ; } |
83 | |
84 | void getAnalysisUsage(AnalysisUsage &AU) const override { |
85 | MachineFunctionPass::getAnalysisUsage(AU); |
86 | AU.addRequired<MachineBranchProbabilityInfoWrapperPass>(); |
87 | AU.addRequired<MachineBlockFrequencyInfoWrapperPass>(); |
88 | AU.addRequired<ProfileSummaryInfoWrapperPass>(); |
89 | AU.addRequired<StaticDataProfileInfoWrapperPass>(); |
90 | // This pass does not modify any required analysis results except |
91 | // StaticDataProfileInfoWrapperPass, but StaticDataProfileInfoWrapperPass |
92 | // is made an immutable pass that it won't be re-scheduled by pass manager |
93 | // anyway. So mark setPreservesAll() here for faster compile time. |
94 | AU.setPreservesAll(); |
95 | } |
96 | |
97 | bool runOnMachineFunction(MachineFunction &MF) override; |
98 | }; |
99 | |
100 | bool StaticDataSplitter::runOnMachineFunction(MachineFunction &MF) { |
101 | MBPI = &getAnalysis<MachineBranchProbabilityInfoWrapperPass>().getMBPI(); |
102 | MBFI = &getAnalysis<MachineBlockFrequencyInfoWrapperPass>().getMBFI(); |
103 | PSI = &getAnalysis<ProfileSummaryInfoWrapperPass>().getPSI(); |
104 | |
105 | SDPI = &getAnalysis<StaticDataProfileInfoWrapperPass>() |
106 | .getStaticDataProfileInfo(); |
107 | |
108 | const bool ProfileAvailable = PSI && PSI->hasProfileSummary() && MBFI && |
109 | MF.getFunction().hasProfileData(); |
110 | |
111 | if (!ProfileAvailable) { |
112 | annotateStaticDataWithoutProfiles(MF); |
113 | updateStatsWithoutProfiles(MF); |
114 | return false; |
115 | } |
116 | |
117 | bool Changed = partitionStaticDataWithProfiles(MF); |
118 | |
119 | updateStatsWithProfiles(MF); |
120 | return Changed; |
121 | } |
122 | |
123 | const Constant * |
124 | StaticDataSplitter::getConstant(const MachineOperand &Op, |
125 | const TargetMachine &TM, |
126 | const MachineConstantPool *MCP) { |
127 | if (!Op.isGlobal() && !Op.isCPI()) |
128 | return nullptr; |
129 | |
130 | if (Op.isGlobal()) { |
131 | // Find global variables with local linkage. |
132 | const GlobalVariable *GV = getLocalLinkageGlobalVariable(GV: Op.getGlobal()); |
133 | // Skip 'llvm.'-prefixed global variables conservatively because they are |
134 | // often handled specially, and skip those not in static data |
135 | // sections. |
136 | if (!GV || GV->getName().starts_with(Prefix: "llvm." ) || |
137 | !inStaticDataSection(GV: *GV, TM)) |
138 | return nullptr; |
139 | return GV; |
140 | } |
141 | assert(Op.isCPI() && "Op must be constant pool index in this branch" ); |
142 | int CPI = Op.getIndex(); |
143 | if (CPI == -1) |
144 | return nullptr; |
145 | |
146 | assert(MCP != nullptr && "Constant pool info is not available." ); |
147 | const MachineConstantPoolEntry &CPE = MCP->getConstants()[CPI]; |
148 | |
149 | if (CPE.isMachineConstantPoolEntry()) |
150 | return nullptr; |
151 | |
152 | return CPE.Val.ConstVal; |
153 | } |
154 | |
155 | bool StaticDataSplitter::partitionStaticDataWithProfiles(MachineFunction &MF) { |
156 | // If any of the static data (jump tables, global variables, constant pools) |
157 | // are captured by the analysis, set `Changed` to true. Note this pass won't |
158 | // invalidate any analysis pass (see `getAnalysisUsage` above), so the main |
159 | // purpose of tracking and conveying the change (to pass manager) is |
160 | // informative as opposed to invalidating any analysis results. As an example |
161 | // of where this information is useful, `PMDataManager::dumpPassInfo` will |
162 | // only dump pass info if a local change happens, otherwise a pass appears as |
163 | // "skipped". |
164 | bool Changed = false; |
165 | |
166 | MachineJumpTableInfo *MJTI = MF.getJumpTableInfo(); |
167 | |
168 | // Jump table could be used by either terminating instructions or |
169 | // non-terminating ones, so we walk all instructions and use |
170 | // `MachineOperand::isJTI()` to identify jump table operands. |
171 | // Similarly, `MachineOperand::isCPI()` is used to identify constant pool |
172 | // usages in the same loop. |
173 | for (const auto &MBB : MF) { |
174 | std::optional<uint64_t> Count = MBFI->getBlockProfileCount(MBB: &MBB); |
175 | for (const MachineInstr &I : MBB) { |
176 | for (const MachineOperand &Op : I.operands()) { |
177 | if (!Op.isJTI() && !Op.isGlobal() && !Op.isCPI()) |
178 | continue; |
179 | |
180 | if (Op.isJTI()) { |
181 | assert(MJTI != nullptr && "Jump table info is not available." ); |
182 | const int JTI = Op.getIndex(); |
183 | // This is not a source block of jump table. |
184 | if (JTI == -1) |
185 | continue; |
186 | |
187 | auto Hotness = MachineFunctionDataHotness::Hot; |
188 | |
189 | // Hotness is based on source basic block hotness. |
190 | // TODO: PSI APIs are about instruction hotness. Introduce API for |
191 | // data access hotness. |
192 | if (Count && PSI->isColdCount(C: *Count)) |
193 | Hotness = MachineFunctionDataHotness::Cold; |
194 | |
195 | Changed |= MJTI->updateJumpTableEntryHotness(JTI, Hotness); |
196 | } else if (const Constant *C = |
197 | getConstant(Op, TM: MF.getTarget(), MCP: MF.getConstantPool())) { |
198 | SDPI->addConstantProfileCount(C, Count); |
199 | Changed = true; |
200 | } |
201 | } |
202 | } |
203 | } |
204 | return Changed; |
205 | } |
206 | |
207 | const GlobalVariable * |
208 | StaticDataSplitter::getLocalLinkageGlobalVariable(const GlobalValue *GV) { |
209 | // LLVM IR Verifier requires that a declaration must have valid declaration |
210 | // linkage, and local linkages are not among the valid ones. So there is no |
211 | // need to check GV is not a declaration here. |
212 | return (GV && GV->hasLocalLinkage()) ? dyn_cast<GlobalVariable>(Val: GV) : nullptr; |
213 | } |
214 | |
215 | bool StaticDataSplitter::inStaticDataSection(const GlobalVariable &GV, |
216 | const TargetMachine &TM) { |
217 | |
218 | SectionKind Kind = TargetLoweringObjectFile::getKindForGlobal(GO: &GV, TM); |
219 | return Kind.isData() || Kind.isReadOnly() || Kind.isReadOnlyWithRel() || |
220 | Kind.isBSS(); |
221 | } |
222 | |
223 | void StaticDataSplitter::updateStatsWithProfiles(const MachineFunction &MF) { |
224 | if (!AreStatisticsEnabled()) |
225 | return; |
226 | |
227 | if (const MachineJumpTableInfo *MJTI = MF.getJumpTableInfo()) { |
228 | for (const auto &JumpTable : MJTI->getJumpTables()) { |
229 | if (JumpTable.Hotness == MachineFunctionDataHotness::Hot) { |
230 | ++NumHotJumpTables; |
231 | } else { |
232 | assert(JumpTable.Hotness == MachineFunctionDataHotness::Cold && |
233 | "A jump table is either hot or cold when profile information is " |
234 | "available." ); |
235 | ++NumColdJumpTables; |
236 | } |
237 | } |
238 | } |
239 | } |
240 | |
241 | void StaticDataSplitter::annotateStaticDataWithoutProfiles( |
242 | const MachineFunction &MF) { |
243 | for (const auto &MBB : MF) |
244 | for (const MachineInstr &I : MBB) |
245 | for (const MachineOperand &Op : I.operands()) |
246 | if (const Constant *C = |
247 | getConstant(Op, TM: MF.getTarget(), MCP: MF.getConstantPool())) |
248 | SDPI->addConstantProfileCount(C, Count: std::nullopt); |
249 | } |
250 | |
251 | void StaticDataSplitter::updateStatsWithoutProfiles(const MachineFunction &MF) { |
252 | if (!AreStatisticsEnabled()) |
253 | return; |
254 | |
255 | if (const MachineJumpTableInfo *MJTI = MF.getJumpTableInfo()) { |
256 | NumUnknownJumpTables += MJTI->getJumpTables().size(); |
257 | } |
258 | } |
259 | |
260 | char StaticDataSplitter::ID = 0; |
261 | |
262 | INITIALIZE_PASS_BEGIN(StaticDataSplitter, DEBUG_TYPE, "Split static data" , |
263 | false, false) |
264 | INITIALIZE_PASS_DEPENDENCY(MachineBranchProbabilityInfoWrapperPass) |
265 | INITIALIZE_PASS_DEPENDENCY(MachineBlockFrequencyInfoWrapperPass) |
266 | INITIALIZE_PASS_DEPENDENCY(ProfileSummaryInfoWrapperPass) |
267 | INITIALIZE_PASS_DEPENDENCY(StaticDataProfileInfoWrapperPass) |
268 | INITIALIZE_PASS_END(StaticDataSplitter, DEBUG_TYPE, "Split static data" , false, |
269 | false) |
270 | |
271 | MachineFunctionPass *llvm::createStaticDataSplitterPass() { |
272 | return new StaticDataSplitter(); |
273 | } |
274 | |