diff options
author | shaw young <58664393+shawbyoung@users.noreply.github.com> | 2024-06-24 15:29:44 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-06-24 15:29:44 -0700 |
commit | 5e097c79d820683b72e2bac8e56df93801ad85ec (patch) | |
tree | ca1cd7a473c612f2dec63ac253dfacc1d3a297eb /bolt/lib | |
parent | a3a44bfbdfefe0928124f9e40d242507f75b87f4 (diff) | |
download | llvm-5e097c79d820683b72e2bac8e56df93801ad85ec.zip llvm-5e097c79d820683b72e2bac8e56df93801ad85ec.tar.gz llvm-5e097c79d820683b72e2bac8e56df93801ad85ec.tar.bz2 |
[BOLT] Hash-based function matching (#95821)
Using the hashes of binary and profiled functions
to recover functions with changed names.
Test Plan: added
hashing-based-function-matching.test.
Diffstat (limited to 'bolt/lib')
-rw-r--r-- | bolt/lib/Profile/YAMLProfileReader.cpp | 68 | ||||
-rw-r--r-- | bolt/lib/Rewrite/RewriteInstance.cpp | 4 | ||||
-rw-r--r-- | bolt/lib/Utils/CommandLineOpts.cpp | 5 |
3 files changed, 67 insertions, 10 deletions
diff --git a/bolt/lib/Profile/YAMLProfileReader.cpp b/bolt/lib/Profile/YAMLProfileReader.cpp index f25f5920..6c4eece 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,19 @@ 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) + 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 +386,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 +427,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 +439,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 +454,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 +482,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..ec234ad 100644 --- a/bolt/lib/Rewrite/RewriteInstance.cpp +++ b/bolt/lib/Rewrite/RewriteInstance.cpp @@ -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> MatchProfileWithFunctionHash; extern cl::list<std::string> ReorderData; extern cl::opt<bolt::ReorderFunctions::ReorderType> ReorderFunctions; extern cl::opt<bool> TerminalTrap; @@ -2982,6 +2983,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..0724e62 100644 --- a/bolt/lib/Utils/CommandLineOpts.cpp +++ b/bolt/lib/Utils/CommandLineOpts.cpp @@ -128,6 +128,11 @@ cl::opt<bool> cl::desc("instrument code to generate accurate profile data"), cl::cat(BoltOptCategory)); +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>"), |