1e8d8bef9SDimitry Andric //===-- ImportedFunctionsInliningStats.cpp ----------------------*- C++ -*-===// 2e8d8bef9SDimitry Andric // 3e8d8bef9SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4e8d8bef9SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5e8d8bef9SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6e8d8bef9SDimitry Andric // 7e8d8bef9SDimitry Andric //===----------------------------------------------------------------------===// 8e8d8bef9SDimitry Andric // Generating inliner statistics for imported functions, mostly useful for 9e8d8bef9SDimitry Andric // ThinLTO. 10e8d8bef9SDimitry Andric //===----------------------------------------------------------------------===// 11e8d8bef9SDimitry Andric 12e8d8bef9SDimitry Andric #include "llvm/Analysis/Utils/ImportedFunctionsInliningStatistics.h" 13e8d8bef9SDimitry Andric #include "llvm/ADT/STLExtras.h" 14e8d8bef9SDimitry Andric #include "llvm/IR/Function.h" 15e8d8bef9SDimitry Andric #include "llvm/IR/Module.h" 16e8d8bef9SDimitry Andric #include "llvm/Support/CommandLine.h" 17e8d8bef9SDimitry Andric #include "llvm/Support/Debug.h" 18e8d8bef9SDimitry Andric #include "llvm/Support/raw_ostream.h" 19e8d8bef9SDimitry Andric #include <algorithm> 20e8d8bef9SDimitry Andric #include <iomanip> 21e8d8bef9SDimitry Andric #include <sstream> 22fe6060f1SDimitry Andric #include <string> 23fe6060f1SDimitry Andric 24e8d8bef9SDimitry Andric using namespace llvm; 25e8d8bef9SDimitry Andric 26bdd1243dSDimitry Andric namespace llvm { 27e8d8bef9SDimitry Andric cl::opt<InlinerFunctionImportStatsOpts> InlinerFunctionImportStats( 28e8d8bef9SDimitry Andric "inliner-function-import-stats", 29e8d8bef9SDimitry Andric cl::init(InlinerFunctionImportStatsOpts::No), 30e8d8bef9SDimitry Andric cl::values(clEnumValN(InlinerFunctionImportStatsOpts::Basic, "basic", 31e8d8bef9SDimitry Andric "basic statistics"), 32e8d8bef9SDimitry Andric clEnumValN(InlinerFunctionImportStatsOpts::Verbose, "verbose", 33e8d8bef9SDimitry Andric "printing of statistics for each inlined function")), 34e8d8bef9SDimitry Andric cl::Hidden, cl::desc("Enable inliner stats for imported functions")); 35*0fca6ea1SDimitry Andric } // namespace llvm 36e8d8bef9SDimitry Andric 37e8d8bef9SDimitry Andric ImportedFunctionsInliningStatistics::InlineGraphNode & 38e8d8bef9SDimitry Andric ImportedFunctionsInliningStatistics::createInlineGraphNode(const Function &F) { 39e8d8bef9SDimitry Andric 40e8d8bef9SDimitry Andric auto &ValueLookup = NodesMap[F.getName()]; 41e8d8bef9SDimitry Andric if (!ValueLookup) { 42e8d8bef9SDimitry Andric ValueLookup = std::make_unique<InlineGraphNode>(); 43e8d8bef9SDimitry Andric ValueLookup->Imported = F.hasMetadata("thinlto_src_module"); 44e8d8bef9SDimitry Andric } 45e8d8bef9SDimitry Andric return *ValueLookup; 46e8d8bef9SDimitry Andric } 47e8d8bef9SDimitry Andric 48e8d8bef9SDimitry Andric void ImportedFunctionsInliningStatistics::recordInline(const Function &Caller, 49e8d8bef9SDimitry Andric const Function &Callee) { 50e8d8bef9SDimitry Andric 51e8d8bef9SDimitry Andric InlineGraphNode &CallerNode = createInlineGraphNode(Caller); 52e8d8bef9SDimitry Andric InlineGraphNode &CalleeNode = createInlineGraphNode(Callee); 53e8d8bef9SDimitry Andric CalleeNode.NumberOfInlines++; 54e8d8bef9SDimitry Andric 55e8d8bef9SDimitry Andric if (!CallerNode.Imported && !CalleeNode.Imported) { 56e8d8bef9SDimitry Andric // Direct inline from not imported callee to not imported caller, so we 57e8d8bef9SDimitry Andric // don't have to add this to graph. It might be very helpful if you wanna 58e8d8bef9SDimitry Andric // get the inliner statistics in compile step where there are no imported 59e8d8bef9SDimitry Andric // functions. In this case the graph would be empty. 60e8d8bef9SDimitry Andric CalleeNode.NumberOfRealInlines++; 61e8d8bef9SDimitry Andric return; 62e8d8bef9SDimitry Andric } 63e8d8bef9SDimitry Andric 64e8d8bef9SDimitry Andric CallerNode.InlinedCallees.push_back(&CalleeNode); 65e8d8bef9SDimitry Andric if (!CallerNode.Imported) { 66e8d8bef9SDimitry Andric // We could avoid second lookup, but it would make the code ultra ugly. 67e8d8bef9SDimitry Andric auto It = NodesMap.find(Caller.getName()); 68e8d8bef9SDimitry Andric assert(It != NodesMap.end() && "The node should be already there."); 69e8d8bef9SDimitry Andric // Save Caller as a starting node for traversal. The string has to be one 70e8d8bef9SDimitry Andric // from map because Caller can disappear (and function name with it). 71e8d8bef9SDimitry Andric NonImportedCallers.push_back(It->first()); 72e8d8bef9SDimitry Andric } 73e8d8bef9SDimitry Andric } 74e8d8bef9SDimitry Andric 75e8d8bef9SDimitry Andric void ImportedFunctionsInliningStatistics::setModuleInfo(const Module &M) { 76e8d8bef9SDimitry Andric ModuleName = M.getName(); 77e8d8bef9SDimitry Andric for (const auto &F : M.functions()) { 78e8d8bef9SDimitry Andric if (F.isDeclaration()) 79e8d8bef9SDimitry Andric continue; 80e8d8bef9SDimitry Andric AllFunctions++; 81e8d8bef9SDimitry Andric ImportedFunctions += int(F.hasMetadata("thinlto_src_module")); 82e8d8bef9SDimitry Andric } 83e8d8bef9SDimitry Andric } 84e8d8bef9SDimitry Andric static std::string getStatString(const char *Msg, int32_t Fraction, int32_t All, 85e8d8bef9SDimitry Andric const char *PercentageOfMsg, 86e8d8bef9SDimitry Andric bool LineEnd = true) { 87e8d8bef9SDimitry Andric double Result = 0; 88e8d8bef9SDimitry Andric if (All != 0) 89e8d8bef9SDimitry Andric Result = 100 * static_cast<double>(Fraction) / All; 90e8d8bef9SDimitry Andric 91e8d8bef9SDimitry Andric std::stringstream Str; 92e8d8bef9SDimitry Andric Str << std::setprecision(4) << Msg << ": " << Fraction << " [" << Result 93e8d8bef9SDimitry Andric << "% of " << PercentageOfMsg << "]"; 94e8d8bef9SDimitry Andric if (LineEnd) 95e8d8bef9SDimitry Andric Str << "\n"; 96e8d8bef9SDimitry Andric return Str.str(); 97e8d8bef9SDimitry Andric } 98e8d8bef9SDimitry Andric 99e8d8bef9SDimitry Andric void ImportedFunctionsInliningStatistics::dump(const bool Verbose) { 100e8d8bef9SDimitry Andric calculateRealInlines(); 101e8d8bef9SDimitry Andric NonImportedCallers.clear(); 102e8d8bef9SDimitry Andric 103e8d8bef9SDimitry Andric int32_t InlinedImportedFunctionsCount = 0; 104e8d8bef9SDimitry Andric int32_t InlinedNotImportedFunctionsCount = 0; 105e8d8bef9SDimitry Andric 106e8d8bef9SDimitry Andric int32_t InlinedImportedFunctionsToImportingModuleCount = 0; 107e8d8bef9SDimitry Andric int32_t InlinedNotImportedFunctionsToImportingModuleCount = 0; 108e8d8bef9SDimitry Andric 109e8d8bef9SDimitry Andric const auto SortedNodes = getSortedNodes(); 110e8d8bef9SDimitry Andric std::string Out; 111e8d8bef9SDimitry Andric Out.reserve(5000); 112e8d8bef9SDimitry Andric raw_string_ostream Ostream(Out); 113e8d8bef9SDimitry Andric 114e8d8bef9SDimitry Andric Ostream << "------- Dumping inliner stats for [" << ModuleName 115e8d8bef9SDimitry Andric << "] -------\n"; 116e8d8bef9SDimitry Andric 117e8d8bef9SDimitry Andric if (Verbose) 118e8d8bef9SDimitry Andric Ostream << "-- List of inlined functions:\n"; 119e8d8bef9SDimitry Andric 120e8d8bef9SDimitry Andric for (const auto &Node : SortedNodes) { 121e8d8bef9SDimitry Andric assert(Node->second->NumberOfInlines >= Node->second->NumberOfRealInlines); 122e8d8bef9SDimitry Andric if (Node->second->NumberOfInlines == 0) 123e8d8bef9SDimitry Andric continue; 124e8d8bef9SDimitry Andric 125e8d8bef9SDimitry Andric if (Node->second->Imported) { 126e8d8bef9SDimitry Andric InlinedImportedFunctionsCount++; 127e8d8bef9SDimitry Andric InlinedImportedFunctionsToImportingModuleCount += 128e8d8bef9SDimitry Andric int(Node->second->NumberOfRealInlines > 0); 129e8d8bef9SDimitry Andric } else { 130e8d8bef9SDimitry Andric InlinedNotImportedFunctionsCount++; 131e8d8bef9SDimitry Andric InlinedNotImportedFunctionsToImportingModuleCount += 132e8d8bef9SDimitry Andric int(Node->second->NumberOfRealInlines > 0); 133e8d8bef9SDimitry Andric } 134e8d8bef9SDimitry Andric 135e8d8bef9SDimitry Andric if (Verbose) 136e8d8bef9SDimitry Andric Ostream << "Inlined " 137e8d8bef9SDimitry Andric << (Node->second->Imported ? "imported " : "not imported ") 138e8d8bef9SDimitry Andric << "function [" << Node->first() << "]" 139e8d8bef9SDimitry Andric << ": #inlines = " << Node->second->NumberOfInlines 140e8d8bef9SDimitry Andric << ", #inlines_to_importing_module = " 141e8d8bef9SDimitry Andric << Node->second->NumberOfRealInlines << "\n"; 142e8d8bef9SDimitry Andric } 143e8d8bef9SDimitry Andric 144e8d8bef9SDimitry Andric auto InlinedFunctionsCount = 145e8d8bef9SDimitry Andric InlinedImportedFunctionsCount + InlinedNotImportedFunctionsCount; 146e8d8bef9SDimitry Andric auto NotImportedFuncCount = AllFunctions - ImportedFunctions; 147e8d8bef9SDimitry Andric auto ImportedNotInlinedIntoModule = 148e8d8bef9SDimitry Andric ImportedFunctions - InlinedImportedFunctionsToImportingModuleCount; 149e8d8bef9SDimitry Andric 150e8d8bef9SDimitry Andric Ostream << "-- Summary:\n" 151e8d8bef9SDimitry Andric << "All functions: " << AllFunctions 152e8d8bef9SDimitry Andric << ", imported functions: " << ImportedFunctions << "\n" 153e8d8bef9SDimitry Andric << getStatString("inlined functions", InlinedFunctionsCount, 154e8d8bef9SDimitry Andric AllFunctions, "all functions") 155e8d8bef9SDimitry Andric << getStatString("imported functions inlined anywhere", 156e8d8bef9SDimitry Andric InlinedImportedFunctionsCount, ImportedFunctions, 157e8d8bef9SDimitry Andric "imported functions") 158e8d8bef9SDimitry Andric << getStatString("imported functions inlined into importing module", 159e8d8bef9SDimitry Andric InlinedImportedFunctionsToImportingModuleCount, 160e8d8bef9SDimitry Andric ImportedFunctions, "imported functions", 161e8d8bef9SDimitry Andric /*LineEnd=*/false) 162e8d8bef9SDimitry Andric << getStatString(", remaining", ImportedNotInlinedIntoModule, 163e8d8bef9SDimitry Andric ImportedFunctions, "imported functions") 164e8d8bef9SDimitry Andric << getStatString("non-imported functions inlined anywhere", 165e8d8bef9SDimitry Andric InlinedNotImportedFunctionsCount, 166e8d8bef9SDimitry Andric NotImportedFuncCount, "non-imported functions") 167e8d8bef9SDimitry Andric << getStatString( 168e8d8bef9SDimitry Andric "non-imported functions inlined into importing module", 169e8d8bef9SDimitry Andric InlinedNotImportedFunctionsToImportingModuleCount, 170e8d8bef9SDimitry Andric NotImportedFuncCount, "non-imported functions"); 171e8d8bef9SDimitry Andric Ostream.flush(); 172e8d8bef9SDimitry Andric dbgs() << Out; 173e8d8bef9SDimitry Andric } 174e8d8bef9SDimitry Andric 175e8d8bef9SDimitry Andric void ImportedFunctionsInliningStatistics::calculateRealInlines() { 176e8d8bef9SDimitry Andric // Removing duplicated Callers. 177e8d8bef9SDimitry Andric llvm::sort(NonImportedCallers); 178*0fca6ea1SDimitry Andric NonImportedCallers.erase(llvm::unique(NonImportedCallers), 179e8d8bef9SDimitry Andric NonImportedCallers.end()); 180e8d8bef9SDimitry Andric 181e8d8bef9SDimitry Andric for (const auto &Name : NonImportedCallers) { 182e8d8bef9SDimitry Andric auto &Node = *NodesMap[Name]; 183e8d8bef9SDimitry Andric if (!Node.Visited) 184e8d8bef9SDimitry Andric dfs(Node); 185e8d8bef9SDimitry Andric } 186e8d8bef9SDimitry Andric } 187e8d8bef9SDimitry Andric 188e8d8bef9SDimitry Andric void ImportedFunctionsInliningStatistics::dfs(InlineGraphNode &GraphNode) { 189e8d8bef9SDimitry Andric assert(!GraphNode.Visited); 190e8d8bef9SDimitry Andric GraphNode.Visited = true; 191e8d8bef9SDimitry Andric for (auto *const InlinedFunctionNode : GraphNode.InlinedCallees) { 192e8d8bef9SDimitry Andric InlinedFunctionNode->NumberOfRealInlines++; 193e8d8bef9SDimitry Andric if (!InlinedFunctionNode->Visited) 194e8d8bef9SDimitry Andric dfs(*InlinedFunctionNode); 195e8d8bef9SDimitry Andric } 196e8d8bef9SDimitry Andric } 197e8d8bef9SDimitry Andric 198e8d8bef9SDimitry Andric ImportedFunctionsInliningStatistics::SortedNodesTy 199e8d8bef9SDimitry Andric ImportedFunctionsInliningStatistics::getSortedNodes() { 200e8d8bef9SDimitry Andric SortedNodesTy SortedNodes; 201e8d8bef9SDimitry Andric SortedNodes.reserve(NodesMap.size()); 202e8d8bef9SDimitry Andric for (const NodesMapTy::value_type &Node : NodesMap) 203e8d8bef9SDimitry Andric SortedNodes.push_back(&Node); 204e8d8bef9SDimitry Andric 205e8d8bef9SDimitry Andric llvm::sort(SortedNodes, [&](const SortedNodesTy::value_type &Lhs, 206e8d8bef9SDimitry Andric const SortedNodesTy::value_type &Rhs) { 207e8d8bef9SDimitry Andric if (Lhs->second->NumberOfInlines != Rhs->second->NumberOfInlines) 208e8d8bef9SDimitry Andric return Lhs->second->NumberOfInlines > Rhs->second->NumberOfInlines; 209e8d8bef9SDimitry Andric if (Lhs->second->NumberOfRealInlines != Rhs->second->NumberOfRealInlines) 210e8d8bef9SDimitry Andric return Lhs->second->NumberOfRealInlines > 211e8d8bef9SDimitry Andric Rhs->second->NumberOfRealInlines; 212e8d8bef9SDimitry Andric return Lhs->first() < Rhs->first(); 213e8d8bef9SDimitry Andric }); 214e8d8bef9SDimitry Andric return SortedNodes; 215e8d8bef9SDimitry Andric } 216