diff options
-rw-r--r-- | bolt/docs/CommandLineArgumentReference.md | 6 | ||||
-rw-r--r-- | bolt/lib/Profile/YAMLProfileReader.cpp | 70 | ||||
-rw-r--r-- | bolt/lib/Rewrite/RewriteInstance.cpp | 8 | ||||
-rw-r--r-- | bolt/lib/Utils/CommandLineOpts.cpp | 8 | ||||
-rw-r--r-- | bolt/test/X86/hashing-based-function-matching.test | 64 | ||||
-rw-r--r-- | llvm/docs/ReleaseNotes.rst | 6 |
6 files changed, 148 insertions, 14 deletions
diff --git a/bolt/docs/CommandLineArgumentReference.md b/bolt/docs/CommandLineArgumentReference.md index d95f30a..e8bcc9f 100644 --- a/bolt/docs/CommandLineArgumentReference.md +++ b/bolt/docs/CommandLineArgumentReference.md @@ -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 @@ -1232,4 +1236,4 @@ - `--print-options` - Print non-default options after command line parsing
\ No newline at end of file + Print non-default options after command line parsing diff --git a/bolt/lib/Profile/YAMLProfileReader.cpp b/bolt/lib/Profile/YAMLProfileReader.cpp index f25f5920..8ec5fa7f 100644 --- a/bolt/lib/Profile/YAMLProfileReader.cpp +++ b/bolt/lib/Profile/YAMLProfileReader.cpp @@ -22,6 +22,8 @@ namespace opts { extern cl::opt<unsigned> Verbosity; extern cl::OptionCategory BoltOptCategory; extern cl::opt<bool> InferStaleProfile; +extern cl::opt<bool> MatchProfileWithFunctionHash; +extern cl::opt<bool> Lite; static llvm::cl::opt<bool> IgnoreHash("profile-ignore-hash", @@ -363,9 +365,21 @@ 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 +388,34 @@ 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; + } } + // Uses the strict hash of profiled and binary functions to match functions + // that are not matched by name or common name. + if (opts::MatchProfileWithFunctionHash) { + std::unordered_map<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 +429,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 +441,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 +456,15 @@ Error YAMLProfileReader::readProfile(BinaryContext &BC) { errs() << "BOLT-WARNING: profile ignored for function " << YamlBF.Name << '\n'; + if (opts::Verbosity >= 2) { + 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 +484,11 @@ Error YAMLProfileReader::readProfile(BinaryContext &BC) { BC.setNumUnusedProfiledObjects(NumUnused); + if (opts::Lite) + for (BinaryFunction *BF : BC.getAllBinaryFunctions()) + if (!BF->hasProfile()) + BF->setIgnored(); + return Error::success(); } diff --git a/bolt/lib/Rewrite/RewriteInstance.cpp b/bolt/lib/Rewrite/RewriteInstance.cpp index 1a3a8af..ee6ac1c 100644 --- a/bolt/lib/Rewrite/RewriteInstance.cpp +++ b/bolt/lib/Rewrite/RewriteInstance.cpp @@ -82,6 +82,8 @@ 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::opt<bool> MatchProfileWithFunctionHash; extern cl::list<std::string> ReorderData; extern cl::opt<bolt::ReorderFunctions::ReorderType> ReorderFunctions; extern cl::opt<bool> TerminalTrap; @@ -140,9 +142,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 " @@ -2982,6 +2981,9 @@ void RewriteInstance::selectFunctionsToProcess() { if (mustSkip(Function)) return false; + if (opts::MatchProfileWithFunctionHash) + return true; + // If the list is not empty, only process functions from the list. if (!opts::ForceFunctionNames.empty() || !ForceFunctionsNR.empty()) { // Regex check (-funcs and -funcs-file options). diff --git a/bolt/lib/Utils/CommandLineOpts.cpp b/bolt/lib/Utils/CommandLineOpts.cpp index 41c89bc..41de30f 100644 --- a/bolt/lib/Utils/CommandLineOpts.cpp +++ b/bolt/lib/Utils/CommandLineOpts.cpp @@ -128,6 +128,14 @@ 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<bool> + MatchProfileWithFunctionHash("match-profile-with-function-hash", + cl::desc("Match profile with function hash"), + cl::Hidden, cl::cat(BoltCategory)); + cl::opt<std::string> OutputFilename("o", cl::desc("<output file>"), diff --git a/bolt/test/X86/hashing-based-function-matching.test b/bolt/test/X86/hashing-based-function-matching.test new file mode 100644 index 0000000..4426da0 --- /dev/null +++ b/bolt/test/X86/hashing-based-function-matching.test @@ -0,0 +1,64 @@ +## 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 2>&1 --profile-ignore-hash=0 | 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 +.LBB05: + call exit + .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: 0x72F82DEAA6FE65FB + exec: 1 + nblocks: 6 + blocks: + - bid: 1 + insns: 1 + succ: [ { bid: 3, cnt: 1} ] +... diff --git a/llvm/docs/ReleaseNotes.rst b/llvm/docs/ReleaseNotes.rst index 76356dd..5100008 100644 --- a/llvm/docs/ReleaseNotes.rst +++ b/llvm/docs/ReleaseNotes.rst @@ -361,6 +361,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 --------------------- |