xref: /llvm-project/llvm/lib/Analysis/ImportedFunctionsInliningStatistics.cpp (revision 236fda550d36d35a00785938c3e38b0f402aeda6)
1 //===-- ImportedFunctionsInliningStats.cpp ----------------------*- C++ -*-===//
2 //
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
6 //
7 //===----------------------------------------------------------------------===//
8 // Generating inliner statistics for imported functions, mostly useful for
9 // ThinLTO.
10 //===----------------------------------------------------------------------===//
11 
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"
19 #include <iomanip>
20 #include <sstream>
21 #include <string>
22 
23 using namespace llvm;
24 
25 namespace llvm {
26 cl::opt<InlinerFunctionImportStatsOpts> InlinerFunctionImportStats(
27     "inliner-function-import-stats",
28     cl::init(InlinerFunctionImportStatsOpts::No),
29     cl::values(clEnumValN(InlinerFunctionImportStatsOpts::Basic, "basic",
30                           "basic statistics"),
31                clEnumValN(InlinerFunctionImportStatsOpts::Verbose, "verbose",
32                           "printing of statistics for each inlined function")),
33     cl::Hidden, cl::desc("Enable inliner stats for imported functions"));
34 } // namespace llvm
35 
36 ImportedFunctionsInliningStatistics::InlineGraphNode &
37 ImportedFunctionsInliningStatistics::createInlineGraphNode(const Function &F) {
38 
39   auto &ValueLookup = NodesMap[F.getName()];
40   if (!ValueLookup) {
41     ValueLookup = std::make_unique<InlineGraphNode>();
42     ValueLookup->Imported = F.hasMetadata("thinlto_src_module");
43   }
44   return *ValueLookup;
45 }
46 
47 void ImportedFunctionsInliningStatistics::recordInline(const Function &Caller,
48                                                        const Function &Callee) {
49 
50   InlineGraphNode &CallerNode = createInlineGraphNode(Caller);
51   InlineGraphNode &CalleeNode = createInlineGraphNode(Callee);
52   CalleeNode.NumberOfInlines++;
53 
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++;
60     return;
61   }
62 
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());
71   }
72 }
73 
74 void ImportedFunctionsInliningStatistics::setModuleInfo(const Module &M) {
75   ModuleName = M.getName();
76   for (const auto &F : M.functions()) {
77     if (F.isDeclaration())
78       continue;
79     AllFunctions++;
80     ImportedFunctions += int(F.hasMetadata("thinlto_src_module"));
81   }
82 }
83 static std::string getStatString(const char *Msg, int32_t Fraction, int32_t All,
84                                  const char *PercentageOfMsg,
85                                  bool LineEnd = true) {
86   double Result = 0;
87   if (All != 0)
88     Result = 100 * static_cast<double>(Fraction) / All;
89 
90   std::stringstream Str;
91   Str << std::setprecision(4) << Msg << ": " << Fraction << " [" << Result
92       << "% of " << PercentageOfMsg << "]";
93   if (LineEnd)
94     Str << "\n";
95   return Str.str();
96 }
97 
98 void ImportedFunctionsInliningStatistics::dump(const bool Verbose) {
99   calculateRealInlines();
100   NonImportedCallers.clear();
101 
102   int32_t InlinedImportedFunctionsCount = 0;
103   int32_t InlinedNotImportedFunctionsCount = 0;
104 
105   int32_t InlinedImportedFunctionsToImportingModuleCount = 0;
106   int32_t InlinedNotImportedFunctionsToImportingModuleCount = 0;
107 
108   const auto SortedNodes = getSortedNodes();
109   std::string Out;
110   Out.reserve(5000);
111   raw_string_ostream Ostream(Out);
112 
113   Ostream << "------- Dumping inliner stats for [" << ModuleName
114           << "] -------\n";
115 
116   if (Verbose)
117     Ostream << "-- List of inlined functions:\n";
118 
119   for (const auto &Node : SortedNodes) {
120     assert(Node->second->NumberOfInlines >= Node->second->NumberOfRealInlines);
121     if (Node->second->NumberOfInlines == 0)
122       continue;
123 
124     if (Node->second->Imported) {
125       InlinedImportedFunctionsCount++;
126       InlinedImportedFunctionsToImportingModuleCount +=
127           int(Node->second->NumberOfRealInlines > 0);
128     } else {
129       InlinedNotImportedFunctionsCount++;
130       InlinedNotImportedFunctionsToImportingModuleCount +=
131           int(Node->second->NumberOfRealInlines > 0);
132     }
133 
134     if (Verbose)
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";
141   }
142 
143   auto InlinedFunctionsCount =
144       InlinedImportedFunctionsCount + InlinedNotImportedFunctionsCount;
145   auto NotImportedFuncCount = AllFunctions - ImportedFunctions;
146   auto ImportedNotInlinedIntoModule =
147       ImportedFunctions - InlinedImportedFunctionsToImportingModuleCount;
148 
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",
160                            /*LineEnd=*/false)
161           << getStatString(", remaining", ImportedNotInlinedIntoModule,
162                            ImportedFunctions, "imported functions")
163           << getStatString("non-imported functions inlined anywhere",
164                            InlinedNotImportedFunctionsCount,
165                            NotImportedFuncCount, "non-imported functions")
166           << getStatString(
167                  "non-imported functions inlined into importing module",
168                  InlinedNotImportedFunctionsToImportingModuleCount,
169                  NotImportedFuncCount, "non-imported functions");
170   Ostream.flush();
171   dbgs() << Out;
172 }
173 
174 void ImportedFunctionsInliningStatistics::calculateRealInlines() {
175   // Removing duplicated Callers.
176   llvm::sort(NonImportedCallers);
177   NonImportedCallers.erase(llvm::unique(NonImportedCallers),
178                            NonImportedCallers.end());
179 
180   for (const auto &Name : NonImportedCallers) {
181     auto &Node = *NodesMap[Name];
182     if (!Node.Visited)
183       dfs(Node);
184   }
185 }
186 
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);
194   }
195 }
196 
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);
203 
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();
212   });
213   return SortedNodes;
214 }
215