<div dir="ltr"><br><div class="gmail_extra"><br><div class="gmail_quote">On Thu, Jul 28, 2016 at 5:27 PM, Piotr Padlewski via llvm-commits <span dir="ltr"><<a href="mailto:llvm-commits@lists.llvm.org" target="_blank">llvm-commits@lists.llvm.org</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex">Author: prazek<br>
Date: Thu Jul 28 19:27:16 2016<br>
New Revision: 277089<br>
<br>
URL: <a href="http://llvm.org/viewvc/llvm-project?rev=277089&view=rev" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project?rev=277089&view=rev</a><br>
Log:<br>
Added ThinLTO inlining statistics<br>
<br>
Summary:<br>
copypasta doc of ImportedFunctionsInliningStatistics class<br>
 \brief Calculate and dump ThinLTO specific inliner stats.<br>
 The main statistics are:<br>
 (1) Number of inlined imported functions,<br>
 (2) Number of imported functions inlined into importing module (indirect),<br>
 (3) Number of non imported functions inlined into importing module<br>
 (indirect).<br>
 The difference between first and the second is that first stat counts<br>
 all performed inlines on imported functions, but the second one only the<br>
 functions that have been eventually inlined to a function in the importing<br>
 module (by a chain of inlines). Because llvm uses bottom-up inliner, it is<br>
 possible to e.g. import function `A`, `B` and then inline `B` to `A`,<br>
 and after this `A` might be too big to be inlined into some other function<br>
 that calls it. It calculates this statistic by building graph, where<br>
 the nodes are functions, and edges are performed inlines and then by marking<br>
 the edges starting from not imported function.<br>
<br>
 If `Verbose` is set to true, then it also dumps statistics<br>
 per each inlined function, sorted by the greatest inlines count like<br>
 - number of performed inlines<br>
 - number of performed inlines to importing module<br>
<br>
Reviewers: eraman, tejohnson, mehdi_amini<br>
<br>
Subscribers: mehdi_amini, llvm-commits<br>
<br>
Differential Revision: <a href="https://reviews.llvm.org/D22491" rel="noreferrer" target="_blank">https://reviews.llvm.org/D22491</a><br>
<br>
Added:<br>
    llvm/trunk/include/llvm/Transforms/Utils/ImportedFunctionsInliningStatistics.h<br>
    llvm/trunk/lib/Transforms/Utils/ImportedFunctionsInliningStatistics.cpp<br>
    llvm/trunk/test/Transforms/Inline/inline_stats.ll<br>
Modified:<br>
    llvm/trunk/include/llvm/Transforms/IPO/InlinerPass.h<br>
    llvm/trunk/lib/Transforms/IPO/Inliner.cpp<br>
    llvm/trunk/lib/Transforms/Utils/CMakeLists.txt<br>
<br>
Modified: llvm/trunk/include/llvm/Transforms/IPO/InlinerPass.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Transforms/IPO/InlinerPass.h?rev=277089&r1=277088&r2=277089&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Transforms/IPO/InlinerPass.h?rev=277089&r1=277088&r2=277089&view=diff</a><br>
==============================================================================<br>
--- llvm/trunk/include/llvm/Transforms/IPO/InlinerPass.h (original)<br>
+++ llvm/trunk/include/llvm/Transforms/IPO/InlinerPass.h Thu Jul 28 19:27:16 2016<br>
@@ -20,6 +20,7 @@<br>
 #include "llvm/Analysis/CallGraphSCCPass.h"<br>
 #include "llvm/Analysis/InlineCost.h"<br>
 #include "llvm/Analysis/TargetTransformInfo.h"<br>
+#include "llvm/Transforms/Utils/ImportedFunctionsInliningStatistics.h"<br>
<br>
 namespace llvm {<br>
 class AssumptionCacheTracker;<br>
@@ -41,6 +42,8 @@ struct Inliner : public CallGraphSCCPass<br>
   /// always explicitly call the implementation here.<br>
   void getAnalysisUsage(AnalysisUsage &Info) const override;<br>
<br>
+  bool doInitialization(CallGraph &CG) override;<br>
+<br>
   // Main run interface method, this implements the interface required by the<br>
   // Pass class.<br>
   bool runOnSCC(CallGraphSCC &SCC) override;<br>
@@ -78,6 +81,7 @@ private:<br>
 protected:<br>
   AssumptionCacheTracker *ACT;<br>
   ProfileSummaryInfo *PSI;<br>
+  ImportedFunctionsInliningStatistics ImportedFunctionsStats;<br>
 };<br>
<br>
 } // End llvm namespace<br>
<br>
Added: llvm/trunk/include/llvm/Transforms/Utils/ImportedFunctionsInliningStatistics.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Transforms/Utils/ImportedFunctionsInliningStatistics.h?rev=277089&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Transforms/Utils/ImportedFunctionsInliningStatistics.h?rev=277089&view=auto</a><br>
==============================================================================<br>
--- llvm/trunk/include/llvm/Transforms/Utils/ImportedFunctionsInliningStatistics.h (added)<br>
+++ llvm/trunk/include/llvm/Transforms/Utils/ImportedFunctionsInliningStatistics.h Thu Jul 28 19:27:16 2016<br>
@@ -0,0 +1,112 @@<br>
+//===-- ImportedFunctionsInliningStats.h ------------------------*- C++ -*-===//<br>
+//<br>
+//                     The LLVM Compiler Infrastructure<br>
+//<br>
+// This file is distributed under the University of Illinois Open Source<br>
+// License. See LICENSE.TXT for details.<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+// Generating inliner statistics for imported functions, mostly useful for<br>
+// ThinLTO.<br>
+//===----------------------------------------------------------------------===//<br>
+<br>
+#ifndef LLVM_TRANSFORMS_UTILS_IMPORTEDFUNCTIONSINLININGSTATISTICS_H<br>
+#define LLVM_TRANSFORMS_UTILS_IMPORTEDFUNCTIONSINLININGSTATISTICS_H<br>
+<br>
+#include "llvm/ADT/SmallVector.h"<br>
+#include "llvm/ADT/StringRef.h"<br>
+#include <string><br>
+#include <unordered_map><br>
+#include <vector><br>
+<br>
+namespace llvm {<br>
+class Module;<br>
+class Function;<br>
+/// \brief Calculate and dump ThinLTO specific inliner stats.<br>
+/// The main statistics are:<br>
+/// (1) Number of inlined imported functions,<br>
+/// (2) Number of imported functions inlined into importing module (indirect),<br>
+/// (3) Number of non imported functions inlined into importing module<br>
+/// (indirect).<br>
+/// The difference between first and the second is that first stat counts<br>
+/// all performed inlines on imported functions, but the second one only the<br>
+/// functions that have been eventually inlined to a function in the importing<br>
+/// module (by a chain of inlines). Because llvm uses bottom-up inliner, it is<br>
+/// possible to e.g. import function `A`, `B` and then inline `B` to `A`,<br>
+/// and after this `A` might be too big to be inlined into some other function<br>
+/// that calls it. It calculates this statistic by building graph, where<br>
+/// the nodes are functions, and edges are performed inlines and then by marking<br>
+/// the edges starting from not imported function.<br>
+///<br>
+/// If `Verbose` is set to true, then it also dumps statistics<br>
+/// per each inlined function, sorted by the greatest inlines count like<br>
+/// - number of performed inlines<br>
+/// - number of performed inlines to importing module<br>
+class ImportedFunctionsInliningStatistics {<br>
+private:<br>
+  /// InlineGraphNode represents node in graph of inlined functions.<br>
+  struct InlineGraphNode {<br>
+    // Default-constructible and movable.<br>
+    InlineGraphNode() = default;<br>
+    InlineGraphNode(InlineGraphNode &&) = default;<br>
+    InlineGraphNode &operator=(InlineGraphNode &&) = default;<br>
+    InlineGraphNode(const InlineGraphNode &) = delete;<br>
+    InlineGraphNode &operator=(const InlineGraphNode &) = delete;<br>
+<br>
+    llvm::SmallVector<InlineGraphNode *, 8> InlinedCallees;<br>
+    /// Incremented every direct inline.<br>
+    int32_t NumberOfInlines = 0;<br>
+    /// Number of inlines into non imported function (possibly indirect via<br>
+    /// intermediate inlines). Computed based on graph search.<br>
+    int32_t NumberOfRealInlines = 0;<br>
+    bool Imported = false;<br>
+    bool Visited = false;<br>
+  };<br>
+<br>
+public:<br>
+  ImportedFunctionsInliningStatistics() = default;<br>
+  ImportedFunctionsInliningStatistics(<br>
+      const ImportedFunctionsInliningStatistics &) = delete;<br>
+<br>
+  /// Set information like AllFunctions, ImportedFunctions, ModuleName.<br>
+  void setModuleInfo(const Module &M);<br>
+  /// Record inline of @param Callee to @param Caller for statistis.<br>
+  void recordInline(const Function &Caller, const Function &Callee);<br>
+  /// Dump stats computed with InlinerStatistics class.<br>
+  /// If @param Verbose is true then separate statistics for every inlined<br>
+  /// function will be printed.<br>
+  void dump(bool Verbose);<br>
+<br>
+private:<br>
+  /// Creates new Node in NodeMap and sets attributes, or returns existed one.<br>
+  InlineGraphNode &createInlineGraphNode(const Function &);<br>
+  void calculateRealInlines();<br>
+  void dfs(InlineGraphNode &GraphNode);<br>
+<br>
+  using NodesMapTy =<br>
+      std::unordered_map<std::string, std::unique_ptr<InlineGraphNode>>;<br></blockquote><div><br></div><div>Since you are now using a map keyed by the function name use a StringMap which is a more efficient way of mapping string keys.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex">
+  using SortedNodesTy =<br>
+      std::vector<std::pair<std::string, std::unique_ptr<InlineGraphNode>>>;<br></blockquote><div><br></div><div>Once the NodesMap is a StringMap, I suppose this vector can hold a StringMapEntry instead of a std::pair. But then you can't clear the NodesMap, note on that below.</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex">
+  /// Clears NodesMap and returns vector of elements sorted by<br>
+  /// (-NumberOfInlines, -NumberOfRealInlines, FunctionName).<br>
+  SortedNodesTy getSortedNodes();<br>
+<br>
+private:<br>
+  /// FIXME: Change map keys to std::string/StringRef to avoid using pointers<br>
+  /// to dead functions.<br></blockquote><div><br></div><div>Remove this comment as you implemented that in the committed version.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex">
+  /// This map manage life of all InlineGraphNodes. Unique pointer to<br>
+  /// InlineGraphNode used since the node pointers are also saved in the<br>
+  /// InlinedCallees vector. If it would store InlineGraphNode instead then the<br>
+  /// address of the node would not be invariant.<br>
+  NodesMapTy NodesMap;<br>
+  /// Non external functions that have some other function inlined inside.<br>
+  /// Should not dereference pointers because Functions might be deleted.<br>
+  std::vector<std::string> NonImportedCallers;<br></blockquote><div><br></div><div>Once the NodesMap is a StringMap, this vector can use the StringRef for the key owned by the map (StringMap makes a copy when you insert), instead of another copy of the string.</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex">+  int AllFunctions = 0; </blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex">+  int ImportedFunctions = 0;<br>
+  StringRef ModuleName;<br>
+};<br>
+<br>
+} // llvm<br>
+<br>
+#endif // LLVM_TRANSFORMS_UTILS_IMPORTEDFUNCTIONSINLININGSTATISTICS_H<br>
<br>
Modified: llvm/trunk/lib/Transforms/IPO/Inliner.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/IPO/Inliner.cpp?rev=277089&r1=277088&r2=277089&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/IPO/Inliner.cpp?rev=277089&r1=277088&r2=277089&view=diff</a><br>
==============================================================================<br>
--- llvm/trunk/lib/Transforms/IPO/Inliner.cpp (original)<br>
+++ llvm/trunk/lib/Transforms/IPO/Inliner.cpp Thu Jul 28 19:27:16 2016<br>
@@ -47,6 +47,24 @@ STATISTIC(NumMergedAllocas, "Number of a<br>
 // if those would be more profitable and blocked inline steps.<br>
 STATISTIC(NumCallerCallersAnalyzed, "Number of caller-callers analyzed");<br>
<br>
+namespace {<br>
+enum class InlinerFunctionImportStatsOpts {<br>
+  No = 0,<br>
+  Basic = 1,<br>
+  Verbose = 2,<br>
+};<br>
+<br>
+cl::opt<InlinerFunctionImportStatsOpts> InlinerFunctionImportStats(<br>
+    "inliner-function-import-stats",<br>
+    cl::init(InlinerFunctionImportStatsOpts::No),<br>
+    cl::values(clEnumValN(InlinerFunctionImportStatsOpts::Basic, "basic",<br>
+                          "basic statistics"),<br>
+               clEnumValN(InlinerFunctionImportStatsOpts::Verbose, "verbose",<br>
+                          "printing of statistics for each inlined function"),<br>
+               clEnumValEnd),<br>
+    cl::Hidden, cl::desc("Enable inliner stats for imported functions"));<br>
+} // namespace<br>
+<br>
 Inliner::Inliner(char &ID) : CallGraphSCCPass(ID), InsertLifetime(true) {}<br>
<br>
 Inliner::Inliner(char &ID, bool InsertLifetime)<br>
@@ -75,11 +93,11 @@ InlinedArrayAllocasTy;<br>
 /// available from other functions inlined into the caller.  If we are able to<br>
 /// inline this call site we attempt to reuse already available allocas or add<br>
 /// any new allocas to the set if not possible.<br>
-static bool<br>
-InlineCallIfPossible(CallSite CS, InlineFunctionInfo &IFI,<br>
-                     InlinedArrayAllocasTy &InlinedArrayAllocas,<br>
-                     int InlineHistory, bool InsertLifetime,<br>
-                     std::function<AAResults &(Function &)> &AARGetter) {<br>
+static bool InlineCallIfPossible(<br>
+    CallSite CS, InlineFunctionInfo &IFI,<br>
+    InlinedArrayAllocasTy &InlinedArrayAllocas, int InlineHistory,<br>
+    bool InsertLifetime, std::function<AAResults &(Function &)> &AARGetter,<br>
+    ImportedFunctionsInliningStatistics &ImportedFunctionsStats) {<br>
   Function *Callee = CS.getCalledFunction();<br>
   Function *Caller = CS.getCaller();<br>
<br>
@@ -90,6 +108,9 @@ InlineCallIfPossible(CallSite CS, Inline<br>
   if (!InlineFunction(CS, IFI, &AAR, InsertLifetime))<br>
     return false;<br>
<br>
+  if (InlinerFunctionImportStats != InlinerFunctionImportStatsOpts::No)<br>
+    ImportedFunctionsStats.recordInline(*Caller, *Callee);<br>
+<br>
   AttributeFuncs::mergeAttributesForInlining(*Caller, *Callee);<br>
<br>
   // Look at all of the allocas that we inlined through this call site.  If we<br>
@@ -371,10 +392,15 @@ static bool InlineHistoryIncludes(Functi<br>
   return false;<br>
 }<br>
<br>
+bool Inliner::doInitialization(CallGraph &CG) {<br>
+  if (InlinerFunctionImportStats != InlinerFunctionImportStatsOpts::No)<br>
+    ImportedFunctionsStats.setModuleInfo(CG.getModule());<br>
+  return false; // No changes to CallGraph.<br>
+}<br>
+<br>
 bool Inliner::runOnSCC(CallGraphSCC &SCC) {<br>
   if (skipSCC(SCC))<br>
     return false;<br>
-<br>
   return inlineCalls(SCC);<br>
 }<br>
<br>
@@ -384,7 +410,8 @@ inlineCallsImpl(CallGraphSCC &SCC, CallG<br>
                 ProfileSummaryInfo *PSI, TargetLibraryInfo &TLI,<br>
                 bool InsertLifetime,<br>
                 std::function<InlineCost(CallSite CS)> GetInlineCost,<br>
-                std::function<AAResults &(Function &)> AARGetter) {<br>
+                std::function<AAResults &(Function &)> AARGetter,<br>
+                ImportedFunctionsInliningStatistics &ImportedFunctionsStats) {<br>
   SmallPtrSet<Function*, 8> SCCFunctions;<br>
   DEBUG(dbgs() << "Inliner visiting SCC:");<br>
   for (CallGraphNode *Node : SCC) {<br>
@@ -502,7 +529,8 @@ inlineCallsImpl(CallGraphSCC &SCC, CallG<br>
<br>
         // Attempt to inline the function.<br>
         if (!InlineCallIfPossible(CS, InlineInfo, InlinedArrayAllocas,<br>
-                                  InlineHistoryID, InsertLifetime, AARGetter)) {<br>
+                                  InlineHistoryID, InsertLifetime, AARGetter,<br>
+                                  ImportedFunctionsStats)) {<br>
           emitOptimizationRemarkMissed(CallerCtx, DEBUG_TYPE, *Caller, DLoc,<br>
                                        Twine(Callee->getName() +<br>
                                              " will not be inlined into " +<br>
@@ -591,12 +619,15 @@ bool Inliner::inlineCalls(CallGraphSCC &<br>
   };<br>
   return inlineCallsImpl(SCC, CG, GetAssumptionCache, PSI, TLI, InsertLifetime,<br>
                          [this](CallSite CS) { return getInlineCost(CS); },<br>
-                         AARGetter);<br>
+                         AARGetter, ImportedFunctionsStats);<br>
 }<br>
<br>
 /// Remove now-dead linkonce functions at the end of<br>
 /// processing to avoid breaking the SCC traversal.<br>
 bool Inliner::doFinalization(CallGraph &CG) {<br>
+  if (InlinerFunctionImportStats != InlinerFunctionImportStatsOpts::No)<br>
+    ImportedFunctionsStats.dump(InlinerFunctionImportStats ==<br>
+                                InlinerFunctionImportStatsOpts::Verbose);<br>
   return removeDeadFunctions(CG);<br>
 }<br>
<br>
<br>
Modified: llvm/trunk/lib/Transforms/Utils/CMakeLists.txt<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/Utils/CMakeLists.txt?rev=277089&r1=277088&r2=277089&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/Utils/CMakeLists.txt?rev=277089&r1=277088&r2=277089&view=diff</a><br>
==============================================================================<br>
--- llvm/trunk/lib/Transforms/Utils/CMakeLists.txt (original)<br>
+++ llvm/trunk/lib/Transforms/Utils/CMakeLists.txt Thu Jul 28 19:27:16 2016<br>
@@ -16,6 +16,7 @@ add_llvm_library(LLVMTransformUtils<br>
   FunctionImportUtils.cpp<br>
   GlobalStatus.cpp<br>
   InlineFunction.cpp<br>
+  ImportedFunctionsInliningStatistics.cpp<br>
   InstructionNamer.cpp<br>
   IntegerDivision.cpp<br>
   LCSSA.cpp<br>
<br>
Added: llvm/trunk/lib/Transforms/Utils/ImportedFunctionsInliningStatistics.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/Utils/ImportedFunctionsInliningStatistics.cpp?rev=277089&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/Utils/ImportedFunctionsInliningStatistics.cpp?rev=277089&view=auto</a><br>
==============================================================================<br>
--- llvm/trunk/lib/Transforms/Utils/ImportedFunctionsInliningStatistics.cpp (added)<br>
+++ llvm/trunk/lib/Transforms/Utils/ImportedFunctionsInliningStatistics.cpp Thu Jul 28 19:27:16 2016<br>
@@ -0,0 +1,201 @@<br>
+//===-- ImportedFunctionsInliningStats.cpp ----------------------*- C++ -*-===//<br>
+//<br>
+//                     The LLVM Compiler Infrastructure<br>
+//<br>
+// This file is distributed under the University of Illinois Open Source<br>
+// License. See LICENSE.TXT for details.<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+// Generating inliner statistics for imported functions, mostly useful for<br>
+// ThinLTO.<br>
+//===----------------------------------------------------------------------===//<br>
+<br>
+#include "llvm/Transforms/Utils/ImportedFunctionsInliningStatistics.h"<br>
+#include "llvm/ADT/STLExtras.h"<br>
+#include "llvm/IR/Function.h"<br>
+#include "llvm/IR/Module.h"<br>
+#include "llvm/Support/Debug.h"<br>
+#include "llvm/Support/raw_ostream.h"<br>
+#include <algorithm><br>
+#include <iomanip><br>
+#include <sstream><br>
+using namespace llvm;<br>
+<br>
+ImportedFunctionsInliningStatistics::InlineGraphNode &<br>
+ImportedFunctionsInliningStatistics::createInlineGraphNode(const Function &F) {<br>
+<br>
+  auto &ValueLookup = NodesMap[F.getName()];<br>
+  if (!ValueLookup) {<br>
+    ValueLookup = llvm::make_unique<InlineGraphNode>();<br>
+    ValueLookup->Imported = F.getMetadata("thinlto_src_module") != nullptr;<br>
+  }<br>
+  return *ValueLookup;<br>
+}<br>
+<br>
+void ImportedFunctionsInliningStatistics::recordInline(const Function &Caller,<br>
+                                                       const Function &Callee) {<br>
+<br>
+  InlineGraphNode &CallerNode = createInlineGraphNode(Caller);<br>
+  InlineGraphNode &CalleeNode = createInlineGraphNode(Callee);<br>
+  CalleeNode.NumberOfInlines++;<br>
+<br>
+  if (!CallerNode.Imported && !CalleeNode.Imported) {<br>
+    // Direct inline from not imported callee to not imported caller, so we<br>
+    // don't have to add this to graph. It might be very helpful if you wanna<br>
+    // get the inliner statistics in compile step where there are no imported<br>
+    // functions. In this case the graph would be empty.<br>
+    CalleeNode.NumberOfRealInlines++;<br>
+    return;<br>
+  }<br>
+<br>
+  CallerNode.InlinedCallees.push_back(&CalleeNode);<br>
+  if (!CallerNode.Imported)<br>
+    // Save Caller as a starting node for traversal.<br>
+    NonImportedCallers.push_back(Caller.getName());<br>
+}<br>
+<br>
+void ImportedFunctionsInliningStatistics::setModuleInfo(const Module &M) {<br>
+  ModuleName = M.getName();<br>
+  for (const auto &F : M.functions()) {<br>
+    AllFunctions++;<br>
+    ImportedFunctions += int(F.getMetadata("thinlto_src_module") != nullptr);<br>
+  }<br>
+}<br>
+static std::string getStatString(const char *Msg, int32_t Fraction, int32_t All,<br>
+                                 const char *PercentageOfMsg,<br>
+                                 bool LineEnd = true) {<br>
+  double Result = 0;<br>
+  if (All != 0)<br>
+    Result = 100 * static_cast<double>(Fraction) / All;<br>
+<br>
+  std::stringstream Str;<br>
+  Str << std::setprecision(4) << Msg << ": " << Fraction << " [" << Result<br>
+      << "% of " << PercentageOfMsg << "]";<br>
+  if (LineEnd)<br>
+    Str << "\n";<br>
+  return Str.str();<br>
+}<br>
+<br>
+void ImportedFunctionsInliningStatistics::dump(const bool Verbose) {<br>
+  calculateRealInlines();<br>
+  NonImportedCallers.clear();<br>
+<br>
+  int32_t InlinedImportedFunctionsCount = 0;<br>
+  int32_t InlinedNotImportedFunctionsCount = 0;<br>
+<br>
+  int32_t InlinedImportedFunctionsToImportingModuleCount = 0;<br>
+  int32_t InlinedNotImportedFunctionsToImportingModuleCount = 0;<br>
+<br>
+  const auto SortedNodes = getSortedNodes();<br>
+  std::string Out;<br>
+  Out.reserve(5000);<br>
+  raw_string_ostream Ostream(Out);<br>
+<br>
+  Ostream << "------- Dumping inliner stats for [" << ModuleName<br>
+          << "] -------\n";<br>
+<br>
+  if (Verbose)<br>
+    Ostream << "-- List of inlined functions:\n";<br>
+<br>
+  for (const auto &Node : SortedNodes) {<br>
+    assert(Node.second->NumberOfInlines >= Node.second->NumberOfRealInlines);<br>
+    if (Node.second->NumberOfInlines == 0)<br>
+      continue;<br>
+<br>
+    if (Node.second->Imported) {<br>
+      InlinedImportedFunctionsCount++;<br>
+      InlinedImportedFunctionsToImportingModuleCount +=<br>
+          int(Node.second->NumberOfRealInlines > 0);<br>
+    } else {<br>
+      InlinedNotImportedFunctionsCount++;<br>
+      InlinedNotImportedFunctionsToImportingModuleCount +=<br>
+          int(Node.second->NumberOfRealInlines > 0);<br>
+    }<br>
+<br>
+    if (Verbose)<br>
+      Ostream << "Inlined "<br>
+              << (Node.second->Imported ? "imported " : "not imported ")<br>
+              << "function [" << Node.first << "]"<br>
+              << ": #inlines = " << Node.second->NumberOfInlines<br>
+              << ", #inlines_to_importing_module = "<br>
+              << Node.second->NumberOfRealInlines << "\n";<br>
+  }<br>
+<br>
+  auto InlinedFunctionsCount =<br>
+      InlinedImportedFunctionsCount + InlinedNotImportedFunctionsCount;<br>
+  auto NotImportedFuncCount = AllFunctions - ImportedFunctions;<br>
+  auto ImportedNotInlinedIntoModule =<br>
+      ImportedFunctions - InlinedImportedFunctionsToImportingModuleCount;<br>
+<br>
+  Ostream << "-- Summary:\n"<br>
+          << "All functions: " << AllFunctions<br>
+          << ", imported functions: " << ImportedFunctions << "\n"<br>
+          << getStatString("inlined functions", InlinedFunctionsCount,<br>
+                           AllFunctions, "all functions")<br>
+          << getStatString("imported functions inlined anywhere",<br>
+                           InlinedImportedFunctionsCount, ImportedFunctions,<br>
+                           "imported functions")<br>
+          << getStatString("imported functions inlined into importing module",<br>
+                           InlinedImportedFunctionsToImportingModuleCount,<br>
+                           ImportedFunctions, "imported functions",<br>
+                           /*LineEnd=*/false)<br>
+          << getStatString(", remaining", ImportedNotInlinedIntoModule,<br>
+                           ImportedFunctions, "imported functions")<br>
+          << getStatString("non-imported functions inlined anywhere",<br>
+                           InlinedNotImportedFunctionsCount,<br>
+                           NotImportedFuncCount, "non-imported functions")<br>
+          << getStatString(<br>
+                 "non-imported functions inlined into importing module",<br>
+                 InlinedNotImportedFunctionsToImportingModuleCount,<br>
+                 NotImportedFuncCount, "non-imported functions");<br>
+  Ostream.flush();<br>
+  dbgs() << Out;<br>
+}<br>
+<br>
+void ImportedFunctionsInliningStatistics::calculateRealInlines() {<br>
+  // Removing duplicated Callers.<br>
+  std::sort(NonImportedCallers.begin(), NonImportedCallers.end());<br>
+  NonImportedCallers.erase(<br>
+      std::unique(NonImportedCallers.begin(), NonImportedCallers.end()),<br>
+      NonImportedCallers.end());<br>
+<br>
+  for (const auto &Name : NonImportedCallers) {<br>
+    auto &Node = *NodesMap[Name];<br>
+    if (!Node.Visited)<br>
+      dfs(Node);<br>
+  }<br>
+}<br>
+<br>
+void ImportedFunctionsInliningStatistics::dfs(InlineGraphNode &GraphNode) {<br>
+  assert(!GraphNode.Visited);<br>
+  GraphNode.Visited = true;<br>
+  for (auto *const InlinedFunctionNode : GraphNode.InlinedCallees) {<br>
+    InlinedFunctionNode->NumberOfRealInlines++;<br>
+    if (!InlinedFunctionNode->Visited)<br>
+      dfs(*InlinedFunctionNode);<br>
+  }<br>
+}<br>
+<br>
+ImportedFunctionsInliningStatistics::SortedNodesTy<br>
+ImportedFunctionsInliningStatistics::getSortedNodes() {<br>
+  SortedNodesTy SortedNodes;<br>
+  SortedNodes.reserve(NodesMap.size());<br>
+<br>
+  for (auto &&Node : NodesMap)<br>
+    SortedNodes.emplace_back(Node.first, std::move(Node.second));<br>
+<br>
+  NodesMap.clear(); // We don't want to leave nullptrs.<br></blockquote><div><br></div><div>What nullptrs are you concerned about leaving? The unique_ptrs will be destroyed when the ImportedFunctionsInliningStatistics is destroyed. If you change NodesMap to StringMap and use StringMapEntry in SortedNodes you will need to avoid the clear here.</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex">
+<br>
+  std::sort(<br>
+      SortedNodes.begin(), SortedNodes.end(),<br>
+      [&](const SortedNodesTy::value_type &Lhs,<br>
+          const SortedNodesTy::value_type &Rhs) {<br>
+        if (Lhs.second->NumberOfInlines != Rhs.second->NumberOfInlines)<br>
+          return Lhs.second->NumberOfInlines > Rhs.second->NumberOfInlines;<br>
+        if (Lhs.second->NumberOfRealInlines != Rhs.second->NumberOfRealInlines)<br>
+          return Lhs.second->NumberOfRealInlines ><br>
+                 Rhs.second->NumberOfRealInlines;<br>
+        return Lhs.first < Rhs.first;<br>
+      });<br>
+  return SortedNodes;<br>
+}<br>
<br>
Added: llvm/trunk/test/Transforms/Inline/inline_stats.ll<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/Inline/inline_stats.ll?rev=277089&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/Inline/inline_stats.ll?rev=277089&view=auto</a><br>
==============================================================================<br>
--- llvm/trunk/test/Transforms/Inline/inline_stats.ll (added)<br>
+++ llvm/trunk/test/Transforms/Inline/inline_stats.ll Thu Jul 28 19:27:16 2016<br>
@@ -0,0 +1,87 @@<br>
+; RUN: opt -S -inline -inliner-function-import-stats=basic < %s 2>&1 | FileCheck %s -check-prefix=CHECK-BASIC -check-prefix=CHECK<br>
+; RUN: opt -S -inline -inliner-function-import-stats=verbose < %s 2>&1 | FileCheck %s -check-prefix="CHECK-VERBOSE" -check-prefix=CHECK<br>
+<br>
+; CHECK: ------- Dumping inliner stats for [<stdin>] -------<br>
+; CHECK-BASIC-NOT: -- List of inlined functions:<br>
+; CHECK-BASIC-NOT: -- Inlined not imported function<br>
+; CHECK-VERBOSE: -- List of inlined functions:<br>
+; CHECK-VERBOSE: Inlined not imported function [internal2]: #inlines = 6, #inlines_to_importing_module = 2<br>
+; CHECK-VERBOSE: Inlined imported function [external2]: #inlines = 4, #inlines_to_importing_module = 1<br>
+; CHECK-VERBOSE: Inlined imported function [external1]: #inlines = 3, #inlines_to_importing_module = 2<br>
+; CHECK-VERBOSE: Inlined imported function [external5]: #inlines = 1, #inlines_to_importing_module = 1<br>
+; CHECK-VERBOSE: Inlined imported function [external3]: #inlines = 1, #inlines_to_importing_module = 0<br>
+<br>
+; CHECK: -- Summary:<br>
+; CHECK: All functions: 10, imported functions: 7<br>
+; CHECK: inlined functions: 5 [50% of all functions]<br>
+; CHECK: imported functions inlined anywhere: 4 [57.14% of imported functions]<br>
+; CHECK: imported functions inlined into importing module: 3 [42.86% of imported functions], remaining: 4 [57.14% of imported functions]<br>
+; CHECK: non-imported functions inlined anywhere: 1 [33.33% of non-imported functions]<br>
+; CHECK: non-imported functions inlined into importing module: 1 [33.33% of non-imported functions]<br>
+<br>
+define void @internal() {<br>
+    call fastcc void @external1()<br>
+    call fastcc void @internal2()<br>
+    call coldcc void @external_big()<br>
+    ret void<br>
+}<br>
+<br>
+define void @internal2() alwaysinline {<br>
+    ret void<br>
+}<br>
+<br>
+define void @internal3() {<br>
+    call fastcc void @external1()<br>
+    call fastcc void @external5()<br>
+    ret void<br>
+}<br>
+<br>
+define void @external1() alwaysinline !thinlto_src_module !0 {<br>
+    call fastcc void @internal2()<br>
+    call fastcc void @external2();<br>
+    ret void<br>
+}<br>
+<br>
+define void @external2() alwaysinline !thinlto_src_module !1 {<br>
+    ret void<br>
+}<br>
+<br>
+define void @external3() alwaysinline !thinlto_src_module !1 {<br>
+    ret void<br>
+}<br>
+<br>
+define void @external4() !thinlto_src_module !1 {<br>
+    call fastcc void @external1()<br>
+    call fastcc void @external2()<br>
+    ret void<br>
+}<br>
+<br>
+define void @external5() !thinlto_src_module !1 {<br>
+    ret void<br>
+}<br>
+<br>
+; Assume big piece of code here. This function won't be inlined, so all the<br>
+; inlined function it will have won't affect real inlines.<br>
+define void @external_big() noinline !thinlto_src_module !1 {<br>
+; CHECK-NOT: call fastcc void @internal2()<br>
+    call fastcc void @internal2()<br>
+    call fastcc void @internal2()<br>
+    call fastcc void @internal2()<br>
+    call fastcc void @internal2()<br>
+<br>
+; CHECK-NOT: call fastcc void @external2()<br>
+    call fastcc void @external2()<br>
+    call fastcc void @external2()<br>
+; CHECK-NOT: call fastcc void @external3()<br>
+    call fastcc void @external3()<br>
+    ret void<br>
+}<br>
+<br>
+; It should not be imported, but it should not break anything.<br>
+define void @external_notcalled() !thinlto_src_module !0 {<br>
+    call void @external_notcalled()<br>
+    ret void<br>
+}<br>
+<br>
+!0 = !{!"file.cc"}<br>
+!1 = !{!"other.cc"}<br>
<br>
<br>
_______________________________________________<br>
llvm-commits mailing list<br>
<a href="mailto:llvm-commits@lists.llvm.org">llvm-commits@lists.llvm.org</a><br>
<a href="http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits" rel="noreferrer" target="_blank">http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits</a><br>
</blockquote></div><br><br clear="all"><div><br></div>-- <br><div class="gmail_signature"><span style="font-family:times;font-size:medium"><table cellspacing="0" cellpadding="0"><tbody><tr style="color:rgb(85,85,85);font-family:sans-serif;font-size:small"><td nowrap style="border-top-style:solid;border-top-color:rgb(213,15,37);border-top-width:2px">Teresa Johnson |</td><td nowrap style="border-top-style:solid;border-top-color:rgb(51,105,232);border-top-width:2px"> Software Engineer |</td><td nowrap style="border-top-style:solid;border-top-color:rgb(0,153,57);border-top-width:2px"> <a href="mailto:tejohnson@google.com" target="_blank">tejohnson@google.com</a> |</td><td nowrap style="border-top-style:solid;border-top-color:rgb(238,178,17);border-top-width:2px"> 408-460-2413</td></tr></tbody></table></span></div>
</div></div>