From ea314fd476166405905103a29dd8578ff6589160 Mon Sep 17 00:00:00 2001 From: Teresa Johnson Date: Fri, 23 Aug 2019 15:18:58 +0000 Subject: [PATCH] [ThinLTO] Fix handling of weak interposable symbols Summary: Keep aliasees alive if their alias is live, otherwise we end up with an alias to a declaration, which is invalid. This can happen when the aliasee is weak and non-prevailing. This fix exposed the fact that we were then attempting to internalize the weak symbol, which was not exported as it was not prevailing. We should not internalize interposable symbols in general, unless this is the prevailing copy, since it can lead to incorrect inlining and other optimizations. Most of the changes in this patch are due to the restructuring required to pass down the prevailing callback. Finally, while implementing the test cases, I found that in the case of a weak aliasee that is still marked not live because its alias isn't live, after dropping the definition we incorrectly marked the declaration with weak linkage when resolving prevailing symbols in the module. This was due to some special case handling for symbols marked WeakLinkage in the summary located before instead of after a subsequent check for the symbol being a declaration. It turns out that we don't actually need this special case handling any more (looking back at the history, when that was added the code was structured quite differently) - we will correctly mark with weak linkage further below when the definition hasn't been dropped. Fixes PR42542. Reviewers: pcc Subscribers: mehdi_amini, inglorion, steven_wu, dexonsmith, dang, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D66264 llvm-svn: 369766 --- llvm/include/llvm/LTO/LTO.h | 4 +- llvm/lib/LTO/LTO.cpp | 18 ++++++-- llvm/lib/LTO/ThinLTOCodeGenerator.cpp | 46 ++++++++++++++----- llvm/lib/Transforms/IPO/FunctionImport.cpp | 35 ++++++-------- .../X86/not-prevailing-weak-aliasee.ll | 33 +++++++++++++ llvm/test/ThinLTO/X86/Inputs/internalize.ll | 6 +++ llvm/test/ThinLTO/X86/internalize.ll | 35 ++++++++++++-- 7 files changed, 135 insertions(+), 42 deletions(-) create mode 100644 llvm/test/LTO/Resolution/X86/not-prevailing-weak-aliasee.ll create mode 100644 llvm/test/ThinLTO/X86/Inputs/internalize.ll diff --git a/llvm/include/llvm/LTO/LTO.h b/llvm/include/llvm/LTO/LTO.h index ca0a8b64523a..4bf93c5ee445 100644 --- a/llvm/include/llvm/LTO/LTO.h +++ b/llvm/include/llvm/LTO/LTO.h @@ -59,7 +59,9 @@ void thinLTOResolvePrevailingInIndex( /// 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. diff --git a/llvm/lib/LTO/LTO.cpp b/llvm/lib/LTO/LTO.cpp index 0ada5015c0a9..d6e7950b078a 100644 --- a/llvm/lib/LTO/LTO.cpp +++ b/llvm/lib/LTO/LTO.cpp @@ -384,7 +384,9 @@ static bool isWeakObjectWithRWAccess(GlobalValueSummary *GVS) { 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 @@ static void thinLTOInternalizeAndPromoteGUID( // 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 @@ static void thinLTOInternalizeAndPromoteGUID( // 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 @@ Error LTO::runThinLTO(AddStreamFn AddStream, NativeObjectCache Cache, 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) { diff --git a/llvm/lib/LTO/ThinLTOCodeGenerator.cpp b/llvm/lib/LTO/ThinLTOCodeGenerator.cpp index eada2529e823..6c8f827d59ae 100644 --- a/llvm/lib/LTO/ThinLTOCodeGenerator.cpp +++ b/llvm/lib/LTO/ThinLTOCodeGenerator.cpp @@ -457,10 +457,9 @@ static void resolvePrevailingInIndex( 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 @@ std::unique_ptr ThinLTOCodeGenerator::linkCombinedIndex() { 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 @@ static void internalizeAndPromoteInIndex( 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 @@ void ThinLTOCodeGenerator::promote(Module &TheModule, ModuleSummaryIndex &Index, 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 @@ void ThinLTOCodeGenerator::internalize(Module &TheModule, 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 @@ void ThinLTOCodeGenerator::run() { // 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 diff --git a/llvm/lib/Transforms/IPO/FunctionImport.cpp b/llvm/lib/Transforms/IPO/FunctionImport.cpp index f915aea004fd..531dbb5b6f21 100644 --- a/llvm/lib/Transforms/IPO/FunctionImport.cpp +++ b/llvm/lib/Transforms/IPO/FunctionImport.cpp @@ -764,7 +764,7 @@ void llvm::computeDeadSymbols( } // 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 @@ void llvm::computeDeadSymbols( 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 @@ void llvm::computeDeadSymbols( // 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 @@ void llvm::thinLTOResolvePrevailingInModule( 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 diff --git a/llvm/test/LTO/Resolution/X86/not-prevailing-weak-aliasee.ll b/llvm/test/LTO/Resolution/X86/not-prevailing-weak-aliasee.ll new file mode 100644 index 000000000000..56dd092e24d1 --- /dev/null +++ b/llvm/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 +} diff --git a/llvm/test/ThinLTO/X86/Inputs/internalize.ll b/llvm/test/ThinLTO/X86/Inputs/internalize.ll new file mode 100644 index 000000000000..d95078c34d53 --- /dev/null +++ b/llvm/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 +} diff --git a/llvm/test/ThinLTO/X86/internalize.ll b/llvm/test/ThinLTO/X86/internalize.ll index 49cab144b564..0f7d4a5ca1f5 100644 --- a/llvm/test/ThinLTO/X86/internalize.ll +++ b/llvm/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 @foo() { } 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 +}