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