[BOLT] Change call count output for ICF

ICF optimization runs multiple passes and the order in which functions
are folded could be dependent on the order they are being processed.
This order is indeterministic as functions are intermediately stored in
std::unordered_map<>. Note that this order is mostly stable, but is not
guaranteed to be and can change e.g. after switching to a different C++
library implementation.

Because the processing (and folding) order is indeterministic, the
previous way of calculating merged function call count could produce
different results.

Change the way we calculate the ICF call count to make it independent of
the function folding/processing order.

Mostly NFC as the output binary should remain the same, the change
affects only the console output.

Reviewed By: yota9

Differential Revision: https://reviews.llvm.org/D144807
This commit is contained in:
Maksim Panchenko
2023-02-24 15:45:21 -08:00
parent 964997a143
commit 03e94f6608
3 changed files with 18 additions and 5 deletions

View File

@@ -349,6 +349,9 @@ private:
/// This attribute is only valid when hasCFG() == true.
bool HasCanonicalCFG{true};
/// True if another function body was merged into this one.
bool HasFunctionsFoldedInto{false};
/// Name for the section this function code should reside in.
std::string CodeSectionName;
@@ -1407,6 +1410,9 @@ public:
/// Return true if the body of the function was merged into another function.
bool isFolded() const { return FoldedIntoFunction != nullptr; }
/// Return true if other functions were folded into this one.
bool hasFunctionsFoldedInto() const { return HasFunctionsFoldedInto; }
/// If this function was folded, return the function it was folded into.
BinaryFunction *getFoldedIntoFunction() const { return FoldedIntoFunction; }
@@ -1770,6 +1776,9 @@ public:
void setFolded(BinaryFunction *BF) { FoldedIntoFunction = BF; }
/// Indicate that another function body was merged with this function.
void setHasFunctionsFoldedInto() { HasFunctionsFoldedInto = true; }
BinaryFunction &setPersonalityFunction(uint64_t Addr) {
assert(!PersonalityFunction && "can't set personality function twice");
PersonalityFunction = BC.getOrCreateGlobalSymbol(Addr, "FUNCat");

View File

@@ -1345,6 +1345,8 @@ void BinaryContext::foldFunction(BinaryFunction &ChildBF,
ChildBF.setFolded(&ParentBF);
}
ParentBF.setHasFunctionsFoldedInto();
}
void BinaryContext::fixBinaryDataHoles() {

View File

@@ -413,7 +413,7 @@ void IdenticalCodeFolding::runOnFunctions(BinaryContext &BC) {
uint64_t NumFunctionsFolded = 0;
std::atomic<uint64_t> NumJTFunctionsFolded{0};
std::atomic<uint64_t> BytesSavedEstimate{0};
std::atomic<uint64_t> CallsSavedEstimate{0};
std::atomic<uint64_t> NumCalled{0};
std::atomic<uint64_t> NumFoldedLastIteration{0};
CongruentBucketsMap CongruentBuckets;
@@ -493,6 +493,8 @@ void IdenticalCodeFolding::runOnFunctions(BinaryContext &BC) {
});
BinaryFunction *ParentBF = Twins[0];
if (!ParentBF->hasFunctionsFoldedInto())
NumCalled += ParentBF->getKnownExecutionCount();
for (unsigned I = 1; I < Twins.size(); ++I) {
BinaryFunction *ChildBF = Twins[I];
LLVM_DEBUG(dbgs() << "BOLT-DEBUG: folding " << *ChildBF << " into "
@@ -506,8 +508,8 @@ void IdenticalCodeFolding::runOnFunctions(BinaryContext &BC) {
// Fold the function and remove from the list of processed functions.
BytesSavedEstimate += ChildBF->getSize();
CallsSavedEstimate += std::min(ChildBF->getKnownExecutionCount(),
ParentBF->getKnownExecutionCount());
if (!ChildBF->hasFunctionsFoldedInto())
NumCalled += ChildBF->getKnownExecutionCount();
BC.foldFunction(*ChildBF, *ParentBF);
++NumFoldedLastIteration;
@@ -579,8 +581,8 @@ void IdenticalCodeFolding::runOnFunctions(BinaryContext &BC) {
<< " functions had jump tables.\n"
<< "BOLT-INFO: Removing all identical functions will save "
<< format("%.2lf", (double)BytesSavedEstimate / 1024)
<< " KB of code space. Folded functions were called "
<< CallsSavedEstimate << " times based on profile.\n";
<< " KB of code space. Folded functions were called " << NumCalled
<< " times based on profile.\n";
}
} // namespace bolt