Skip to content

Commit

Permalink
ThinLTO: add early "dead-stripping" on the Index
Browse files Browse the repository at this point in the history
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

llvm-svn: 291177
  • Loading branch information
teresajohnson committed Jan 5, 2017
1 parent 8f05c78 commit 6c475a7
Show file tree
Hide file tree
Showing 12 changed files with 365 additions and 32 deletions.
19 changes: 17 additions & 2 deletions llvm/include/llvm/IR/ModuleSummaryIndex.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,16 @@ class GlobalValueSummary {
/// be renamed or references something that can't be renamed).
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
explicit GVFlags(GlobalValue::LinkageTypes Linkage,
bool NotEligibleToImport)
: Linkage(Linkage), NotEligibleToImport(NotEligibleToImport) {}
bool NotEligibleToImport, bool LiveRoot)
: Linkage(Linkage), NotEligibleToImport(NotEligibleToImport),
LiveRoot(LiveRoot) {}
};

private:
Expand Down Expand Up @@ -195,6 +201,14 @@ class GlobalValueSummary {
/// Return true if this global value can't be imported.
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.
void setNotEligibleToImport() { Flags.NotEligibleToImport = true; }

Expand Down Expand Up @@ -366,6 +380,7 @@ class ModuleSummaryIndex {
const_gvsummary_iterator begin() const { return GlobalValueMap.begin(); }
gvsummary_iterator end() { 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.
const GlobalValueSummaryList &getGlobalValueSummaryList(StringRef ValueName) {
Expand Down
3 changes: 2 additions & 1 deletion llvm/include/llvm/IR/ModuleSummaryIndexYAML.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ template <> struct CustomMappingTraits<GlobalValueSummaryMapTy> {
}
auto &Elem = V[KeyInt];
for (auto &FSum : FSums) {
GlobalValueSummary::GVFlags GVFlags(GlobalValue::ExternalLinkage, false);
GlobalValueSummary::GVFlags GVFlags(GlobalValue::ExternalLinkage, false,
false);
Elem.push_back(llvm::make_unique<FunctionSummary>(
GVFlags, 0, ArrayRef<ValueInfo>{},
ArrayRef<FunctionSummary::EdgeTy>{}, std::move(FSum.TypeTests)));
Expand Down
7 changes: 7 additions & 0 deletions llvm/include/llvm/LTO/LTO.h
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,10 @@ class LTO {
/// The unmangled name of the global.
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;

/// This field keeps track of the partition number of this global. The
Expand All @@ -405,6 +409,9 @@ class LTO {
/// This global is either used by more than one partition or has an
/// external reference, and therefore cannot be internalized.
External = -2u,

/// The RegularLTO partition
RegularLTO = 0,
};
};

Expand Down
13 changes: 12 additions & 1 deletion llvm/include/llvm/Transforms/IPO/FunctionImport.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,15 @@ class FunctionImportPass : public PassInfoMixin<FunctionImportPass> {
/// \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
/// 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(
const ModuleSummaryIndex &Index,
const StringMap<GVSummaryMapTy> &ModuleToDefinedGVSummaries,
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.
///
Expand All @@ -100,6 +104,13 @@ void ComputeCrossModuleImportForModule(
StringRef ModulePath, const ModuleSummaryIndex &Index,
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
/// \p ModulePath.
//
Expand Down
31 changes: 27 additions & 4 deletions llvm/lib/Analysis/ModuleSummaryAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,8 @@ computeFunctionSummary(ModuleSummaryIndex &Index, const Module &M,
// Inliner doesn't handle variadic functions.
// FIXME: refactor this to use the same code that inliner is using.
F.isVarArg();
GlobalValueSummary::GVFlags Flags(F.getLinkage(), NotEligibleForImport);
GlobalValueSummary::GVFlags Flags(F.getLinkage(), NotEligibleForImport,
/* LiveRoot = */ false);
auto FuncSummary = llvm::make_unique<FunctionSummary>(
Flags, NumInsts, RefEdges.takeVector(), CallGraphEdges.takeVector(),
TypeTests.takeVector());
Expand All @@ -205,7 +206,8 @@ computeVariableSummary(ModuleSummaryIndex &Index, const GlobalVariable &V,
SmallPtrSet<const User *, 8> Visited;
findRefEdges(&V, RefEdges, Visited);
bool NonRenamableLocal = isNonRenamableLocal(V);
GlobalValueSummary::GVFlags Flags(V.getLinkage(), NonRenamableLocal);
GlobalValueSummary::GVFlags Flags(V.getLinkage(), NonRenamableLocal,
/* LiveRoot = */ false);
auto GVarSummary =
llvm::make_unique<GlobalVarSummary>(Flags, RefEdges.takeVector());
if (NonRenamableLocal)
Expand All @@ -217,7 +219,8 @@ static void
computeAliasSummary(ModuleSummaryIndex &Index, const GlobalAlias &A,
DenseSet<GlobalValue::GUID> &CantBePromoted) {
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 *Aliasee = A.getBaseObject();
auto *AliaseeSummary = Index.getGlobalValueSummary(*Aliasee);
Expand All @@ -228,6 +231,16 @@ computeAliasSummary(ModuleSummaryIndex &Index, const GlobalAlias &A,
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(
const Module &M,
std::function<BlockFrequencyInfo *(const Function &F)> GetBFICallback,
Expand Down Expand Up @@ -293,6 +306,15 @@ ModuleSummaryIndex llvm::buildModuleSummaryIndex(
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()) {
// Collect the local values defined by module level asm, and set up
// summaries for these symbols so that they can be marked as NoRename,
Expand All @@ -316,7 +338,8 @@ ModuleSummaryIndex llvm::buildModuleSummaryIndex(
return;
assert(GV->isDeclaration() && "Def in module asm already has definition");
GlobalValueSummary::GVFlags GVFlags(GlobalValue::InternalLinkage,
/* NotEligibleToImport */ true);
/* NotEligibleToImport */ true,
/* LiveRoot */ true);
CantBePromoted.insert(GlobalValue::getGUID(Name));
// Create the appropriate summary type.
if (isa<Function>(GV)) {
Expand Down
6 changes: 5 additions & 1 deletion llvm/lib/Bitcode/Reader/BitcodeReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -802,7 +802,11 @@ static GlobalValueSummary::GVFlags getDecodedGVSummaryFlags(uint64_t RawFlags,
auto Linkage = GlobalValue::LinkageTypes(RawFlags & 0xF); // 4 bits
RawFlags = RawFlags >> 4;
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) {
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -972,6 +972,7 @@ static uint64_t getEncodedGVSummaryFlags(GlobalValueSummary::GVFlags Flags) {
uint64_t RawFlags = 0;

RawFlags |= Flags.NotEligibleToImport; // bool
RawFlags |= (Flags.LiveRoot << 1);
// 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
// account here as well.
Expand Down
41 changes: 36 additions & 5 deletions llvm/lib/LTO/LTO.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -337,12 +337,21 @@ void LTO::addSymbolToGlobalRes(SmallPtrSet<GlobalValue *, 8> &Used,
if (Res.Prevailing)
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)) ||
(GlobalRes.Partition != GlobalResolution::Unknown &&
GlobalRes.Partition != Partition))
GlobalRes.Partition != Partition)) {
GlobalRes.Partition = GlobalResolution::External;
else
} else
// First recorded reference, save the current 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,
Expand Down Expand Up @@ -848,6 +857,19 @@ Error LTO::runThinLTO(AddStreamFn AddStream, NativeObjectCache Cache,
if (!ModuleToDefinedGVSummaries.count(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(
ThinLTO.ModuleMap.size());
StringMap<FunctionImporter::ExportSetTy> ExportLists(
Expand All @@ -856,12 +878,21 @@ Error LTO::runThinLTO(AddStreamFn AddStream, NativeObjectCache Cache,

if (Conf.OptLevel > 0) {
ComputeCrossModuleImport(ThinLTO.CombinedIndex, ModuleToDefinedGVSummaries,
ImportLists, ExportLists);
ImportLists, ExportLists, &DeadSymbols);

std::set<GlobalValue::GUID> ExportedGUIDs;
for (auto &Res : GlobalResolutions) {
if (!Res.second.IRName.empty() &&
Res.second.Partition == GlobalResolution::External)
// First check if the symbol was flagged as having external references.
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));
}

Expand Down
42 changes: 29 additions & 13 deletions llvm/lib/LTO/ThinLTOCodeGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -581,11 +581,18 @@ void ThinLTOCodeGenerator::promote(Module &TheModule,
StringMap<GVSummaryMapTy> 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
StringMap<FunctionImporter::ImportMapTy> ImportLists(ModuleCount);
StringMap<FunctionImporter::ExportSetTy> ExportLists(ModuleCount);
ComputeCrossModuleImport(Index, ModuleToDefinedGVSummaries, ImportLists,
ExportLists);
ExportLists, &DeadSymbols);

// Resolve LinkOnce/Weak symbols.
StringMap<std::map<GlobalValue::GUID, GlobalValue::LinkageTypes>> ResolvedODR;
Expand All @@ -594,10 +601,6 @@ void ThinLTOCodeGenerator::promote(Module &TheModule,
thinLTOResolveWeakForLinkerModule(
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
// in the module.
auto isExported = [&](StringRef ModuleIdentifier, GlobalValue::GUID GUID) {
Expand All @@ -623,11 +626,18 @@ void ThinLTOCodeGenerator::crossModuleImport(Module &TheModule,
StringMap<GVSummaryMapTy> ModuleToDefinedGVSummaries(ModuleCount);
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
StringMap<FunctionImporter::ImportMapTy> ImportLists(ModuleCount);
StringMap<FunctionImporter::ExportSetTy> ExportLists(ModuleCount);
ComputeCrossModuleImport(Index, ModuleToDefinedGVSummaries, ImportLists,
ExportLists);
ExportLists, &DeadSymbols);
auto &ImportList = ImportLists[TheModule.getModuleIdentifier()];

crossImportIntoModule(TheModule, Index, ModuleMap, ImportList);
Expand Down Expand Up @@ -697,11 +707,14 @@ void ThinLTOCodeGenerator::internalize(Module &TheModule,
StringMap<GVSummaryMapTy> ModuleToDefinedGVSummaries(ModuleCount);
Index.collectDefinedGVSummariesPerModule(ModuleToDefinedGVSummaries);

// Compute "dead" symbols, we don't want to import/export these!
auto DeadSymbols = computeDeadSymbols(Index, GUIDPreservedSymbols);

// Generate import/export list
StringMap<FunctionImporter::ImportMapTy> ImportLists(ModuleCount);
StringMap<FunctionImporter::ExportSetTy> ExportLists(ModuleCount);
ComputeCrossModuleImport(Index, ModuleToDefinedGVSummaries, ImportLists,
ExportLists);
ExportLists, &DeadSymbols);
auto &ExportList = ExportLists[ModuleIdentifier];

// Be friendly and don't nuke totally the module when the client didn't
Expand Down Expand Up @@ -836,17 +849,20 @@ void ThinLTOCodeGenerator::run() {
StringMap<GVSummaryMapTy> ModuleToDefinedGVSummaries(ModuleCount);
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
// combined index.
StringMap<FunctionImporter::ImportMapTy> ImportLists(ModuleCount);
StringMap<FunctionImporter::ExportSetTy> ExportLists(ModuleCount);
ComputeCrossModuleImport(*Index, ModuleToDefinedGVSummaries, ImportLists,
ExportLists);

// 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);
ExportLists, &DeadSymbols);

// We use a std::map here to be able to have a defined ordering when
// producing a hash for the cache entry.
Expand Down

0 comments on commit 6c475a7

Please sign in to comment.