xref: /llvm-project/llvm/lib/Analysis/ImportedFunctionsInliningStatistics.cpp (revision 236fda550d36d35a00785938c3e38b0f402aeda6)
195ce32c7SMircea Trofin //===-- ImportedFunctionsInliningStats.cpp ----------------------*- C++ -*-===//
295ce32c7SMircea Trofin //
395ce32c7SMircea Trofin // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
495ce32c7SMircea Trofin // See https://llvm.org/LICENSE.txt for license information.
595ce32c7SMircea Trofin // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
695ce32c7SMircea Trofin //
795ce32c7SMircea Trofin //===----------------------------------------------------------------------===//
895ce32c7SMircea Trofin // Generating inliner statistics for imported functions, mostly useful for
995ce32c7SMircea Trofin // ThinLTO.
1095ce32c7SMircea Trofin //===----------------------------------------------------------------------===//
1195ce32c7SMircea Trofin 
1295ce32c7SMircea Trofin #include "llvm/Analysis/Utils/ImportedFunctionsInliningStatistics.h"
1395ce32c7SMircea Trofin #include "llvm/ADT/STLExtras.h"
1495ce32c7SMircea Trofin #include "llvm/IR/Function.h"
1595ce32c7SMircea Trofin #include "llvm/IR/Module.h"
16ccec2cf1SMircea Trofin #include "llvm/Support/CommandLine.h"
1795ce32c7SMircea Trofin #include "llvm/Support/Debug.h"
1895ce32c7SMircea Trofin #include "llvm/Support/raw_ostream.h"
1995ce32c7SMircea Trofin #include <iomanip>
2095ce32c7SMircea Trofin #include <sstream>
21ddcdeae3SSimon Pilgrim #include <string>
22ddcdeae3SSimon Pilgrim 
2395ce32c7SMircea Trofin using namespace llvm;
2495ce32c7SMircea Trofin 
25fa71c164SFangrui Song namespace llvm {
26ccec2cf1SMircea Trofin cl::opt<InlinerFunctionImportStatsOpts> InlinerFunctionImportStats(
27ccec2cf1SMircea Trofin     "inliner-function-import-stats",
28ccec2cf1SMircea Trofin     cl::init(InlinerFunctionImportStatsOpts::No),
29ccec2cf1SMircea Trofin     cl::values(clEnumValN(InlinerFunctionImportStatsOpts::Basic, "basic",
30ccec2cf1SMircea Trofin                           "basic statistics"),
31ccec2cf1SMircea Trofin                clEnumValN(InlinerFunctionImportStatsOpts::Verbose, "verbose",
32ccec2cf1SMircea Trofin                           "printing of statistics for each inlined function")),
33ccec2cf1SMircea Trofin     cl::Hidden, cl::desc("Enable inliner stats for imported functions"));
34*7b57a1b4SMohammed Keyvanzadeh } // namespace llvm
35ccec2cf1SMircea Trofin 
3695ce32c7SMircea Trofin ImportedFunctionsInliningStatistics::InlineGraphNode &
3795ce32c7SMircea Trofin ImportedFunctionsInliningStatistics::createInlineGraphNode(const Function &F) {
3895ce32c7SMircea Trofin 
3995ce32c7SMircea Trofin   auto &ValueLookup = NodesMap[F.getName()];
4095ce32c7SMircea Trofin   if (!ValueLookup) {
4195ce32c7SMircea Trofin     ValueLookup = std::make_unique<InlineGraphNode>();
4295ce32c7SMircea Trofin     ValueLookup->Imported = F.hasMetadata("thinlto_src_module");
4395ce32c7SMircea Trofin   }
4495ce32c7SMircea Trofin   return *ValueLookup;
4595ce32c7SMircea Trofin }
4695ce32c7SMircea Trofin 
4795ce32c7SMircea Trofin void ImportedFunctionsInliningStatistics::recordInline(const Function &Caller,
4895ce32c7SMircea Trofin                                                        const Function &Callee) {
4995ce32c7SMircea Trofin 
5095ce32c7SMircea Trofin   InlineGraphNode &CallerNode = createInlineGraphNode(Caller);
5195ce32c7SMircea Trofin   InlineGraphNode &CalleeNode = createInlineGraphNode(Callee);
5295ce32c7SMircea Trofin   CalleeNode.NumberOfInlines++;
5395ce32c7SMircea Trofin 
5495ce32c7SMircea Trofin   if (!CallerNode.Imported && !CalleeNode.Imported) {
5595ce32c7SMircea Trofin     // Direct inline from not imported callee to not imported caller, so we
5695ce32c7SMircea Trofin     // don't have to add this to graph. It might be very helpful if you wanna
5795ce32c7SMircea Trofin     // get the inliner statistics in compile step where there are no imported
5895ce32c7SMircea Trofin     // functions. In this case the graph would be empty.
5995ce32c7SMircea Trofin     CalleeNode.NumberOfRealInlines++;
6095ce32c7SMircea Trofin     return;
6195ce32c7SMircea Trofin   }
6295ce32c7SMircea Trofin 
6395ce32c7SMircea Trofin   CallerNode.InlinedCallees.push_back(&CalleeNode);
6495ce32c7SMircea Trofin   if (!CallerNode.Imported) {
6595ce32c7SMircea Trofin     // We could avoid second lookup, but it would make the code ultra ugly.
6695ce32c7SMircea Trofin     auto It = NodesMap.find(Caller.getName());
6795ce32c7SMircea Trofin     assert(It != NodesMap.end() && "The node should be already there.");
6895ce32c7SMircea Trofin     // Save Caller as a starting node for traversal. The string has to be one
6995ce32c7SMircea Trofin     // from map because Caller can disappear (and function name with it).
7095ce32c7SMircea Trofin     NonImportedCallers.push_back(It->first());
7195ce32c7SMircea Trofin   }
7295ce32c7SMircea Trofin }
7395ce32c7SMircea Trofin 
7495ce32c7SMircea Trofin void ImportedFunctionsInliningStatistics::setModuleInfo(const Module &M) {
7595ce32c7SMircea Trofin   ModuleName = M.getName();
7695ce32c7SMircea Trofin   for (const auto &F : M.functions()) {
7795ce32c7SMircea Trofin     if (F.isDeclaration())
7895ce32c7SMircea Trofin       continue;
7995ce32c7SMircea Trofin     AllFunctions++;
8095ce32c7SMircea Trofin     ImportedFunctions += int(F.hasMetadata("thinlto_src_module"));
8195ce32c7SMircea Trofin   }
8295ce32c7SMircea Trofin }
8395ce32c7SMircea Trofin static std::string getStatString(const char *Msg, int32_t Fraction, int32_t All,
8495ce32c7SMircea Trofin                                  const char *PercentageOfMsg,
8595ce32c7SMircea Trofin                                  bool LineEnd = true) {
8695ce32c7SMircea Trofin   double Result = 0;
8795ce32c7SMircea Trofin   if (All != 0)
8895ce32c7SMircea Trofin     Result = 100 * static_cast<double>(Fraction) / All;
8995ce32c7SMircea Trofin 
9095ce32c7SMircea Trofin   std::stringstream Str;
9195ce32c7SMircea Trofin   Str << std::setprecision(4) << Msg << ": " << Fraction << " [" << Result
9295ce32c7SMircea Trofin       << "% of " << PercentageOfMsg << "]";
9395ce32c7SMircea Trofin   if (LineEnd)
9495ce32c7SMircea Trofin     Str << "\n";
9595ce32c7SMircea Trofin   return Str.str();
9695ce32c7SMircea Trofin }
9795ce32c7SMircea Trofin 
9895ce32c7SMircea Trofin void ImportedFunctionsInliningStatistics::dump(const bool Verbose) {
9995ce32c7SMircea Trofin   calculateRealInlines();
10095ce32c7SMircea Trofin   NonImportedCallers.clear();
10195ce32c7SMircea Trofin 
10295ce32c7SMircea Trofin   int32_t InlinedImportedFunctionsCount = 0;
10395ce32c7SMircea Trofin   int32_t InlinedNotImportedFunctionsCount = 0;
10495ce32c7SMircea Trofin 
10595ce32c7SMircea Trofin   int32_t InlinedImportedFunctionsToImportingModuleCount = 0;
10695ce32c7SMircea Trofin   int32_t InlinedNotImportedFunctionsToImportingModuleCount = 0;
10795ce32c7SMircea Trofin 
10895ce32c7SMircea Trofin   const auto SortedNodes = getSortedNodes();
10995ce32c7SMircea Trofin   std::string Out;
11095ce32c7SMircea Trofin   Out.reserve(5000);
11195ce32c7SMircea Trofin   raw_string_ostream Ostream(Out);
11295ce32c7SMircea Trofin 
11395ce32c7SMircea Trofin   Ostream << "------- Dumping inliner stats for [" << ModuleName
11495ce32c7SMircea Trofin           << "] -------\n";
11595ce32c7SMircea Trofin 
11695ce32c7SMircea Trofin   if (Verbose)
11795ce32c7SMircea Trofin     Ostream << "-- List of inlined functions:\n";
11895ce32c7SMircea Trofin 
11995ce32c7SMircea Trofin   for (const auto &Node : SortedNodes) {
12095ce32c7SMircea Trofin     assert(Node->second->NumberOfInlines >= Node->second->NumberOfRealInlines);
12195ce32c7SMircea Trofin     if (Node->second->NumberOfInlines == 0)
12295ce32c7SMircea Trofin       continue;
12395ce32c7SMircea Trofin 
12495ce32c7SMircea Trofin     if (Node->second->Imported) {
12595ce32c7SMircea Trofin       InlinedImportedFunctionsCount++;
12695ce32c7SMircea Trofin       InlinedImportedFunctionsToImportingModuleCount +=
12795ce32c7SMircea Trofin           int(Node->second->NumberOfRealInlines > 0);
12895ce32c7SMircea Trofin     } else {
12995ce32c7SMircea Trofin       InlinedNotImportedFunctionsCount++;
13095ce32c7SMircea Trofin       InlinedNotImportedFunctionsToImportingModuleCount +=
13195ce32c7SMircea Trofin           int(Node->second->NumberOfRealInlines > 0);
13295ce32c7SMircea Trofin     }
13395ce32c7SMircea Trofin 
13495ce32c7SMircea Trofin     if (Verbose)
13595ce32c7SMircea Trofin       Ostream << "Inlined "
13695ce32c7SMircea Trofin               << (Node->second->Imported ? "imported " : "not imported ")
13795ce32c7SMircea Trofin               << "function [" << Node->first() << "]"
13895ce32c7SMircea Trofin               << ": #inlines = " << Node->second->NumberOfInlines
13995ce32c7SMircea Trofin               << ", #inlines_to_importing_module = "
14095ce32c7SMircea Trofin               << Node->second->NumberOfRealInlines << "\n";
14195ce32c7SMircea Trofin   }
14295ce32c7SMircea Trofin 
14395ce32c7SMircea Trofin   auto InlinedFunctionsCount =
14495ce32c7SMircea Trofin       InlinedImportedFunctionsCount + InlinedNotImportedFunctionsCount;
14595ce32c7SMircea Trofin   auto NotImportedFuncCount = AllFunctions - ImportedFunctions;
14695ce32c7SMircea Trofin   auto ImportedNotInlinedIntoModule =
14795ce32c7SMircea Trofin       ImportedFunctions - InlinedImportedFunctionsToImportingModuleCount;
14895ce32c7SMircea Trofin 
14995ce32c7SMircea Trofin   Ostream << "-- Summary:\n"
15095ce32c7SMircea Trofin           << "All functions: " << AllFunctions
15195ce32c7SMircea Trofin           << ", imported functions: " << ImportedFunctions << "\n"
15295ce32c7SMircea Trofin           << getStatString("inlined functions", InlinedFunctionsCount,
15395ce32c7SMircea Trofin                            AllFunctions, "all functions")
15495ce32c7SMircea Trofin           << getStatString("imported functions inlined anywhere",
15595ce32c7SMircea Trofin                            InlinedImportedFunctionsCount, ImportedFunctions,
15695ce32c7SMircea Trofin                            "imported functions")
15795ce32c7SMircea Trofin           << getStatString("imported functions inlined into importing module",
15895ce32c7SMircea Trofin                            InlinedImportedFunctionsToImportingModuleCount,
15995ce32c7SMircea Trofin                            ImportedFunctions, "imported functions",
16095ce32c7SMircea Trofin                            /*LineEnd=*/false)
16195ce32c7SMircea Trofin           << getStatString(", remaining", ImportedNotInlinedIntoModule,
16295ce32c7SMircea Trofin                            ImportedFunctions, "imported functions")
16395ce32c7SMircea Trofin           << getStatString("non-imported functions inlined anywhere",
16495ce32c7SMircea Trofin                            InlinedNotImportedFunctionsCount,
16595ce32c7SMircea Trofin                            NotImportedFuncCount, "non-imported functions")
16695ce32c7SMircea Trofin           << getStatString(
16795ce32c7SMircea Trofin                  "non-imported functions inlined into importing module",
16895ce32c7SMircea Trofin                  InlinedNotImportedFunctionsToImportingModuleCount,
16995ce32c7SMircea Trofin                  NotImportedFuncCount, "non-imported functions");
17095ce32c7SMircea Trofin   Ostream.flush();
17195ce32c7SMircea Trofin   dbgs() << Out;
17295ce32c7SMircea Trofin }
17395ce32c7SMircea Trofin 
17495ce32c7SMircea Trofin void ImportedFunctionsInliningStatistics::calculateRealInlines() {
17595ce32c7SMircea Trofin   // Removing duplicated Callers.
17695ce32c7SMircea Trofin   llvm::sort(NonImportedCallers);
1777c6d0d26SKazu Hirata   NonImportedCallers.erase(llvm::unique(NonImportedCallers),
17895ce32c7SMircea Trofin                            NonImportedCallers.end());
17995ce32c7SMircea Trofin 
18095ce32c7SMircea Trofin   for (const auto &Name : NonImportedCallers) {
18195ce32c7SMircea Trofin     auto &Node = *NodesMap[Name];
18295ce32c7SMircea Trofin     if (!Node.Visited)
18395ce32c7SMircea Trofin       dfs(Node);
18495ce32c7SMircea Trofin   }
18595ce32c7SMircea Trofin }
18695ce32c7SMircea Trofin 
18795ce32c7SMircea Trofin void ImportedFunctionsInliningStatistics::dfs(InlineGraphNode &GraphNode) {
18895ce32c7SMircea Trofin   assert(!GraphNode.Visited);
18995ce32c7SMircea Trofin   GraphNode.Visited = true;
19095ce32c7SMircea Trofin   for (auto *const InlinedFunctionNode : GraphNode.InlinedCallees) {
19195ce32c7SMircea Trofin     InlinedFunctionNode->NumberOfRealInlines++;
19295ce32c7SMircea Trofin     if (!InlinedFunctionNode->Visited)
19395ce32c7SMircea Trofin       dfs(*InlinedFunctionNode);
19495ce32c7SMircea Trofin   }
19595ce32c7SMircea Trofin }
19695ce32c7SMircea Trofin 
19795ce32c7SMircea Trofin ImportedFunctionsInliningStatistics::SortedNodesTy
19895ce32c7SMircea Trofin ImportedFunctionsInliningStatistics::getSortedNodes() {
19995ce32c7SMircea Trofin   SortedNodesTy SortedNodes;
20095ce32c7SMircea Trofin   SortedNodes.reserve(NodesMap.size());
20195ce32c7SMircea Trofin   for (const NodesMapTy::value_type &Node : NodesMap)
20295ce32c7SMircea Trofin     SortedNodes.push_back(&Node);
20395ce32c7SMircea Trofin 
20495ce32c7SMircea Trofin   llvm::sort(SortedNodes, [&](const SortedNodesTy::value_type &Lhs,
20595ce32c7SMircea Trofin                               const SortedNodesTy::value_type &Rhs) {
20695ce32c7SMircea Trofin     if (Lhs->second->NumberOfInlines != Rhs->second->NumberOfInlines)
20795ce32c7SMircea Trofin       return Lhs->second->NumberOfInlines > Rhs->second->NumberOfInlines;
20895ce32c7SMircea Trofin     if (Lhs->second->NumberOfRealInlines != Rhs->second->NumberOfRealInlines)
20995ce32c7SMircea Trofin       return Lhs->second->NumberOfRealInlines >
21095ce32c7SMircea Trofin              Rhs->second->NumberOfRealInlines;
21195ce32c7SMircea Trofin     return Lhs->first() < Rhs->first();
21295ce32c7SMircea Trofin   });
21395ce32c7SMircea Trofin   return SortedNodes;
21495ce32c7SMircea Trofin }
215