[llvm] [BOLT] Make mispreds optional for preaggregated B/T/R (PR #202856)

Amir Ayupov via llvm-commits llvm-commits at lists.llvm.org
Tue Jun 9 22:18:54 PDT 2026


https://github.com/aaupov created https://github.com/llvm/llvm-project/pull/202856

Extend the format with an optional mispredictions field.
Update parsing and pre-aggregated profile writer.

Test Plan:
added PreAggregatedX86TestHelper.OptionalMispredField
updated pre-aggregated-records.s


>From fd5073e7bb0378bcc25f176911e63aca45536159 Mon Sep 17 00:00:00 2001
From: Amir Ayupov <aaupov at fb.com>
Date: Tue, 9 Jun 2026 22:18:42 -0700
Subject: [PATCH] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20initia?=
 =?UTF-8?q?l=20version?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Created using spr 1.3.8-beta.1
---
 bolt/docs/profiles.md                      |  4 ++--
 bolt/include/bolt/Profile/DataAggregator.h | 13 +++++++++--
 bolt/lib/Profile/DataAggregator.cpp        |  9 +++++---
 bolt/test/X86/pre-aggregated-records.s     | 17 +++++++++-----
 bolt/unittests/Profile/DataAggregator.cpp  | 27 ++++++++++++++++++++++
 5 files changed, 57 insertions(+), 13 deletions(-)

diff --git a/bolt/docs/profiles.md b/bolt/docs/profiles.md
index 4900c2fb008d0..7eeb13b3d5215 100644
--- a/bolt/docs/profiles.md
+++ b/bolt/docs/profiles.md
@@ -62,8 +62,8 @@ without going through `perf.data`.
 ```
 E <event>
 S <start> <count>
-[TR] <branch> <ft_start> <ft_end> <count>
-B <start> <end> <count> <mispred_count>
+[TR] <branch> <ft_start> <ft_end> <count> [<mispred_count>]
+B <start> <end> <count> [<mispred_count>]
 [Ff] <start> <end> <count>
 r <start> <end> <count>
 ```
diff --git a/bolt/include/bolt/Profile/DataAggregator.h b/bolt/include/bolt/Profile/DataAggregator.h
index 1757d3aa56c65..6fb25529561f8 100644
--- a/bolt/include/bolt/Profile/DataAggregator.h
+++ b/bolt/include/bolt/Profile/DataAggregator.h
@@ -142,6 +142,7 @@ class DataAggregator : public DataReader {
     uint64_t TakenCount{0};
     uint64_t MispredCount{0};
   };
+  friend raw_ostream &operator<<(raw_ostream &OS, const TakenBranchInfo &TBI);
 
   /// Intermediate storage for profile data. We save the results of parsing
   /// and use them later for processing and assigning profile.
@@ -433,8 +434,8 @@ class DataAggregator : public DataReader {
   /// File format syntax:
   /// E <event>
   /// S <start> <count>
-  /// [TR] <start> <end> <ft_end> <count>
-  /// B <start> <end> <count> <mispred_count>
+  /// [TR] <start> <end> <ft_end> <count> [<mispred_count>]
+  /// B <start> <end> <count> [<mispred_count>]
   /// [Ffr] <start> <end> <count>
   ///
   /// where <start>, <end>, <ft_end> have the format [<id>:]<offset>
@@ -472,12 +473,14 @@ class DataAggregator : public DataReader {
   ///
   /// Trace profile combining branches and fall-throughs:
   /// T 4b196f 4b19e0 4b19ef 2
+  /// T 4b196f 4b19e0 4b19ef 2 1
   ///
   /// Legacy branch profile with separate branches and fall-throughs:
   /// F 41be50 41be50 3
   /// F 41be90 41be90 4
   /// B 4b1942 39b57f0 3 0
   /// B 4b196f 4b19e0 2 0
+  /// B 4b196f 4b19e0 2
   void parsePreAggregated();
 
   /// Parse the full output of pre-aggregated LBR samples generated by
@@ -678,6 +681,12 @@ inline raw_ostream &operator<<(raw_ostream &OS,
   return OS;
 }
 
+inline raw_ostream &operator<<(raw_ostream &OS,
+                               const DataAggregator::TakenBranchInfo &TBI) {
+  OS << TBI.TakenCount << " " << TBI.MispredCount;
+  return OS;
+}
+
 inline raw_ostream &operator<<(raw_ostream &OS,
                                const DataAggregator::PerfProcessType &T) {
   OS << DataAggregator::PerfProcessInfo::PerfProcessTypeNames[T];
diff --git a/bolt/lib/Profile/DataAggregator.cpp b/bolt/lib/Profile/DataAggregator.cpp
index 0c879d1646b27..761edcc951f4c 100644
--- a/bolt/lib/Profile/DataAggregator.cpp
+++ b/bolt/lib/Profile/DataAggregator.cpp
@@ -1590,7 +1590,7 @@ std::error_code DataAggregator::parseAggregatedLBREntry() {
     using SSI = StringSwitch<int>;
     AddrNum =
         SSI(Str).Cases({"T", "R"}, 3).Case("S", 1).Case("E", 0).Default(2);
-    CounterNum = SSI(Str).Case("B", 2).Case("E", 0).Default(1);
+    CounterNum = Str != "E";
   }
 
   /// Parse locations depending on entry type, recording them in \p Addr array.
@@ -1609,6 +1609,8 @@ std::error_code DataAggregator::parseAggregatedLBREntry() {
   }
 
   /// Parse counters depending on entry type.
+  if (Type == BRANCH || Type == TRACE || Type == RETURN)
+    CounterNum += ParsingBuf.split('\n').first.contains(FieldSeparator);
   for (int I = 0; I < CounterNum; ++I) {
     while (checkAndConsumeFS()) {
     }
@@ -2467,8 +2469,9 @@ DataAggregator::writePreAggregatedFile(StringRef OutputFilename) const {
     return EC;
 
   for (const auto &[Trace, Info] : Traces)
-    OS << Trace << " " << Info.TakenCount << '\n';
-  OS << formatv("E {0:$[,]}\n", EventNames.keys());
+    OS << Trace << " " << Info << '\n';
+  if (!EventNames.empty())
+    OS << formatv("E {0:$[,]}\n", EventNames.keys());
   for (const auto &[PC, Count] : BasicSamples)
     OS << formatv("S {0:x-} {1}\n", PC, Count);
 
diff --git a/bolt/test/X86/pre-aggregated-records.s b/bolt/test/X86/pre-aggregated-records.s
index 98fcf08c677fd..29a1ae080de96 100644
--- a/bolt/test/X86/pre-aggregated-records.s
+++ b/bolt/test/X86/pre-aggregated-records.s
@@ -1,7 +1,7 @@
-## Test parsing of R (Return), r (FT_EXTERNAL_RETURN), and -1 hex (BR_ONLY)
-## records in pre-aggregated profiles, plus error paths for invalid record
-## types and malformed hex addresses. Closes e2e gaps that were previously
-## only exercised by unit tests.
+## Test parsing of R (Return), r (FT_EXTERNAL_RETURN), -1 hex (BR_ONLY),
+## and optional mispred counts in pre-aggregated profiles, plus error paths
+## for invalid record types and malformed hex addresses. Closes e2e gaps that
+## were previously only exercised by unit tests.
 
 # REQUIRES: system-linux
 
@@ -9,10 +9,11 @@
 # RUN: ld.lld %t.o -o %t
 # RUN: link_fdata %s %t %t.preagg PREAGG
 
-## Parse R, r, B-with-neg1, T-with-neg1, plus a regular F as a sanity check.
+## Parse R, r, B-with-neg1, T-with-neg1, optional mispred counts, plus a
+## regular F as a sanity check.
 # RUN: perf2bolt %t -p %t.preagg --pa -o %t.fdata 2>&1 \
 # RUN:   | FileCheck %s --check-prefix=CHECK-PARSE
-# CHECK-PARSE: read 6 aggregated brstack entries
+# CHECK-PARSE: read 9 aggregated brstack entries
 
 ## Error path: invalid record type letter.
 # RUN: not perf2bolt %t -o %t.bad.fdata --pa \
@@ -58,3 +59,7 @@ Lretins:
 # PREAGG: T #Lbr# #Ltrue# -1 4
 ## F: regular fall-through sanity check.
 # PREAGG: F #Ltrue# #Lret# 3
+## Optional mispred count on B/T/R records.
+# PREAGG: B #Lbr# #Lfalse# 8
+# PREAGG: T #Lbr# #Lfalse# #Lret# 9 4
+# PREAGG: R #Lretins# #Lretins# #Lbr# 10 5
diff --git a/bolt/unittests/Profile/DataAggregator.cpp b/bolt/unittests/Profile/DataAggregator.cpp
index 0d5f4933189df..e5e4611a639c9 100644
--- a/bolt/unittests/Profile/DataAggregator.cpp
+++ b/bolt/unittests/Profile/DataAggregator.cpp
@@ -239,6 +239,33 @@ TEST_F(PreAggregatedX86TestHelper, ReturnEntry) {
   EXPECT_EQ(Traces[0].second.TakenCount, 4u);
 }
 
+TEST_F(PreAggregatedX86TestHelper, OptionalMispredField) {
+  std::vector<std::pair<Trace, TakenBranchInfo>> Traces;
+  parseAndCollectTraces("B 100 200 1\n"
+                        "T 300 400 500 3 4\n"
+                        "R 600 700 800 5 6\n",
+                        Traces);
+  ASSERT_EQ(Traces.size(), 3u);
+
+  EXPECT_EQ(Traces[0].first.Branch, 0x100ULL);
+  EXPECT_EQ(Traces[0].first.From, 0x200ULL);
+  EXPECT_EQ(Traces[0].first.To, Trace::BR_ONLY);
+  EXPECT_EQ(Traces[0].second.TakenCount, 1u);
+  EXPECT_EQ(Traces[0].second.MispredCount, 0u);
+
+  EXPECT_EQ(Traces[1].first.Branch, 0x300ULL);
+  EXPECT_EQ(Traces[1].first.From, 0x400ULL);
+  EXPECT_EQ(Traces[1].first.To, 0x500ULL);
+  EXPECT_EQ(Traces[1].second.TakenCount, 3u);
+  EXPECT_EQ(Traces[1].second.MispredCount, 4u);
+
+  EXPECT_EQ(Traces[2].first.Branch, 0x600ULL);
+  EXPECT_EQ(Traces[2].first.From, 0x700ULL);
+  EXPECT_EQ(Traces[2].first.To, 0x800ULL);
+  EXPECT_EQ(Traces[2].second.TakenCount, 5u);
+  EXPECT_EQ(Traces[2].second.MispredCount, 6u);
+}
+
 TEST_F(PreAggregatedX86TestHelper, TraceWithNeg1AsBROnly) {
   // T entry with -1 as fall-through target: parsed as BR_ONLY.
   std::vector<std::pair<Trace, TakenBranchInfo>> Traces;



More information about the llvm-commits mailing list