Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BOLT] Hash-based function matching #95821

Merged
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion bolt/include/bolt/Rewrite/DWARFRewriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "bolt/Core/DIEBuilder.h"
#include "bolt/Core/DebugData.h"
#include "bolt/Core/DebugNames.h"
#include "bolt/Core/GDBIndex.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/CodeGen/DIE.h"
#include "llvm/DWP/DWP.h"
Expand Down Expand Up @@ -131,7 +132,8 @@ class DWARFRewriter {
makeFinalLocListsSection(DWARFVersion Version);

/// Finalize type sections in the main binary.
CUOffsetMap finalizeTypeSections(DIEBuilder &DIEBlder, DIEStreamer &Streamer);
CUOffsetMap finalizeTypeSections(DIEBuilder &DIEBlder, DIEStreamer &Streamer,
GDBIndex &GDBIndexSection);

/// Process and write out CUs that are passsed in.
void finalizeCompileUnits(DIEBuilder &DIEBlder, DIEStreamer &Streamer,
Expand Down
1 change: 1 addition & 0 deletions bolt/lib/Core/BinaryEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,7 @@ void BinaryEmitter::emitFunctionBody(BinaryFunction &BF, FunctionFragment &FF,
// This assumes the second instruction in the macro-op pair will get
// assigned to its own MCRelaxableFragment. Since all JCC instructions
// are relaxable, we should be safe.
Streamer.emitNeverAlignCodeAtEnd(/*Alignment to avoid=*/64, *BC.STI);
}

if (!EmitCodeOnly) {
Expand Down
29 changes: 26 additions & 3 deletions bolt/lib/Profile/YAMLProfileReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -363,9 +363,7 @@ Error YAMLProfileReader::readProfile(BinaryContext &BC) {
return Profile.Hash == static_cast<uint64_t>(BF.getHash());
};

// We have to do 2 passes since LTO introduces an ambiguity in function
// names. The first pass assigns profiles that match 100% by name and
// by hash. The second pass allows name ambiguity for LTO private functions.
// This first pass assigns profiles that match 100% by name and by hash.
for (auto [YamlBF, BF] : llvm::zip_equal(YamlBP.Functions, ProfileBFs)) {
if (!BF)
continue;
Expand All @@ -383,6 +381,30 @@ Error YAMLProfileReader::readProfile(BinaryContext &BC) {
matchProfileToFunction(YamlBF, Function);
}

// Uses the strict hash of profiled and binary functions to match functions
shawbyoung marked this conversation as resolved.
Show resolved Hide resolved
// that are not matched by name or common name.
std::unordered_map<size_t, BinaryFunction *> StrictBinaryFunctionHashes;
shawbyoung marked this conversation as resolved.
Show resolved Hide resolved
StrictBinaryFunctionHashes.reserve(BC.getBinaryFunctions().size());

for (auto &[_, BF] : BC.getBinaryFunctions()) {
if (ProfiledFunctions.count(&BF))
continue;
BF.computeHash(YamlBP.Header.IsDFSOrder, YamlBP.Header.HashFunction);
shawbyoung marked this conversation as resolved.
Show resolved Hide resolved
StrictBinaryFunctionHashes[BF.getHash()] = &BF;
}

for (auto YamlBF : YamlBP.Functions) {
if (YamlBF.Used)
continue;
auto It = StrictBinaryFunctionHashes.find(YamlBF.Hash);
if (It != StrictBinaryFunctionHashes.end() &&
!ProfiledFunctions.count(It->second)) {
auto *BF = It->second;
matchProfileToFunction(YamlBF, *BF);
}
}

// This second pass allows name ambiguity for LTO private functions.
for (const auto &[CommonName, LTOProfiles] : LTOCommonNameMap) {
if (!LTOCommonNameFunctionMap.contains(CommonName))
continue;
Expand Down Expand Up @@ -420,6 +442,7 @@ Error YAMLProfileReader::readProfile(BinaryContext &BC) {
errs() << "BOLT-WARNING: profile ignored for function " << YamlBF.Name
<< '\n';


shawbyoung marked this conversation as resolved.
Show resolved Hide resolved
// Set for parseFunctionProfile().
NormalizeByInsnCount = usesEvent("cycles") || usesEvent("instructions");
NormalizeByCalls = usesEvent("branches");
Expand Down
61 changes: 34 additions & 27 deletions bolt/lib/Rewrite/DWARFRewriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ namespace bolt {
class DIEStreamer : public DwarfStreamer {
DIEBuilder *DIEBldr;
DWARFRewriter &Rewriter;
GDBIndex &GDBIndexSection;

private:
/// Emit the compilation unit header for \p Unit in the debug_info
Expand Down Expand Up @@ -247,7 +248,7 @@ class DIEStreamer : public DwarfStreamer {
const uint64_t TypeSignature = cast<DWARFTypeUnit>(Unit).getTypeHash();
DIE *TypeDIE = DIEBldr->getTypeDIE(Unit);
const DIEBuilder::DWARFUnitInfo &UI = DIEBldr->getUnitInfoByDwarfUnit(Unit);
Rewriter.addGDBTypeUnitEntry(
GDBIndexSection.addGDBTypeUnitEntry(
{UI.UnitOffset, TypeSignature, TypeDIE->getOffset()});
if (Unit.getVersion() < 5) {
// Switch the section to .debug_types section.
Expand Down Expand Up @@ -279,11 +280,12 @@ class DIEStreamer : public DwarfStreamer {

public:
DIEStreamer(DIEBuilder *DIEBldr, DWARFRewriter &Rewriter,
GDBIndex &GDBIndexSection,
DWARFLinkerBase::OutputFileType OutFileType,
raw_pwrite_stream &OutFile,
DWARFLinkerBase::MessageHandlerTy Warning)
: DwarfStreamer(OutFileType, OutFile, Warning), DIEBldr(DIEBldr),
Rewriter(Rewriter){};
Rewriter(Rewriter), GDBIndexSection(GDBIndexSection) {};

using DwarfStreamer::emitCompileUnitHeader;

Expand Down Expand Up @@ -326,12 +328,11 @@ static cl::opt<bool> KeepARanges(
"keep or generate .debug_aranges section if .gdb_index is written"),
cl::Hidden, cl::cat(BoltCategory));

static cl::opt<bool>
DeterministicDebugInfo("deterministic-debuginfo",
cl::desc("disables parallel execution of tasks that may produce "
"nondeterministic debug info"),
cl::init(true),
cl::cat(BoltCategory));
static cl::opt<bool> DeterministicDebugInfo(
"deterministic-debuginfo",
cl::desc("disables parallel execution of tasks that may produce "
"nondeterministic debug info"),
cl::init(true), cl::cat(BoltCategory));

static cl::opt<std::string> DwarfOutputPath(
"dwarf-output-path",
Expand Down Expand Up @@ -460,10 +461,11 @@ static std::optional<uint64_t> getAsAddress(const DWARFUnit &DU,
static std::unique_ptr<DIEStreamer>
createDIEStreamer(const Triple &TheTriple, raw_pwrite_stream &OutFile,
StringRef Swift5ReflectionSegmentName, DIEBuilder &DIEBldr,
DWARFRewriter &Rewriter) {
DWARFRewriter &Rewriter, GDBIndex &GDBIndexSection) {

std::unique_ptr<DIEStreamer> Streamer = std::make_unique<DIEStreamer>(
&DIEBldr, Rewriter, DWARFLinkerBase::OutputFileType::Object, OutFile,
&DIEBldr, Rewriter, GDBIndexSection,
DWARFLinkerBase::OutputFileType::Object, OutFile,
[&](const Twine &Warning, StringRef Context, const DWARFDie *) {});
Error Err = Streamer->init(TheTriple, Swift5ReflectionSegmentName);
if (Err)
Expand All @@ -484,13 +486,12 @@ emitUnit(DIEBuilder &DIEBldr, DIEStreamer &Streamer, DWARFUnit &Unit) {
return {U.UnitOffset, U.UnitLength, TypeHash};
}

static void emitDWOBuilder(const std::string &DWOName,
DIEBuilder &DWODIEBuilder, DWARFRewriter &Rewriter,
DWARFUnit &SplitCU, DWARFUnit &CU,
DWARFRewriter::DWPState &State,
DebugLocWriter &LocWriter,
DebugStrOffsetsWriter &StrOffstsWriter,
DebugStrWriter &StrWriter) {
static void
emitDWOBuilder(const std::string &DWOName, DIEBuilder &DWODIEBuilder,
DWARFRewriter &Rewriter, DWARFUnit &SplitCU, DWARFUnit &CU,
DWARFRewriter::DWPState &State, DebugLocWriter &LocWriter,
DebugStrOffsetsWriter &StrOffstsWriter,
DebugStrWriter &StrWriter, GDBIndex &GDBIndexSection) {
// Populate debug_info and debug_abbrev for current dwo into StringRef.
DWODIEBuilder.generateAbbrevs();
DWODIEBuilder.finish();
Expand All @@ -500,8 +501,9 @@ static void emitDWOBuilder(const std::string &DWOName,
std::make_shared<raw_svector_ostream>(OutBuffer);
const object::ObjectFile *File = SplitCU.getContext().getDWARFObj().getFile();
auto TheTriple = std::make_unique<Triple>(File->makeTriple());
std::unique_ptr<DIEStreamer> Streamer = createDIEStreamer(
*TheTriple, *ObjOS, "DwoStreamerInitAug2", DWODIEBuilder, Rewriter);
std::unique_ptr<DIEStreamer> Streamer =
createDIEStreamer(*TheTriple, *ObjOS, "DwoStreamerInitAug2",
DWODIEBuilder, Rewriter, GDBIndexSection);
DWARFRewriter::UnitMetaVectorType TUMetaVector;
DWARFRewriter::UnitMeta CUMI = {0, 0, 0};
if (SplitCU.getContext().getMaxDWOVersion() >= 5) {
Expand Down Expand Up @@ -652,6 +654,7 @@ void DWARFRewriter::updateDebugInfo() {

DWARF5AcceleratorTable DebugNamesTable(opts::CreateDebugNames, BC,
*StrWriter);
GDBIndex GDBIndexSection(BC);
DWPState State;
if (opts::WriteDWP)
initDWPState(State);
Expand Down Expand Up @@ -704,7 +707,8 @@ void DWARFRewriter::updateDebugInfo() {
TempRangesSectionWriter->finalizeSection();

emitDWOBuilder(DWOName, DWODIEBuilder, *this, **SplitCU, *Unit, State,
DebugLocDWoWriter, DWOStrOffstsWriter, DWOStrWriter);
DebugLocDWoWriter, DWOStrOffstsWriter, DWOStrWriter,
GDBIndexSection);
}

if (Unit->getVersion() >= 5) {
Expand All @@ -729,9 +733,10 @@ void DWARFRewriter::updateDebugInfo() {
std::make_unique<raw_svector_ostream>(OutBuffer);
const object::ObjectFile *File = BC.DwCtx->getDWARFObj().getFile();
auto TheTriple = std::make_unique<Triple>(File->makeTriple());
std::unique_ptr<DIEStreamer> Streamer =
createDIEStreamer(*TheTriple, *ObjOS, "TypeStreamer", DIEBlder, *this);
CUOffsetMap OffsetMap = finalizeTypeSections(DIEBlder, *Streamer);
std::unique_ptr<DIEStreamer> Streamer = createDIEStreamer(
*TheTriple, *ObjOS, "TypeStreamer", DIEBlder, *this, GDBIndexSection);
CUOffsetMap OffsetMap =
finalizeTypeSections(DIEBlder, *Streamer, GDBIndexSection);

const bool SingleThreadedMode =
opts::NoThreads || opts::DeterministicDebugInfo;
Expand Down Expand Up @@ -761,7 +766,8 @@ void DWARFRewriter::updateDebugInfo() {

finalizeDebugSections(DIEBlder, DebugNamesTable, *Streamer, *ObjOS,
OffsetMap);
updateGdbIndexSection(OffsetMap, CUIndex);
GDBIndexSection.updateGdbIndexSection(OffsetMap, CUIndex,
*ARangesSectionWriter);
}

void DWARFRewriter::updateUnitDebugInfo(
Expand Down Expand Up @@ -1429,7 +1435,8 @@ void DWARFRewriter::updateLineTableOffsets(const MCAsmLayout &Layout) {
}

CUOffsetMap DWARFRewriter::finalizeTypeSections(DIEBuilder &DIEBlder,
DIEStreamer &Streamer) {
DIEStreamer &Streamer,
GDBIndex &GDBIndexSection) {
// update TypeUnit DW_AT_stmt_list with new .debug_line information.
auto updateLineTable = [&](const DWARFUnit &Unit) -> void {
DIE *UnitDIE = DIEBlder.getUnitDIEbyUnit(Unit);
Expand All @@ -1449,8 +1456,8 @@ CUOffsetMap DWARFRewriter::finalizeTypeSections(DIEBuilder &DIEBlder,
std::make_shared<raw_svector_ostream>(OutBuffer);
const object::ObjectFile *File = BC.DwCtx->getDWARFObj().getFile();
auto TheTriple = std::make_unique<Triple>(File->makeTriple());
std::unique_ptr<DIEStreamer> TypeStreamer =
createDIEStreamer(*TheTriple, *ObjOS, "TypeStreamer", DIEBlder, *this);
std::unique_ptr<DIEStreamer> TypeStreamer = createDIEStreamer(
*TheTriple, *ObjOS, "TypeStreamer", DIEBlder, *this, GDBIndexSection);

// generate debug_info and CUMap
CUOffsetMap CUMap;
Expand Down
67 changes: 67 additions & 0 deletions bolt/test/X86/hashing-based-function-matching.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
## Test YAMLProfileReader support for pass-through blocks in non-matching edges:
## match the profile edge A -> C to the CFG with blocks A -> B -> C.
shawbyoung marked this conversation as resolved.
Show resolved Hide resolved

# REQUIRES: system-linux
# RUN: split-file %s %t
# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %t/main.s -o %t.o
# RUN: %clang %cflags %t.o -o %t.exe -Wl,-q -nostdlib
# RUN: llvm-bolt %t.exe -o %t.out --data %t/yaml -v=1 \
# RUN: --print-cfg 2>&1 | FileCheck %s
shawbyoung marked this conversation as resolved.
Show resolved Hide resolved

# CHECK: Binary Function "main" after building cfg
# CHECK: Profile Acc : 100.0%
# CHECK-NOT: BOLT-WARNING: no successor for block .LFT0 that matches index 3 or block .Ltmp0

#--- main.s
.globl main
.type main, @function
main:
.cfi_startproc
.LBB00:
pushq %rbp
movq %rsp, %rbp
subq $16, %rsp
testq %rax, %rax
js .LBB03
.LBB01:
jne .LBB04
.LBB02:
nop
.LBB03:
xorl %eax, %eax
addq $16, %rsp
popq %rbp
retq
.LBB04:
xorl %eax, %eax
addq $16, %rsp
popq %rbp
retq
## For relocations against .text
.LBB05:
call exit
.cfi_endproc
.size main, .-main

#--- yaml
---
header:
profile-version: 1
binary-name: 'profile-passthrough-block.s.tmp.exe'
binary-build-id: '<unknown>'
profile-flags: [ lbr ]
profile-origin: branch profile reader
profile-events: ''
dfs-order: false
hash-func: xxh3
functions:
- name: main2
fid: 0
hash: 0x72f82deaa6fe65fb
exec: 1
nblocks: 6
blocks:
- bid: 1
insns: 1
succ: [ { bid: 3, cnt: 1} ]
...
4 changes: 4 additions & 0 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -5483,6 +5483,10 @@ def pg : Flag<["-"], "pg">, HelpText<"Enable mcount instrumentation">,
MarshallingInfoFlag<CodeGenOpts<"InstrumentForProfiling">>;
def pipe : Flag<["-", "--"], "pipe">,
HelpText<"Use pipes between commands, when possible">;
// Facebook T92898286
def post_link_optimize : Flag<["--"], "post-link-optimize">,
HelpText<"Apply post-link optimizations using BOLT">;
// End Facebook T92898286
def prebind__all__twolevel__modules : Flag<["-"], "prebind_all_twolevel_modules">;
def prebind : Flag<["-"], "prebind">;
def preload : Flag<["-"], "preload">;
Expand Down
29 changes: 29 additions & 0 deletions clang/lib/Driver/ToolChains/Gnu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -672,12 +672,41 @@ void tools::gnutools::Linker::ConstructJob(Compilation &C, const JobAction &JA,
}
}

// Facebook T92898286
if (Args.hasArg(options::OPT_post_link_optimize))
CmdArgs.push_back("-q");
// End Facebook T92898286

Args.AddAllArgs(CmdArgs, options::OPT_T);

const char *Exec = Args.MakeArgString(ToolChain.GetLinkerPath());
C.addCommand(std::make_unique<Command>(JA, *this,
ResponseFileSupport::AtFileCurCP(),
Exec, CmdArgs, Inputs, Output));
// Facebook T92898286
if (!Args.hasArg(options::OPT_post_link_optimize) || !Output.isFilename())
return;

const char *MvExec = Args.MakeArgString(ToolChain.GetProgramPath("mv"));
ArgStringList MoveCmdArgs;
MoveCmdArgs.push_back(Output.getFilename());
const char *PreBoltBin =
Args.MakeArgString(Twine(Output.getFilename()) + ".pre-bolt");
MoveCmdArgs.push_back(PreBoltBin);
C.addCommand(std::make_unique<Command>(JA, *this, ResponseFileSupport::None(),
MvExec, MoveCmdArgs, std::nullopt));

ArgStringList BoltCmdArgs;
const char *BoltExec =
Args.MakeArgString(ToolChain.GetProgramPath("llvm-bolt"));
BoltCmdArgs.push_back(PreBoltBin);
BoltCmdArgs.push_back("-reorder-blocks=reverse");
BoltCmdArgs.push_back("-update-debug-sections");
BoltCmdArgs.push_back("-o");
BoltCmdArgs.push_back(Output.getFilename());
C.addCommand(std::make_unique<Command>(JA, *this, ResponseFileSupport::None(),
BoltExec, BoltCmdArgs, std::nullopt));
// End Facebook T92898286
}

void tools::gnutools::Assembler::ConstructJob(Compilation &C,
Expand Down
14 changes: 13 additions & 1 deletion cross-project-tests/lit.cfg.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,13 @@ def get_required_attr(config, attr_name):
# use_clang() and use_lld() respectively, so set them to "", if needed.
if not hasattr(config, "clang_src_dir"):
config.clang_src_dir = ""
llvm_config.use_clang(required=("clang" in config.llvm_enabled_projects))
# Facebook T92898286
should_test_bolt = get_required_attr(config, "llvm_test_bolt")
if should_test_bolt:
llvm_config.use_clang(required=("clang" in config.llvm_enabled_projects), additional_flags=["--post-link-optimize"])
else:
llvm_config.use_clang(required=("clang" in config.llvm_enabled_projects))
# End Facebook T92898286

if not hasattr(config, "lld_src_dir"):
config.lld_src_dir = ""
Expand Down Expand Up @@ -293,3 +299,9 @@ def get_clang_default_dwarf_version_string(triple):
# Allow 'REQUIRES: XXX-registered-target' in tests.
for arch in config.targets_to_build:
config.available_features.add(arch.lower() + "-registered-target")

# Facebook T92898286
# Ensure the user's PYTHONPATH is included.
if "PYTHONPATH" in os.environ:
config.environment["PYTHONPATH"] = os.environ["PYTHONPATH"]
# End Facebook T92898286
4 changes: 4 additions & 0 deletions cross-project-tests/lit.site.cfg.py.in
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ config.mlir_src_root = "@MLIR_SOURCE_DIR@"

config.llvm_use_sanitizer = "@LLVM_USE_SANITIZER@"

# Facebook T92898286
config.llvm_test_bolt = lit.util.pythonize_bool("@LLVM_TEST_BOLT@")
# End Facebook T92898286

import lit.llvm
lit.llvm.initialize(lit_config, config)

Expand Down
Loading
Loading