1 //===- bolt/Passes/ContinuityStats.cpp --------------------------*- C++ -*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // This file implements the continuity stats calculation pass.
11 //===----------------------------------------------------------------------===//
13 #include "bolt/Passes/ContinuityStats.h"
14 #include "bolt/Core/BinaryBasicBlock.h"
15 #include "bolt/Core/BinaryFunction.h"
16 #include "bolt/Utils/CommandLineOpts.h"
17 #include "llvm/Support/CommandLine.h"
19 #include <unordered_map>
20 #include <unordered_set>
22 #define DEBUG_TYPE "bolt-opts"
28 extern cl::opt
<unsigned> Verbosity
;
29 cl::opt
<unsigned> NumFunctionsForContinuityCheck(
30 "num-functions-for-continuity-check",
31 cl::desc("number of hottest functions to print aggregated "
32 "CFG discontinuity stats of."),
33 cl::init(1000), cl::ZeroOrMore
, cl::Hidden
, cl::cat(BoltOptCategory
));
37 using FunctionListType
= std::vector
<const BinaryFunction
*>;
38 using function_iterator
= FunctionListType::iterator
;
41 void printDistribution(raw_ostream
&OS
, std::vector
<T
> &values
,
42 bool Fraction
= false) {
45 // Sort values from largest to smallest and print the MAX, TOP 1%, 5%, 10%,
46 // 20%, 50%, 80%, MIN. If Fraction is true, then values are printed as
47 // fractions instead of integers.
48 std::sort(values
.begin(), values
.end());
50 auto printLine
= [&](std::string Text
, double Percent
) {
51 int Rank
= int(values
.size() * (1.0 - Percent
/ 100));
53 Rank
= values
.size() - 1;
55 OS
<< " " << Text
<< std::string(9 - Text
.length(), ' ') << ": "
56 << format("%.2lf%%", values
[Rank
] * 100) << "\n";
58 OS
<< " " << Text
<< std::string(9 - Text
.length(), ' ') << ": "
59 << values
[Rank
] << "\n";
63 const int percentages
[] = {1, 5, 10, 20, 50, 80};
64 for (size_t i
= 0; i
< sizeof(percentages
) / sizeof(percentages
[0]); ++i
) {
65 printLine("TOP " + std::to_string(percentages
[i
]) + "%", percentages
[i
]);
67 printLine("MIN", 100);
70 void printCFGContinuityStats(raw_ostream
&OS
,
71 iterator_range
<function_iterator
> &Functions
) {
72 // Given a perfect profile, every positive-execution-count BB should be
73 // connected to an entry of the function through a positive-execution-count
74 // directed path in the control flow graph.
75 std::vector
<size_t> NumUnreachables
;
76 std::vector
<size_t> SumECUnreachables
;
77 std::vector
<double> FractionECUnreachables
;
79 for (auto it
= Functions
.begin(); it
!= Functions
.end(); ++it
) {
80 const BinaryFunction
*Function
= *it
;
81 if (Function
->size() <= 1)
84 // Compute the sum of all BB execution counts (ECs).
85 size_t NumPosECBBs
= 0;
86 size_t SumAllBBEC
= 0;
87 for (const BinaryBasicBlock
&BB
: *Function
) {
88 const size_t BBEC
= BB
.getKnownExecutionCount();
89 NumPosECBBs
+= BBEC
> 0 ? 1 : 0;
93 // Perform BFS on subgraph of CFG induced by positive weight edges.
94 // Compute the number of BBs reachable from the entry(s) of the function and
95 // the sum of their execution counts (ECs).
96 std::unordered_map
<unsigned, const BinaryBasicBlock
*> IndexToBB
;
97 std::unordered_set
<unsigned> Visited
;
98 std::queue
<unsigned> Queue
;
99 for (const BinaryBasicBlock
&BB
: *Function
) {
100 // Make sure BB.getIndex() is not already in IndexToBB.
101 assert(IndexToBB
.find(BB
.getIndex()) == IndexToBB
.end());
102 IndexToBB
[BB
.getIndex()] = &BB
;
103 if (BB
.isEntryPoint() && BB
.getKnownExecutionCount() > 0) {
104 Queue
.push(BB
.getIndex());
105 Visited
.insert(BB
.getIndex());
108 while (!Queue
.empty()) {
109 const unsigned BBIndex
= Queue
.front();
110 const BinaryBasicBlock
*BB
= IndexToBB
[BBIndex
];
112 auto SuccBIIter
= BB
->branch_info_begin();
113 for (const BinaryBasicBlock
*Succ
: BB
->successors()) {
114 const uint64_t Count
= SuccBIIter
->Count
;
115 if (Count
== BinaryBasicBlock::COUNT_NO_PROFILE
|| Count
== 0) {
119 if (!Visited
.insert(Succ
->getIndex()).second
) {
123 Queue
.push(Succ
->getIndex());
128 const size_t NumReachableBBs
= Visited
.size();
130 // Loop through Visited, and sum the corresponding BBs' execution counts
132 size_t SumReachableBBEC
= 0;
133 for (const unsigned BBIndex
: Visited
) {
134 const BinaryBasicBlock
*BB
= IndexToBB
[BBIndex
];
135 SumReachableBBEC
+= BB
->getKnownExecutionCount();
138 const size_t NumPosECBBsUnreachableFromEntry
=
139 NumPosECBBs
- NumReachableBBs
;
140 const size_t SumUnreachableBBEC
= SumAllBBEC
- SumReachableBBEC
;
141 const double FractionECUnreachable
=
142 (double)SumUnreachableBBEC
/ SumAllBBEC
;
144 if (opts::Verbosity
>= 2 && FractionECUnreachable
>= 0.05) {
145 OS
<< "Non-trivial CFG discontinuity observed in function "
146 << Function
->getPrintName() << "\n";
147 LLVM_DEBUG(Function
->dump());
150 NumUnreachables
.push_back(NumPosECBBsUnreachableFromEntry
);
151 SumECUnreachables
.push_back(SumUnreachableBBEC
);
152 FractionECUnreachables
.push_back(FractionECUnreachable
);
155 if (FractionECUnreachables
.empty())
158 std::sort(FractionECUnreachables
.begin(), FractionECUnreachables
.end());
159 const int Rank
= int(FractionECUnreachables
.size() * 0.95);
160 OS
<< format("top 5%% function CFG discontinuity is %.2lf%%\n",
161 FractionECUnreachables
[Rank
] * 100);
163 if (opts::Verbosity
>= 1) {
164 OS
<< "abbreviations: EC = execution count, POS BBs = positive EC BBs\n"
165 << "distribution of NUM(unreachable POS BBs) among all focal "
167 printDistribution(OS
, NumUnreachables
);
169 OS
<< "distribution of SUM_EC(unreachable POS BBs) among all focal "
171 printDistribution(OS
, SumECUnreachables
);
173 OS
<< "distribution of [(SUM_EC(unreachable POS BBs) / SUM_EC(all "
174 "POS BBs))] among all focal functions\n";
175 printDistribution(OS
, FractionECUnreachables
, /*Fraction=*/true);
179 void printAll(BinaryContext
&BC
, FunctionListType
&ValidFunctions
,
180 size_t NumTopFunctions
) {
181 // Sort the list of functions by execution counts (reverse).
182 llvm::sort(ValidFunctions
,
183 [&](const BinaryFunction
*A
, const BinaryFunction
*B
) {
184 return A
->getKnownExecutionCount() > B
->getKnownExecutionCount();
187 const size_t RealNumTopFunctions
=
188 std::min(NumTopFunctions
, ValidFunctions
.size());
190 iterator_range
<function_iterator
> Functions(
191 ValidFunctions
.begin(), ValidFunctions
.begin() + RealNumTopFunctions
);
193 BC
.outs() << format("BOLT-INFO: among the hottest %zu functions ",
194 RealNumTopFunctions
);
195 printCFGContinuityStats(BC
.outs(), Functions
);
197 // Print more detailed bucketed stats if requested.
198 if (opts::Verbosity
>= 1 && RealNumTopFunctions
>= 5) {
199 const size_t PerBucketSize
= RealNumTopFunctions
/ 5;
201 "Detailed stats for 5 buckets, each with %zu functions:\n",
204 // For each bucket, print the CFG continuity stats of the functions in the
206 for (size_t BucketIndex
= 0; BucketIndex
< 5; ++BucketIndex
) {
207 const size_t StartIndex
= BucketIndex
* PerBucketSize
;
208 const size_t EndIndex
= StartIndex
+ PerBucketSize
;
209 iterator_range
<function_iterator
> Functions(
210 ValidFunctions
.begin() + StartIndex
,
211 ValidFunctions
.begin() + EndIndex
);
212 const size_t MaxFunctionExecutionCount
=
213 ValidFunctions
[StartIndex
]->getKnownExecutionCount();
214 const size_t MinFunctionExecutionCount
=
215 ValidFunctions
[EndIndex
- 1]->getKnownExecutionCount();
216 BC
.outs() << format("----------------\n| Bucket %zu: "
217 "|\n----------------\n",
220 "execution counts of the %zu functions in the bucket: "
222 EndIndex
- StartIndex
, MinFunctionExecutionCount
,
223 MaxFunctionExecutionCount
);
224 printCFGContinuityStats(BC
.outs(), Functions
);
230 bool PrintContinuityStats::shouldOptimize(const BinaryFunction
&BF
) const {
231 if (BF
.empty() || !BF
.hasValidProfile())
234 return BinaryFunctionPass::shouldOptimize(BF
);
237 Error
PrintContinuityStats::runOnFunctions(BinaryContext
&BC
) {
238 // Create a list of functions with valid profiles.
239 FunctionListType ValidFunctions
;
240 for (const auto &BFI
: BC
.getBinaryFunctions()) {
241 const BinaryFunction
*Function
= &BFI
.second
;
242 if (PrintContinuityStats::shouldOptimize(*Function
))
243 ValidFunctions
.push_back(Function
);
245 if (ValidFunctions
.empty() || opts::NumFunctionsForContinuityCheck
== 0)
246 return Error::success();
248 printAll(BC
, ValidFunctions
, opts::NumFunctionsForContinuityCheck
);
249 return Error::success();