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