[llvm] [llvm-profgen] Add branch/target validation (PR #188620)
via llvm-commits
llvm-commits at lists.llvm.org
Wed Apr 15 19:53:41 PDT 2026
https://github.com/HighW4y2H3ll updated https://github.com/llvm/llvm-project/pull/188620
>From 57920ac1116c94e0d8ab781a8381a90b456586b7 Mon Sep 17 00:00:00 2001
From: h2h <h2h at meta.com>
Date: Tue, 24 Mar 2026 14:35:38 -0700
Subject: [PATCH 1/4] [llvm-profgen] Add branch/target validation
---
llvm/tools/llvm-profgen/PerfReader.cpp | 55 ++++++++++++++++++++++
llvm/tools/llvm-profgen/PerfReader.h | 2 +
llvm/tools/llvm-profgen/ProfiledBinary.cpp | 13 +++++
llvm/tools/llvm-profgen/ProfiledBinary.h | 10 ++++
4 files changed, 80 insertions(+)
diff --git a/llvm/tools/llvm-profgen/PerfReader.cpp b/llvm/tools/llvm-profgen/PerfReader.cpp
index 1dc59321fd91f..56d91044f8441 100644
--- a/llvm/tools/llvm-profgen/PerfReader.cpp
+++ b/llvm/tools/llvm-profgen/PerfReader.cpp
@@ -1344,6 +1344,60 @@ void PerfScriptReader::warnInvalidRange() {
"range end acrossing the unconditinal jmp.");
}
+void PerfScriptReader::warnIfBranchTargetMismatch() {
+ // Collect unique branch source and target addresses from LBR samples,
+ // then check what percentage don't match known instructions in the binary.
+
+ std::unordered_set<uint64_t> SampleBranches;
+ std::unordered_set<uint64_t> SampleIndirectTargets;
+ std::unordered_set<uint64_t> SampleTargets;
+
+ for (const auto &Item : AggregatedSamples) {
+ const PerfSample *Sample = Item.first.getPtr();
+ for (const LBREntry &LBR : Sample->LBRStack) {
+ uint64_t Source = LBR.Source;
+ uint64_t Target = LBR.Target;
+ if (Source == ExternalAddr || Target == ExternalAddr)
+ continue;
+ SampleBranches.insert(Source);
+ if (Binary->addressIsIndirectBranch(Source))
+ SampleIndirectTargets.insert(Target);
+ else
+ SampleTargets.insert(Target);
+ }
+ }
+
+ auto CheckMismatch = [&](StringRef Kind,
+ const std::unordered_set<uint64_t> &SampleAddrs,
+ auto IsValidAddr) {
+ if (SampleAddrs.empty())
+ return;
+ uint64_t Mismatched = 0;
+ for (uint64_t Addr : SampleAddrs) {
+ if (!IsValidAddr(Addr))
+ Mismatched++;
+ }
+ double MismatchPct =
+ static_cast<double>(Mismatched) / SampleAddrs.size() * 100;
+ if (Mismatched) {
+ WithColor::warning()
+ << format("%.2f", MismatchPct) << "% of sampled " << Kind
+ << " addresses (" << Mismatched << "/" << SampleAddrs.size()
+ << ") do not match the binary, likely due to problematic raw samples or mismatch in binary.\n"
+ }
+ };
+
+ CheckMismatch("branch", SampleBranches, [&](uint64_t Addr) {
+ return Binary->addressIsTransfer(Addr);
+ });
+ CheckMismatch("target", SampleTargets, [&](uint64_t Addr) {
+ return Binary->addressIsBranchTarget(Addr) || Binary->findFuncRangeForStartAddr(Addr);
+ });
+ CheckMismatch("indirect branch target", SampleIndirectTargets, [&](uint64_t Addr) {
+ return Binary->addressIsCode(Addr);
+ });
+}
+
void PerfScriptReader::parsePerfTraces() {
// Parse perf traces and do aggregation.
parseAndAggregateTrace();
@@ -1360,6 +1414,7 @@ void PerfScriptReader::parsePerfTraces() {
// Generate unsymbolized profile.
warnTruncatedStack();
warnInvalidRange();
+ warnIfBranchTargetMismatch();
generateUnsymbolizedProfile();
AggregatedSamples.clear();
diff --git a/llvm/tools/llvm-profgen/PerfReader.h b/llvm/tools/llvm-profgen/PerfReader.h
index 6e233d17f8e62..1ee6369de649a 100644
--- a/llvm/tools/llvm-profgen/PerfReader.h
+++ b/llvm/tools/llvm-profgen/PerfReader.h
@@ -650,6 +650,8 @@ class PerfScriptReader : public PerfReaderBase {
void warnTruncatedStack();
// Warn if range is invalid.
void warnInvalidRange();
+ // Warn if sampled branch/target addresses don't match the binary.
+ void warnIfBranchTargetMismatch();
// Extract call stack from the perf trace lines
bool extractCallstack(TraceStream &TraceIt,
SmallVectorImpl<uint64_t> &CallStack);
diff --git a/llvm/tools/llvm-profgen/ProfiledBinary.cpp b/llvm/tools/llvm-profgen/ProfiledBinary.cpp
index 915e991e4068c..6a4ab7d6a854b 100644
--- a/llvm/tools/llvm-profgen/ProfiledBinary.cpp
+++ b/llvm/tools/llvm-profgen/ProfiledBinary.cpp
@@ -628,6 +628,8 @@ bool ProfiledBinary::dissassembleSymbol(std::size_t SI, ArrayRef<uint8_t> Bytes,
if (MCDesc.isCall()) {
CallAddressSet.insert(Address);
UncondBranchAddrSet.insert(Address);
+ // Record the instruction after call as the branch target of a ret
+ BranchTargetAddressSet.insert(Address + Size);
} else if (MCDesc.isReturn()) {
RetAddressSet.insert(Address);
UncondBranchAddrSet.insert(Address);
@@ -637,6 +639,17 @@ bool ProfiledBinary::dissassembleSymbol(std::size_t SI, ArrayRef<uint8_t> Bytes,
BranchAddressSet.insert(Address);
}
+ if (MCDesc.isIndirectBranch()) {
+ IndirectBranchAddressSet.insert(Address);
+ }
+
+ // Record branch target addresses for branches and calls.
+ if (MCDesc.isCall() || MCDesc.isBranch()) {
+ uint64_t Target = 0;
+ if (MIA->evaluateBranch(Inst, Address, Size, Target))
+ BranchTargetAddressSet.insert(Target);
+ }
+
// Record potential call targets for tail frame inference later-on.
if (InferMissingFrames && FRange) {
uint64_t Target = 0;
diff --git a/llvm/tools/llvm-profgen/ProfiledBinary.h b/llvm/tools/llvm-profgen/ProfiledBinary.h
index e907c4a8a1647..f72c2ba44f5e2 100644
--- a/llvm/tools/llvm-profgen/ProfiledBinary.h
+++ b/llvm/tools/llvm-profgen/ProfiledBinary.h
@@ -282,6 +282,10 @@ class ProfiledBinary {
std::set<uint64_t> UncondBranchAddrSet;
// A set of branch instruction addresses.
std::unordered_set<uint64_t> BranchAddressSet;
+ // A set of indirect branch instruction addresses.
+ std::unordered_set<uint64_t> IndirectBranchAddressSet;
+ // A set of branch target addresses (destinations of branches/calls).
+ std::unordered_set<uint64_t> BranchTargetAddressSet;
// Estimate and track function prolog and epilog ranges.
PrologEpilogTracker ProEpilogTracker;
@@ -465,6 +469,12 @@ class ProfiledBinary {
return ProEpilogTracker.PrologEpilogSet.count(Address);
}
+ bool addressIsBranchTarget(uint64_t Address) const {
+ return BranchTargetAddressSet.count(Address);
+ }
+ bool addressIsIndirectBranch(uint64_t Address) const {
+ return IndirectBranchAddressSet.count(Address);
+ }
bool addressIsTransfer(uint64_t Address) {
return BranchAddressSet.count(Address) || RetAddressSet.count(Address) ||
CallAddressSet.count(Address);
>From 428e86c0d2cc9cce79b6b06b6748e28cffd2fe44 Mon Sep 17 00:00:00 2001
From: h2h <h2h at meta.com>
Date: Wed, 25 Mar 2026 15:59:10 -0700
Subject: [PATCH 2/4] formatting
---
llvm/tools/llvm-profgen/PerfReader.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/tools/llvm-profgen/PerfReader.cpp b/llvm/tools/llvm-profgen/PerfReader.cpp
index 56d91044f8441..e306539fb09f1 100644
--- a/llvm/tools/llvm-profgen/PerfReader.cpp
+++ b/llvm/tools/llvm-profgen/PerfReader.cpp
@@ -1383,7 +1383,7 @@ void PerfScriptReader::warnIfBranchTargetMismatch() {
WithColor::warning()
<< format("%.2f", MismatchPct) << "% of sampled " << Kind
<< " addresses (" << Mismatched << "/" << SampleAddrs.size()
- << ") do not match the binary, likely due to problematic raw samples or mismatch in binary.\n"
+ << ") do not match the binary, likely due to problematic raw samples or mismatch in binary.\n";
}
};
>From 3af39ce3e6c245eb0f78f1097317195997999690 Mon Sep 17 00:00:00 2001
From: h2h <h2h at meta.com>
Date: Tue, 7 Apr 2026 08:11:11 -0700
Subject: [PATCH 3/4] Improve performance
---
llvm/tools/llvm-profgen/PerfReader.cpp | 66 +++++++++++---------------
1 file changed, 29 insertions(+), 37 deletions(-)
diff --git a/llvm/tools/llvm-profgen/PerfReader.cpp b/llvm/tools/llvm-profgen/PerfReader.cpp
index e306539fb09f1..7cd961e0bf527 100644
--- a/llvm/tools/llvm-profgen/PerfReader.cpp
+++ b/llvm/tools/llvm-profgen/PerfReader.cpp
@@ -1348,9 +1348,10 @@ void PerfScriptReader::warnIfBranchTargetMismatch() {
// Collect unique branch source and target addresses from LBR samples,
// then check what percentage don't match known instructions in the binary.
- std::unordered_set<uint64_t> SampleBranches;
- std::unordered_set<uint64_t> SampleIndirectTargets;
- std::unordered_set<uint64_t> SampleTargets;
+ uint64_t MismatchedBranches = 0;
+ uint64_t MismatchedIndirectTargets = 0;
+ uint64_t MismatchedTargets = 0;
+ uint64_t TotalSamples = 0;
for (const auto &Item : AggregatedSamples) {
const PerfSample *Sample = Item.first.getPtr();
@@ -1359,43 +1360,34 @@ void PerfScriptReader::warnIfBranchTargetMismatch() {
uint64_t Target = LBR.Target;
if (Source == ExternalAddr || Target == ExternalAddr)
continue;
- SampleBranches.insert(Source);
- if (Binary->addressIsIndirectBranch(Source))
- SampleIndirectTargets.insert(Target);
- else
- SampleTargets.insert(Target);
+ TotalSamples++;
+
+ // Validate Branch sources are Call/Branch/Indirect Branch
+ if (!Binary->addressIsTransfer(Source))
+ MismatchedBranches++;
+
+ // Validate Indirect Branch targets landed in code. This may over estimate
+ // the vaid targets only because there's no good way to determine jump
+ // table targets
+ if (Binary->addressIsIndirectBranch(Source)) {
+ if (!Binary->addressIsCode(Target))
+ MismatchedIndirectTargets++;
+ } else if (!Binary->addressIsBranchTarget(Target) &&
+ !Binary->findFuncRangeForStartAddr(Target))
+ MismatchedTargets++;
}
}
- auto CheckMismatch = [&](StringRef Kind,
- const std::unordered_set<uint64_t> &SampleAddrs,
- auto IsValidAddr) {
- if (SampleAddrs.empty())
- return;
- uint64_t Mismatched = 0;
- for (uint64_t Addr : SampleAddrs) {
- if (!IsValidAddr(Addr))
- Mismatched++;
- }
- double MismatchPct =
- static_cast<double>(Mismatched) / SampleAddrs.size() * 100;
- if (Mismatched) {
- WithColor::warning()
- << format("%.2f", MismatchPct) << "% of sampled " << Kind
- << " addresses (" << Mismatched << "/" << SampleAddrs.size()
- << ") do not match the binary, likely due to problematic raw samples or mismatch in binary.\n";
- }
- };
-
- CheckMismatch("branch", SampleBranches, [&](uint64_t Addr) {
- return Binary->addressIsTransfer(Addr);
- });
- CheckMismatch("target", SampleTargets, [&](uint64_t Addr) {
- return Binary->addressIsBranchTarget(Addr) || Binary->findFuncRangeForStartAddr(Addr);
- });
- CheckMismatch("indirect branch target", SampleIndirectTargets, [&](uint64_t Addr) {
- return Binary->addressIsCode(Addr);
- });
+ emitWarningSummary(MismatchedBranches, TotalSamples,
+ "of branches do not match the binary, likely due to "
+ "problematic raw samples or mismatch in binary.");
+ emitWarningSummary(MismatchedTargets, TotalSamples,
+ "of branch targets do not match the binary, likely due to "
+ "problematic raw samples or mismatch in binary.");
+ emitWarningSummary(
+ MismatchedIndirectTargets, TotalSamples,
+ "of indirect branch targets do not match the binary, likely due to "
+ "problematic raw samples or mismatch in binary.");
}
void PerfScriptReader::parsePerfTraces() {
>From 7dddc5bca44dac83c1581392976dd0842bd2cf4e Mon Sep 17 00:00:00 2001
From: HighW4y2H3ll <zhenghaohuu at gmail.com>
Date: Wed, 15 Apr 2026 19:53:30 -0700
Subject: [PATCH 4/4] Update llvm/tools/llvm-profgen/PerfReader.cpp
Co-authored-by: Amir Ayupov <fads93 at gmail.com>
---
llvm/tools/llvm-profgen/PerfReader.cpp | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/llvm/tools/llvm-profgen/PerfReader.cpp b/llvm/tools/llvm-profgen/PerfReader.cpp
index 7cd961e0bf527..009503017ec00 100644
--- a/llvm/tools/llvm-profgen/PerfReader.cpp
+++ b/llvm/tools/llvm-profgen/PerfReader.cpp
@@ -1379,8 +1379,7 @@ void PerfScriptReader::warnIfBranchTargetMismatch() {
}
emitWarningSummary(MismatchedBranches, TotalSamples,
- "of branches do not match the binary, likely due to "
- "problematic raw samples or mismatch in binary.");
+ "of branch samples do not match the binary.");
emitWarningSummary(MismatchedTargets, TotalSamples,
"of branch targets do not match the binary, likely due to "
"problematic raw samples or mismatch in binary.");
More information about the llvm-commits
mailing list