llvm-project/llvm/lib/CodeGen/MachineFunctionSplitter.cpp
Snehasish Kumar 94faadaca4 [llvm][CodeGen] Machine Function Splitter
We introduce a codegen optimization pass which splits functions into hot and cold
parts. This pass leverages the basic block sections feature recently
introduced in LLVM from the Propeller project. The pass targets
functions with profile coverage, identifies cold blocks and moves them
to a separate section. The linker groups all cold blocks across
functions together, decreasing fragmentation and improving icache and
itlb utilization.

We evaluated the Machine Function Splitter pass on clang bootstrap and
SPECInt 2017.

For clang bootstrap we observe a mean 2.33% runtime improvement with a
~32% reduction in itlb and stlb misses. Additionally, L1 icache misses
reduced by 9.5% while L2 instruction misses reduced by 20%.

For SPECInt we report the change in IntRate the C/C++
benchmarks. All benchmarks apart from mcf and x264 improve, on average
by 0.6% with the max for deepsjeng at 1.6%.

Benchmark		% Change
500.perlbench_r		 0.78
502.gcc_r		 0.82
505.mcf_r		-0.30
520.omnetpp_r		 0.18
523.xalancbmk_r		 0.37
525.x264_r		-0.46
531.deepsjeng_r		 1.61
541.leela_r		 0.83
557.xz_r		 0.15

Differential Revision: https://reviews.llvm.org/D85368
2020-08-28 11:10:14 -07:00

149 lines
5.7 KiB
C++

//===-- MachineFunctionSplitter.cpp - Split machine functions //-----------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// \file
// Uses profile information to split out cold blocks.
//
// This pass splits out cold machine basic blocks from the parent function. This
// implementation leverages the basic block section framework. Blocks marked
// cold by this pass are grouped together in a separate section prefixed with
// ".text.unlikely.*". The linker can then group these together as a cold
// section. The split part of the function is a contiguous region identified by
// the symbol "foo.cold". Grouping all cold blocks across functions together
// decreases fragmentation and improves icache and itlb utilization. Note that
// the overall changes to the binary size are negligible; only a small number of
// additional jump instructions may be introduced.
//
// For the original RFC of this pass please see
// https://groups.google.com/d/msg/llvm-dev/RUegaMg-iqc/wFAVxa6fCgAJ
//===----------------------------------------------------------------------===//
#include "llvm/ADT/Statistic.h"
#include "llvm/Analysis/ProfileSummaryInfo.h"
#include "llvm/CodeGen/BasicBlockSectionUtils.h"
#include "llvm/CodeGen/MachineBasicBlock.h"
#include "llvm/CodeGen/MachineBlockFrequencyInfo.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineModuleInfo.h"
#include "llvm/CodeGen/Passes.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Module.h"
#include "llvm/InitializePasses.h"
#include "llvm/Support/CommandLine.h"
using namespace llvm;
static cl::opt<unsigned>
PercentileCutoff("mfs-psi-cutoff",
cl::desc("Percentile profile summary cutoff used to "
"determine cold blocks. Unused if set to zero."),
cl::init(0), cl::Hidden);
static cl::opt<unsigned> ColdCountThreshold(
"mfs-count-threshold",
cl::desc(
"Minimum number of times a block must be executed to be retained."),
cl::init(1), cl::Hidden);
namespace {
class MachineFunctionSplitter : public MachineFunctionPass {
public:
static char ID;
MachineFunctionSplitter() : MachineFunctionPass(ID) {
initializeMachineFunctionSplitterPass(*PassRegistry::getPassRegistry());
}
StringRef getPassName() const override {
return "Machine Function Splitter Transformation";
}
void getAnalysisUsage(AnalysisUsage &AU) const override;
bool runOnMachineFunction(MachineFunction &F) override;
};
} // end anonymous namespace
static bool isColdBlock(MachineBasicBlock &MBB,
const MachineBlockFrequencyInfo *MBFI,
ProfileSummaryInfo *PSI) {
Optional<uint64_t> Count = MBFI->getBlockProfileCount(&MBB);
if (!Count.hasValue())
return true;
if (PercentileCutoff > 0) {
return PSI->isColdCountNthPercentile(PercentileCutoff, *Count);
}
return (*Count < ColdCountThreshold);
}
bool MachineFunctionSplitter::runOnMachineFunction(MachineFunction &MF) {
// TODO: We only target functions with profile data. Static information may
// also be considered but we don't see performance improvements yet.
if (!MF.getFunction().hasProfileData())
return false;
// TODO: We don't split functions where a section attribute has been set
// since the split part may not be placed in a contiguous region. It may also
// be more beneficial to augment the linker to ensure contiguous layout of
// split functions within the same section as specified by the attribute.
if (!MF.getFunction().getSection().empty())
return false;
// We don't want to proceed further for cold functions
// or functions of unknown hotness. Lukewarm functions have no prefix.
Optional<StringRef> SectionPrefix = MF.getFunction().getSectionPrefix();
if (SectionPrefix.hasValue() &&
(SectionPrefix.getValue().equals(".unlikely") ||
SectionPrefix.getValue().equals(".unknown"))) {
return false;
}
// Renumbering blocks here preserves the order of the blocks as
// sortBasicBlocksAndUpdateBranches uses the numeric identifier to sort
// blocks. Preserving the order of blocks is essential to retaining decisions
// made by prior passes such as MachineBlockPlacement.
MF.RenumberBlocks();
MF.setBBSectionsType(BasicBlockSection::Preset);
auto *MBFI = &getAnalysis<MachineBlockFrequencyInfo>();
auto *PSI = &getAnalysis<ProfileSummaryInfoWrapperPass>().getPSI();
for (auto &MBB : MF) {
// FIXME: We retain the entry block and conservatively keep all landing pad
// blocks as part of the original function. Once D73739 is submitted, we can
// improve the handling of ehpads.
if ((MBB.pred_empty() || MBB.isEHPad()))
continue;
if (isColdBlock(MBB, MBFI, PSI))
MBB.setSectionID(MBBSectionID::ColdSectionID);
}
auto Comparator = [](const MachineBasicBlock &X, const MachineBasicBlock &Y) {
return X.getSectionID().Type < Y.getSectionID().Type;
};
llvm::sortBasicBlocksAndUpdateBranches(MF, Comparator);
return true;
}
void MachineFunctionSplitter::getAnalysisUsage(AnalysisUsage &AU) const {
AU.addRequired<MachineModuleInfoWrapperPass>();
AU.addRequired<MachineBlockFrequencyInfo>();
AU.addRequired<ProfileSummaryInfoWrapperPass>();
}
char MachineFunctionSplitter::ID = 0;
INITIALIZE_PASS(MachineFunctionSplitter, "machine-function-splitter",
"Split machine functions using profile information", false,
false)
MachineFunctionPass *llvm::createMachineFunctionSplitterPass() {
return new MachineFunctionSplitter();
}