https://github.com/llvm/llvm-project/pull/122183 adds a codegen pass to infer machine jump table entry's hotness from the MBB hotness. This is a follow-up PR to produce `.hot` and or `.unlikely` section prefix for jump table's (read-only) data sections in the relocatable `.o` files. When this patch is enabled, linker will see {`.rodata`, `.rodata.hot`, `.rodata.unlikely`} in input sections. It can map `.rodata.hot` and `.rodata` in the input sections to `.rodata.hot` in the executable, and map `.rodata.unlikely` into `.rodata` with a pending extension to `--keep-text-section-prefix` like059e7cbb66, or with a linker script. 1. To partition hot and jump tables, the AsmPrinter pass slices a function's jump table indices into two groups, one for hot and the other for cold jump tables. It then emits hot jump tables into a `.hot`-prefixed data section and cold ones into a `.unlikely`-prefixed data section, retaining the relative order of `LJT<N>` labels within each group. 2. [ELF only] To have data sections with _dynamic_ names (e.g., `.rodata.hot[.func]`), we implement `TargetLoweringObjectFile::getSectionForJumpTable` method that accepts a `MachineJumpTableEntry` parameter, and update `selectELFSectionForGlobal` to generate `.hot` or `.unlikely` based on MJTE's hotness. - The dynamic JT section name doesn't depend on `-ffunction-section=true` or `-funique-section-names=true`, even though it leverages the similar underlying mechanism to have a MCSection with on-demand name as `-ffunction-section` does. 3. The new code path is off by default. - Typically, `TargetOptions` conveys clang or LLVM tools' options to code generation passes. To follow the pattern, add option `EnableStaticDataPartitioning` bit in `TargetOptions` and make it readable through `TargetMachine`. - To enable the new code path in tools like `llc`, `partition-static-data-sections` option is introduced in `CodeGen/CommandFlags.h/cpp`. - A subsequent patch ([draft](8f36a13743)) will add a clang option to enable the new code path. --------- Co-authored-by: Ellis Hoag <ellis.sparky.hoag@gmail.com>
167 lines
5.9 KiB
C++
167 lines
5.9 KiB
C++
//===- 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;
|
|
|
|
// Returns true iff any jump table is hot-cold categorized.
|
|
bool splitJumpTables(MachineFunction &MF);
|
|
|
|
// Same as above but works on functions with profile information.
|
|
bool splitJumpTablesWithProfiles(const MachineFunction &MF,
|
|
MachineJumpTableInfo &MJTI);
|
|
|
|
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<MachineBranchProbabilityInfoWrapperPass>();
|
|
AU.addRequired<MachineBlockFrequencyInfoWrapperPass>();
|
|
AU.addRequired<ProfileSummaryInfoWrapperPass>();
|
|
}
|
|
|
|
bool runOnMachineFunction(MachineFunction &MF) override;
|
|
};
|
|
|
|
bool StaticDataSplitter::runOnMachineFunction(MachineFunction &MF) {
|
|
MBPI = &getAnalysis<MachineBranchProbabilityInfoWrapperPass>().getMBPI();
|
|
MBFI = &getAnalysis<MachineBlockFrequencyInfoWrapperPass>().getMBFI();
|
|
PSI = &getAnalysis<ProfileSummaryInfoWrapperPass>().getPSI();
|
|
|
|
return splitJumpTables(MF);
|
|
}
|
|
|
|
bool StaticDataSplitter::splitJumpTablesWithProfiles(
|
|
const MachineFunction &MF, MachineJumpTableInfo &MJTI) {
|
|
int NumChangedJumpTables = 0;
|
|
|
|
// 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())
|
|
continue;
|
|
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;
|
|
}
|
|
|
|
bool StaticDataSplitter::splitJumpTables(MachineFunction &MF) {
|
|
MachineJumpTableInfo *MJTI = MF.getJumpTableInfo();
|
|
if (!MJTI || MJTI->getJumpTables().empty())
|
|
return false;
|
|
|
|
const bool ProfileAvailable = PSI && PSI->hasProfileSummary() && MBFI &&
|
|
MF.getFunction().hasProfileData();
|
|
auto statOnExit = llvm::make_scope_exit([&] {
|
|
if (!AreStatisticsEnabled())
|
|
return;
|
|
|
|
if (!ProfileAvailable) {
|
|
NumUnknownJumpTables += MJTI->getJumpTables().size();
|
|
return;
|
|
}
|
|
|
|
for (size_t JTI = 0; JTI < MJTI->getJumpTables().size(); JTI++) {
|
|
auto Hotness = MJTI->getJumpTables()[JTI].Hotness;
|
|
if (Hotness == MachineFunctionDataHotness::Hot) {
|
|
++NumHotJumpTables;
|
|
} else {
|
|
assert(Hotness == MachineFunctionDataHotness::Cold &&
|
|
"A jump table is either hot or cold when profile information is "
|
|
"available.");
|
|
++NumColdJumpTables;
|
|
}
|
|
}
|
|
});
|
|
|
|
// Place jump tables according to block hotness if function has profile data.
|
|
if (ProfileAvailable)
|
|
return splitJumpTablesWithProfiles(MF, *MJTI);
|
|
|
|
return true;
|
|
}
|
|
|
|
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();
|
|
}
|