215 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8// Generating inliner statistics for imported functions, mostly useful for9// 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 23using namespace llvm;24 25namespace llvm {26cl::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 llvm35 36ImportedFunctionsInliningStatistics::InlineGraphNode &37ImportedFunctionsInliningStatistics::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 47void 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 we56 // don't have to add this to graph. It might be very helpful if you wanna57 // get the inliner statistics in compile step where there are no imported58 // 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 one69 // from map because Caller can disappear (and function name with it).70 NonImportedCallers.push_back(It->first());71 }72}73 74void 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}83static 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 << " [" << Result92 << "% of " << PercentageOfMsg << "]";93 if (LineEnd)94 Str << "\n";95 return Str.str();96}97 98void 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 [" << ModuleName114 << "] -------\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->NumberOfInlines139 << ", #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: " << AllFunctions151 << ", 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 174void 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 187void 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 197ImportedFunctionsInliningStatistics::SortedNodesTy198ImportedFunctionsInliningStatistics::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