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 <algorithm>
2095ce32c7SMircea Trofin #include <iomanip>
2195ce32c7SMircea Trofin #include <sstream>
22*ddcdeae3SSimon Pilgrim #include <string>
23*ddcdeae3SSimon Pilgrim
2495ce32c7SMircea Trofin using namespace llvm;
2595ce32c7SMircea Trofin
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"));
34ccec2cf1SMircea Trofin
3595ce32c7SMircea Trofin ImportedFunctionsInliningStatistics::InlineGraphNode &
createInlineGraphNode(const Function & F)3695ce32c7SMircea Trofin ImportedFunctionsInliningStatistics::createInlineGraphNode(const Function &F) {
3795ce32c7SMircea Trofin
3895ce32c7SMircea Trofin auto &ValueLookup = NodesMap[F.getName()];
3995ce32c7SMircea Trofin if (!ValueLookup) {
4095ce32c7SMircea Trofin ValueLookup = std::make_unique<InlineGraphNode>();
4195ce32c7SMircea Trofin ValueLookup->Imported = F.hasMetadata("thinlto_src_module");
4295ce32c7SMircea Trofin }
4395ce32c7SMircea Trofin return *ValueLookup;
4495ce32c7SMircea Trofin }
4595ce32c7SMircea Trofin
recordInline(const Function & Caller,const Function & Callee)4695ce32c7SMircea Trofin void ImportedFunctionsInliningStatistics::recordInline(const Function &Caller,
4795ce32c7SMircea Trofin const Function &Callee) {
4895ce32c7SMircea Trofin
4995ce32c7SMircea Trofin InlineGraphNode &CallerNode = createInlineGraphNode(Caller);
5095ce32c7SMircea Trofin InlineGraphNode &CalleeNode = createInlineGraphNode(Callee);
5195ce32c7SMircea Trofin CalleeNode.NumberOfInlines++;
5295ce32c7SMircea Trofin
5395ce32c7SMircea Trofin if (!CallerNode.Imported && !CalleeNode.Imported) {
5495ce32c7SMircea Trofin // Direct inline from not imported callee to not imported caller, so we
5595ce32c7SMircea Trofin // don't have to add this to graph. It might be very helpful if you wanna
5695ce32c7SMircea Trofin // get the inliner statistics in compile step where there are no imported
5795ce32c7SMircea Trofin // functions. In this case the graph would be empty.
5895ce32c7SMircea Trofin CalleeNode.NumberOfRealInlines++;
5995ce32c7SMircea Trofin return;
6095ce32c7SMircea Trofin }
6195ce32c7SMircea Trofin
6295ce32c7SMircea Trofin CallerNode.InlinedCallees.push_back(&CalleeNode);
6395ce32c7SMircea Trofin if (!CallerNode.Imported) {
6495ce32c7SMircea Trofin // We could avoid second lookup, but it would make the code ultra ugly.
6595ce32c7SMircea Trofin auto It = NodesMap.find(Caller.getName());
6695ce32c7SMircea Trofin assert(It != NodesMap.end() && "The node should be already there.");
6795ce32c7SMircea Trofin // Save Caller as a starting node for traversal. The string has to be one
6895ce32c7SMircea Trofin // from map because Caller can disappear (and function name with it).
6995ce32c7SMircea Trofin NonImportedCallers.push_back(It->first());
7095ce32c7SMircea Trofin }
7195ce32c7SMircea Trofin }
7295ce32c7SMircea Trofin
setModuleInfo(const Module & M)7395ce32c7SMircea Trofin void ImportedFunctionsInliningStatistics::setModuleInfo(const Module &M) {
7495ce32c7SMircea Trofin ModuleName = M.getName();
7595ce32c7SMircea Trofin for (const auto &F : M.functions()) {
7695ce32c7SMircea Trofin if (F.isDeclaration())
7795ce32c7SMircea Trofin continue;
7895ce32c7SMircea Trofin AllFunctions++;
7995ce32c7SMircea Trofin ImportedFunctions += int(F.hasMetadata("thinlto_src_module"));
8095ce32c7SMircea Trofin }
8195ce32c7SMircea Trofin }
getStatString(const char * Msg,int32_t Fraction,int32_t All,const char * PercentageOfMsg,bool LineEnd=true)8295ce32c7SMircea Trofin static std::string getStatString(const char *Msg, int32_t Fraction, int32_t All,
8395ce32c7SMircea Trofin const char *PercentageOfMsg,
8495ce32c7SMircea Trofin bool LineEnd = true) {
8595ce32c7SMircea Trofin double Result = 0;
8695ce32c7SMircea Trofin if (All != 0)
8795ce32c7SMircea Trofin Result = 100 * static_cast<double>(Fraction) / All;
8895ce32c7SMircea Trofin
8995ce32c7SMircea Trofin std::stringstream Str;
9095ce32c7SMircea Trofin Str << std::setprecision(4) << Msg << ": " << Fraction << " [" << Result
9195ce32c7SMircea Trofin << "% of " << PercentageOfMsg << "]";
9295ce32c7SMircea Trofin if (LineEnd)
9395ce32c7SMircea Trofin Str << "\n";
9495ce32c7SMircea Trofin return Str.str();
9595ce32c7SMircea Trofin }
9695ce32c7SMircea Trofin
dump(const bool Verbose)9795ce32c7SMircea Trofin void ImportedFunctionsInliningStatistics::dump(const bool Verbose) {
9895ce32c7SMircea Trofin calculateRealInlines();
9995ce32c7SMircea Trofin NonImportedCallers.clear();
10095ce32c7SMircea Trofin
10195ce32c7SMircea Trofin int32_t InlinedImportedFunctionsCount = 0;
10295ce32c7SMircea Trofin int32_t InlinedNotImportedFunctionsCount = 0;
10395ce32c7SMircea Trofin
10495ce32c7SMircea Trofin int32_t InlinedImportedFunctionsToImportingModuleCount = 0;
10595ce32c7SMircea Trofin int32_t InlinedNotImportedFunctionsToImportingModuleCount = 0;
10695ce32c7SMircea Trofin
10795ce32c7SMircea Trofin const auto SortedNodes = getSortedNodes();
10895ce32c7SMircea Trofin std::string Out;
10995ce32c7SMircea Trofin Out.reserve(5000);
11095ce32c7SMircea Trofin raw_string_ostream Ostream(Out);
11195ce32c7SMircea Trofin
11295ce32c7SMircea Trofin Ostream << "------- Dumping inliner stats for [" << ModuleName
11395ce32c7SMircea Trofin << "] -------\n";
11495ce32c7SMircea Trofin
11595ce32c7SMircea Trofin if (Verbose)
11695ce32c7SMircea Trofin Ostream << "-- List of inlined functions:\n";
11795ce32c7SMircea Trofin
11895ce32c7SMircea Trofin for (const auto &Node : SortedNodes) {
11995ce32c7SMircea Trofin assert(Node->second->NumberOfInlines >= Node->second->NumberOfRealInlines);
12095ce32c7SMircea Trofin if (Node->second->NumberOfInlines == 0)
12195ce32c7SMircea Trofin continue;
12295ce32c7SMircea Trofin
12395ce32c7SMircea Trofin if (Node->second->Imported) {
12495ce32c7SMircea Trofin InlinedImportedFunctionsCount++;
12595ce32c7SMircea Trofin InlinedImportedFunctionsToImportingModuleCount +=
12695ce32c7SMircea Trofin int(Node->second->NumberOfRealInlines > 0);
12795ce32c7SMircea Trofin } else {
12895ce32c7SMircea Trofin InlinedNotImportedFunctionsCount++;
12995ce32c7SMircea Trofin InlinedNotImportedFunctionsToImportingModuleCount +=
13095ce32c7SMircea Trofin int(Node->second->NumberOfRealInlines > 0);
13195ce32c7SMircea Trofin }
13295ce32c7SMircea Trofin
13395ce32c7SMircea Trofin if (Verbose)
13495ce32c7SMircea Trofin Ostream << "Inlined "
13595ce32c7SMircea Trofin << (Node->second->Imported ? "imported " : "not imported ")
13695ce32c7SMircea Trofin << "function [" << Node->first() << "]"
13795ce32c7SMircea Trofin << ": #inlines = " << Node->second->NumberOfInlines
13895ce32c7SMircea Trofin << ", #inlines_to_importing_module = "
13995ce32c7SMircea Trofin << Node->second->NumberOfRealInlines << "\n";
14095ce32c7SMircea Trofin }
14195ce32c7SMircea Trofin
14295ce32c7SMircea Trofin auto InlinedFunctionsCount =
14395ce32c7SMircea Trofin InlinedImportedFunctionsCount + InlinedNotImportedFunctionsCount;
14495ce32c7SMircea Trofin auto NotImportedFuncCount = AllFunctions - ImportedFunctions;
14595ce32c7SMircea Trofin auto ImportedNotInlinedIntoModule =
14695ce32c7SMircea Trofin ImportedFunctions - InlinedImportedFunctionsToImportingModuleCount;
14795ce32c7SMircea Trofin
14895ce32c7SMircea Trofin Ostream << "-- Summary:\n"
14995ce32c7SMircea Trofin << "All functions: " << AllFunctions
15095ce32c7SMircea Trofin << ", imported functions: " << ImportedFunctions << "\n"
15195ce32c7SMircea Trofin << getStatString("inlined functions", InlinedFunctionsCount,
15295ce32c7SMircea Trofin AllFunctions, "all functions")
15395ce32c7SMircea Trofin << getStatString("imported functions inlined anywhere",
15495ce32c7SMircea Trofin InlinedImportedFunctionsCount, ImportedFunctions,
15595ce32c7SMircea Trofin "imported functions")
15695ce32c7SMircea Trofin << getStatString("imported functions inlined into importing module",
15795ce32c7SMircea Trofin InlinedImportedFunctionsToImportingModuleCount,
15895ce32c7SMircea Trofin ImportedFunctions, "imported functions",
15995ce32c7SMircea Trofin /*LineEnd=*/false)
16095ce32c7SMircea Trofin << getStatString(", remaining", ImportedNotInlinedIntoModule,
16195ce32c7SMircea Trofin ImportedFunctions, "imported functions")
16295ce32c7SMircea Trofin << getStatString("non-imported functions inlined anywhere",
16395ce32c7SMircea Trofin InlinedNotImportedFunctionsCount,
16495ce32c7SMircea Trofin NotImportedFuncCount, "non-imported functions")
16595ce32c7SMircea Trofin << getStatString(
16695ce32c7SMircea Trofin "non-imported functions inlined into importing module",
16795ce32c7SMircea Trofin InlinedNotImportedFunctionsToImportingModuleCount,
16895ce32c7SMircea Trofin NotImportedFuncCount, "non-imported functions");
16995ce32c7SMircea Trofin Ostream.flush();
17095ce32c7SMircea Trofin dbgs() << Out;
17195ce32c7SMircea Trofin }
17295ce32c7SMircea Trofin
calculateRealInlines()17395ce32c7SMircea Trofin void ImportedFunctionsInliningStatistics::calculateRealInlines() {
17495ce32c7SMircea Trofin // Removing duplicated Callers.
17595ce32c7SMircea Trofin llvm::sort(NonImportedCallers);
17695ce32c7SMircea Trofin NonImportedCallers.erase(
17795ce32c7SMircea Trofin std::unique(NonImportedCallers.begin(), NonImportedCallers.end()),
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
dfs(InlineGraphNode & GraphNode)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
getSortedNodes()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