@@ -52,6 +52,16 @@ struct FlowInfo {
52
52
FunctionFlowMapTy CallGraphIncomingFlows;
53
53
};
54
54
55
+ // When reporting exception handling stats, we only consider functions with at
56
+ // least MinLPECSum counts in landing pads to avoid false positives due to
57
+ // sampling noise
58
+ const uint16_t MinLPECSum = 50 ;
59
+
60
+ // When reporting CFG flow conservation stats, we only consider blocks with
61
+ // execution counts > MinBlockCount when reporting the distribution of worst
62
+ // gaps.
63
+ const uint16_t MinBlockCount = 500 ;
64
+
55
65
template <typename T>
56
66
void printDistribution (raw_ostream &OS, std::vector<T> &values,
57
67
bool Fraction = false ) {
@@ -91,8 +101,12 @@ void printCFGContinuityStats(raw_ostream &OS,
91
101
std::vector<double > FractionECUnreachables;
92
102
93
103
for (const BinaryFunction *Function : Functions) {
94
- if (Function->size () <= 1 )
104
+ if (Function->size () <= 1 ) {
105
+ NumUnreachables.push_back (0 );
106
+ SumECUnreachables.push_back (0 );
107
+ FractionECUnreachables.push_back (0.0 );
95
108
continue ;
109
+ }
96
110
97
111
// Compute the sum of all BB execution counts (ECs).
98
112
size_t NumPosECBBs = 0 ;
@@ -142,8 +156,10 @@ void printCFGContinuityStats(raw_ostream &OS,
142
156
const size_t NumPosECBBsUnreachableFromEntry =
143
157
NumPosECBBs - NumReachableBBs;
144
158
const size_t SumUnreachableBBEC = SumAllBBEC - SumReachableBBEC;
145
- const double FractionECUnreachable =
146
- (double )SumUnreachableBBEC / SumAllBBEC;
159
+
160
+ double FractionECUnreachable = 0.0 ;
161
+ if (SumAllBBEC > 0 )
162
+ FractionECUnreachable = (double )SumUnreachableBBEC / SumAllBBEC;
147
163
148
164
if (opts::Verbosity >= 2 && FractionECUnreachable >= 0.05 ) {
149
165
OS << " Non-trivial CFG discontinuity observed in function "
@@ -157,9 +173,6 @@ void printCFGContinuityStats(raw_ostream &OS,
157
173
FractionECUnreachables.push_back (FractionECUnreachable);
158
174
}
159
175
160
- if (FractionECUnreachables.empty ())
161
- return ;
162
-
163
176
llvm::sort (FractionECUnreachables);
164
177
const int Rank = int (FractionECUnreachables.size () *
165
178
opts::PercentileForProfileQualityCheck / 100 );
@@ -187,8 +200,10 @@ void printCallGraphFlowConservationStats(
187
200
std::vector<double > CallGraphGaps;
188
201
189
202
for (const BinaryFunction *Function : Functions) {
190
- if (Function->size () <= 1 || !Function->isSimple ())
203
+ if (Function->size () <= 1 || !Function->isSimple ()) {
204
+ CallGraphGaps.push_back (0.0 );
191
205
continue ;
206
+ }
192
207
193
208
const uint64_t FunctionNum = Function->getFunctionNumber ();
194
209
std::vector<uint64_t > &IncomingFlows =
@@ -199,60 +214,63 @@ void printCallGraphFlowConservationStats(
199
214
TotalFlowMap.CallGraphIncomingFlows ;
200
215
201
216
// Only consider functions that are not a program entry.
202
- if (CallGraphIncomingFlows.find (FunctionNum) ! =
217
+ if (CallGraphIncomingFlows.find (FunctionNum) = =
203
218
CallGraphIncomingFlows.end ()) {
204
- uint64_t EntryInflow = 0 ;
205
- uint64_t EntryOutflow = 0 ;
206
- uint32_t NumConsideredEntryBlocks = 0 ;
207
-
208
- Function->forEachEntryPoint ([&](uint64_t Offset, const MCSymbol *Label) {
209
- const BinaryBasicBlock *EntryBB =
210
- Function->getBasicBlockAtOffset (Offset);
211
- if (!EntryBB || EntryBB->succ_size () == 0 )
212
- return true ;
213
- NumConsideredEntryBlocks++;
214
- EntryInflow += IncomingFlows[EntryBB->getLayoutIndex ()];
215
- EntryOutflow += OutgoingFlows[EntryBB->getLayoutIndex ()];
219
+ CallGraphGaps.push_back (0.0 );
220
+ continue ;
221
+ }
222
+
223
+ uint64_t EntryInflow = 0 ;
224
+ uint64_t EntryOutflow = 0 ;
225
+ uint32_t NumConsideredEntryBlocks = 0 ;
226
+
227
+ Function->forEachEntryPoint ([&](uint64_t Offset, const MCSymbol *Label) {
228
+ const BinaryBasicBlock *EntryBB = Function->getBasicBlockAtOffset (Offset);
229
+ if (!EntryBB || EntryBB->succ_size () == 0 )
216
230
return true ;
217
- });
218
-
219
- uint64_t NetEntryOutflow = 0 ;
220
- if (EntryOutflow < EntryInflow) {
221
- if (opts::Verbosity >= 2 ) {
222
- // We expect entry blocks' CFG outflow >= inflow, i.e., it has a
223
- // non-negative net outflow. If this is not the case, then raise a
224
- // warning if requested.
225
- OS << " BOLT WARNING: unexpected entry block CFG outflow < inflow "
226
- " in function "
227
- << Function->getPrintName () << " \n " ;
228
- if (opts::Verbosity >= 3 )
229
- Function->dump ();
230
- }
231
- } else {
232
- NetEntryOutflow = EntryOutflow - EntryInflow;
233
- }
234
- if (NumConsideredEntryBlocks > 0 ) {
235
- const uint64_t CallGraphInflow =
236
- TotalFlowMap.CallGraphIncomingFlows [Function->getFunctionNumber ()];
237
- const uint64_t Min = std::min (NetEntryOutflow, CallGraphInflow);
238
- const uint64_t Max = std::max (NetEntryOutflow, CallGraphInflow);
239
- const double CallGraphGap = 1 - (double )Min / Max;
240
-
241
- if (opts::Verbosity >= 2 && CallGraphGap >= 0.5 ) {
242
- OS << " Nontrivial call graph gap of size "
243
- << formatv (" {0:P}" , CallGraphGap) << " observed in function "
244
- << Function->getPrintName () << " \n " ;
245
- if (opts::Verbosity >= 3 )
246
- Function->dump ();
247
- }
231
+ NumConsideredEntryBlocks++;
232
+ EntryInflow += IncomingFlows[EntryBB->getLayoutIndex ()];
233
+ EntryOutflow += OutgoingFlows[EntryBB->getLayoutIndex ()];
234
+ return true ;
235
+ });
248
236
249
- CallGraphGaps.push_back (CallGraphGap);
237
+ uint64_t NetEntryOutflow = 0 ;
238
+ if (EntryOutflow < EntryInflow) {
239
+ if (opts::Verbosity >= 2 ) {
240
+ // We expect entry blocks' CFG outflow >= inflow, i.e., it has a
241
+ // non-negative net outflow. If this is not the case, then raise a
242
+ // warning if requested.
243
+ OS << " BOLT WARNING: unexpected entry block CFG outflow < inflow "
244
+ " in function "
245
+ << Function->getPrintName () << " \n " ;
246
+ if (opts::Verbosity >= 3 )
247
+ Function->dump ();
250
248
}
249
+ } else {
250
+ NetEntryOutflow = EntryOutflow - EntryInflow;
251
251
}
252
- }
252
+ if (NumConsideredEntryBlocks > 0 ) {
253
+ const uint64_t CallGraphInflow =
254
+ TotalFlowMap.CallGraphIncomingFlows [Function->getFunctionNumber ()];
255
+ const uint64_t Min = std::min (NetEntryOutflow, CallGraphInflow);
256
+ const uint64_t Max = std::max (NetEntryOutflow, CallGraphInflow);
257
+ double CallGraphGap = 0.0 ;
258
+ if (Max > 0 )
259
+ CallGraphGap = 1 - (double )Min / Max;
260
+
261
+ if (opts::Verbosity >= 2 && CallGraphGap >= 0.5 ) {
262
+ OS << " Non-trivial call graph gap of size "
263
+ << formatv (" {0:P}" , CallGraphGap) << " observed in function "
264
+ << Function->getPrintName () << " \n " ;
265
+ if (opts::Verbosity >= 3 )
266
+ Function->dump ();
267
+ }
253
268
254
- if (CallGraphGaps.empty ())
255
- return ;
269
+ CallGraphGaps.push_back (CallGraphGap);
270
+ } else {
271
+ CallGraphGaps.push_back (0.0 );
272
+ }
273
+ }
256
274
257
275
llvm::sort (CallGraphGaps);
258
276
const int Rank =
@@ -265,18 +283,19 @@ void printCallGraphFlowConservationStats(
265
283
}
266
284
}
267
285
268
- void printCFGFlowConservationStats (raw_ostream &OS,
286
+ void printCFGFlowConservationStats (const BinaryContext &BC, raw_ostream &OS,
269
287
iterator_range<function_iterator> &Functions,
270
288
FlowInfo &TotalFlowMap) {
271
289
std::vector<double > CFGGapsWeightedAvg;
272
290
std::vector<double > CFGGapsWorst;
273
291
std::vector<uint64_t > CFGGapsWorstAbs;
274
- // We only consider blocks with execution counts > MinBlockCount when
275
- // reporting the distribution of worst gaps.
276
- const uint16_t MinBlockCount = 500 ;
277
292
for (const BinaryFunction *Function : Functions) {
278
- if (Function->size () <= 1 || !Function->isSimple ())
293
+ if (Function->size () <= 1 || !Function->isSimple ()) {
294
+ CFGGapsWeightedAvg.push_back (0.0 );
295
+ CFGGapsWorst.push_back (0.0 );
296
+ CFGGapsWorstAbs.push_back (0 );
279
297
continue ;
298
+ }
280
299
281
300
const uint64_t FunctionNum = Function->getFunctionNumber ();
282
301
std::vector<uint64_t > &MaxCountMaps =
@@ -295,12 +314,34 @@ void printCFGFlowConservationStats(raw_ostream &OS,
295
314
if (BB.isEntryPoint () || BB.succ_size () == 0 )
296
315
continue ;
297
316
317
+ if (BB.getKnownExecutionCount () == 0 || BB.getNumNonPseudos () == 0 )
318
+ continue ;
319
+
320
+ // We don't consider blocks that is a landing pad or has a
321
+ // positive-execution-count landing pad
322
+ if (BB.isLandingPad ())
323
+ continue ;
324
+
325
+ if (llvm::any_of (BB.landing_pads (),
326
+ std::mem_fn (&BinaryBasicBlock::getKnownExecutionCount)))
327
+ continue ;
328
+
329
+ // We don't consider blocks that end with a recursive call instruction
330
+ const MCInst *Inst = BB.getLastNonPseudoInstr ();
331
+ if (BC.MIB ->isCall (*Inst)) {
332
+ const MCSymbol *DstSym = BC.MIB ->getTargetSymbol (*Inst);
333
+ const BinaryFunction *DstFunc =
334
+ DstSym ? BC.getFunctionForSymbol (DstSym) : nullptr ;
335
+ if (DstFunc == Function)
336
+ continue ;
337
+ }
338
+
298
339
const uint64_t Max = MaxCountMaps[BB.getLayoutIndex ()];
299
340
const uint64_t Min = MinCountMaps[BB.getLayoutIndex ()];
300
- const double Gap = 1 - (double )Min / Max;
341
+ double Gap = 0.0 ;
342
+ if (Max > 0 )
343
+ Gap = 1 - (double )Min / Max;
301
344
double Weight = BB.getKnownExecutionCount () * BB.getNumNonPseudos ();
302
- if (Weight == 0 )
303
- continue ;
304
345
// We use log to prevent the stats from being dominated by extremely hot
305
346
// blocks
306
347
Weight = log (Weight);
@@ -316,39 +357,36 @@ void printCFGFlowConservationStats(raw_ostream &OS,
316
357
BBWorstGapAbs = &BB;
317
358
}
318
359
}
319
- if (WeightSum > 0 ) {
320
- const double WeightedGap = WeightedGapSum / WeightSum;
321
- if (opts::Verbosity >= 2 && (WeightedGap >= 0.1 || WorstGap >= 0.9 )) {
322
- OS << " Nontrivial CFG gap observed in function "
323
- << Function->getPrintName () << " \n "
324
- << " Weighted gap: " << formatv (" {0:P}" , WeightedGap) << " \n " ;
325
- if (BBWorstGap)
326
- OS << " Worst gap: " << formatv (" {0:P}" , WorstGap)
327
- << " at BB with input offset: 0x"
328
- << Twine::utohexstr (BBWorstGap->getInputOffset ()) << " \n " ;
329
- if (BBWorstGapAbs)
330
- OS << " Worst gap (absolute value): " << WorstGapAbs << " at BB with "
331
- << " input offset 0x"
332
- << Twine::utohexstr (BBWorstGapAbs->getInputOffset ()) << " \n " ;
333
- if (opts::Verbosity >= 3 )
334
- Function->dump ();
335
- }
336
-
337
- CFGGapsWeightedAvg.push_back (WeightedGap);
338
- CFGGapsWorst.push_back (WorstGap);
339
- CFGGapsWorstAbs.push_back (WorstGapAbs);
360
+ double WeightedGap = WeightedGapSum;
361
+ if (WeightSum > 0 )
362
+ WeightedGap /= WeightSum;
363
+ if (opts::Verbosity >= 2 && WorstGap >= 0.9 ) {
364
+ OS << " Non-trivial CFG gap observed in function "
365
+ << Function->getPrintName () << " \n "
366
+ << " Weighted gap: " << formatv (" {0:P}" , WeightedGap) << " \n " ;
367
+ if (BBWorstGap)
368
+ OS << " Worst gap: " << formatv (" {0:P}" , WorstGap)
369
+ << " at BB with input offset: 0x"
370
+ << Twine::utohexstr (BBWorstGap->getInputOffset ()) << " \n " ;
371
+ if (BBWorstGapAbs)
372
+ OS << " Worst gap (absolute value): " << WorstGapAbs << " at BB with "
373
+ << " input offset 0x"
374
+ << Twine::utohexstr (BBWorstGapAbs->getInputOffset ()) << " \n " ;
375
+ if (opts::Verbosity >= 3 )
376
+ Function->dump ();
340
377
}
378
+ CFGGapsWeightedAvg.push_back (WeightedGap);
379
+ CFGGapsWorst.push_back (WorstGap);
380
+ CFGGapsWorstAbs.push_back (WorstGapAbs);
341
381
}
342
382
343
- if (CFGGapsWeightedAvg.empty ())
344
- return ;
345
383
llvm::sort (CFGGapsWeightedAvg);
346
384
const int RankWA = int (CFGGapsWeightedAvg.size () *
347
385
opts::PercentileForProfileQualityCheck / 100 );
348
386
llvm::sort (CFGGapsWorst);
349
387
const int RankW =
350
388
int (CFGGapsWorst.size () * opts::PercentileForProfileQualityCheck / 100 );
351
- OS << formatv (" CFG flow conservation gap {0:P} (weighted) {1:P} (worst)\n " ,
389
+ OS << formatv (" CFG flow conservation gap {0:P} (weighted) {1:P} (worst); " ,
352
390
CFGGapsWeightedAvg[RankWA], CFGGapsWorst[RankW]);
353
391
if (opts::Verbosity >= 1 ) {
354
392
OS << " distribution of weighted CFG flow conservation gaps\n " ;
@@ -365,6 +403,74 @@ void printCFGFlowConservationStats(raw_ostream &OS,
365
403
}
366
404
}
367
405
406
+ void printExceptionHandlingStats (const BinaryContext &BC, raw_ostream &OS,
407
+ iterator_range<function_iterator> &Functions) {
408
+ std::vector<double > LPCountFractionsOfTotalBBEC;
409
+ std::vector<double > LPCountFractionsOfTotalInvokeEC;
410
+ for (const BinaryFunction *Function : Functions) {
411
+ size_t LPECSum = 0 ;
412
+ size_t BBECSum = 0 ;
413
+ size_t InvokeECSum = 0 ;
414
+ for (BinaryBasicBlock &BB : *Function) {
415
+ const size_t BBEC = BB.getKnownExecutionCount ();
416
+ BBECSum += BBEC;
417
+ if (BB.isLandingPad ())
418
+ LPECSum += BBEC;
419
+ for (const MCInst &Inst : BB) {
420
+ if (!BC.MIB ->isInvoke (Inst))
421
+ continue ;
422
+ const std::optional<MCPlus::MCLandingPad> EHInfo =
423
+ BC.MIB ->getEHInfo (Inst);
424
+ if (EHInfo->first )
425
+ InvokeECSum += BBEC;
426
+ }
427
+ }
428
+
429
+ if (LPECSum <= MinLPECSum) {
430
+ LPCountFractionsOfTotalBBEC.push_back (0.0 );
431
+ LPCountFractionsOfTotalInvokeEC.push_back (0.0 );
432
+ continue ;
433
+ }
434
+ double FracTotalBBEC = 0.0 ;
435
+ if (BBECSum > 0 )
436
+ FracTotalBBEC = (double )LPECSum / BBECSum;
437
+ double FracTotalInvokeEC = 0.0 ;
438
+ if (InvokeECSum > 0 )
439
+ FracTotalInvokeEC = (double )LPECSum / InvokeECSum;
440
+ LPCountFractionsOfTotalBBEC.push_back (FracTotalBBEC);
441
+ LPCountFractionsOfTotalInvokeEC.push_back (FracTotalInvokeEC);
442
+
443
+ if (opts::Verbosity >= 2 && FracTotalInvokeEC >= 0.05 ) {
444
+ OS << " Non-trivial usage of exception handling observed in function "
445
+ << Function->getPrintName () << " \n "
446
+ << formatv (
447
+ " Fraction of total InvokeEC that goes to landing pads: {0:P}\n " ,
448
+ FracTotalInvokeEC);
449
+ if (opts::Verbosity >= 3 )
450
+ Function->dump ();
451
+ }
452
+ }
453
+
454
+ llvm::sort (LPCountFractionsOfTotalBBEC);
455
+ const int RankBBEC = int (LPCountFractionsOfTotalBBEC.size () *
456
+ opts::PercentileForProfileQualityCheck / 100 );
457
+ llvm::sort (LPCountFractionsOfTotalInvokeEC);
458
+ const int RankInvoke = int (LPCountFractionsOfTotalInvokeEC.size () *
459
+ opts::PercentileForProfileQualityCheck / 100 );
460
+ OS << formatv (" exception handling usage {0:P} (of total BBEC) {1:P} (of "
461
+ " total InvokeEC)\n " ,
462
+ LPCountFractionsOfTotalBBEC[RankBBEC],
463
+ LPCountFractionsOfTotalInvokeEC[RankInvoke]);
464
+ if (opts::Verbosity >= 1 ) {
465
+ OS << " distribution of exception handling usage as a fraction of total "
466
+ " BBEC of each function\n " ;
467
+ printDistribution (OS, LPCountFractionsOfTotalBBEC, /* Fraction=*/ true );
468
+ OS << " distribution of exception handling usage as a fraction of total "
469
+ " InvokeEC of each function\n " ;
470
+ printDistribution (OS, LPCountFractionsOfTotalInvokeEC, /* Fraction=*/ true );
471
+ }
472
+ }
473
+
368
474
void computeFlowMappings (const BinaryContext &BC, FlowInfo &TotalFlowMap) {
369
475
// Increment block inflow and outflow with CFG jump counts.
370
476
TotalFlowMapTy &TotalIncomingFlows = TotalFlowMap.TotalIncomingFlows ;
@@ -519,8 +625,8 @@ void printAll(BinaryContext &BC, FunctionListType &ValidFunctions,
519
625
100 - opts::PercentileForProfileQualityCheck);
520
626
printCFGContinuityStats (BC.outs (), Functions);
521
627
printCallGraphFlowConservationStats (BC.outs (), Functions, TotalFlowMap);
522
- printCFGFlowConservationStats (BC.outs (), Functions, TotalFlowMap);
523
-
628
+ printCFGFlowConservationStats (BC, BC .outs (), Functions, TotalFlowMap);
629
+ printExceptionHandlingStats (BC, BC. outs (), Functions);
524
630
// Print more detailed bucketed stats if requested.
525
631
if (opts::Verbosity >= 1 && RealNumTopFunctions >= 5 ) {
526
632
const size_t PerBucketSize = RealNumTopFunctions / 5 ;
@@ -550,7 +656,8 @@ void printAll(BinaryContext &BC, FunctionListType &ValidFunctions,
550
656
MaxFunctionExecutionCount);
551
657
printCFGContinuityStats (BC.outs (), Functions);
552
658
printCallGraphFlowConservationStats (BC.outs (), Functions, TotalFlowMap);
553
- printCFGFlowConservationStats (BC.outs (), Functions, TotalFlowMap);
659
+ printCFGFlowConservationStats (BC, BC.outs (), Functions, TotalFlowMap);
660
+ printExceptionHandlingStats (BC, BC.outs (), Functions);
554
661
}
555
662
}
556
663
}
0 commit comments