Skip to content

Commit a24eac8

Browse files
authored
[BOLT] Adding a unittest that covers Arm SPE PBT aggregation (#160095)
When the SPE Previous Branch Target address (FEAT_SPE_PBT) feature is available, an SPE sample by combining this PBT feature, has two entries. Arm SPE records SRC/DEST addresses of the latest sampled branch operation, and it stores into the first entry. PBT records the target address of most recently taken branch in program order before the sampled operation, it places into the second entry. They are formed a chain of two consecutive branches. Where: - The previous branch operation (PBT) is always taken. - In SPE entry, the current source branch (SRC) may be either fall-through or taken, and the target address (DEST) of the recorded branch operation is always what was architecturally executed. However PBT doesn't provide as much information as SPE does. It lacks those information such as the address of source branch, branch type, and prediction bit. These information are always filled with zero in PBT entry. Therefore Bolt cannot evaluate the prediction, and source branch fields, it leaves them zero during the aggregation process. Tests includes a fully expanded example.
1 parent cad96ad commit a24eac8

File tree

1 file changed

+88
-0
lines changed

1 file changed

+88
-0
lines changed

bolt/unittests/Profile/PerfSpeEvents.cpp

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,4 +161,92 @@ TEST_F(PerfSpeEventsTestHelper, SpeBranchesWithBrstack) {
161161
parseAndCheckBrstackEvents(1234, ExpectedSamples);
162162
}
163163

164+
TEST_F(PerfSpeEventsTestHelper, SpeBranchesWithBrstackAndPbt) {
165+
// Check perf input with SPE branch events as brstack format by
166+
// combining with the previous branch target address (named as PBT).
167+
// Example collection command:
168+
// ```
169+
// perf record -e 'arm_spe_0/branch_filter=1/u' -- BINARY
170+
// ```
171+
// How Bolt extracts the branch events:
172+
// ```
173+
// perf script -F pid,brstack --itrace=bl
174+
// ```
175+
176+
opts::ArmSPE = true;
177+
opts::ReadPerfEvents =
178+
// "<PID> <SRC>/<DEST>/PN/-/-/10/COND/- <NULL>/<PBT>/-/-/-/0//-\n"
179+
" 4567 0xa002/0xa003/PN/-/-/10/COND/- 0x0/0xa001/-/-/-/0//-\n"
180+
" 4567 0xb002/0xb003/P/-/-/4/RET/- 0x0/0xb001/-/-/-/0//-\n"
181+
" 4567 0xc456/0xc789/P/-/-/13/-/- 0x0/0xc123/-/-/-/0//-\n"
182+
" 4567 0xd456/0xd789/M/-/-/7/RET/- 0x0/0xd123/-/-/-/0//-\n"
183+
" 4567 0xe005/0xe009/P/-/-/14/RET/- 0x0/0xe001/-/-/-/0//-\n"
184+
" 4567 0xd456/0xd789/M/-/-/7/RET/- 0x0/0xd123/-/-/-/0//-\n"
185+
" 4567 0xf002/0xf003/MN/-/-/8/COND/- 0x0/0xf001/-/-/-/0//-\n"
186+
" 4567 0xc456/0xc789/P/-/-/13/-/- 0x0/0xc123/-/-/-/0//-\n";
187+
188+
// ExpectedSamples contains the aggregated information about
189+
// a branch {{From, To, TraceTo}, {TakenCount, MispredCount}}.
190+
// Where
191+
// - From: is the source address of the sampled branch operation.
192+
// - To: is the target address of the sampled branch operation.
193+
// - TraceTo could be either
194+
// - A 'Type = Trace::BR_ONLY', which means the trace only contains branch
195+
// data.
196+
// - Or an address, when the trace contains information about the previous
197+
// branch.
198+
//
199+
// When FEAT_SPE_PBT is present, Arm SPE emits two records per sample:
200+
// - the current branch (Spe.From/Spe.To), and
201+
// - the previous taken branch target (PBT) (PBT.From, PBT.To).
202+
//
203+
// Together they behave like a depth-1 branch stack where:
204+
// - the PBT entry is always taken
205+
// - the current branch entry may represent a taken branch or a fall-through
206+
// - the destination (Spe.To) is the architecturally executed target
207+
//
208+
// There can be fall-throughs to be inferred between the PBT entry and
209+
// the current branch (Spe.From), but there cannot be between current
210+
// branch's (Spe.From/Spe.To).
211+
//
212+
// PBT records only the target address (PBT.To), meaning we have no
213+
// information as the branch source (PBT.From=0x0), branch type, and the
214+
// prediction bit.
215+
//
216+
// Consider the trace pair:
217+
// {{Spe.From, Spe.To, Type}, {TK, MP}},
218+
// {{PBT.From, PBT.To, TraceTo}, {TK, MP}}
219+
// {{0xd456, 0xd789, Trace::BR_ONLY}, {2, 2}}, {{0x0, 0xd123, 0xd456}, {2, 0}}
220+
//
221+
// The first entry is the Spe record, which represents a trace from 0xd456
222+
// (Spe.From) to 0xd789 (Spe.To). Type = Trace::BR_ONLY, as Bolt processes the
223+
// current branch event first. At this point we have no information about the
224+
// previous trace (PBT). This entry has a TakenCount = 2, as we have two
225+
// samples for (0xd456, 0xd789) in our input. It also has MispredsCount = 2,
226+
// as 'M' misprediction flag appears in both cases.
227+
//
228+
// The second entry is the PBT record. TakenCount = 2 because the
229+
// (PBT.From = 0x0, PBT.To = 0xd123) branch target appears twice in the input,
230+
// and MispredsCount = 0 because prediction data is absent. There is no branch
231+
// source information, so the PBT.From field is zero (0x0). TraceTo = 0xd456
232+
// connect the flow from the previous taken branch at 0xd123 (PBT.To) to the
233+
// current source branch at 0xd456 (Spe.From), which then continues to 0xd789
234+
// (Spe.To).
235+
std::vector<std::pair<Trace, TakenBranchInfo>> ExpectedSamples = {
236+
{{0xa002, 0xa003, Trace::BR_ONLY}, {1, 0}},
237+
{{0x0, 0xa001, 0xa002}, {1, 0}},
238+
{{0xb002, 0xb003, Trace::BR_ONLY}, {1, 0}},
239+
{{0x0, 0xb001, 0xb002}, {1, 0}},
240+
{{0xc456, 0xc789, Trace::BR_ONLY}, {2, 0}},
241+
{{0x0, 0xc123, 0xc456}, {2, 0}},
242+
{{0xd456, 0xd789, Trace::BR_ONLY}, {2, 2}},
243+
{{0x0, 0xd123, 0xd456}, {2, 0}},
244+
{{0xe005, 0xe009, Trace::BR_ONLY}, {1, 0}},
245+
{{0x0, 0xe001, 0xe005}, {1, 0}},
246+
{{0xf002, 0xf003, Trace::BR_ONLY}, {1, 1}},
247+
{{0x0, 0xf001, 0xf002}, {1, 0}}};
248+
249+
parseAndCheckBrstackEvents(4567, ExpectedSamples);
250+
}
251+
164252
#endif

0 commit comments

Comments
 (0)