1 //===-- ImportedFunctionsInliningStats.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 //===----------------------------------------------------------------------===//
8 // Generating inliner statistics for imported functions, mostly useful for
10 //===----------------------------------------------------------------------===//
12 #include "llvm/Analysis/Utils/ImportedFunctionsInliningStatistics.h"
13 #include "llvm/ADT/STLExtras.h"
14 #include "llvm/IR/Function.h"
15 #include "llvm/IR/Module.h"
16 #include "llvm/Support/CommandLine.h"
17 #include "llvm/Support/Debug.h"
18 #include "llvm/Support/raw_ostream.h"
26 cl::opt
<InlinerFunctionImportStatsOpts
> InlinerFunctionImportStats(
27 "inliner-function-import-stats",
28 cl::init(InlinerFunctionImportStatsOpts::No
),
29 cl::values(clEnumValN(InlinerFunctionImportStatsOpts::Basic
, "basic",
31 clEnumValN(InlinerFunctionImportStatsOpts::Verbose
, "verbose",
32 "printing of statistics for each inlined function")),
33 cl::Hidden
, cl::desc("Enable inliner stats for imported functions"));
36 ImportedFunctionsInliningStatistics::InlineGraphNode
&
37 ImportedFunctionsInliningStatistics::createInlineGraphNode(const Function
&F
) {
39 auto &ValueLookup
= NodesMap
[F
.getName()];
41 ValueLookup
= std::make_unique
<InlineGraphNode
>();
42 ValueLookup
->Imported
= F
.hasMetadata("thinlto_src_module");
47 void ImportedFunctionsInliningStatistics::recordInline(const Function
&Caller
,
48 const Function
&Callee
) {
50 InlineGraphNode
&CallerNode
= createInlineGraphNode(Caller
);
51 InlineGraphNode
&CalleeNode
= createInlineGraphNode(Callee
);
52 CalleeNode
.NumberOfInlines
++;
54 if (!CallerNode
.Imported
&& !CalleeNode
.Imported
) {
55 // Direct inline from not imported callee to not imported caller, so we
56 // don't have to add this to graph. It might be very helpful if you wanna
57 // get the inliner statistics in compile step where there are no imported
58 // functions. In this case the graph would be empty.
59 CalleeNode
.NumberOfRealInlines
++;
63 CallerNode
.InlinedCallees
.push_back(&CalleeNode
);
64 if (!CallerNode
.Imported
) {
65 // We could avoid second lookup, but it would make the code ultra ugly.
66 auto It
= NodesMap
.find(Caller
.getName());
67 assert(It
!= NodesMap
.end() && "The node should be already there.");
68 // Save Caller as a starting node for traversal. The string has to be one
69 // from map because Caller can disappear (and function name with it).
70 NonImportedCallers
.push_back(It
->first());
74 void ImportedFunctionsInliningStatistics::setModuleInfo(const Module
&M
) {
75 ModuleName
= M
.getName();
76 for (const auto &F
: M
.functions()) {
77 if (F
.isDeclaration())
80 ImportedFunctions
+= int(F
.hasMetadata("thinlto_src_module"));
83 static std::string
getStatString(const char *Msg
, int32_t Fraction
, int32_t All
,
84 const char *PercentageOfMsg
,
85 bool LineEnd
= true) {
88 Result
= 100 * static_cast<double>(Fraction
) / All
;
90 std::stringstream Str
;
91 Str
<< std::setprecision(4) << Msg
<< ": " << Fraction
<< " [" << Result
92 << "% of " << PercentageOfMsg
<< "]";
98 void ImportedFunctionsInliningStatistics::dump(const bool Verbose
) {
99 calculateRealInlines();
100 NonImportedCallers
.clear();
102 int32_t InlinedImportedFunctionsCount
= 0;
103 int32_t InlinedNotImportedFunctionsCount
= 0;
105 int32_t InlinedImportedFunctionsToImportingModuleCount
= 0;
106 int32_t InlinedNotImportedFunctionsToImportingModuleCount
= 0;
108 const auto SortedNodes
= getSortedNodes();
111 raw_string_ostream
Ostream(Out
);
113 Ostream
<< "------- Dumping inliner stats for [" << ModuleName
117 Ostream
<< "-- List of inlined functions:\n";
119 for (const auto &Node
: SortedNodes
) {
120 assert(Node
->second
->NumberOfInlines
>= Node
->second
->NumberOfRealInlines
);
121 if (Node
->second
->NumberOfInlines
== 0)
124 if (Node
->second
->Imported
) {
125 InlinedImportedFunctionsCount
++;
126 InlinedImportedFunctionsToImportingModuleCount
+=
127 int(Node
->second
->NumberOfRealInlines
> 0);
129 InlinedNotImportedFunctionsCount
++;
130 InlinedNotImportedFunctionsToImportingModuleCount
+=
131 int(Node
->second
->NumberOfRealInlines
> 0);
135 Ostream
<< "Inlined "
136 << (Node
->second
->Imported
? "imported " : "not imported ")
137 << "function [" << Node
->first() << "]"
138 << ": #inlines = " << Node
->second
->NumberOfInlines
139 << ", #inlines_to_importing_module = "
140 << Node
->second
->NumberOfRealInlines
<< "\n";
143 auto InlinedFunctionsCount
=
144 InlinedImportedFunctionsCount
+ InlinedNotImportedFunctionsCount
;
145 auto NotImportedFuncCount
= AllFunctions
- ImportedFunctions
;
146 auto ImportedNotInlinedIntoModule
=
147 ImportedFunctions
- InlinedImportedFunctionsToImportingModuleCount
;
149 Ostream
<< "-- Summary:\n"
150 << "All functions: " << AllFunctions
151 << ", imported functions: " << ImportedFunctions
<< "\n"
152 << getStatString("inlined functions", InlinedFunctionsCount
,
153 AllFunctions
, "all functions")
154 << getStatString("imported functions inlined anywhere",
155 InlinedImportedFunctionsCount
, ImportedFunctions
,
156 "imported functions")
157 << getStatString("imported functions inlined into importing module",
158 InlinedImportedFunctionsToImportingModuleCount
,
159 ImportedFunctions
, "imported functions",
161 << getStatString(", remaining", ImportedNotInlinedIntoModule
,
162 ImportedFunctions
, "imported functions")
163 << getStatString("non-imported functions inlined anywhere",
164 InlinedNotImportedFunctionsCount
,
165 NotImportedFuncCount
, "non-imported functions")
167 "non-imported functions inlined into importing module",
168 InlinedNotImportedFunctionsToImportingModuleCount
,
169 NotImportedFuncCount
, "non-imported functions");
174 void ImportedFunctionsInliningStatistics::calculateRealInlines() {
175 // Removing duplicated Callers.
176 llvm::sort(NonImportedCallers
);
177 NonImportedCallers
.erase(llvm::unique(NonImportedCallers
),
178 NonImportedCallers
.end());
180 for (const auto &Name
: NonImportedCallers
) {
181 auto &Node
= *NodesMap
[Name
];
187 void ImportedFunctionsInliningStatistics::dfs(InlineGraphNode
&GraphNode
) {
188 assert(!GraphNode
.Visited
);
189 GraphNode
.Visited
= true;
190 for (auto *const InlinedFunctionNode
: GraphNode
.InlinedCallees
) {
191 InlinedFunctionNode
->NumberOfRealInlines
++;
192 if (!InlinedFunctionNode
->Visited
)
193 dfs(*InlinedFunctionNode
);
197 ImportedFunctionsInliningStatistics::SortedNodesTy
198 ImportedFunctionsInliningStatistics::getSortedNodes() {
199 SortedNodesTy SortedNodes
;
200 SortedNodes
.reserve(NodesMap
.size());
201 for (const NodesMapTy::value_type
&Node
: NodesMap
)
202 SortedNodes
.push_back(&Node
);
204 llvm::sort(SortedNodes
, [&](const SortedNodesTy::value_type
&Lhs
,
205 const SortedNodesTy::value_type
&Rhs
) {
206 if (Lhs
->second
->NumberOfInlines
!= Rhs
->second
->NumberOfInlines
)
207 return Lhs
->second
->NumberOfInlines
> Rhs
->second
->NumberOfInlines
;
208 if (Lhs
->second
->NumberOfRealInlines
!= Rhs
->second
->NumberOfRealInlines
)
209 return Lhs
->second
->NumberOfRealInlines
>
210 Rhs
->second
->NumberOfRealInlines
;
211 return Lhs
->first() < Rhs
->first();