Index: llvm/trunk/include/llvm/IR/ModuleSummaryIndex.h =================================================================== --- llvm/trunk/include/llvm/IR/ModuleSummaryIndex.h +++ llvm/trunk/include/llvm/IR/ModuleSummaryIndex.h @@ -69,9 +69,27 @@ using GlobalValueSummaryList = std::vector>; struct GlobalValueSummaryInfo { - /// The GlobalValue corresponding to this summary. This is only used in - /// per-module summaries. - const GlobalValue *GV = nullptr; + union NameOrGV { + NameOrGV(bool IsAnalysis) { + if (IsAnalysis) + GV = nullptr; + else + Name = ""; + } + + /// The GlobalValue corresponding to this summary. This is only used in + /// per-module summaries, when module analysis is being run. + const GlobalValue *GV; + + /// Summary string representation. This StringRef points to BC module + /// string table and is valid until module data is stored in memory. + /// This is guaranteed to happen until runThinLTOBackend function is + /// called, so it is safe to use this field during thin link. This field + /// is only valid if summary index was loaded from BC file. + StringRef Name; + } U; + + GlobalValueSummaryInfo(bool IsAnalysis) : U(IsAnalysis) {} /// List of global value summary structures for a particular value held /// in the GlobalValueMap. Requires a vector in the case of multiple @@ -91,32 +109,60 @@ /// Struct that holds a reference to a particular GUID in a global value /// summary. struct ValueInfo { - const GlobalValueSummaryMapTy::value_type *Ref = nullptr; + PointerIntPair + RefAndFlag; ValueInfo() = default; - ValueInfo(const GlobalValueSummaryMapTy::value_type *Ref) : Ref(Ref) {} + ValueInfo(bool IsAnalysis, const GlobalValueSummaryMapTy::value_type *R) { + RefAndFlag.setPointer(R); + RefAndFlag.setInt(IsAnalysis); + } - operator bool() const { return Ref; } + operator bool() const { return getRef(); } - GlobalValue::GUID getGUID() const { return Ref->first; } - const GlobalValue *getValue() const { return Ref->second.GV; } + GlobalValue::GUID getGUID() const { return getRef()->first; } + const GlobalValue *getValue() const { + assert(isFromAnalysis()); + return getRef()->second.U.GV; + } ArrayRef> getSummaryList() const { - return Ref->second.SummaryList; + return getRef()->second.SummaryList; + } + + StringRef name() const { + return isFromAnalysis() ? getRef()->second.U.GV->getName() + : getRef()->second.U.Name; + } + + bool isFromAnalysis() const { return RefAndFlag.getInt(); } + + const GlobalValueSummaryMapTy::value_type *getRef() const { + return RefAndFlag.getPointer(); } }; template <> struct DenseMapInfo { static inline ValueInfo getEmptyKey() { - return ValueInfo((GlobalValueSummaryMapTy::value_type *)-1); + return ValueInfo(false, (GlobalValueSummaryMapTy::value_type *)-8); } static inline ValueInfo getTombstoneKey() { - return ValueInfo((GlobalValueSummaryMapTy::value_type *)-2); + return ValueInfo(false, (GlobalValueSummaryMapTy::value_type *)-16); } - static bool isEqual(ValueInfo L, ValueInfo R) { return L.Ref == R.Ref; } - static unsigned getHashValue(ValueInfo I) { return (uintptr_t)I.Ref; } + static inline bool isSpecialKey(ValueInfo V) { + return V == getTombstoneKey() || V == getEmptyKey(); + } + + static bool isEqual(ValueInfo L, ValueInfo R) { + // We are not supposed to mix ValueInfo(s) with different analysis flag + // in a same container. + assert(isSpecialKey(L) || isSpecialKey(R) || + (L.isFromAnalysis() == R.isFromAnalysis())); + return L.getRef() == R.getRef(); + } + static unsigned getHashValue(ValueInfo I) { return (uintptr_t)I.getRef(); } }; /// \brief Function and variable summary information to aid decisions and @@ -619,6 +665,11 @@ /// considered live. bool WithGlobalValueDeadStripping = false; + /// If true then we're performing analysis of IR module, filling summary + /// accordingly. The value of 'false' means we're reading summary from + /// BC or YAML source. Affects the type of value stored in NameOrGV union + bool IsAnalysis; + std::set CfiFunctionDefs; std::set CfiFunctionDecls; @@ -627,10 +678,16 @@ GlobalValueSummaryMapTy::value_type * getOrInsertValuePtr(GlobalValue::GUID GUID) { - return &*GlobalValueMap.emplace(GUID, GlobalValueSummaryInfo{}).first; + return &*GlobalValueMap.emplace(GUID, GlobalValueSummaryInfo(IsAnalysis)).first; } public: + // See IsAnalysis variable comment. + ModuleSummaryIndex(bool IsPerformingAnalysis) + : IsAnalysis(IsPerformingAnalysis) {} + + bool isPerformingAnalysis() const { return IsAnalysis; } + gvsummary_iterator begin() { return GlobalValueMap.begin(); } const_gvsummary_iterator begin() const { return GlobalValueMap.begin(); } gvsummary_iterator end() { return GlobalValueMap.end(); } @@ -652,19 +709,28 @@ /// Return a ValueInfo for GUID if it exists, otherwise return ValueInfo(). ValueInfo getValueInfo(GlobalValue::GUID GUID) const { auto I = GlobalValueMap.find(GUID); - return ValueInfo(I == GlobalValueMap.end() ? nullptr : &*I); + return ValueInfo(IsAnalysis, I == GlobalValueMap.end() ? nullptr : &*I); } /// Return a ValueInfo for \p GUID. ValueInfo getOrInsertValueInfo(GlobalValue::GUID GUID) { - return ValueInfo(getOrInsertValuePtr(GUID)); + return ValueInfo(IsAnalysis, getOrInsertValuePtr(GUID)); + } + + /// Return a ValueInfo for \p GUID setting value \p Name. + ValueInfo getOrInsertValueInfo(GlobalValue::GUID GUID, StringRef Name) { + assert(!IsAnalysis); + auto VP = getOrInsertValuePtr(GUID); + VP->second.U.Name = Name; + return ValueInfo(IsAnalysis, VP); } /// Return a ValueInfo for \p GV and mark it as belonging to GV. ValueInfo getOrInsertValueInfo(const GlobalValue *GV) { + assert(IsAnalysis); auto VP = getOrInsertValuePtr(GV->getGUID()); - VP->second.GV = GV; - return ValueInfo(VP); + VP->second.U.GV = GV; + return ValueInfo(IsAnalysis, VP); } /// Return the GUID for \p OriginalId in the OidGuidMap. @@ -692,7 +758,7 @@ addOriginalName(VI.getGUID(), Summary->getOriginalName()); // Here we have a notionally const VI, but the value it points to is owned // by the non-const *this. - const_cast(VI.Ref) + const_cast(VI.getRef()) ->second.SummaryList.push_back(std::move(Summary)); } @@ -823,6 +889,9 @@ /// Summary). void collectDefinedGVSummariesPerModule( StringMap &ModuleToDefinedGVSummaries) const; + + /// Export summary to dot file for GraphViz. + void exportToDot(raw_ostream& OS) const; }; } // end namespace llvm Index: llvm/trunk/include/llvm/IR/ModuleSummaryIndexYAML.h =================================================================== --- llvm/trunk/include/llvm/IR/ModuleSummaryIndexYAML.h +++ llvm/trunk/include/llvm/IR/ModuleSummaryIndexYAML.h @@ -207,7 +207,8 @@ io.setError("key not an integer"); return; } - auto &Elem = V[KeyInt]; + auto P = V.emplace(KeyInt, /*IsAnalysis=*/false); + auto &Elem = (*P.first).second; for (auto &FSum : FSums) { Elem.SummaryList.push_back(llvm::make_unique( GlobalValueSummary::GVFlags( Index: llvm/trunk/lib/Analysis/ModuleSummaryAnalysis.cpp =================================================================== --- llvm/trunk/lib/Analysis/ModuleSummaryAnalysis.cpp +++ llvm/trunk/lib/Analysis/ModuleSummaryAnalysis.cpp @@ -372,7 +372,7 @@ std::function GetBFICallback, ProfileSummaryInfo *PSI) { assert(PSI); - ModuleSummaryIndex Index; + ModuleSummaryIndex Index(/*IsPerformingAnalysis=*/true); // Identify the local values in the llvm.used and llvm.compiler.used sets, // which should not be exported as they would then require renaming and Index: llvm/trunk/lib/Bitcode/Reader/BitcodeReader.cpp =================================================================== --- llvm/trunk/lib/Bitcode/Reader/BitcodeReader.cpp +++ llvm/trunk/lib/Bitcode/Reader/BitcodeReader.cpp @@ -4813,8 +4813,12 @@ if (PrintSummaryGUIDs) dbgs() << "GUID " << ValueGUID << "(" << OriginalNameID << ") is " << ValueName << "\n"; - ValueIdToValueInfoMap[ValueID] = - std::make_pair(TheIndex.getOrInsertValueInfo(ValueGUID), OriginalNameID); + + // UseStrtab is false for legacy summary formats and value names are + // created on stack. We can't use them outside of parseValueSymbolTable. + ValueIdToValueInfoMap[ValueID] = std::make_pair( + TheIndex.getOrInsertValueInfo(ValueGUID, UseStrtab ? ValueName : ""), + OriginalNameID); } // Specialized value symbol table parser used when reading module index @@ -5679,7 +5683,8 @@ BitstreamCursor Stream(Buffer); Stream.JumpToBit(ModuleBit); - auto Index = llvm::make_unique(); + auto Index = + llvm::make_unique(/*IsPerformingAnalysis=*/false); ModuleSummaryIndexBitcodeReader R(std::move(Stream), Strtab, *Index, ModuleIdentifier, 0); Index: llvm/trunk/lib/IR/ModuleSummaryIndex.cpp =================================================================== --- llvm/trunk/lib/IR/ModuleSummaryIndex.cpp +++ llvm/trunk/lib/IR/ModuleSummaryIndex.cpp @@ -14,6 +14,7 @@ #include "llvm/IR/ModuleSummaryIndex.h" #include "llvm/ADT/StringMap.h" +#include "llvm/Support/Path.h" using namespace llvm; // Collect for the given module the list of function it defines @@ -69,3 +70,246 @@ return true; return false; } + +namespace { +struct Attributes { + void add(const Twine &Name, const Twine &Value, + const Twine &Comment = Twine()); + std::string getAsString() const; + + std::vector Attrs; + std::string Comments; +}; + +struct Edge { + uint64_t SrcMod; + int Hotness; + GlobalValue::GUID Src; + GlobalValue::GUID Dst; +}; +} + +void Attributes::add(const Twine &Name, const Twine &Value, + const Twine &Comment) { + std::string A = Name.str(); + A += "=\""; + A += Value.str(); + A += "\""; + Attrs.push_back(A); + if (!Comment.isTriviallyEmpty()) { + if (Comments.empty()) + Comments = " // "; + else + Comments += ", "; + Comments += Comment.str(); + } +} + +std::string Attributes::getAsString() const { + if (Attrs.empty()) + return ""; + + std::string Ret = "["; + for (auto &A : Attrs) + Ret += A + ","; + Ret.pop_back(); + Ret += "];"; + Ret += Comments; + return Ret; +} + +static std::string linkageToString(GlobalValue::LinkageTypes LT) { + switch (LT) { + case GlobalValue::ExternalLinkage: + return "extern"; + case GlobalValue::AvailableExternallyLinkage: + return "av_ext"; + case GlobalValue::LinkOnceAnyLinkage: + return "linkonce"; + case GlobalValue::LinkOnceODRLinkage: + return "linkonce_odr"; + case GlobalValue::WeakAnyLinkage: + return "weak"; + case GlobalValue::WeakODRLinkage: + return "weak_odr"; + case GlobalValue::AppendingLinkage: + return "appending"; + case GlobalValue::InternalLinkage: + return "internal"; + case GlobalValue::PrivateLinkage: + return "private"; + case GlobalValue::ExternalWeakLinkage: + return "extern_weak"; + case GlobalValue::CommonLinkage: + return "common"; + } + + return ""; +} + +static std::string fflagsToString(FunctionSummary::FFlags F) { + auto FlagValue = [](unsigned V) { return V ? '1' : '0'; }; + char FlagRep[] = {FlagValue(F.ReadNone), FlagValue(F.ReadOnly), + FlagValue(F.NoRecurse), FlagValue(F.ReturnDoesNotAlias), 0}; + + return FlagRep; +} + +// Get string representation of function instruction count and flags. +static std::string getSummaryAttributes(GlobalValueSummary* GVS) { + auto *FS = dyn_cast_or_null(GVS); + if (!FS) + return ""; + + return std::string("inst: ") + std::to_string(FS->instCount()) + + ", ffl: " + fflagsToString(FS->fflags()); +} + +static std::string getNodeVisualName(const ValueInfo &VI) { + return VI.name().empty() ? std::string("@") + std::to_string(VI.getGUID()) + : VI.name().str(); +} + +static std::string getNodeLabel(const ValueInfo &VI, GlobalValueSummary *GVS) { + if (isa(GVS)) + return getNodeVisualName(VI); + + std::string Attrs = getSummaryAttributes(GVS); + std::string Label = + getNodeVisualName(VI) + "|" + linkageToString(GVS->linkage()); + if (!Attrs.empty()) + Label += std::string(" (") + Attrs + ")"; + Label += "}"; + + return Label; +} + +// Write definition of external node, which doesn't have any +// specific module associated with it. Typically this is function +// or variable defined in native object or library. +static void defineExternalNode(raw_ostream &OS, const char *Pfx, + const ValueInfo &VI) { + auto StrId = std::to_string(VI.getGUID()); + OS << " " << StrId << " [label=\"" << getNodeVisualName(VI) + << "\"]; // defined externally\n"; +} + +void ModuleSummaryIndex::exportToDot(raw_ostream& OS) const { + std::vector CrossModuleEdges; + DenseMap> NodeMap; + StringMap ModuleToDefinedGVS; + collectDefinedGVSummariesPerModule(ModuleToDefinedGVS); + + // Get node identifier in form MXXX_. The MXXX prefix is required, + // because we may have multiple linkonce functions summaries. + auto NodeId = [](uint64_t ModId, GlobalValue::GUID Id) { + return ModId == (uint64_t)-1 ? std::to_string(Id) + : std::string("M") + std::to_string(ModId) + + "_" + std::to_string(Id); + }; + + auto DrawEdge = [&](const char *Pfx, int SrcMod, GlobalValue::GUID SrcId, + int DstMod, GlobalValue::GUID DstId, int TypeOrHotness) { + // 0 corresponds to alias edge, 1 to ref edge, 2 to call with unknown + // hotness, ... + TypeOrHotness += 2; + static const char *EdgeAttrs[] = { + " [style=dotted]; // alias", + " [style=dashed]; // ref", + " // call (hotness : Unknown)", + " [color=blue]; // call (hotness : Cold)", + " // call (hotness : None)", + " [color=brown]; // call (hotness : Hot)", + " [style=bold,color=red]; // call (hotness : Critical)"}; + + assert(static_cast(TypeOrHotness) < + sizeof(EdgeAttrs) / sizeof(EdgeAttrs[0])); + OS << Pfx << NodeId(SrcMod, SrcId) << " -> " << NodeId(DstMod, DstId) + << EdgeAttrs[TypeOrHotness] << "\n"; + }; + + OS << "digraph Summary {\n"; + for (auto &ModIt : ModuleToDefinedGVS) { + auto ModId = getModuleId(ModIt.first()); + OS << " // Module: " << ModIt.first() << "\n"; + OS << " subgraph cluster_" << std::to_string(ModId) << " {\n"; + OS << " style = filled;\n"; + OS << " color = lightgrey;\n"; + OS << " label = \"" << sys::path::filename(ModIt.first()) << "\";\n"; + OS << " node [style=filled,fillcolor=lightblue];\n"; + + auto &GVSMap = ModIt.second; + auto Draw = [&](GlobalValue::GUID IdFrom, GlobalValue::GUID IdTo, int Hotness) { + if (!GVSMap.count(IdTo)) { + CrossModuleEdges.push_back({ModId, Hotness, IdFrom, IdTo}); + return; + } + DrawEdge(" ", ModId, IdFrom, ModId, IdTo, Hotness); + }; + + for (auto &SummaryIt : GVSMap) { + NodeMap[SummaryIt.first].push_back(ModId); + auto Flags = SummaryIt.second->flags(); + Attributes A; + if (isa(SummaryIt.second)) { + A.add("shape", "record", "function"); + } else if (isa(SummaryIt.second)) { + A.add("style", "dotted,filled", "alias"); + A.add("shape", "box"); + } else { + A.add("shape", "Mrecord", "variable"); + } + + auto VI = getValueInfo(SummaryIt.first); + A.add("label", getNodeLabel(VI, SummaryIt.second)); + if (!Flags.Live) + A.add("fillcolor", "red", "dead"); + else if (Flags.NotEligibleToImport) + A.add("fillcolor", "yellow", "not eligible to import"); + + OS << " " << NodeId(ModId, SummaryIt.first) << " " << A.getAsString() + << "\n"; + } + OS << " // Edges:\n"; + + for (auto &SummaryIt : GVSMap) { + auto *GVS = SummaryIt.second; + for (auto &R : GVS->refs()) + Draw(SummaryIt.first, R.getGUID(), -1); + + if (auto *AS = dyn_cast_or_null(SummaryIt.second)) { + auto AliaseeOrigId = AS->getAliasee().getOriginalName(); + auto AliaseeId = getGUIDFromOriginalID(AliaseeOrigId); + + Draw(SummaryIt.first, AliaseeId ? AliaseeId : AliaseeOrigId, -2); + continue; + } + + if (auto *FS = dyn_cast_or_null(SummaryIt.second)) + for (auto &CGEdge : FS->calls()) + Draw(SummaryIt.first, CGEdge.first.getGUID(), + static_cast(CGEdge.second.Hotness)); + } + OS << " }\n"; + } + + for (auto &E : CrossModuleEdges) { + auto &ModList = NodeMap[E.Dst]; + if (ModList.empty()) { + defineExternalNode(OS, " ", getValueInfo(E.Dst)); + // Add fake module to the list to draw an edge to an external node + // in the loop below. + ModList.push_back(-1); + } + for (auto DstMod : ModList) + // The edge representing call or ref is drawn to every module where target + // symbol is defined. When target is a linkonce symbol there can be + // multiple edges representing a single call or ref, both intra-module and + // cross-module. As we've already drawn all intra-module edges before we + // skip it here. + if (DstMod != E.SrcMod) + DrawEdge(" ", E.SrcMod, E.Src, DstMod, E.Dst, E.Hotness); + } + + OS << "}"; +} Index: llvm/trunk/lib/LTO/LTO.cpp =================================================================== --- llvm/trunk/lib/LTO/LTO.cpp +++ llvm/trunk/lib/LTO/LTO.cpp @@ -388,7 +388,8 @@ Ctx(Conf), CombinedModule(llvm::make_unique("ld-temp.o", Ctx)), Mover(llvm::make_unique(*CombinedModule)) {} -LTO::ThinLTOState::ThinLTOState(ThinBackend Backend) : Backend(Backend) { +LTO::ThinLTOState::ThinLTOState(ThinBackend Backend) + : Backend(Backend), CombinedIndex(/*IsPeformingAnalysis*/ false) { if (!Backend) this->Backend = createInProcessThinBackend(llvm::heavyweight_hardware_concurrency()); Index: llvm/trunk/lib/LTO/LTOBackend.cpp =================================================================== --- llvm/trunk/lib/LTO/LTOBackend.cpp +++ llvm/trunk/lib/LTO/LTOBackend.cpp @@ -103,6 +103,12 @@ if (EC) reportOpenError(Path, EC.message()); WriteIndexToFile(Index, OS); + + Path = OutputFileName + "index.dot"; + raw_fd_ostream OSDot(Path, EC, sys::fs::OpenFlags::F_None); + if (EC) + reportOpenError(Path, EC.message()); + Index.exportToDot(OSDot); return true; }; Index: llvm/trunk/lib/LTO/ThinLTOCodeGenerator.cpp =================================================================== --- llvm/trunk/lib/LTO/ThinLTOCodeGenerator.cpp +++ llvm/trunk/lib/LTO/ThinLTOCodeGenerator.cpp @@ -592,7 +592,7 @@ */ std::unique_ptr ThinLTOCodeGenerator::linkCombinedIndex() { std::unique_ptr CombinedIndex = - llvm::make_unique(); + llvm::make_unique(/*IsPeformingAnalysis=*/false); uint64_t NextModuleId = 0; for (auto &ModuleBuffer : Modules) { if (Error Err = readModuleSummaryIndex(ModuleBuffer.getMemBuffer(), Index: llvm/trunk/lib/Transforms/IPO/FunctionImport.cpp =================================================================== --- llvm/trunk/lib/Transforms/IPO/FunctionImport.cpp +++ llvm/trunk/lib/Transforms/IPO/FunctionImport.cpp @@ -231,7 +231,7 @@ // it, rather than needing to perform this mapping on each walk. auto GUID = Index.getGUIDFromOriginalID(VI.getGUID()); if (GUID == 0) - return nullptr; + return ValueInfo(); return Index.getValueInfo(GUID); } @@ -517,7 +517,7 @@ for (auto &S : Entry.second.SummaryList) if (S->isLive()) { DEBUG(dbgs() << "Live root: " << Entry.first << "\n"); - Worklist.push_back(ValueInfo(&Entry)); + Worklist.push_back(ValueInfo(/*IsAnalysis=*/false, &Entry)); ++LiveSymbols; break; } Index: llvm/trunk/lib/Transforms/IPO/LowerTypeTests.cpp =================================================================== --- llvm/trunk/lib/Transforms/IPO/LowerTypeTests.cpp +++ llvm/trunk/lib/Transforms/IPO/LowerTypeTests.cpp @@ -1529,7 +1529,7 @@ } bool LowerTypeTestsModule::runForTesting(Module &M) { - ModuleSummaryIndex Summary; + ModuleSummaryIndex Summary(/*IsPerformingAnalysis=*/false); // Handle the command-line summary arguments. This code is for testing // purposes only, so we handle errors directly. Index: llvm/trunk/lib/Transforms/IPO/WholeProgramDevirt.cpp =================================================================== --- llvm/trunk/lib/Transforms/IPO/WholeProgramDevirt.cpp +++ llvm/trunk/lib/Transforms/IPO/WholeProgramDevirt.cpp @@ -584,7 +584,7 @@ bool DevirtModule::runForTesting( Module &M, function_ref AARGetter, function_ref OREGetter) { - ModuleSummaryIndex Summary; + ModuleSummaryIndex Summary(/*IsPerformingAnalysis=*/false); // Handle the command-line summary arguments. This code is for testing // purposes only, so we handle errors directly. Index: llvm/trunk/test/ThinLTO/X86/Inputs/dot-dumper.ll =================================================================== --- llvm/trunk/test/ThinLTO/X86/Inputs/dot-dumper.ll +++ llvm/trunk/test/ThinLTO/X86/Inputs/dot-dumper.ll @@ -0,0 +1,20 @@ +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +@A = local_unnamed_addr global i32 10, align 4 +@B = local_unnamed_addr global i32 20, align 4 + +; Function Attrs: norecurse nounwind readonly uwtable +define i32 @foo() local_unnamed_addr #0 { + %1 = load i32, i32* @B, align 4 + %2 = load i32, i32* @A, align 4 + %3 = add nsw i32 %2, %1 + ret i32 %3 +} + +; Function Attrs: norecurse nounwind readnone uwtable +define i32 @bar() local_unnamed_addr { + ret i32 42 +} + +attributes #0 = { noinline } Index: llvm/trunk/test/ThinLTO/X86/dot-dumper.ll =================================================================== --- llvm/trunk/test/ThinLTO/X86/dot-dumper.ll +++ llvm/trunk/test/ThinLTO/X86/dot-dumper.ll @@ -0,0 +1,54 @@ +; RUN: opt -module-summary %s -o %t1.bc +; RUN: opt -module-summary %p/Inputs/dot-dumper.ll -o %t2.bc +; RUN: llvm-lto2 run -save-temps %t1.bc %t2.bc -o %t3 \ +; RUN: -r=%t1.bc,main,px \ +; RUN: -r=%t1.bc,main_alias,p \ +; RUN: -r=%t1.bc,foo, \ +; RUN: -r=%t1.bc,A, \ +; RUN: -r=%t2.bc,foo,p \ +; RUN: -r=%t2.bc,bar,p \ +; RUN: -r=%t2.bc,A,p \ +; RUN: -r=%t2.bc,B,p +; RUN: cat %t3.index.dot | FileCheck %s + +; CHECK: digraph Summary +; CHECK-NEXT: Module: + +; CHECK-LABEL: subgraph cluster_0 +; Node definitions can appear in any order, but they should go before edge list. +; CHECK-DAG: M0_[[MAIN_ALIAS:[0-9]+]] [{{.*}}main_alias{{.*}}]; // alias, dead +; CHECK-DAG: M0_[[MAIN:[0-9]+]] [{{.*}}main|extern{{.*}}]; // function +; CHECK: // Edges: +; CHECK-NEXT: M0_[[MAIN_ALIAS]] -> M0_[[MAIN]] [{{.*}}]; // alias + +; CHECK-LABEL: subgraph cluster_1 { +; CHECK: M1_[[A:[0-9]+]] [{{.*}}A|extern{{.*}}]; // variable + +; CHECK-DAG: M1_[[FOO:[0-9]+]] [{{.*}}foo|extern{{.*}}]; // function, not eligible to import +; CHECK-DAG: M1_[[B:[0-9]+]] [{{.*}}B|extern{{.*}}]; // variable +; CHECK-DAG: M1_[[BAR:[0-9]+]] [{{.*}}bar|extern{{.*}}]; // function, dead +; CHECK: Edges: + +; Order of edges in dot file is undefined +; CHECK-DAG: M1_[[FOO]] -> M1_[[B]] [{{.*}}]; // ref +; CHECK-DAG: M1_[[FOO]] -> M1_[[A]] [{{.*}}]; // ref +; CHECK: } + +; Cross-module edges +; CHECK-DAG: M0_[[MAIN]] -> M1_[[FOO]] // call +; CHECK-DAG: M0_[[MAIN]] -> M1_[[A]] [{{.*}}]; // ref + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +@A = external local_unnamed_addr global i32, align 4 + +; Function Attrs: nounwind uwtable +define i32 @main() local_unnamed_addr { + %1 = tail call i32 (...) @foo() + %2 = load i32, i32* @A, align 4 + %3 = add nsw i32 %2, %1 + ret i32 %3 +} +@main_alias = weak_odr alias i32 (), i32 ()* @main +declare i32 @foo(...) local_unnamed_addr Index: llvm/trunk/tools/llvm-lto/llvm-lto.cpp =================================================================== --- llvm/trunk/tools/llvm-lto/llvm-lto.cpp +++ llvm/trunk/tools/llvm-lto/llvm-lto.cpp @@ -367,7 +367,7 @@ /// This is meant to enable testing of ThinLTO combined index generation, /// currently available via the gold plugin via -thinlto. static void createCombinedModuleSummaryIndex() { - ModuleSummaryIndex CombinedIndex; + ModuleSummaryIndex CombinedIndex(/*IsPerformingAnalysis=*/false); uint64_t NextModuleId = 0; for (auto &Filename : InputFilenames) { ExitOnError ExitOnErr("llvm-lto: error loading file '" + Filename + "': ");