Index: include/llvm/IR/ModuleSummaryIndex.h =================================================================== --- include/llvm/IR/ModuleSummaryIndex.h +++ include/llvm/IR/ModuleSummaryIndex.h @@ -101,6 +101,11 @@ /// possibly referenced from inline assembly, etc). unsigned NoRename : 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; + /// Indicate if a function contains inline assembly (which is opaque), /// that may reference a local value. This is used to prevent importing /// of this function, since we can't promote and rename the uses of the @@ -114,15 +119,15 @@ /// Convenience Constructors explicit GVFlags(GlobalValue::LinkageTypes Linkage, bool NoRename, - bool HasInlineAsmMaybeReferencingInternal, + bool LiveRoot, bool HasInlineAsmMaybeReferencingInternal, bool IsNotViableToInline) - : Linkage(Linkage), NoRename(NoRename), + : Linkage(Linkage), NoRename(NoRename), LiveRoot(LiveRoot), HasInlineAsmMaybeReferencingInternal( HasInlineAsmMaybeReferencingInternal), IsNotViableToInline(IsNotViableToInline) {} GVFlags(const GlobalValue &GV) - : Linkage(GV.getLinkage()), NoRename(GV.hasSection()), + : Linkage(GV.getLinkage()), NoRename(GV.hasSection()), LiveRoot(false), HasInlineAsmMaybeReferencingInternal(false) { IsNotViableToInline = false; if (const auto *F = dyn_cast<Function>(&GV)) @@ -210,6 +215,14 @@ /// possibly referenced from inline assembly, etc). void setNoRename() { Flags.NoRename = true; } + /// 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; } + /// Return true if this global value possibly references another value /// that can't be renamed. bool hasInlineAsmMaybeReferencingInternal() const { @@ -392,6 +405,7 @@ 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) { Index: include/llvm/LTO/LTO.h =================================================================== --- include/llvm/LTO/LTO.h +++ include/llvm/LTO/LTO.h @@ -382,6 +382,10 @@ /// 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 @@ -405,6 +409,9 @@ /// 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, }; }; Index: include/llvm/Transforms/IPO/FunctionImport.h =================================================================== --- include/llvm/Transforms/IPO/FunctionImport.h +++ include/llvm/Transforms/IPO/FunctionImport.h @@ -91,11 +91,15 @@ /// \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. /// @@ -105,6 +109,13 @@ 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. // Index: lib/Analysis/ModuleSummaryAnalysis.cpp =================================================================== --- lib/Analysis/ModuleSummaryAnalysis.cpp +++ lib/Analysis/ModuleSummaryAnalysis.cpp @@ -193,6 +193,16 @@ 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, @@ -259,6 +269,15 @@ Summary->setNoRename(); } + // 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, @@ -283,6 +302,7 @@ GlobalValueSummary::GVFlags GVFlags( GlobalValue::InternalLinkage, /* NoRename */ true, + /* LiveRoot */ true, /* HasInlineAsmMaybeReferencingInternal */ false, /* IsNotViableToInline */ true); // Create the appropriate summary type. Index: lib/Bitcode/Reader/BitcodeReader.cpp =================================================================== --- lib/Bitcode/Reader/BitcodeReader.cpp +++ lib/Bitcode/Reader/BitcodeReader.cpp @@ -789,7 +789,11 @@ bool NoRename = RawFlags & 0x1; bool IsNotViableToInline = RawFlags & 0x2; bool HasInlineAsmMaybeReferencingInternal = RawFlags & 0x4; - return GlobalValueSummary::GVFlags(Linkage, NoRename, + // 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 & 0x8) || Version < 3; + return GlobalValueSummary::GVFlags(Linkage, NoRename, LiveRoot, HasInlineAsmMaybeReferencingInternal, IsNotViableToInline); } @@ -4784,9 +4788,9 @@ } const uint64_t Version = Record[0]; const bool IsOldProfileFormat = Version == 1; - if (!IsOldProfileFormat && Version != 2) + if (Version < 1 || Version > 3) return error("Invalid summary version " + Twine(Version) + - ", 1 or 2 expected"); + ", 1, 2 or 3 expected"); Record.clear(); // Keep around the last seen summary to be used when we see an optional Index: lib/Bitcode/Writer/BitcodeWriter.cpp =================================================================== --- lib/Bitcode/Writer/BitcodeWriter.cpp +++ lib/Bitcode/Writer/BitcodeWriter.cpp @@ -967,6 +967,7 @@ RawFlags |= Flags.NoRename; // bool RawFlags |= (Flags.IsNotViableToInline << 1); RawFlags |= (Flags.HasInlineAsmMaybeReferencingInternal << 2); + RawFlags |= (Flags.LiveRoot << 3); // 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. @@ -3351,7 +3352,7 @@ // Current version for the summary. // This is bumped whenever we introduce changes in the way some record are // interpreted, like flags for instance. -static const uint64_t INDEX_VERSION = 2; +static const uint64_t INDEX_VERSION = 3; /// Emit the per-module summary section alongside the rest of /// the module's bitcode. Index: lib/LTO/LTO.cpp =================================================================== --- lib/LTO/LTO.cpp +++ lib/LTO/LTO.cpp @@ -330,12 +330,21 @@ 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, @@ -840,6 +849,19 @@ 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( @@ -848,7 +870,7 @@ if (Conf.OptLevel > 0) { ComputeCrossModuleImport(ThinLTO.CombinedIndex, ModuleToDefinedGVSummaries, - ImportLists, ExportLists); + ImportLists, ExportLists, &DeadSymbols); std::set<GlobalValue::GUID> ExportedGUIDs; for (auto &Res : GlobalResolutions) { @@ -865,7 +887,7 @@ const auto &ExportList = ExportLists.find(ModuleIdentifier); return (ExportList != ExportLists.end() && ExportList->second.count(GUID)) || - ExportedGUIDs.count(GUID); + (!DeadSymbols.count(GUID) && ExportedGUIDs.count(GUID)); }; thinLTOInternalizeAndPromoteInIndex(ThinLTO.CombinedIndex, isExported); Index: lib/LTO/ThinLTOCodeGenerator.cpp =================================================================== --- lib/LTO/ThinLTOCodeGenerator.cpp +++ lib/LTO/ThinLTOCodeGenerator.cpp @@ -578,11 +578,18 @@ 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; @@ -591,17 +598,13 @@ 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) { const auto &ExportList = ExportLists.find(ModuleIdentifier); return (ExportList != ExportLists.end() && ExportList->second.count(GUID)) || - GUIDPreservedSymbols.count(GUID); + (!DeadSymbols.count(GUID) && GUIDPreservedSymbols.count(GUID)); }; thinLTOInternalizeAndPromoteInIndex(Index, isExported); @@ -620,11 +623,18 @@ 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); @@ -694,11 +704,14 @@ 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 @@ -711,7 +724,7 @@ const auto &ExportList = ExportLists.find(ModuleIdentifier); return (ExportList != ExportLists.end() && ExportList->second.count(GUID)) || - GUIDPreservedSymbols.count(GUID); + (!DeadSymbols.count(GUID) && GUIDPreservedSymbols.count(GUID)); }; thinLTOInternalizeAndPromoteInIndex(Index, isExported); thinLTOInternalizeModule(TheModule, @@ -832,17 +845,20 @@ 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. @@ -858,7 +874,7 @@ const auto &ExportList = ExportLists.find(ModuleIdentifier); return (ExportList != ExportLists.end() && ExportList->second.count(GUID)) || - GUIDPreservedSymbols.count(GUID); + (!DeadSymbols.count(GUID) && GUIDPreservedSymbols.count(GUID)); }; // Use global summary-based analysis to identify symbols that can be Index: lib/Transforms/IPO/FunctionImport.cpp =================================================================== --- lib/Transforms/IPO/FunctionImport.cpp +++ lib/Transforms/IPO/FunctionImport.cpp @@ -345,7 +345,8 @@ static void ComputeImportForModule( const GVSummaryMapTy &DefinedGVSummaries, const ModuleSummaryIndex &Index, 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 // we will analyse the callees and may import further down the callgraph. SmallVector<EdgeInfo, 128> Worklist; @@ -353,6 +354,10 @@ // Populate the worklist with the import for the functions in the current // module for (auto &GVSummary : DefinedGVSummaries) { + if (DeadSymbols && DeadSymbols->count(GVSummary.first)) { + DEBUG(dbgs() << "Ignores Dead GUID: " << GVSummary.first << "\n"); + continue; + } auto *Summary = GVSummary.second; if (auto *AS = dyn_cast<AliasSummary>(Summary)) Summary = &AS->getAliasee(); @@ -392,14 +397,15 @@ const ModuleSummaryIndex &Index, const StringMap<GVSummaryMapTy> &ModuleToDefinedGVSummaries, 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 (auto &DefinedGVSummaries : ModuleToDefinedGVSummaries) { auto &ImportList = ImportLists[DefinedGVSummaries.first()]; DEBUG(dbgs() << "Computing import for Module '" << DefinedGVSummaries.first() << "'\n"); ComputeImportForModule(DefinedGVSummaries.second, Index, ImportList, - &ExportLists); + &ExportLists, DeadSymbols); } // When computing imports we added all GUIDs referenced by anything @@ -461,6 +467,82 @@ #endif } +DenseSet<GlobalValue::GUID> llvm::computeDeadSymbols( + const ModuleSummaryIndex &Index, + const DenseSet<GlobalValue::GUID> &GUIDPreservedSymbols) { + 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"); + return DeadSymbols; +} + /// Compute the set of summaries needed for a ThinLTO backend compilation of /// \p ModulePath. void llvm::gatherImportedSummariesForModule( Index: test/Bitcode/summary_version.ll =================================================================== --- test/Bitcode/summary_version.ll +++ test/Bitcode/summary_version.ll @@ -2,7 +2,7 @@ ; RUN: opt -module-summary %s -o - | llvm-bcanalyzer -dump | FileCheck %s ; CHECK: <GLOBALVAL_SUMMARY_BLOCK -; CHECK: <VERSION op0=2/> +; CHECK: <VERSION op0=3/> Index: test/ThinLTO/X86/Inputs/deadstrip.ll =================================================================== --- /dev/null +++ test/ThinLTO/X86/Inputs/deadstrip.ll @@ -0,0 +1,19 @@ +target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-macosx10.11.0" + +; Called from a @dead_func() in the other file, should not be imported there +define void @baz() { + 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 +} + +declare void @dead_func() +define void @another_dead_func() { + call void @dead_func() + ret void +} Index: test/ThinLTO/X86/deadstrip.ll =================================================================== --- /dev/null +++ test/ThinLTO/X86/deadstrip.ll @@ -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 +}