Index: include/llvm/LTO/LTO.h =================================================================== --- include/llvm/LTO/LTO.h +++ include/llvm/LTO/LTO.h @@ -59,7 +59,9 @@ /// must apply the changes to the Module via thinLTOInternalizeModule. void thinLTOInternalizeAndPromoteInIndex( ModuleSummaryIndex &Index, - function_ref isExported); + function_ref isExported, + function_ref + isPrevailing); /// Computes a unique hash for the Module considering the current list of /// export/import and other global analysis results. Index: lib/LTO/LTO.cpp =================================================================== --- lib/LTO/LTO.cpp +++ lib/LTO/LTO.cpp @@ -384,7 +384,9 @@ static void thinLTOInternalizeAndPromoteGUID( GlobalValueSummaryList &GVSummaryList, GlobalValue::GUID GUID, - function_ref isExported) { + function_ref isExported, + function_ref + isPrevailing) { for (auto &S : GVSummaryList) { if (isExported(S->modulePath(), GUID)) { if (GlobalValue::isLocalLinkage(S->linkage())) @@ -393,6 +395,8 @@ // Ignore local and appending linkage values since the linker // doesn't resolve them. !GlobalValue::isLocalLinkage(S->linkage()) && + (!GlobalValue::isInterposableLinkage(S->linkage()) || + isPrevailing(GUID, S.get())) && S->linkage() != GlobalValue::AppendingLinkage && // We can't internalize available_externally globals because this // can break function pointer equality. @@ -411,9 +415,12 @@ // as external and non-exported values as internal. void llvm::thinLTOInternalizeAndPromoteInIndex( ModuleSummaryIndex &Index, - function_ref isExported) { + function_ref isExported, + function_ref + isPrevailing) { for (auto &I : Index) - thinLTOInternalizeAndPromoteGUID(I.second.SummaryList, I.first, isExported); + thinLTOInternalizeAndPromoteGUID(I.second.SummaryList, I.first, isExported, + isPrevailing); } // Requires a destructor for std::vector. @@ -1322,12 +1329,13 @@ ExportList->second.count(GUID)) || ExportedGUIDs.count(GUID); }; - thinLTOInternalizeAndPromoteInIndex(ThinLTO.CombinedIndex, isExported); - auto isPrevailing = [&](GlobalValue::GUID GUID, const GlobalValueSummary *S) { return ThinLTO.PrevailingModuleForGUID[GUID] == S->modulePath(); }; + thinLTOInternalizeAndPromoteInIndex(ThinLTO.CombinedIndex, isExported, + isPrevailing); + auto recordNewLinkage = [&](StringRef ModuleIdentifier, GlobalValue::GUID GUID, GlobalValue::LinkageTypes NewLinkage) { Index: lib/LTO/ThinLTOCodeGenerator.cpp =================================================================== --- lib/LTO/ThinLTOCodeGenerator.cpp +++ lib/LTO/ThinLTOCodeGenerator.cpp @@ -457,10 +457,9 @@ ModuleSummaryIndex &Index, StringMap> &ResolvedODR, - const DenseSet &GUIDPreservedSymbols) { - - DenseMap PrevailingCopy; - computePrevailingCopies(Index, PrevailingCopy); + const DenseSet &GUIDPreservedSymbols, + const DenseMap + &PrevailingCopy) { auto isPrevailing = [&](GlobalValue::GUID GUID, const GlobalValueSummary *S) { const auto &Prevailing = PrevailingCopy.find(GUID); @@ -576,6 +575,8 @@ static void internalizeAndPromoteInIndex( const StringMap &ExportLists, const DenseSet &GUIDPreservedSymbols, + const DenseMap + &PrevailingCopy, ModuleSummaryIndex &Index) { auto isExported = [&](StringRef ModuleIdentifier, GlobalValue::GUID GUID) { const auto &ExportList = ExportLists.find(ModuleIdentifier); @@ -584,7 +585,15 @@ GUIDPreservedSymbols.count(GUID); }; - thinLTOInternalizeAndPromoteInIndex(Index, isExported); + auto isPrevailing = [&](GlobalValue::GUID GUID, const GlobalValueSummary *S) { + const auto &Prevailing = PrevailingCopy.find(GUID); + // Not in map means that there was only one copy, which must be prevailing. + if (Prevailing == PrevailingCopy.end()) + return true; + return Prevailing->second == S; + }; + + thinLTOInternalizeAndPromoteInIndex(Index, isExported, isPrevailing); } static void computeDeadSymbolsInIndex( @@ -629,16 +638,21 @@ ComputeCrossModuleImport(Index, ModuleToDefinedGVSummaries, ImportLists, ExportLists); + DenseMap PrevailingCopy; + computePrevailingCopies(Index, PrevailingCopy); + // Resolve prevailing symbols StringMap> ResolvedODR; - resolvePrevailingInIndex(Index, ResolvedODR, GUIDPreservedSymbols); + resolvePrevailingInIndex(Index, ResolvedODR, GUIDPreservedSymbols, + PrevailingCopy); thinLTOResolvePrevailingInModule( TheModule, ModuleToDefinedGVSummaries[ModuleIdentifier]); // Promote the exported values in the index, so that they are promoted // in the module. - internalizeAndPromoteInIndex(ExportLists, GUIDPreservedSymbols, Index); + internalizeAndPromoteInIndex(ExportLists, GUIDPreservedSymbols, + PrevailingCopy, Index); promoteModule(TheModule, Index); } @@ -785,13 +799,18 @@ if (ExportList.empty() && GUIDPreservedSymbols.empty()) return; + DenseMap PrevailingCopy; + computePrevailingCopies(Index, PrevailingCopy); + // Resolve prevailing symbols StringMap> ResolvedODR; - resolvePrevailingInIndex(Index, ResolvedODR, GUIDPreservedSymbols); + resolvePrevailingInIndex(Index, ResolvedODR, GUIDPreservedSymbols, + PrevailingCopy); // Promote the exported values in the index, so that they are promoted // in the module. - internalizeAndPromoteInIndex(ExportLists, GUIDPreservedSymbols, Index); + internalizeAndPromoteInIndex(ExportLists, GUIDPreservedSymbols, + PrevailingCopy, Index); promoteModule(TheModule, Index); @@ -944,14 +963,19 @@ // on the index, and nuke this map. StringMap> ResolvedODR; + DenseMap PrevailingCopy; + computePrevailingCopies(*Index, PrevailingCopy); + // Resolve prevailing symbols, this has to be computed early because it // impacts the caching. - resolvePrevailingInIndex(*Index, ResolvedODR, GUIDPreservedSymbols); + resolvePrevailingInIndex(*Index, ResolvedODR, GUIDPreservedSymbols, + PrevailingCopy); // Use global summary-based analysis to identify symbols that can be // internalized (because they aren't exported or preserved as per callback). // Changes are made in the index, consumed in the ThinLTO backends. - internalizeAndPromoteInIndex(ExportLists, GUIDPreservedSymbols, *Index); + internalizeAndPromoteInIndex(ExportLists, GUIDPreservedSymbols, + PrevailingCopy, *Index); // Make sure that every module has an entry in the ExportLists, ImportList, // GVSummary and ResolvedODR maps to enable threaded access to these maps Index: lib/Transforms/IPO/FunctionImport.cpp =================================================================== --- lib/Transforms/IPO/FunctionImport.cpp +++ lib/Transforms/IPO/FunctionImport.cpp @@ -764,7 +764,7 @@ } // Make value live and add it to the worklist if it was not live before. - auto visit = [&](ValueInfo VI) { + auto visit = [&](ValueInfo VI, bool IsAliasee) { // FIXME: If we knew which edges were created for indirect call profiles, // we could skip them here. Any that are live should be reached via // other edges, e.g. reference edges. Otherwise, using a profile collected @@ -800,12 +800,15 @@ Interposable = true; } - if (!KeepAliveLinkage) - return; + if (!IsAliasee) { + if (!KeepAliveLinkage) + return; - if (Interposable) - report_fatal_error( - "Interposable and available_externally/linkonce_odr/weak_odr symbol"); + if (Interposable) + report_fatal_error( + "Interposable and available_externally/linkonce_odr/weak_odr " + "symbol"); + } } for (auto &S : VI.getSummaryList()) @@ -821,16 +824,16 @@ // If this is an alias, visit the aliasee VI to ensure that all copies // are marked live and it is added to the worklist for further // processing of its references. - visit(AS->getAliaseeVI()); + visit(AS->getAliaseeVI(), true); continue; } Summary->setLive(true); for (auto Ref : Summary->refs()) - visit(Ref); + visit(Ref, false); if (auto *FS = dyn_cast(Summary.get())) for (auto Call : FS->calls()) - visit(Call.first); + visit(Call.first, false); } } Index.setWithGlobalValueDeadStripping(); @@ -948,23 +951,11 @@ auto NewLinkage = GS->second->linkage(); if (NewLinkage == GV.getLinkage()) return; - - // Switch the linkage to weakany if asked for, e.g. we do this for - // linker redefined symbols (via --wrap or --defsym). - // We record that the visibility should be changed here in `addThinLTO` - // as we need access to the resolution vectors for each input file in - // order to find which symbols have been redefined. - // We may consider reorganizing this code and moving the linkage recording - // somewhere else, e.g. in thinLTOResolvePrevailingInIndex. - if (NewLinkage == GlobalValue::WeakAnyLinkage) { - GV.setLinkage(NewLinkage); - return; - } - if (GlobalValue::isLocalLinkage(GV.getLinkage()) || // In case it was dead and already converted to declaration. GV.isDeclaration()) return; + // Check for a non-prevailing def that has interposable linkage // (e.g. non-odr weak or linkonce). In that case we can't simply // convert to available_externally, since it would lose the Index: test/LTO/Resolution/X86/not-prevailing-weak-aliasee.ll =================================================================== --- /dev/null +++ test/LTO/Resolution/X86/not-prevailing-weak-aliasee.ll @@ -0,0 +1,33 @@ +; Test to ensure that non-prevailing weak aliasee is kept as a weak definition +; when the alias is not dead. +; RUN: opt -module-summary %s -o %t1.bc +; RUN: llvm-lto2 run %t1.bc \ +; RUN: -r=%t1.bc,__a,lx \ +; RUN: -r=%t1.bc,__b,l \ +; RUN: -r=%t1.bc,a,plx \ +; RUN: -r=%t1.bc,b,pl \ +; RUN: -o %t2.o -save-temps + +; Check that __a is kept as a weak def. __b can be dropped since its alias is +; not live and will also be dropped. +; RUN: llvm-dis %t2.o.1.1.promote.bc -o - | FileCheck %s +; CHECK: define weak hidden void @__a +; CHECK: declare hidden void @__b +; CHECK: declare void @b + +target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128" +target triple = "aarch64-unknown-linux-gnu" + +@a = hidden alias void (), void ()* @__a + +define weak hidden void @__a() { +entry: + ret void +} + +@b = hidden alias void (), void ()* @__b + +define weak hidden void @__b() { +entry: + ret void +} Index: test/ThinLTO/X86/Inputs/internalize.ll =================================================================== --- /dev/null +++ test/ThinLTO/X86/Inputs/internalize.ll @@ -0,0 +1,6 @@ +target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-macosx10.11.0" + +define weak void @weak_func_nonprevailing() { + ret void +} Index: test/ThinLTO/X86/internalize.ll =================================================================== --- test/ThinLTO/X86/internalize.ll +++ test/ThinLTO/X86/internalize.ll @@ -1,5 +1,8 @@ ; RUN: opt -module-summary %s -o %t1.bc -; RUN: llvm-lto -thinlto-action=thinlink -o %t.index.bc %t1.bc +; RUN: opt -module-summary %p/Inputs/internalize.ll -o %t2.bc +; Link in %t2.bc first to force its copy of @weak_func_nonprevailing as +; prevailing the %t1.bc copy as non-prevailing. +; RUN: llvm-lto -thinlto-action=thinlink -o %t.index.bc %t2.bc %t1.bc ; RUN: llvm-lto -thinlto-action=internalize -thinlto-index %t.index.bc %t1.bc -o - | llvm-dis -o - | FileCheck %s --check-prefix=REGULAR ; RUN: llvm-lto -thinlto-action=internalize -thinlto-index %t.index.bc %t1.bc -o - --exported-symbol=foo | llvm-dis -o - | FileCheck %s --check-prefix=INTERNALIZE @@ -13,7 +16,10 @@ ; RUN: llvm-lto2 run %t1.bc -o %t.o -save-temps \ ; RUN: -r=%t1.bc,_foo,pxl \ ; RUN: -r=%t1.bc,_bar,pl \ -; RUN: -r=%t1.bc,_linkonce_func,pl +; RUN: -r=%t1.bc,_linkonce_func,pl \ +; RUN: -r=%t1.bc,_weak_func_prevailing,pl \ +; RUN: -r=%t1.bc,_alias1,plx \ +; RUN: -r=%t1.bc,_weak_func_nonprevailing,l ; RUN: llvm-dis < %t.o.1.2.internalize.bc | FileCheck %s --check-prefix=INTERNALIZE2 ; Test the enable-lto-internalization option by setting it to false. @@ -22,7 +28,10 @@ ; RUN: llvm-lto2 run %t1.bc -o %t.o -save-temps -enable-lto-internalization=false \ ; RUN: -r=%t1.bc,_foo,pxl \ ; RUN: -r=%t1.bc,_bar,pl \ -; RUN: -r=%t1.bc,_linkonce_func,pl +; RUN: -r=%t1.bc,_linkonce_func,pl \ +; RUN: -r=%t1.bc,_weak_func_prevailing,pl \ +; RUN: -r=%t1.bc,_alias1,plx \ +; RUN: -r=%t1.bc,_weak_func_nonprevailing,l ; RUN: llvm-dis < %t.o.1.2.internalize.bc | FileCheck %s --check-prefix=INTERNALIZE2-OPTION-DISABLE ; REGULAR: define void @foo @@ -31,15 +40,23 @@ ; INTERNALIZE: define void @foo ; INTERNALIZE: define internal void @bar ; INTERNALIZE: define internal void @linkonce_func() +; INTERNALIZE: define internal void @weak_func_prevailing() +; INTERNALIZE: define weak void @weak_func_nonprevailing() ; INTERNALIZE-OPTION-DISABLE: define void @foo ; INTERNALIZE-OPTION-DISABLE: define void @bar ; INTERNALIZE-OPTION-DISABLE: define weak void @linkonce_func() +; INTERNALIZE-OPTION-DISABLE: define weak void @weak_func_prevailing() +; INTERNALIZE-OPTION-DISABLE: define weak void @weak_func_nonprevailing() ; INTERNALIZE2: define dso_local void @foo ; INTERNALIZE2: define internal void @bar ; INTERNALIZE2: define internal void @linkonce_func() +; INTERNALIZE2: define internal void @weak_func_prevailing() +; INTERNALIZE2: define weak dso_local void @weak_func_nonprevailing() ; INTERNALIZE2-OPTION-DISABLE: define dso_local void @foo ; INTERNALIZE2-OPTION-DISABLE: define dso_local void @bar ; INTERNALIZE2-OPTION-DISABLE: define weak dso_local void @linkonce_func() +; INTERNALIZE2-OPTION-DISABLE: define weak dso_local void @weak_func_prevailing() +; INTERNALIZE2-OPTION-DISABLE: define weak dso_local void @weak_func_nonprevailing() target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-apple-macosx10.11.0" @@ -50,8 +67,20 @@ } define void @bar() { call void @linkonce_func() + call void @weak_func_prevailing() + call void @weak_func_nonprevailing() ret void } define linkonce void @linkonce_func() { ret void } +define weak void @weak_func_prevailing() { + ret void +} +; Make @weak_func_nonprevailing an aliasee to ensure it is still marked +; live and kept as a definition even when non-prevailing. We want to ensure +; this definition is not internalized. +@alias1 = hidden alias void (), void ()* @weak_func_nonprevailing +define weak void @weak_func_nonprevailing() { + ret void +}