mirror of
https://github.com/RPCS3/llvm.git
synced 2024-12-16 00:17:32 +00:00
ThinLTO: add early "dead-stripping" on the Index
Summary: Using the linker-supplied list of "preserved" symbols, we can compute the list of "dead" symbols, i.e. the one that are not reachable from a "preserved" symbol transitively on the reference graph. Right now we are using this information to mark these functions as non-eligible for import. The impact is two folds: - Reduction of compile time: we don't import these functions anywhere or import the function these symbols are calling. - The limited number of import/export leads to better internalization. Patch originally by Mehdi Amini. Reviewers: mehdi_amini, pcc Subscribers: llvm-commits Differential Revision: https://reviews.llvm.org/D23488 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@291177 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
44b0ebe8a3
commit
a4ca999339
@ -121,10 +121,16 @@ public:
|
|||||||
/// be renamed or references something that can't be renamed).
|
/// be renamed or references something that can't be renamed).
|
||||||
unsigned NotEligibleToImport : 1;
|
unsigned NotEligibleToImport : 1;
|
||||||
|
|
||||||
|
/// Indicate that the global value must be considered a live root for
|
||||||
|
/// index-based liveness analysis. Used for special LLVM values such as
|
||||||
|
/// llvm.global_ctors that the linker does not know about.
|
||||||
|
unsigned LiveRoot : 1;
|
||||||
|
|
||||||
/// Convenience Constructors
|
/// Convenience Constructors
|
||||||
explicit GVFlags(GlobalValue::LinkageTypes Linkage,
|
explicit GVFlags(GlobalValue::LinkageTypes Linkage,
|
||||||
bool NotEligibleToImport)
|
bool NotEligibleToImport, bool LiveRoot)
|
||||||
: Linkage(Linkage), NotEligibleToImport(NotEligibleToImport) {}
|
: Linkage(Linkage), NotEligibleToImport(NotEligibleToImport),
|
||||||
|
LiveRoot(LiveRoot) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -195,6 +201,14 @@ public:
|
|||||||
/// Return true if this global value can't be imported.
|
/// Return true if this global value can't be imported.
|
||||||
bool notEligibleToImport() const { return Flags.NotEligibleToImport; }
|
bool notEligibleToImport() const { return Flags.NotEligibleToImport; }
|
||||||
|
|
||||||
|
/// Return true if this global value must be considered a root for live
|
||||||
|
/// value analysis on the index.
|
||||||
|
bool liveRoot() const { return Flags.LiveRoot; }
|
||||||
|
|
||||||
|
/// Flag that this global value must be considered a root for live
|
||||||
|
/// value analysis on the index.
|
||||||
|
void setLiveRoot() { Flags.LiveRoot = true; }
|
||||||
|
|
||||||
/// Flag that this global value cannot be imported.
|
/// Flag that this global value cannot be imported.
|
||||||
void setNotEligibleToImport() { Flags.NotEligibleToImport = true; }
|
void setNotEligibleToImport() { Flags.NotEligibleToImport = true; }
|
||||||
|
|
||||||
@ -366,6 +380,7 @@ public:
|
|||||||
const_gvsummary_iterator begin() const { return GlobalValueMap.begin(); }
|
const_gvsummary_iterator begin() const { return GlobalValueMap.begin(); }
|
||||||
gvsummary_iterator end() { return GlobalValueMap.end(); }
|
gvsummary_iterator end() { return GlobalValueMap.end(); }
|
||||||
const_gvsummary_iterator end() const { return GlobalValueMap.end(); }
|
const_gvsummary_iterator end() const { return GlobalValueMap.end(); }
|
||||||
|
size_t size() const { return GlobalValueMap.size(); }
|
||||||
|
|
||||||
/// Get the list of global value summary objects for a given value name.
|
/// Get the list of global value summary objects for a given value name.
|
||||||
const GlobalValueSummaryList &getGlobalValueSummaryList(StringRef ValueName) {
|
const GlobalValueSummaryList &getGlobalValueSummaryList(StringRef ValueName) {
|
||||||
|
@ -78,7 +78,8 @@ template <> struct CustomMappingTraits<GlobalValueSummaryMapTy> {
|
|||||||
}
|
}
|
||||||
auto &Elem = V[KeyInt];
|
auto &Elem = V[KeyInt];
|
||||||
for (auto &FSum : FSums) {
|
for (auto &FSum : FSums) {
|
||||||
GlobalValueSummary::GVFlags GVFlags(GlobalValue::ExternalLinkage, false);
|
GlobalValueSummary::GVFlags GVFlags(GlobalValue::ExternalLinkage, false,
|
||||||
|
false);
|
||||||
Elem.push_back(llvm::make_unique<FunctionSummary>(
|
Elem.push_back(llvm::make_unique<FunctionSummary>(
|
||||||
GVFlags, 0, ArrayRef<ValueInfo>{},
|
GVFlags, 0, ArrayRef<ValueInfo>{},
|
||||||
ArrayRef<FunctionSummary::EdgeTy>{}, std::move(FSum.TypeTests)));
|
ArrayRef<FunctionSummary::EdgeTy>{}, std::move(FSum.TypeTests)));
|
||||||
|
@ -382,6 +382,10 @@ private:
|
|||||||
/// The unmangled name of the global.
|
/// The unmangled name of the global.
|
||||||
std::string IRName;
|
std::string IRName;
|
||||||
|
|
||||||
|
/// Keep track if the symbol is visible outside of ThinLTO (i.e. in
|
||||||
|
/// either a regular object or the regular LTO partition).
|
||||||
|
bool VisibleOutsideThinLTO = false;
|
||||||
|
|
||||||
bool UnnamedAddr = true;
|
bool UnnamedAddr = true;
|
||||||
|
|
||||||
/// This field keeps track of the partition number of this global. The
|
/// This field keeps track of the partition number of this global. The
|
||||||
@ -405,6 +409,9 @@ private:
|
|||||||
/// This global is either used by more than one partition or has an
|
/// This global is either used by more than one partition or has an
|
||||||
/// external reference, and therefore cannot be internalized.
|
/// external reference, and therefore cannot be internalized.
|
||||||
External = -2u,
|
External = -2u,
|
||||||
|
|
||||||
|
/// The RegularLTO partition
|
||||||
|
RegularLTO = 0,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -86,11 +86,15 @@ public:
|
|||||||
/// \p ExportLists contains for each Module the set of globals (GUID) that will
|
/// \p ExportLists contains for each Module the set of globals (GUID) that will
|
||||||
/// be imported by another module, or referenced by such a function. I.e. this
|
/// be imported by another module, or referenced by such a function. I.e. this
|
||||||
/// is the set of globals that need to be promoted/renamed appropriately.
|
/// is the set of globals that need to be promoted/renamed appropriately.
|
||||||
|
///
|
||||||
|
/// \p DeadSymbols (optional) contains a list of GUID that are deemed "dead" and
|
||||||
|
/// will be ignored for the purpose of importing.
|
||||||
void ComputeCrossModuleImport(
|
void ComputeCrossModuleImport(
|
||||||
const ModuleSummaryIndex &Index,
|
const ModuleSummaryIndex &Index,
|
||||||
const StringMap<GVSummaryMapTy> &ModuleToDefinedGVSummaries,
|
const StringMap<GVSummaryMapTy> &ModuleToDefinedGVSummaries,
|
||||||
StringMap<FunctionImporter::ImportMapTy> &ImportLists,
|
StringMap<FunctionImporter::ImportMapTy> &ImportLists,
|
||||||
StringMap<FunctionImporter::ExportSetTy> &ExportLists);
|
StringMap<FunctionImporter::ExportSetTy> &ExportLists,
|
||||||
|
const DenseSet<GlobalValue::GUID> *DeadSymbols = nullptr);
|
||||||
|
|
||||||
/// Compute all the imports for the given module using the Index.
|
/// Compute all the imports for the given module using the Index.
|
||||||
///
|
///
|
||||||
@ -100,6 +104,13 @@ void ComputeCrossModuleImportForModule(
|
|||||||
StringRef ModulePath, const ModuleSummaryIndex &Index,
|
StringRef ModulePath, const ModuleSummaryIndex &Index,
|
||||||
FunctionImporter::ImportMapTy &ImportList);
|
FunctionImporter::ImportMapTy &ImportList);
|
||||||
|
|
||||||
|
/// Compute all the symbols that are "dead": i.e these that can't be reached
|
||||||
|
/// in the graph from any of the given symbols listed in
|
||||||
|
/// \p GUIDPreservedSymbols.
|
||||||
|
DenseSet<GlobalValue::GUID>
|
||||||
|
computeDeadSymbols(const ModuleSummaryIndex &Index,
|
||||||
|
const DenseSet<GlobalValue::GUID> &GUIDPreservedSymbols);
|
||||||
|
|
||||||
/// Compute the set of summaries needed for a ThinLTO backend compilation of
|
/// Compute the set of summaries needed for a ThinLTO backend compilation of
|
||||||
/// \p ModulePath.
|
/// \p ModulePath.
|
||||||
//
|
//
|
||||||
|
@ -189,7 +189,8 @@ computeFunctionSummary(ModuleSummaryIndex &Index, const Module &M,
|
|||||||
// Inliner doesn't handle variadic functions.
|
// Inliner doesn't handle variadic functions.
|
||||||
// FIXME: refactor this to use the same code that inliner is using.
|
// FIXME: refactor this to use the same code that inliner is using.
|
||||||
F.isVarArg();
|
F.isVarArg();
|
||||||
GlobalValueSummary::GVFlags Flags(F.getLinkage(), NotEligibleForImport);
|
GlobalValueSummary::GVFlags Flags(F.getLinkage(), NotEligibleForImport,
|
||||||
|
/* LiveRoot = */ false);
|
||||||
auto FuncSummary = llvm::make_unique<FunctionSummary>(
|
auto FuncSummary = llvm::make_unique<FunctionSummary>(
|
||||||
Flags, NumInsts, RefEdges.takeVector(), CallGraphEdges.takeVector(),
|
Flags, NumInsts, RefEdges.takeVector(), CallGraphEdges.takeVector(),
|
||||||
TypeTests.takeVector());
|
TypeTests.takeVector());
|
||||||
@ -205,7 +206,8 @@ computeVariableSummary(ModuleSummaryIndex &Index, const GlobalVariable &V,
|
|||||||
SmallPtrSet<const User *, 8> Visited;
|
SmallPtrSet<const User *, 8> Visited;
|
||||||
findRefEdges(&V, RefEdges, Visited);
|
findRefEdges(&V, RefEdges, Visited);
|
||||||
bool NonRenamableLocal = isNonRenamableLocal(V);
|
bool NonRenamableLocal = isNonRenamableLocal(V);
|
||||||
GlobalValueSummary::GVFlags Flags(V.getLinkage(), NonRenamableLocal);
|
GlobalValueSummary::GVFlags Flags(V.getLinkage(), NonRenamableLocal,
|
||||||
|
/* LiveRoot = */ false);
|
||||||
auto GVarSummary =
|
auto GVarSummary =
|
||||||
llvm::make_unique<GlobalVarSummary>(Flags, RefEdges.takeVector());
|
llvm::make_unique<GlobalVarSummary>(Flags, RefEdges.takeVector());
|
||||||
if (NonRenamableLocal)
|
if (NonRenamableLocal)
|
||||||
@ -217,7 +219,8 @@ static void
|
|||||||
computeAliasSummary(ModuleSummaryIndex &Index, const GlobalAlias &A,
|
computeAliasSummary(ModuleSummaryIndex &Index, const GlobalAlias &A,
|
||||||
DenseSet<GlobalValue::GUID> &CantBePromoted) {
|
DenseSet<GlobalValue::GUID> &CantBePromoted) {
|
||||||
bool NonRenamableLocal = isNonRenamableLocal(A);
|
bool NonRenamableLocal = isNonRenamableLocal(A);
|
||||||
GlobalValueSummary::GVFlags Flags(A.getLinkage(), NonRenamableLocal);
|
GlobalValueSummary::GVFlags Flags(A.getLinkage(), NonRenamableLocal,
|
||||||
|
/* LiveRoot = */ false);
|
||||||
auto AS = llvm::make_unique<AliasSummary>(Flags, ArrayRef<ValueInfo>{});
|
auto AS = llvm::make_unique<AliasSummary>(Flags, ArrayRef<ValueInfo>{});
|
||||||
auto *Aliasee = A.getBaseObject();
|
auto *Aliasee = A.getBaseObject();
|
||||||
auto *AliaseeSummary = Index.getGlobalValueSummary(*Aliasee);
|
auto *AliaseeSummary = Index.getGlobalValueSummary(*Aliasee);
|
||||||
@ -228,6 +231,16 @@ computeAliasSummary(ModuleSummaryIndex &Index, const GlobalAlias &A,
|
|||||||
Index.addGlobalValueSummary(A.getName(), std::move(AS));
|
Index.addGlobalValueSummary(A.getName(), std::move(AS));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set LiveRoot flag on entries matching the given value name.
|
||||||
|
static void setLiveRoot(ModuleSummaryIndex &Index, StringRef Name) {
|
||||||
|
auto SummaryList =
|
||||||
|
Index.findGlobalValueSummaryList(GlobalValue::getGUID(Name));
|
||||||
|
if (SummaryList == Index.end())
|
||||||
|
return;
|
||||||
|
for (auto &Summary : SummaryList->second)
|
||||||
|
Summary->setLiveRoot();
|
||||||
|
}
|
||||||
|
|
||||||
ModuleSummaryIndex llvm::buildModuleSummaryIndex(
|
ModuleSummaryIndex llvm::buildModuleSummaryIndex(
|
||||||
const Module &M,
|
const Module &M,
|
||||||
std::function<BlockFrequencyInfo *(const Function &F)> GetBFICallback,
|
std::function<BlockFrequencyInfo *(const Function &F)> GetBFICallback,
|
||||||
@ -293,6 +306,15 @@ ModuleSummaryIndex llvm::buildModuleSummaryIndex(
|
|||||||
Summary->setNotEligibleToImport();
|
Summary->setNotEligibleToImport();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The linker doesn't know about these LLVM produced values, so we need
|
||||||
|
// to flag them as live in the index to ensure index-based dead value
|
||||||
|
// analysis treats them as live roots of the analysis.
|
||||||
|
setLiveRoot(Index, "llvm.used");
|
||||||
|
setLiveRoot(Index, "llvm.compiler.used");
|
||||||
|
setLiveRoot(Index, "llvm.global_ctors");
|
||||||
|
setLiveRoot(Index, "llvm.global_dtors");
|
||||||
|
setLiveRoot(Index, "llvm.global.annotations");
|
||||||
|
|
||||||
if (!M.getModuleInlineAsm().empty()) {
|
if (!M.getModuleInlineAsm().empty()) {
|
||||||
// Collect the local values defined by module level asm, and set up
|
// Collect the local values defined by module level asm, and set up
|
||||||
// summaries for these symbols so that they can be marked as NoRename,
|
// summaries for these symbols so that they can be marked as NoRename,
|
||||||
@ -316,7 +338,8 @@ ModuleSummaryIndex llvm::buildModuleSummaryIndex(
|
|||||||
return;
|
return;
|
||||||
assert(GV->isDeclaration() && "Def in module asm already has definition");
|
assert(GV->isDeclaration() && "Def in module asm already has definition");
|
||||||
GlobalValueSummary::GVFlags GVFlags(GlobalValue::InternalLinkage,
|
GlobalValueSummary::GVFlags GVFlags(GlobalValue::InternalLinkage,
|
||||||
/* NotEligibleToImport */ true);
|
/* NotEligibleToImport */ true,
|
||||||
|
/* LiveRoot */ true);
|
||||||
CantBePromoted.insert(GlobalValue::getGUID(Name));
|
CantBePromoted.insert(GlobalValue::getGUID(Name));
|
||||||
// Create the appropriate summary type.
|
// Create the appropriate summary type.
|
||||||
if (isa<Function>(GV)) {
|
if (isa<Function>(GV)) {
|
||||||
|
@ -802,7 +802,11 @@ static GlobalValueSummary::GVFlags getDecodedGVSummaryFlags(uint64_t RawFlags,
|
|||||||
auto Linkage = GlobalValue::LinkageTypes(RawFlags & 0xF); // 4 bits
|
auto Linkage = GlobalValue::LinkageTypes(RawFlags & 0xF); // 4 bits
|
||||||
RawFlags = RawFlags >> 4;
|
RawFlags = RawFlags >> 4;
|
||||||
bool NotEligibleToImport = (RawFlags & 0x1) || Version < 3;
|
bool NotEligibleToImport = (RawFlags & 0x1) || Version < 3;
|
||||||
return GlobalValueSummary::GVFlags(Linkage, NotEligibleToImport);
|
// The LiveRoot flag wasn't introduced until version 3. For dead stripping
|
||||||
|
// to work correctly on earlier versions, we must conservatively treat all
|
||||||
|
// values as live.
|
||||||
|
bool LiveRoot = (RawFlags & 0x2) || Version < 3;
|
||||||
|
return GlobalValueSummary::GVFlags(Linkage, NotEligibleToImport, LiveRoot);
|
||||||
}
|
}
|
||||||
|
|
||||||
static GlobalValue::VisibilityTypes getDecodedVisibility(unsigned Val) {
|
static GlobalValue::VisibilityTypes getDecodedVisibility(unsigned Val) {
|
||||||
|
@ -972,6 +972,7 @@ static uint64_t getEncodedGVSummaryFlags(GlobalValueSummary::GVFlags Flags) {
|
|||||||
uint64_t RawFlags = 0;
|
uint64_t RawFlags = 0;
|
||||||
|
|
||||||
RawFlags |= Flags.NotEligibleToImport; // bool
|
RawFlags |= Flags.NotEligibleToImport; // bool
|
||||||
|
RawFlags |= (Flags.LiveRoot << 1);
|
||||||
// Linkage don't need to be remapped at that time for the summary. Any future
|
// Linkage don't need to be remapped at that time for the summary. Any future
|
||||||
// change to the getEncodedLinkage() function will need to be taken into
|
// change to the getEncodedLinkage() function will need to be taken into
|
||||||
// account here as well.
|
// account here as well.
|
||||||
|
@ -337,12 +337,21 @@ void LTO::addSymbolToGlobalRes(SmallPtrSet<GlobalValue *, 8> &Used,
|
|||||||
if (Res.Prevailing)
|
if (Res.Prevailing)
|
||||||
GlobalRes.IRName = GV->getName();
|
GlobalRes.IRName = GV->getName();
|
||||||
}
|
}
|
||||||
|
// Set the partition to external if we know it is used elsewhere, e.g.
|
||||||
|
// it is visible to a regular object, is referenced from llvm.compiler_used,
|
||||||
|
// or was already recorded as being referenced from a different partition.
|
||||||
if (Res.VisibleToRegularObj || (GV && Used.count(GV)) ||
|
if (Res.VisibleToRegularObj || (GV && Used.count(GV)) ||
|
||||||
(GlobalRes.Partition != GlobalResolution::Unknown &&
|
(GlobalRes.Partition != GlobalResolution::Unknown &&
|
||||||
GlobalRes.Partition != Partition))
|
GlobalRes.Partition != Partition)) {
|
||||||
GlobalRes.Partition = GlobalResolution::External;
|
GlobalRes.Partition = GlobalResolution::External;
|
||||||
else
|
} else
|
||||||
|
// First recorded reference, save the current partition.
|
||||||
GlobalRes.Partition = Partition;
|
GlobalRes.Partition = Partition;
|
||||||
|
|
||||||
|
// Flag as visible outside of ThinLTO if visible from a regular object or
|
||||||
|
// if this is a reference in the regular LTO partition.
|
||||||
|
GlobalRes.VisibleOutsideThinLTO |=
|
||||||
|
(Res.VisibleToRegularObj || (Partition == GlobalResolution::RegularLTO));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void writeToResolutionFile(raw_ostream &OS, InputFile *Input,
|
static void writeToResolutionFile(raw_ostream &OS, InputFile *Input,
|
||||||
@ -848,6 +857,19 @@ Error LTO::runThinLTO(AddStreamFn AddStream, NativeObjectCache Cache,
|
|||||||
if (!ModuleToDefinedGVSummaries.count(Mod.first))
|
if (!ModuleToDefinedGVSummaries.count(Mod.first))
|
||||||
ModuleToDefinedGVSummaries.try_emplace(Mod.first);
|
ModuleToDefinedGVSummaries.try_emplace(Mod.first);
|
||||||
|
|
||||||
|
// Compute "dead" symbols, we don't want to import/export these!
|
||||||
|
DenseSet<GlobalValue::GUID> GUIDPreservedSymbols;
|
||||||
|
for (auto &Res : GlobalResolutions) {
|
||||||
|
if (Res.second.VisibleOutsideThinLTO &&
|
||||||
|
// IRName will be defined if we have seen the prevailing copy of
|
||||||
|
// this value. If not, no need to preserve any ThinLTO copies.
|
||||||
|
!Res.second.IRName.empty())
|
||||||
|
GUIDPreservedSymbols.insert(GlobalValue::getGUID(Res.second.IRName));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto DeadSymbols =
|
||||||
|
computeDeadSymbols(ThinLTO.CombinedIndex, GUIDPreservedSymbols);
|
||||||
|
|
||||||
StringMap<FunctionImporter::ImportMapTy> ImportLists(
|
StringMap<FunctionImporter::ImportMapTy> ImportLists(
|
||||||
ThinLTO.ModuleMap.size());
|
ThinLTO.ModuleMap.size());
|
||||||
StringMap<FunctionImporter::ExportSetTy> ExportLists(
|
StringMap<FunctionImporter::ExportSetTy> ExportLists(
|
||||||
@ -856,12 +878,21 @@ Error LTO::runThinLTO(AddStreamFn AddStream, NativeObjectCache Cache,
|
|||||||
|
|
||||||
if (Conf.OptLevel > 0) {
|
if (Conf.OptLevel > 0) {
|
||||||
ComputeCrossModuleImport(ThinLTO.CombinedIndex, ModuleToDefinedGVSummaries,
|
ComputeCrossModuleImport(ThinLTO.CombinedIndex, ModuleToDefinedGVSummaries,
|
||||||
ImportLists, ExportLists);
|
ImportLists, ExportLists, &DeadSymbols);
|
||||||
|
|
||||||
std::set<GlobalValue::GUID> ExportedGUIDs;
|
std::set<GlobalValue::GUID> ExportedGUIDs;
|
||||||
for (auto &Res : GlobalResolutions) {
|
for (auto &Res : GlobalResolutions) {
|
||||||
if (!Res.second.IRName.empty() &&
|
// First check if the symbol was flagged as having external references.
|
||||||
Res.second.Partition == GlobalResolution::External)
|
if (Res.second.Partition != GlobalResolution::External)
|
||||||
|
continue;
|
||||||
|
// IRName will be defined if we have seen the prevailing copy of
|
||||||
|
// this value. If not, no need to mark as exported from a ThinLTO
|
||||||
|
// partition (and we can't get the GUID).
|
||||||
|
if (Res.second.IRName.empty())
|
||||||
|
continue;
|
||||||
|
auto GUID = GlobalValue::getGUID(Res.second.IRName);
|
||||||
|
// Mark exported unless index-based analysis determined it to be dead.
|
||||||
|
if (!DeadSymbols.count(GUID))
|
||||||
ExportedGUIDs.insert(GlobalValue::getGUID(Res.second.IRName));
|
ExportedGUIDs.insert(GlobalValue::getGUID(Res.second.IRName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -581,11 +581,18 @@ void ThinLTOCodeGenerator::promote(Module &TheModule,
|
|||||||
StringMap<GVSummaryMapTy> ModuleToDefinedGVSummaries;
|
StringMap<GVSummaryMapTy> ModuleToDefinedGVSummaries;
|
||||||
Index.collectDefinedGVSummariesPerModule(ModuleToDefinedGVSummaries);
|
Index.collectDefinedGVSummariesPerModule(ModuleToDefinedGVSummaries);
|
||||||
|
|
||||||
|
// Convert the preserved symbols set from string to GUID
|
||||||
|
auto GUIDPreservedSymbols = computeGUIDPreservedSymbols(
|
||||||
|
PreservedSymbols, Triple(TheModule.getTargetTriple()));
|
||||||
|
|
||||||
|
// Compute "dead" symbols, we don't want to import/export these!
|
||||||
|
auto DeadSymbols = computeDeadSymbols(Index, GUIDPreservedSymbols);
|
||||||
|
|
||||||
// Generate import/export list
|
// Generate import/export list
|
||||||
StringMap<FunctionImporter::ImportMapTy> ImportLists(ModuleCount);
|
StringMap<FunctionImporter::ImportMapTy> ImportLists(ModuleCount);
|
||||||
StringMap<FunctionImporter::ExportSetTy> ExportLists(ModuleCount);
|
StringMap<FunctionImporter::ExportSetTy> ExportLists(ModuleCount);
|
||||||
ComputeCrossModuleImport(Index, ModuleToDefinedGVSummaries, ImportLists,
|
ComputeCrossModuleImport(Index, ModuleToDefinedGVSummaries, ImportLists,
|
||||||
ExportLists);
|
ExportLists, &DeadSymbols);
|
||||||
|
|
||||||
// Resolve LinkOnce/Weak symbols.
|
// Resolve LinkOnce/Weak symbols.
|
||||||
StringMap<std::map<GlobalValue::GUID, GlobalValue::LinkageTypes>> ResolvedODR;
|
StringMap<std::map<GlobalValue::GUID, GlobalValue::LinkageTypes>> ResolvedODR;
|
||||||
@ -594,10 +601,6 @@ void ThinLTOCodeGenerator::promote(Module &TheModule,
|
|||||||
thinLTOResolveWeakForLinkerModule(
|
thinLTOResolveWeakForLinkerModule(
|
||||||
TheModule, ModuleToDefinedGVSummaries[ModuleIdentifier]);
|
TheModule, ModuleToDefinedGVSummaries[ModuleIdentifier]);
|
||||||
|
|
||||||
// Convert the preserved symbols set from string to GUID
|
|
||||||
auto GUIDPreservedSymbols = computeGUIDPreservedSymbols(
|
|
||||||
PreservedSymbols, Triple(TheModule.getTargetTriple()));
|
|
||||||
|
|
||||||
// Promote the exported values in the index, so that they are promoted
|
// Promote the exported values in the index, so that they are promoted
|
||||||
// in the module.
|
// in the module.
|
||||||
auto isExported = [&](StringRef ModuleIdentifier, GlobalValue::GUID GUID) {
|
auto isExported = [&](StringRef ModuleIdentifier, GlobalValue::GUID GUID) {
|
||||||
@ -623,11 +626,18 @@ void ThinLTOCodeGenerator::crossModuleImport(Module &TheModule,
|
|||||||
StringMap<GVSummaryMapTy> ModuleToDefinedGVSummaries(ModuleCount);
|
StringMap<GVSummaryMapTy> ModuleToDefinedGVSummaries(ModuleCount);
|
||||||
Index.collectDefinedGVSummariesPerModule(ModuleToDefinedGVSummaries);
|
Index.collectDefinedGVSummariesPerModule(ModuleToDefinedGVSummaries);
|
||||||
|
|
||||||
|
// Convert the preserved symbols set from string to GUID
|
||||||
|
auto GUIDPreservedSymbols = computeGUIDPreservedSymbols(
|
||||||
|
PreservedSymbols, Triple(TheModule.getTargetTriple()));
|
||||||
|
|
||||||
|
// Compute "dead" symbols, we don't want to import/export these!
|
||||||
|
auto DeadSymbols = computeDeadSymbols(Index, GUIDPreservedSymbols);
|
||||||
|
|
||||||
// Generate import/export list
|
// Generate import/export list
|
||||||
StringMap<FunctionImporter::ImportMapTy> ImportLists(ModuleCount);
|
StringMap<FunctionImporter::ImportMapTy> ImportLists(ModuleCount);
|
||||||
StringMap<FunctionImporter::ExportSetTy> ExportLists(ModuleCount);
|
StringMap<FunctionImporter::ExportSetTy> ExportLists(ModuleCount);
|
||||||
ComputeCrossModuleImport(Index, ModuleToDefinedGVSummaries, ImportLists,
|
ComputeCrossModuleImport(Index, ModuleToDefinedGVSummaries, ImportLists,
|
||||||
ExportLists);
|
ExportLists, &DeadSymbols);
|
||||||
auto &ImportList = ImportLists[TheModule.getModuleIdentifier()];
|
auto &ImportList = ImportLists[TheModule.getModuleIdentifier()];
|
||||||
|
|
||||||
crossImportIntoModule(TheModule, Index, ModuleMap, ImportList);
|
crossImportIntoModule(TheModule, Index, ModuleMap, ImportList);
|
||||||
@ -697,11 +707,14 @@ void ThinLTOCodeGenerator::internalize(Module &TheModule,
|
|||||||
StringMap<GVSummaryMapTy> ModuleToDefinedGVSummaries(ModuleCount);
|
StringMap<GVSummaryMapTy> ModuleToDefinedGVSummaries(ModuleCount);
|
||||||
Index.collectDefinedGVSummariesPerModule(ModuleToDefinedGVSummaries);
|
Index.collectDefinedGVSummariesPerModule(ModuleToDefinedGVSummaries);
|
||||||
|
|
||||||
|
// Compute "dead" symbols, we don't want to import/export these!
|
||||||
|
auto DeadSymbols = computeDeadSymbols(Index, GUIDPreservedSymbols);
|
||||||
|
|
||||||
// Generate import/export list
|
// Generate import/export list
|
||||||
StringMap<FunctionImporter::ImportMapTy> ImportLists(ModuleCount);
|
StringMap<FunctionImporter::ImportMapTy> ImportLists(ModuleCount);
|
||||||
StringMap<FunctionImporter::ExportSetTy> ExportLists(ModuleCount);
|
StringMap<FunctionImporter::ExportSetTy> ExportLists(ModuleCount);
|
||||||
ComputeCrossModuleImport(Index, ModuleToDefinedGVSummaries, ImportLists,
|
ComputeCrossModuleImport(Index, ModuleToDefinedGVSummaries, ImportLists,
|
||||||
ExportLists);
|
ExportLists, &DeadSymbols);
|
||||||
auto &ExportList = ExportLists[ModuleIdentifier];
|
auto &ExportList = ExportLists[ModuleIdentifier];
|
||||||
|
|
||||||
// Be friendly and don't nuke totally the module when the client didn't
|
// Be friendly and don't nuke totally the module when the client didn't
|
||||||
@ -836,17 +849,20 @@ void ThinLTOCodeGenerator::run() {
|
|||||||
StringMap<GVSummaryMapTy> ModuleToDefinedGVSummaries(ModuleCount);
|
StringMap<GVSummaryMapTy> ModuleToDefinedGVSummaries(ModuleCount);
|
||||||
Index->collectDefinedGVSummariesPerModule(ModuleToDefinedGVSummaries);
|
Index->collectDefinedGVSummariesPerModule(ModuleToDefinedGVSummaries);
|
||||||
|
|
||||||
|
// Convert the preserved symbols set from string to GUID, this is needed for
|
||||||
|
// computing the caching hash and the internalization.
|
||||||
|
auto GUIDPreservedSymbols =
|
||||||
|
computeGUIDPreservedSymbols(PreservedSymbols, TMBuilder.TheTriple);
|
||||||
|
|
||||||
|
// Compute "dead" symbols, we don't want to import/export these!
|
||||||
|
auto DeadSymbols = computeDeadSymbols(*Index, GUIDPreservedSymbols);
|
||||||
|
|
||||||
// Collect the import/export lists for all modules from the call-graph in the
|
// Collect the import/export lists for all modules from the call-graph in the
|
||||||
// combined index.
|
// combined index.
|
||||||
StringMap<FunctionImporter::ImportMapTy> ImportLists(ModuleCount);
|
StringMap<FunctionImporter::ImportMapTy> ImportLists(ModuleCount);
|
||||||
StringMap<FunctionImporter::ExportSetTy> ExportLists(ModuleCount);
|
StringMap<FunctionImporter::ExportSetTy> ExportLists(ModuleCount);
|
||||||
ComputeCrossModuleImport(*Index, ModuleToDefinedGVSummaries, ImportLists,
|
ComputeCrossModuleImport(*Index, ModuleToDefinedGVSummaries, ImportLists,
|
||||||
ExportLists);
|
ExportLists, &DeadSymbols);
|
||||||
|
|
||||||
// Convert the preserved symbols set from string to GUID, this is needed for
|
|
||||||
// computing the caching hash and the internalization.
|
|
||||||
auto GUIDPreservedSymbols =
|
|
||||||
computeGUIDPreservedSymbols(PreservedSymbols, TMBuilder.TheTriple);
|
|
||||||
|
|
||||||
// We use a std::map here to be able to have a defined ordering when
|
// We use a std::map here to be able to have a defined ordering when
|
||||||
// producing a hash for the cache entry.
|
// producing a hash for the cache entry.
|
||||||
|
@ -36,7 +36,10 @@
|
|||||||
|
|
||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
|
|
||||||
STATISTIC(NumImported, "Number of functions imported");
|
STATISTIC(NumImportedFunctions, "Number of functions imported");
|
||||||
|
STATISTIC(NumImportedModules, "Number of modules imported from");
|
||||||
|
STATISTIC(NumDeadSymbols, "Number of dead stripped symbols in index");
|
||||||
|
STATISTIC(NumLiveSymbols, "Number of live symbols in index");
|
||||||
|
|
||||||
/// Limit on instruction count of imported functions.
|
/// Limit on instruction count of imported functions.
|
||||||
static cl::opt<unsigned> ImportInstrLimit(
|
static cl::opt<unsigned> ImportInstrLimit(
|
||||||
@ -69,6 +72,9 @@ static cl::opt<float> ImportColdMultiplier(
|
|||||||
static cl::opt<bool> PrintImports("print-imports", cl::init(false), cl::Hidden,
|
static cl::opt<bool> PrintImports("print-imports", cl::init(false), cl::Hidden,
|
||||||
cl::desc("Print imported functions"));
|
cl::desc("Print imported functions"));
|
||||||
|
|
||||||
|
static cl::opt<bool> ComputeDead("compute-dead", cl::init(true), cl::Hidden,
|
||||||
|
cl::desc("Compute dead symbols"));
|
||||||
|
|
||||||
// Temporary allows the function import pass to disable always linking
|
// Temporary allows the function import pass to disable always linking
|
||||||
// referenced discardable symbols.
|
// referenced discardable symbols.
|
||||||
static cl::opt<bool>
|
static cl::opt<bool>
|
||||||
@ -274,7 +280,8 @@ static void computeImportForFunction(
|
|||||||
static void ComputeImportForModule(
|
static void ComputeImportForModule(
|
||||||
const GVSummaryMapTy &DefinedGVSummaries, const ModuleSummaryIndex &Index,
|
const GVSummaryMapTy &DefinedGVSummaries, const ModuleSummaryIndex &Index,
|
||||||
FunctionImporter::ImportMapTy &ImportList,
|
FunctionImporter::ImportMapTy &ImportList,
|
||||||
StringMap<FunctionImporter::ExportSetTy> *ExportLists = nullptr) {
|
StringMap<FunctionImporter::ExportSetTy> *ExportLists = nullptr,
|
||||||
|
const DenseSet<GlobalValue::GUID> *DeadSymbols = nullptr) {
|
||||||
// Worklist contains the list of function imported in this module, for which
|
// Worklist contains the list of function imported in this module, for which
|
||||||
// we will analyse the callees and may import further down the callgraph.
|
// we will analyse the callees and may import further down the callgraph.
|
||||||
SmallVector<EdgeInfo, 128> Worklist;
|
SmallVector<EdgeInfo, 128> Worklist;
|
||||||
@ -282,6 +289,10 @@ static void ComputeImportForModule(
|
|||||||
// Populate the worklist with the import for the functions in the current
|
// Populate the worklist with the import for the functions in the current
|
||||||
// module
|
// module
|
||||||
for (auto &GVSummary : DefinedGVSummaries) {
|
for (auto &GVSummary : DefinedGVSummaries) {
|
||||||
|
if (DeadSymbols && DeadSymbols->count(GVSummary.first)) {
|
||||||
|
DEBUG(dbgs() << "Ignores Dead GUID: " << GVSummary.first << "\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
auto *Summary = GVSummary.second;
|
auto *Summary = GVSummary.second;
|
||||||
if (auto *AS = dyn_cast<AliasSummary>(Summary))
|
if (auto *AS = dyn_cast<AliasSummary>(Summary))
|
||||||
Summary = &AS->getAliasee();
|
Summary = &AS->getAliasee();
|
||||||
@ -321,14 +332,15 @@ void llvm::ComputeCrossModuleImport(
|
|||||||
const ModuleSummaryIndex &Index,
|
const ModuleSummaryIndex &Index,
|
||||||
const StringMap<GVSummaryMapTy> &ModuleToDefinedGVSummaries,
|
const StringMap<GVSummaryMapTy> &ModuleToDefinedGVSummaries,
|
||||||
StringMap<FunctionImporter::ImportMapTy> &ImportLists,
|
StringMap<FunctionImporter::ImportMapTy> &ImportLists,
|
||||||
StringMap<FunctionImporter::ExportSetTy> &ExportLists) {
|
StringMap<FunctionImporter::ExportSetTy> &ExportLists,
|
||||||
|
const DenseSet<GlobalValue::GUID> *DeadSymbols) {
|
||||||
// For each module that has function defined, compute the import/export lists.
|
// For each module that has function defined, compute the import/export lists.
|
||||||
for (auto &DefinedGVSummaries : ModuleToDefinedGVSummaries) {
|
for (auto &DefinedGVSummaries : ModuleToDefinedGVSummaries) {
|
||||||
auto &ImportList = ImportLists[DefinedGVSummaries.first()];
|
auto &ImportList = ImportLists[DefinedGVSummaries.first()];
|
||||||
DEBUG(dbgs() << "Computing import for Module '"
|
DEBUG(dbgs() << "Computing import for Module '"
|
||||||
<< DefinedGVSummaries.first() << "'\n");
|
<< DefinedGVSummaries.first() << "'\n");
|
||||||
ComputeImportForModule(DefinedGVSummaries.second, Index, ImportList,
|
ComputeImportForModule(DefinedGVSummaries.second, Index, ImportList,
|
||||||
&ExportLists);
|
&ExportLists, DeadSymbols);
|
||||||
}
|
}
|
||||||
|
|
||||||
// When computing imports we added all GUIDs referenced by anything
|
// When computing imports we added all GUIDs referenced by anything
|
||||||
@ -390,6 +402,86 @@ void llvm::ComputeCrossModuleImportForModule(
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DenseSet<GlobalValue::GUID> llvm::computeDeadSymbols(
|
||||||
|
const ModuleSummaryIndex &Index,
|
||||||
|
const DenseSet<GlobalValue::GUID> &GUIDPreservedSymbols) {
|
||||||
|
if (!ComputeDead)
|
||||||
|
return DenseSet<GlobalValue::GUID>();
|
||||||
|
if (GUIDPreservedSymbols.empty())
|
||||||
|
// Don't do anything when nothing is live, this is friendly with tests.
|
||||||
|
return DenseSet<GlobalValue::GUID>();
|
||||||
|
DenseSet<GlobalValue::GUID> LiveSymbols = GUIDPreservedSymbols;
|
||||||
|
SmallVector<GlobalValue::GUID, 128> Worklist;
|
||||||
|
Worklist.reserve(LiveSymbols.size() * 2);
|
||||||
|
for (auto GUID : LiveSymbols) {
|
||||||
|
DEBUG(dbgs() << "Live root: " << GUID << "\n");
|
||||||
|
Worklist.push_back(GUID);
|
||||||
|
}
|
||||||
|
// Add values flagged in the index as live roots to the worklist.
|
||||||
|
for (const auto &Entry : Index) {
|
||||||
|
bool IsLiveRoot = llvm::any_of(
|
||||||
|
Entry.second,
|
||||||
|
[&](const std::unique_ptr<llvm::GlobalValueSummary> &Summary) {
|
||||||
|
return Summary->liveRoot();
|
||||||
|
});
|
||||||
|
if (!IsLiveRoot)
|
||||||
|
continue;
|
||||||
|
DEBUG(dbgs() << "Live root (summary): " << Entry.first << "\n");
|
||||||
|
Worklist.push_back(Entry.first);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!Worklist.empty()) {
|
||||||
|
auto GUID = Worklist.pop_back_val();
|
||||||
|
auto It = Index.findGlobalValueSummaryList(GUID);
|
||||||
|
if (It == Index.end()) {
|
||||||
|
DEBUG(dbgs() << "Not in index: " << GUID << "\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: we should only make the prevailing copy live here
|
||||||
|
for (auto &Summary : It->second) {
|
||||||
|
for (auto Ref : Summary->refs()) {
|
||||||
|
auto RefGUID = Ref.getGUID();
|
||||||
|
if (LiveSymbols.insert(RefGUID).second) {
|
||||||
|
DEBUG(dbgs() << "Marking live (ref): " << RefGUID << "\n");
|
||||||
|
Worklist.push_back(RefGUID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (auto *FS = dyn_cast<FunctionSummary>(Summary.get())) {
|
||||||
|
for (auto Call : FS->calls()) {
|
||||||
|
auto CallGUID = Call.first.getGUID();
|
||||||
|
if (LiveSymbols.insert(CallGUID).second) {
|
||||||
|
DEBUG(dbgs() << "Marking live (call): " << CallGUID << "\n");
|
||||||
|
Worklist.push_back(CallGUID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (auto *AS = dyn_cast<AliasSummary>(Summary.get())) {
|
||||||
|
auto AliaseeGUID = AS->getAliasee().getOriginalName();
|
||||||
|
if (LiveSymbols.insert(AliaseeGUID).second) {
|
||||||
|
DEBUG(dbgs() << "Marking live (alias): " << AliaseeGUID << "\n");
|
||||||
|
Worklist.push_back(AliaseeGUID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DenseSet<GlobalValue::GUID> DeadSymbols;
|
||||||
|
DeadSymbols.reserve(
|
||||||
|
std::min(Index.size(), Index.size() - LiveSymbols.size()));
|
||||||
|
for (auto &Entry : Index) {
|
||||||
|
auto GUID = Entry.first;
|
||||||
|
if (!LiveSymbols.count(GUID)) {
|
||||||
|
DEBUG(dbgs() << "Marking dead: " << GUID << "\n");
|
||||||
|
DeadSymbols.insert(GUID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DEBUG(dbgs() << LiveSymbols.size() << " symbols Live, and "
|
||||||
|
<< DeadSymbols.size() << " symbols Dead \n");
|
||||||
|
NumDeadSymbols += DeadSymbols.size();
|
||||||
|
NumLiveSymbols += LiveSymbols.size();
|
||||||
|
return DeadSymbols;
|
||||||
|
}
|
||||||
|
|
||||||
/// Compute the set of summaries needed for a ThinLTO backend compilation of
|
/// Compute the set of summaries needed for a ThinLTO backend compilation of
|
||||||
/// \p ModulePath.
|
/// \p ModulePath.
|
||||||
void llvm::gatherImportedSummariesForModule(
|
void llvm::gatherImportedSummariesForModule(
|
||||||
@ -648,9 +740,10 @@ Expected<bool> FunctionImporter::importFunctions(
|
|||||||
report_fatal_error("Function Import: link error");
|
report_fatal_error("Function Import: link error");
|
||||||
|
|
||||||
ImportedCount += GlobalsToImport.size();
|
ImportedCount += GlobalsToImport.size();
|
||||||
|
NumImportedModules++;
|
||||||
}
|
}
|
||||||
|
|
||||||
NumImported += ImportedCount;
|
NumImportedFunctions += ImportedCount;
|
||||||
|
|
||||||
DEBUG(dbgs() << "Imported " << ImportedCount << " functions for Module "
|
DEBUG(dbgs() << "Imported " << ImportedCount << " functions for Module "
|
||||||
<< DestModule.getModuleIdentifier() << "\n");
|
<< DestModule.getModuleIdentifier() << "\n");
|
||||||
|
22
test/ThinLTO/X86/Inputs/deadstrip.ll
Normal file
22
test/ThinLTO/X86/Inputs/deadstrip.ll
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
|
||||||
|
target triple = "x86_64-apple-macosx10.11.0"
|
||||||
|
|
||||||
|
declare void @dead_func()
|
||||||
|
|
||||||
|
; Called from a @dead_func() in the other file, should not be imported there
|
||||||
|
; Ensure the cycle formed by calling @dead_func doesn't prevent stripping.
|
||||||
|
define void @baz() {
|
||||||
|
call void @dead_func()
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
; Called via llvm.global_ctors, should be detected as live via the
|
||||||
|
; marking of llvm.global_ctors as a live root in the index.
|
||||||
|
define void @boo() {
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define void @another_dead_func() {
|
||||||
|
call void @dead_func()
|
||||||
|
ret void
|
||||||
|
}
|
109
test/ThinLTO/X86/deadstrip.ll
Normal file
109
test/ThinLTO/X86/deadstrip.ll
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
; RUN: opt -module-summary %s -o %t1.bc
|
||||||
|
; RUN: opt -module-summary %p/Inputs/deadstrip.ll -o %t2.bc
|
||||||
|
; RUN: llvm-lto -thinlto-action=thinlink -o %t.index.bc %t1.bc %t2.bc
|
||||||
|
|
||||||
|
; RUN: llvm-lto -exported-symbol=_main -thinlto-action=promote %t1.bc -thinlto-index=%t.index.bc -o - | llvm-lto -exported-symbol=_main -thinlto-action=internalize -thinlto-index %t.index.bc -thinlto-module-id=%t1.bc - -o - | llvm-dis -o - | FileCheck %s
|
||||||
|
; RUN: llvm-lto -exported-symbol=_main -thinlto-action=promote %t2.bc -thinlto-index=%t.index.bc -o - | llvm-lto -exported-symbol=_main -thinlto-action=internalize -thinlto-index %t.index.bc -thinlto-module-id=%t2.bc - -o - | llvm-dis -o - | FileCheck %s --check-prefix=CHECK2
|
||||||
|
|
||||||
|
; RUN: llvm-lto -exported-symbol=_main -thinlto-action=run %t1.bc %t2.bc
|
||||||
|
; RUN: llvm-nm %t1.bc.thinlto.o | FileCheck %s --check-prefix=CHECK-NM
|
||||||
|
|
||||||
|
; RUN: llvm-lto2 %t1.bc %t2.bc -o %t.out -save-temps \
|
||||||
|
; RUN: -r %t1.bc,_main,plx \
|
||||||
|
; RUN: -r %t1.bc,_bar,pl \
|
||||||
|
; RUN: -r %t1.bc,_dead_func,pl \
|
||||||
|
; RUN: -r %t1.bc,_baz,l \
|
||||||
|
; RUN: -r %t1.bc,_boo,l \
|
||||||
|
; RUN: -r %t2.bc,_baz,pl \
|
||||||
|
; RUN: -r %t2.bc,_boo,pl \
|
||||||
|
; RUN: -r %t2.bc,_dead_func,pl \
|
||||||
|
; RUN: -r %t2.bc,_another_dead_func,pl
|
||||||
|
; RUN: llvm-dis < %t.out.0.3.import.bc | FileCheck %s
|
||||||
|
; RUN: llvm-dis < %t.out.1.3.import.bc | FileCheck %s --check-prefix=CHECK2
|
||||||
|
; RUN: llvm-nm %t.out.1 | FileCheck %s --check-prefix=CHECK2-NM
|
||||||
|
|
||||||
|
; Dead-stripping on the index allows to internalize these,
|
||||||
|
; and limit the import of @baz thanks to early pruning.
|
||||||
|
; CHECK-NOT: available_externally {{.*}} @baz()
|
||||||
|
; CHECK: @llvm.global_ctors =
|
||||||
|
; CHECK: define internal void @_GLOBAL__I_a()
|
||||||
|
; CHECK: define internal void @bar() {
|
||||||
|
; CHECK: define internal void @bar_internal()
|
||||||
|
; CHECK: define internal void @dead_func() {
|
||||||
|
; CHECK-NOT: available_externally {{.*}} @baz()
|
||||||
|
|
||||||
|
; Make sure we didn't internalize @boo, which is reachable via
|
||||||
|
; llvm.global_ctors
|
||||||
|
; CHECK2: define void @boo()
|
||||||
|
; We should have eventually revoved @baz since it was internalized and unused
|
||||||
|
; CHECK2-NM-NOT: _baz
|
||||||
|
|
||||||
|
; The final binary should not contain any of the dead functions,
|
||||||
|
; only main is expected because bar is expected to be inlined and stripped out.
|
||||||
|
; CHECK-NM-NOT: bar
|
||||||
|
; CHECK-NM-NOT: dead
|
||||||
|
; CHECK-NM: T _main
|
||||||
|
; CHECK-NM-NOT: bar
|
||||||
|
; CHECK-NM-NOT: dead
|
||||||
|
|
||||||
|
; Next test the case where Inputs/deadstrip.ll does not get a module index,
|
||||||
|
; which will cause it to be handled by regular LTO in the new LTO API.
|
||||||
|
; In that case there are uses of @dead_func in the regular LTO partition
|
||||||
|
; and it shouldn't be internalized.
|
||||||
|
; RUN: opt %p/Inputs/deadstrip.ll -o %t3.bc
|
||||||
|
; RUN: llvm-lto2 %t1.bc %t3.bc -o %t4.out -save-temps \
|
||||||
|
; RUN: -r %t1.bc,_main,plx \
|
||||||
|
; RUN: -r %t1.bc,_bar,pl \
|
||||||
|
; RUN: -r %t1.bc,_dead_func,pl \
|
||||||
|
; RUN: -r %t1.bc,_baz,l \
|
||||||
|
; RUN: -r %t1.bc,_boo,l \
|
||||||
|
; RUN: -r %t3.bc,_baz,pl \
|
||||||
|
; RUN: -r %t3.bc,_boo,pl \
|
||||||
|
; RUN: -r %t3.bc,_dead_func,pl \
|
||||||
|
; RUN: -r %t3.bc,_another_dead_func,pl
|
||||||
|
; RUN: llvm-dis < %t4.out.1.3.import.bc | FileCheck %s --check-prefix=CHECK-NOTDEAD
|
||||||
|
; RUN: llvm-nm %t4.out.0 | FileCheck %s --check-prefix=CHECK-NM-NOTDEAD
|
||||||
|
|
||||||
|
; We can't internalize @dead_func because of the use in the regular LTO
|
||||||
|
; partition.
|
||||||
|
; CHECK-NOTDEAD: define void @dead_func()
|
||||||
|
; We also can't eliminate @baz because it is in the regular LTO partition
|
||||||
|
; and called from @dead_func.
|
||||||
|
; CHECK-NM-NOTDEAD: T _baz
|
||||||
|
|
||||||
|
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
|
||||||
|
target triple = "x86_64-apple-macosx10.11.0"
|
||||||
|
|
||||||
|
|
||||||
|
@llvm.global_ctors = appending global [1 x { i32, void ()* }] [{ i32, void ()* } { i32 65535, void ()* @_GLOBAL__I_a }]
|
||||||
|
|
||||||
|
declare void @baz()
|
||||||
|
|
||||||
|
declare void @boo()
|
||||||
|
|
||||||
|
define internal void @_GLOBAL__I_a() #1 section "__TEXT,__StaticInit,regular,pure_instructions" {
|
||||||
|
entry:
|
||||||
|
call void @boo()
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define void @bar() {
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define internal void @bar_internal() {
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define void @dead_func() {
|
||||||
|
call void @bar()
|
||||||
|
call void @baz()
|
||||||
|
call void @bar_internal()
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define void @main() {
|
||||||
|
call void @bar()
|
||||||
|
call void @bar_internal()
|
||||||
|
ret void
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user