[llvm] 257706f - [llvm-profgen] Add branch/target validation (#188620)
via llvm-commits
llvm-commits at lists.llvm.org
Sun Apr 19 18:30:36 PDT 2026
Author: HighW4y2H3ll
Date: 2026-04-19T18:30:31-07:00
New Revision: 257706f21227ba596c81b5c97a3d4f0d33aa15d5
URL: https://github.com/llvm/llvm-project/commit/257706f21227ba596c81b5c97a3d4f0d33aa15d5
DIFF: https://github.com/llvm/llvm-project/commit/257706f21227ba596c81b5c97a3d4f0d33aa15d5.diff
LOG: [llvm-profgen] Add branch/target validation (#188620)
Add extra branch source and target validation checks for LBR samples.
This is to check whether there are branch source samples that do not
match a call/branch/ret instruction in the binary, and branch target
samples that do not match a resolved Imm target address, or a function
start address (in case of an indirect call).
Example output:
```
# X86
warning: 0.01%(27/376876) of sampled target addresses do not match the binary.
# AArch64
warning: 0.01%(63782/795824826) of branch samples do not match the binary.
warning: 0.01%(70468/795824826) of branch targets do not match the binary.
```
Run time overhead:
```
Before:
real 61m38.081s
user 60m50.768s
sys 0m42.931s
After:
real 62m33.176s
user 61m47.650s
sys 0m42.204s
```
Added:
Modified:
llvm/tools/llvm-profgen/PerfReader.cpp
llvm/tools/llvm-profgen/PerfReader.h
llvm/tools/llvm-profgen/ProfiledBinary.cpp
llvm/tools/llvm-profgen/ProfiledBinary.h
Removed:
################################################################################
diff --git a/llvm/tools/llvm-profgen/PerfReader.cpp b/llvm/tools/llvm-profgen/PerfReader.cpp
index 1dc59321fd91f..9706b0546aada 100644
--- a/llvm/tools/llvm-profgen/PerfReader.cpp
+++ b/llvm/tools/llvm-profgen/PerfReader.cpp
@@ -1344,6 +1344,48 @@ 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.
+
+ 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();
+ for (const LBREntry &LBR : Sample->LBRStack) {
+ uint64_t Source = LBR.Source;
+ uint64_t Target = LBR.Target;
+ if (Source == ExternalAddr || Target == ExternalAddr)
+ continue;
+ 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++;
+ }
+ }
+
+ emitWarningSummary(MismatchedBranches, TotalSamples,
+ "of branch samples do not match the binary.");
+ emitWarningSummary(MismatchedTargets, TotalSamples,
+ "of branch targets do not match the binary.");
+ emitWarningSummary(MismatchedIndirectTargets, TotalSamples,
+ "of indirect branch targets do not match the binary.");
+}
+
void PerfScriptReader::parsePerfTraces() {
// Parse perf traces and do aggregation.
parseAndAggregateTrace();
@@ -1360,6 +1402,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 2a4c7594d3a93..9011e0b644261 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 88832ba65efea..1e61a2864c4e5 100644
--- a/llvm/tools/llvm-profgen/ProfiledBinary.cpp
+++ b/llvm/tools/llvm-profgen/ProfiledBinary.cpp
@@ -645,6 +645,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);
@@ -654,6 +656,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 a437c57b8c0d7..af8d2dbb8eb06 100644
--- a/llvm/tools/llvm-profgen/ProfiledBinary.h
+++ b/llvm/tools/llvm-profgen/ProfiledBinary.h
@@ -283,6 +283,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;
@@ -479,6 +483,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);
More information about the llvm-commits
mailing list