mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-16 19:56:38 +00:00
[BOLT] Match functions with exact hash (#96572)
Added flag '--match-profile-with-function-hash' to match functions based on exact hash. After identical and LTO name matching, more functions can be recovered for inference with exact hash, in the case of function renaming with no functional changes. Collisions are possible in the unlikely case where multiple functions share the same exact hash. The flag is off by default as it requires the processing of all binary functions and subsequently is expensive. Test Plan: added hashing-based-function-matching.test.
This commit is contained in:
parent
aec7670b5d
commit
49fdbbcfed
@ -259,6 +259,10 @@
|
||||
|
||||
Always use long jumps/nops for Linux kernel static keys
|
||||
|
||||
- `--match-profile-with-function-hash`
|
||||
|
||||
Match profile with function hash
|
||||
|
||||
- `--max-data-relocations=<uint>`
|
||||
|
||||
Maximum number of data relocations to process
|
||||
|
@ -22,12 +22,18 @@ namespace opts {
|
||||
extern cl::opt<unsigned> Verbosity;
|
||||
extern cl::OptionCategory BoltOptCategory;
|
||||
extern cl::opt<bool> InferStaleProfile;
|
||||
extern cl::opt<bool> Lite;
|
||||
|
||||
static llvm::cl::opt<bool>
|
||||
IgnoreHash("profile-ignore-hash",
|
||||
cl::desc("ignore hash while reading function profile"),
|
||||
cl::Hidden, cl::cat(BoltOptCategory));
|
||||
|
||||
llvm::cl::opt<bool>
|
||||
MatchProfileWithFunctionHash("match-profile-with-function-hash",
|
||||
cl::desc("Match profile with function hash"),
|
||||
cl::Hidden, cl::cat(BoltOptCategory));
|
||||
|
||||
llvm::cl::opt<bool> ProfileUseDFS("profile-use-dfs",
|
||||
cl::desc("use DFS order for YAML profile"),
|
||||
cl::Hidden, cl::cat(BoltOptCategory));
|
||||
@ -329,6 +335,8 @@ Error YAMLProfileReader::preprocessProfile(BinaryContext &BC) {
|
||||
}
|
||||
|
||||
bool YAMLProfileReader::mayHaveProfileData(const BinaryFunction &BF) {
|
||||
if (opts::MatchProfileWithFunctionHash)
|
||||
return true;
|
||||
for (StringRef Name : BF.getNames())
|
||||
if (ProfileFunctionNames.contains(Name))
|
||||
return true;
|
||||
@ -363,9 +371,24 @@ 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.
|
||||
uint64_t MatchedWithExactName = 0;
|
||||
uint64_t MatchedWithHash = 0;
|
||||
uint64_t MatchedWithLTOCommonName = 0;
|
||||
|
||||
// Computes hash for binary functions.
|
||||
if (opts::MatchProfileWithFunctionHash) {
|
||||
for (auto &[_, BF] : BC.getBinaryFunctions()) {
|
||||
BF.computeHash(YamlBP.Header.IsDFSOrder, YamlBP.Header.HashFunction);
|
||||
}
|
||||
} else if (!opts::IgnoreHash) {
|
||||
for (BinaryFunction *BF : ProfileBFs) {
|
||||
if (!BF)
|
||||
continue;
|
||||
BF->computeHash(YamlBP.Header.IsDFSOrder, YamlBP.Header.HashFunction);
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
@ -374,15 +397,35 @@ Error YAMLProfileReader::readProfile(BinaryContext &BC) {
|
||||
// the profile.
|
||||
Function.setExecutionCount(BinaryFunction::COUNT_NO_PROFILE);
|
||||
|
||||
// Recompute hash once per function.
|
||||
if (!opts::IgnoreHash)
|
||||
Function.computeHash(YamlBP.Header.IsDFSOrder,
|
||||
YamlBP.Header.HashFunction);
|
||||
|
||||
if (profileMatches(YamlBF, Function))
|
||||
if (profileMatches(YamlBF, Function)) {
|
||||
matchProfileToFunction(YamlBF, Function);
|
||||
++MatchedWithExactName;
|
||||
}
|
||||
}
|
||||
|
||||
// Iterates through profiled functions to match the first binary function with
|
||||
// the same exact hash. Serves to match identical, renamed functions.
|
||||
// Collisions are possible where multiple functions share the same exact hash.
|
||||
if (opts::MatchProfileWithFunctionHash) {
|
||||
DenseMap<size_t, BinaryFunction *> StrictHashToBF;
|
||||
StrictHashToBF.reserve(BC.getBinaryFunctions().size());
|
||||
|
||||
for (auto &[_, BF] : BC.getBinaryFunctions())
|
||||
StrictHashToBF[BF.getHash()] = &BF;
|
||||
|
||||
for (yaml::bolt::BinaryFunctionProfile &YamlBF : YamlBP.Functions) {
|
||||
if (YamlBF.Used)
|
||||
continue;
|
||||
auto It = StrictHashToBF.find(YamlBF.Hash);
|
||||
if (It != StrictHashToBF.end() && !ProfiledFunctions.count(It->second)) {
|
||||
BinaryFunction *BF = It->second;
|
||||
matchProfileToFunction(YamlBF, *BF);
|
||||
++MatchedWithHash;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This second pass allows name ambiguity for LTO private functions.
|
||||
for (const auto &[CommonName, LTOProfiles] : LTOCommonNameMap) {
|
||||
if (!LTOCommonNameFunctionMap.contains(CommonName))
|
||||
continue;
|
||||
@ -396,6 +439,7 @@ Error YAMLProfileReader::readProfile(BinaryContext &BC) {
|
||||
for (BinaryFunction *BF : Functions) {
|
||||
if (!ProfiledFunctions.count(BF) && profileMatches(*YamlBF, *BF)) {
|
||||
matchProfileToFunction(*YamlBF, *BF);
|
||||
++MatchedWithLTOCommonName;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -407,8 +451,10 @@ Error YAMLProfileReader::readProfile(BinaryContext &BC) {
|
||||
// partially.
|
||||
if (!ProfileMatched && LTOProfiles.size() == 1 && Functions.size() == 1 &&
|
||||
!LTOProfiles.front()->Used &&
|
||||
!ProfiledFunctions.count(*Functions.begin()))
|
||||
!ProfiledFunctions.count(*Functions.begin())) {
|
||||
matchProfileToFunction(*LTOProfiles.front(), **Functions.begin());
|
||||
++MatchedWithLTOCommonName;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto [YamlBF, BF] : llvm::zip_equal(YamlBP.Functions, ProfileBFs))
|
||||
@ -420,6 +466,15 @@ Error YAMLProfileReader::readProfile(BinaryContext &BC) {
|
||||
errs() << "BOLT-WARNING: profile ignored for function " << YamlBF.Name
|
||||
<< '\n';
|
||||
|
||||
if (opts::Verbosity >= 1) {
|
||||
outs() << "BOLT-INFO: matched " << MatchedWithExactName
|
||||
<< " functions with identical names\n";
|
||||
outs() << "BOLT-INFO: matched " << MatchedWithHash
|
||||
<< " functions with hash\n";
|
||||
outs() << "BOLT-INFO: matched " << MatchedWithLTOCommonName
|
||||
<< " functions with matching LTO common names\n";
|
||||
}
|
||||
|
||||
// Set for parseFunctionProfile().
|
||||
NormalizeByInsnCount = usesEvent("cycles") || usesEvent("instructions");
|
||||
NormalizeByCalls = usesEvent("branches");
|
||||
@ -439,6 +494,12 @@ Error YAMLProfileReader::readProfile(BinaryContext &BC) {
|
||||
|
||||
BC.setNumUnusedProfiledObjects(NumUnused);
|
||||
|
||||
if (opts::Lite && opts::MatchProfileWithFunctionHash) {
|
||||
for (BinaryFunction *BF : BC.getAllBinaryFunctions())
|
||||
if (!BF->hasProfile())
|
||||
BF->setIgnored();
|
||||
}
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
|
@ -82,6 +82,7 @@ extern cl::opt<bool> Hugify;
|
||||
extern cl::opt<bool> Instrument;
|
||||
extern cl::opt<JumpTableSupportLevel> JumpTables;
|
||||
extern cl::opt<bool> KeepNops;
|
||||
extern cl::opt<bool> Lite;
|
||||
extern cl::list<std::string> ReorderData;
|
||||
extern cl::opt<bolt::ReorderFunctions::ReorderType> ReorderFunctions;
|
||||
extern cl::opt<bool> TerminalTrap;
|
||||
@ -140,9 +141,6 @@ KeepTmp("keep-tmp",
|
||||
cl::Hidden,
|
||||
cl::cat(BoltCategory));
|
||||
|
||||
cl::opt<bool> Lite("lite", cl::desc("skip processing of cold functions"),
|
||||
cl::cat(BoltCategory));
|
||||
|
||||
static cl::opt<unsigned>
|
||||
LiteThresholdPct("lite-threshold-pct",
|
||||
cl::desc("threshold (in percent) for selecting functions to process in lite "
|
||||
|
@ -128,6 +128,9 @@ cl::opt<bool>
|
||||
cl::desc("instrument code to generate accurate profile data"),
|
||||
cl::cat(BoltOptCategory));
|
||||
|
||||
cl::opt<bool> Lite("lite", cl::desc("skip processing of cold functions"),
|
||||
cl::cat(BoltCategory));
|
||||
|
||||
cl::opt<std::string>
|
||||
OutputFilename("o",
|
||||
cl::desc("<output file>"),
|
||||
|
63
bolt/test/X86/hashing-based-function-matching.test
Normal file
63
bolt/test/X86/hashing-based-function-matching.test
Normal file
@ -0,0 +1,63 @@
|
||||
## Tests function matching in YAMLProfileReader by function hash.
|
||||
|
||||
# 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=2 \
|
||||
# RUN: --print-cfg --match-profile-with-function-hash --profile-ignore-hash=0 2>&1 | FileCheck %s
|
||||
|
||||
# CHECK: BOLT-INFO: matched 1 functions with hash
|
||||
|
||||
#--- 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
|
||||
.reloc 0, R_X86_64_NONE
|
||||
.cfi_endproc
|
||||
.size main, .-main
|
||||
|
||||
#--- yaml
|
||||
---
|
||||
header:
|
||||
profile-version: 1
|
||||
binary-name: 'hashing-based-function-matching.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: 0x6E7F15254DE2478
|
||||
exec: 1
|
||||
nblocks: 6
|
||||
blocks:
|
||||
- bid: 1
|
||||
insns: 1
|
||||
succ: [ { bid: 3, cnt: 1} ]
|
||||
...
|
@ -372,6 +372,12 @@ Changes to the LLVM tools
|
||||
Changes to LLDB
|
||||
---------------------------------
|
||||
|
||||
Changes to BOLT
|
||||
---------------------------------
|
||||
* Now supports ``--match-profile-with-function-hash`` to match profiled and
|
||||
binary functions with exact hash, allowing for the matching of renamed but
|
||||
identical functions.
|
||||
|
||||
Changes to Sanitizers
|
||||
---------------------
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user