mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-12-02 00:16:25 +00:00
[ThinLTO] Implement summary visualizer
Differential revision: https://reviews.llvm.org/D41297 llvm-svn: 323062
This commit is contained in:
parent
2bbac01b81
commit
5a1f095ecc
@ -69,9 +69,27 @@ class GlobalValueSummary;
|
||||
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
|
||||
@ -91,32 +109,60 @@ using GlobalValueSummaryMapTy =
|
||||
/// 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 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 @@ private:
|
||||
/// 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;
|
||||
|
||||
@ -627,10 +678,16 @@ private:
|
||||
|
||||
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 @@ public:
|
||||
/// 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 @@ public:
|
||||
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));
|
||||
}
|
||||
|
||||
@ -823,6 +889,9 @@ public:
|
||||
/// Summary).
|
||||
void collectDefinedGVSummariesPerModule(
|
||||
StringMap<GVSummaryMapTy> &ModuleToDefinedGVSummaries) const;
|
||||
|
||||
/// Export summary to dot file for GraphViz.
|
||||
void exportToDot(raw_ostream& OS) const;
|
||||
};
|
||||
|
||||
} // end namespace llvm
|
||||
|
@ -207,7 +207,8 @@ template <> struct CustomMappingTraits<GlobalValueSummaryMapTy> {
|
||||
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(
|
||||
|
@ -372,7 +372,7 @@ ModuleSummaryIndex llvm::buildModuleSummaryIndex(
|
||||
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
|
||||
|
@ -4813,8 +4813,12 @@ void ModuleSummaryIndexBitcodeReader::setValueGUID(
|
||||
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 @@ Expected<std::unique_ptr<ModuleSummaryIndex>> BitcodeModule::getSummary() {
|
||||
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);
|
||||
|
||||
|
@ -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 @@ bool ModuleSummaryIndex::isGUIDLive(GlobalValue::GUID GUID) const {
|
||||
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";
|
||||
}
|
||||
|
||||
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 << "}";
|
||||
}
|
||||
|
@ -388,7 +388,8 @@ LTO::RegularLTOState::RegularLTOState(unsigned ParallelCodeGenParallelismLevel,
|
||||
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());
|
||||
|
@ -103,6 +103,12 @@ Error Config::addSaveTemps(std::string OutputFileName,
|
||||
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;
|
||||
};
|
||||
|
||||
|
@ -592,7 +592,7 @@ std::unique_ptr<TargetMachine> TargetMachineBuilder::create() const {
|
||||
*/
|
||||
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(),
|
||||
|
@ -231,7 +231,7 @@ updateValueInfoForIndirectCalls(const ModuleSummaryIndex &Index, ValueInfo VI) {
|
||||
// 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 @@ void llvm::computeDeadSymbols(
|
||||
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;
|
||||
}
|
||||
|
@ -1529,7 +1529,7 @@ LowerTypeTestsModule::LowerTypeTestsModule(
|
||||
}
|
||||
|
||||
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.
|
||||
|
@ -584,7 +584,7 @@ PreservedAnalyses WholeProgramDevirtPass::run(Module &M,
|
||||
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.
|
||||
|
20
test/ThinLTO/X86/Inputs/dot-dumper.ll
Normal file
20
test/ThinLTO/X86/Inputs/dot-dumper.ll
Normal file
@ -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 }
|
54
test/ThinLTO/X86/dot-dumper.ll
Normal file
54
test/ThinLTO/X86/dot-dumper.ll
Normal file
@ -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
|
@ -367,7 +367,7 @@ static void listSymbols(const TargetOptions &Options) {
|
||||
/// 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 + "': ");
|
||||
|
Loading…
Reference in New Issue
Block a user