//===- StaticDataSplitter.cpp ---------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // The pass uses branch profile data to assign hotness based section qualifiers // for the following types of static data: // - Jump tables // - Constant pools (TODO) // - Other module-internal data (TODO) // // For the original RFC of this pass please see // https://discourse.llvm.org/t/rfc-profile-guided-static-data-partitioning/83744 #include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/Statistic.h" #include "llvm/Analysis/ProfileSummaryInfo.h" #include "llvm/CodeGen/MBFIWrapper.h" #include "llvm/CodeGen/MachineBasicBlock.h" #include "llvm/CodeGen/MachineBlockFrequencyInfo.h" #include "llvm/CodeGen/MachineBranchProbabilityInfo.h" #include "llvm/CodeGen/MachineConstantPool.h" #include "llvm/CodeGen/MachineFunction.h" #include "llvm/CodeGen/MachineFunctionPass.h" #include "llvm/CodeGen/MachineJumpTableInfo.h" #include "llvm/CodeGen/Passes.h" #include "llvm/InitializePasses.h" #include "llvm/Pass.h" #include "llvm/Support/CommandLine.h" using namespace llvm; #define DEBUG_TYPE "static-data-splitter" STATISTIC(NumHotJumpTables, "Number of hot jump tables seen."); STATISTIC(NumColdJumpTables, "Number of cold jump tables seen."); STATISTIC(NumUnknownJumpTables, "Number of jump tables with unknown hotness. They are from functions " "without profile information."); class StaticDataSplitter : public MachineFunctionPass { const MachineBranchProbabilityInfo *MBPI = nullptr; const MachineBlockFrequencyInfo *MBFI = nullptr; const ProfileSummaryInfo *PSI = nullptr; // Update LLVM statistics for a machine function without profiles. void updateStatsWithoutProfiles(const MachineFunction &MF); // Update LLVM statistics for a machine function with profiles. void updateStatsWithProfiles(const MachineFunction &MF); // Use profiles to partition static data. bool partitionStaticDataWithProfiles(MachineFunction &MF); public: static char ID; StaticDataSplitter() : MachineFunctionPass(ID) { initializeStaticDataSplitterPass(*PassRegistry::getPassRegistry()); } StringRef getPassName() const override { return "Static Data Splitter"; } void getAnalysisUsage(AnalysisUsage &AU) const override { MachineFunctionPass::getAnalysisUsage(AU); AU.addRequired(); AU.addRequired(); AU.addRequired(); } bool runOnMachineFunction(MachineFunction &MF) override; }; bool StaticDataSplitter::runOnMachineFunction(MachineFunction &MF) { MBPI = &getAnalysis().getMBPI(); MBFI = &getAnalysis().getMBFI(); PSI = &getAnalysis().getPSI(); const bool ProfileAvailable = PSI && PSI->hasProfileSummary() && MBFI && MF.getFunction().hasProfileData(); if (!ProfileAvailable) { updateStatsWithoutProfiles(MF); return false; } bool Changed = partitionStaticDataWithProfiles(MF); updateStatsWithProfiles(MF); return Changed; } bool StaticDataSplitter::partitionStaticDataWithProfiles(MachineFunction &MF) { int NumChangedJumpTables = 0; MachineJumpTableInfo *MJTI = MF.getJumpTableInfo(); // Jump table could be used by either terminating instructions or // non-terminating ones, so we walk all instructions and use // `MachineOperand::isJTI()` to identify jump table operands. // Similarly, `MachineOperand::isCPI()` can identify constant pool usages // in the same loop. for (const auto &MBB : MF) { for (const MachineInstr &I : MBB) { for (const MachineOperand &Op : I.operands()) { if (Op.isJTI()) { assert(MJTI != nullptr && "Jump table info is not available."); const int JTI = Op.getIndex(); // This is not a source block of jump table. if (JTI == -1) continue; auto Hotness = MachineFunctionDataHotness::Hot; // Hotness is based on source basic block hotness. // TODO: PSI APIs are about instruction hotness. Introduce API for // data access hotness. if (PSI->isColdBlock(&MBB, MBFI)) Hotness = MachineFunctionDataHotness::Cold; if (MJTI->updateJumpTableEntryHotness(JTI, Hotness)) ++NumChangedJumpTables; } } } } return NumChangedJumpTables > 0; } void StaticDataSplitter::updateStatsWithProfiles(const MachineFunction &MF) { if (!AreStatisticsEnabled()) return; if (const MachineJumpTableInfo *MJTI = MF.getJumpTableInfo()) { for (const auto &JumpTable : MJTI->getJumpTables()) { if (JumpTable.Hotness == MachineFunctionDataHotness::Hot) { ++NumHotJumpTables; } else { assert(JumpTable.Hotness == MachineFunctionDataHotness::Cold && "A jump table is either hot or cold when profile information is " "available."); ++NumColdJumpTables; } } } } void StaticDataSplitter::updateStatsWithoutProfiles(const MachineFunction &MF) { if (!AreStatisticsEnabled()) return; if (const MachineJumpTableInfo *MJTI = MF.getJumpTableInfo()) { NumUnknownJumpTables += MJTI->getJumpTables().size(); } } char StaticDataSplitter::ID = 0; INITIALIZE_PASS_BEGIN(StaticDataSplitter, DEBUG_TYPE, "Split static data", false, false) INITIALIZE_PASS_DEPENDENCY(MachineBranchProbabilityInfoWrapperPass) INITIALIZE_PASS_DEPENDENCY(MachineBlockFrequencyInfoWrapperPass) INITIALIZE_PASS_DEPENDENCY(ProfileSummaryInfoWrapperPass) INITIALIZE_PASS_END(StaticDataSplitter, DEBUG_TYPE, "Split static data", false, false) MachineFunctionPass *llvm::createStaticDataSplitterPass() { return new StaticDataSplitter(); }