Index: include/llvm/IR/ModuleSummaryIndex.h =================================================================== --- include/llvm/IR/ModuleSummaryIndex.h +++ include/llvm/IR/ModuleSummaryIndex.h @@ -106,6 +106,11 @@ } }; +inline bool operator<(const ValueInfo &A, const ValueInfo &B) { + assert(A.Ref && B.Ref && "Need ValueInfo with non-null Ref to compare GUIDs"); + return A.getGUID() < B.getGUID(); +} + template <> struct DenseMapInfo { static inline ValueInfo getEmptyKey() { return ValueInfo((GlobalValueSummaryMapTy::value_type *)-1); @@ -296,6 +301,24 @@ unsigned ReturnDoesNotAlias : 1; }; + /// Create an empty FunctionSummary (with specified call edges). + /// Used to represent external nodes and the dummy root node. + static FunctionSummary + makeDummyFunctionSummary(std::vector Edges) { + return FunctionSummary( + FunctionSummary::GVFlags( + GlobalValue::LinkageTypes::AvailableExternallyLinkage, + /*NotEligibleToImport=*/true, /*Live=*/true), + 0, FunctionSummary::FFlags{}, std::vector(), + std::move(Edges), std::vector(), + std::vector(), + std::vector(), + std::vector(), + std::vector()); + } + + static FunctionSummary ExternalNode; + private: /// Number of instructions (ignoring debug instructions, e.g.) computed /// during the initial compile step when the summary index is first built. @@ -415,6 +438,8 @@ TIdInfo = llvm::make_unique(); TIdInfo->TypeTests.push_back(Guid); } + + friend struct GraphTraits; }; template <> struct DenseMapInfo { @@ -584,6 +609,48 @@ const_gvsummary_iterator end() const { return GlobalValueMap.end(); } size_t size() const { return GlobalValueMap.size(); } + // Calculate the callgraph root + FunctionSummary calculateCallGraphRoot() { + std::map FunctionHasParent; + std::function discoverNodes = [&](ValueInfo V) { + if (!V.getSummaryList().size()) + return; // skip external functions that don't have summaries + + // Mark discovered if we haven't yet + FunctionHasParent.emplace(std::make_pair(V, false)); + + FunctionSummary *F = + dyn_cast(V.getSummaryList().front().get()); + assert(F != nullptr && "Expected FunctionSummary node"); + + for (auto &C : F->calls()) { + auto S = FunctionHasParent.find(C.first); + if (S != FunctionHasParent.end() && S->second) + continue; + FunctionHasParent[C.first] = true; + discoverNodes(C.first); + } + }; + + for (auto &S : *this) { + if (!S.second.SummaryList.size() || // skip external functions + !isa(S.second.SummaryList.front().get())) + continue; + discoverNodes(getValueInfo(S.first)); + } + + std::vector Edges; + // create edges to all roots in the Index + for (auto &P : FunctionHasParent) { + if (P.second) + continue; // skip over non-root nodes + Edges.push_back(std::make_pair(P.first, CalleeInfo{})); + } + assert(Edges.size() && "Couldn't find any roots in index callgraph!"); + auto CallGraphRoot = FunctionSummary::makeDummyFunctionSummary(Edges); + return CallGraphRoot; + } + bool withGlobalValueDeadStripping() const { return WithGlobalValueDeadStripping; } @@ -769,6 +836,55 @@ /// Summary). void collectDefinedGVSummariesPerModule( StringMap &ModuleToDefinedGVSummaries) const; + + void dumpSCCs(raw_ostream &O); +}; + +/// GraphTraits definition to build SCC for the index +template <> struct GraphTraits { + typedef ValueInfo NodeRef; + + static NodeRef valueInfoFromEdge(FunctionSummary::EdgeTy &P) { + return P.first; + } + using ChildIteratorType = + mapped_iterator::iterator, + decltype(&valueInfoFromEdge)>; + + static NodeRef getEntryNode(ValueInfo V) { return V; } + + static ChildIteratorType child_begin(NodeRef N) { + if (!N.getSummaryList().size()) // handle external function + return ChildIteratorType( + FunctionSummary::ExternalNode.CallGraphEdgeList.begin(), + &valueInfoFromEdge); + FunctionSummary *F = + cast(N.getSummaryList().front().get()); + return ChildIteratorType(F->CallGraphEdgeList.begin(), &valueInfoFromEdge); + } + + static ChildIteratorType child_end(NodeRef N) { + if (!N.getSummaryList().size()) // handle external function + return ChildIteratorType( + FunctionSummary::ExternalNode.CallGraphEdgeList.end(), + &valueInfoFromEdge); + FunctionSummary *F = + cast(N.getSummaryList().front().get()); + return ChildIteratorType(F->CallGraphEdgeList.end(), &valueInfoFromEdge); + } +}; + +template <> +struct GraphTraits : public GraphTraits { + static NodeRef getEntryNode(ModuleSummaryIndex *I) { + std::unique_ptr Root = + make_unique(I->calculateCallGraphRoot()); + GlobalValueSummaryInfo G; + G.SummaryList.push_back(std::move(Root)); + static auto P = + GlobalValueSummaryMapTy::value_type(GlobalValue::GUID(0), std::move(G)); + return ValueInfo(&P); + } }; } // end namespace llvm Index: lib/IR/ModuleSummaryIndex.cpp =================================================================== --- lib/IR/ModuleSummaryIndex.cpp +++ lib/IR/ModuleSummaryIndex.cpp @@ -13,9 +13,13 @@ //===----------------------------------------------------------------------===// #include "llvm/IR/ModuleSummaryIndex.h" +#include "llvm/ADT/SCCIterator.h" #include "llvm/ADT/StringMap.h" using namespace llvm; +FunctionSummary FunctionSummary::ExternalNode = + FunctionSummary::makeDummyFunctionSummary({}); + // Collect for the given module the list of function it defines // (GUID -> Summary). void ModuleSummaryIndex::collectDefinedFunctionsForModule( @@ -69,3 +73,21 @@ return true; return false; } + +LLVM_DUMP_METHOD +void ModuleSummaryIndex::dumpSCCs(raw_ostream &O) { + for (scc_iterator I = + scc_begin(this); + !I.isAtEnd(); ++I) { + O << "SCC (" << utostr(I->size()) << " node" << (I->size() == 1 ? "" : "s") + << ") {\n"; + for (const ValueInfo V : *I) { + FunctionSummary *F = nullptr; + if (V.getSummaryList().size()) + F = cast(V.getSummaryList().front().get()); + O << " " << (F ? F->modulePath() : "External") << " " + << utostr(V.getGUID()) << (I.hasLoop() ? " (has loop)" : "") << "\n"; + } + O << "}\n"; + } +} Index: lib/LTO/LTO.cpp =================================================================== --- lib/LTO/LTO.cpp +++ lib/LTO/LTO.cpp @@ -50,6 +50,10 @@ #define DEBUG_TYPE "lto" +static cl::opt + DumpThinCGSCCs("dump-thin-cg-sccs", cl::init(false), cl::Hidden, + cl::desc("Dump the SCCs in the ThinLTO index's callgraph")); + // The values are (type identifier, summary) pairs. typedef DenseMap< GlobalValue::GUID, @@ -1054,6 +1058,9 @@ ThinLTO.ModuleMap.size()); StringMap> ResolvedODR; + if (DumpThinCGSCCs) + ThinLTO.CombinedIndex.dumpSCCs(outs()); + if (Conf.OptLevel > 0) { ComputeCrossModuleImport(ThinLTO.CombinedIndex, ModuleToDefinedGVSummaries, ImportLists, ExportLists); Index: test/ThinLTO/X86/module_summary_graph_traits.ll =================================================================== --- /dev/null +++ test/ThinLTO/X86/module_summary_graph_traits.ll @@ -0,0 +1,50 @@ +; RUN: opt -module-summary %s -o %t1.bc +; RUN: llvm-lto2 run -dump-thin-cg-sccs %t1.bc -o %t.index.bc \ +; RUN: -r %t1.bc,external,px -r %t1.bc,l2,pl -r %t1.bc,l1,pl \ +; RUN: -r %t1.bc,simple,pl -r %t1.bc,root,pl | FileCheck %s + +; CHECK: SCC (1 node) { +; CHECK-NEXT: tmp1.bc 765152853862302398 (has loop) +; CHECK-NEXT: } + +; CHECK: SCC (1 node) { +; CHECK-NEXT: tmp1.bc 15440740835768581517 +; CHECK-NEXT: } + +; CHECK: SCC (1 node) { +; CHECK-NEXT: External 5224464028922159466 +; CHECK-NEXT: } + +; CHECK: SCC (1 node) { +; CHECK-NEXT: tmp1.bc 17000277804057984823 (has loop) +; CHECK-NEXT: } + +; CHECK: SCC (1 node) { +; CHECK-NEXT: tmp1.bc 5800840261926955363 (has loop) +; CHECK-NEXT: } + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +declare void @external() + +define void @l2() { + call void @l1() + ret void +} + +define void @l1() { + call void @l2() + ret void +} + +define i32 @simple() { + ret i32 23 +} + +define void @root() { + call void @l1() + call i32 @simple() + call void @external() + ret void +}