using GlobalValueSummaryList = std::vector<std::unique_ptr<GlobalValueSummary>>;
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
/// Struct that holds a reference to a particular GUID in a global value
/// summary.
struct ValueInfo {
- const GlobalValueSummaryMapTy::value_type *Ref = nullptr;
+ PointerIntPair<const GlobalValueSummaryMapTy::value_type *, 1, bool>
+ 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<std::unique_ptr<GlobalValueSummary>> 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<ValueInfo> {
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 inline bool isSpecialKey(ValueInfo V) {
+ return V == getTombstoneKey() || V == getEmptyKey();
}
- static bool isEqual(ValueInfo L, ValueInfo R) { return L.Ref == R.Ref; }
- static unsigned getHashValue(ValueInfo I) { return (uintptr_t)I.Ref; }
+ 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
/// 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<std::string> CfiFunctionDefs;
std::set<std::string> CfiFunctionDecls;
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(); }
/// 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.
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<GlobalValueSummaryMapTy::value_type *>(VI.Ref)
+ const_cast<GlobalValueSummaryMapTy::value_type *>(VI.getRef())
->second.SummaryList.push_back(std::move(Summary));
}
/// Summary).
void collectDefinedGVSummariesPerModule(
StringMap<GVSummaryMapTy> &ModuleToDefinedGVSummaries) const;
+
+ /// Export summary to dot file for GraphViz.
+ void exportToDot(raw_ostream& OS) const;
};
} // end namespace llvm
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<FunctionSummary>(
GlobalValueSummary::GVFlags(
std::function<BlockFrequencyInfo *(const Function &F)> 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
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
BitstreamCursor Stream(Buffer);
Stream.JumpToBit(ModuleBit);
- auto Index = llvm::make_unique<ModuleSummaryIndex>();
+ auto Index =
+ llvm::make_unique<ModuleSummaryIndex>(/*IsPerformingAnalysis=*/false);
ModuleSummaryIndexBitcodeReader R(std::move(Stream), Strtab, *Index,
ModuleIdentifier, 0);
#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
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<std::string> 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 "<unknown>";
+}
+
+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<FunctionSummary>(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<AliasSummary>(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<Edge> CrossModuleEdges;
+ DenseMap<GlobalValue::GUID, std::vector<uint64_t>> NodeMap;
+ StringMap<GVSummaryMapTy> ModuleToDefinedGVS;
+ collectDefinedGVSummariesPerModule(ModuleToDefinedGVS);
+
+ // Get node identifier in form MXXX_<GUID>. 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<size_t>(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<FunctionSummary>(SummaryIt.second)) {
+ A.add("shape", "record", "function");
+ } else if (isa<AliasSummary>(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<AliasSummary>(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<FunctionSummary>(SummaryIt.second))
+ for (auto &CGEdge : FS->calls())
+ Draw(SummaryIt.first, CGEdge.first.getGUID(),
+ static_cast<int>(CGEdge.second.Hotness));
+ }
+ OS << " }\n";
+ }
+
+ OS << " // Cross-module edges:\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 << "}";
+}
Ctx(Conf), CombinedModule(llvm::make_unique<Module>("ld-temp.o", Ctx)),
Mover(llvm::make_unique<IRMover>(*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());
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;
};
*/
std::unique_ptr<ModuleSummaryIndex> ThinLTOCodeGenerator::linkCombinedIndex() {
std::unique_ptr<ModuleSummaryIndex> CombinedIndex =
- llvm::make_unique<ModuleSummaryIndex>();
+ llvm::make_unique<ModuleSummaryIndex>(/*IsPeformingAnalysis=*/false);
uint64_t NextModuleId = 0;
for (auto &ModuleBuffer : Modules) {
if (Error Err = readModuleSummaryIndex(ModuleBuffer.getMemBuffer(),
// 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);
}
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;
}
}
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.
bool DevirtModule::runForTesting(
Module &M, function_ref<AAResults &(Function &)> AARGetter,
function_ref<OptimizationRemarkEmitter &(Function *)> 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.
--- /dev/null
+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 }
--- /dev/null
+; 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
+
+; Never assume specific order of clusters, nodes or edges
+; RUN: cat %t3.index.dot | FileCheck --check-prefix=STRUCTURE %s
+; RUN: cat %t3.index.dot | FileCheck --check-prefix=CLUSTER0 %s
+; RUN: cat %t3.index.dot | FileCheck --check-prefix=CLUSTER1 %s
+
+; STRUCTURE: digraph Summary {
+; STRUCTURE-DAG: subgraph cluster_0
+; STRUCTURE-DAG: subgraph cluster_1
+; STRUCTURE: // Cross-module edges:
+; STRUCTURE-DAG: M0_{{[0-9]+}} -> M1_{{[0-9]+}} // call
+; STRUCTURE-DAG: M0_{{[0-9]+}} -> M1_{{[0-9]+}} [{{.*}}]; // ref
+; STRUCTURE-NEXT: }
+
+; CLUSTER0: // Module: {{.*}}1.bc
+; CLUSTER0-NEXT: subgraph cluster_0 {
+; CLUSTER0-DAG: M0_[[MAIN_ALIAS:[0-9]+]] [{{.*}}main_alias{{.*}}]; // alias, dead
+; CLUSTER0-DAG: M0_[[MAIN:[0-9]+]] [{{.*}}main|extern{{.*}}]; // function
+; CLUSTER0-NEXT: // Edges:
+; CLUSTER0-NEXT: M0_[[MAIN_ALIAS]] -> M0_[[MAIN]] [{{.*}}]; // alias
+; CLUSTER0-NEXT: }
+
+; CLUSTER1: // Module: {{.*}}2.bc
+; CLUSTER1-NEXT: subgraph cluster_1 {
+; CLUSTER1-DAG: M1_[[A:[0-9]+]] [{{.*}}A|extern{{.*}}]; // variable
+; CLUSTER1-DAG: M1_[[FOO:[0-9]+]] [{{.*}}foo|extern{{.*}}]; // function, not eligible to import
+; CLUSTER1-DAG: M1_[[B:[0-9]+]] [{{.*}}B|extern{{.*}}]; // variable
+; CLUSTER1-DAG: M1_[[BAR:[0-9]+]] [{{.*}}bar|extern{{.*}}]; // function, dead
+; CLUSTER1-NEXT: // Edges:
+; CLUSTER1-DAG: M1_[[FOO]] -> M1_[[B]] [{{.*}}]; // ref
+; CLUSTER1-DAG: M1_[[FOO]] -> M1_[[A]] [{{.*}}]; // ref
+; CLUSTER1-DAG: }
+
+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
/// 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 + "': ");