ThinLTO: special handling for LinkOnce functions

These function can be dropped by the compiler if they are no longer
referenced in the current module. However there is a change that
another module is still referencing them because of the import.

Multiple solutions can be used:

- Always import LinkOnce when a caller is imported. This ensure that
  every module with a call to a LinkOnce has the definition and will
  be able to emit it if it emits the call.
- Turn the LinkOnce into Weak, so that it is always emitted.
- Turn all LinkOnce into available_externally and come back after all
  modules are codegen'ed to emit only one copy of the linkonce, when
  there is still a reference to it.

This patch implement the second option, with am optimization that
only *one* module will turn the LinkOnce into Weak, while the others
will turn it into available_externally, so that there is exactly one
copy emitted for the whole compilation.

http://reviews.llvm.org/D18346

From: Mehdi Amini <mehdi.amini@apple.com>
llvm-svn: 265190
This commit is contained in:
Mehdi Amini 2016-04-01 21:53:50 +00:00
parent ad40523f14
commit 3173b24456
3 changed files with 187 additions and 0 deletions

View File

@ -94,6 +94,104 @@ static void saveTempBitcode(const Module &TheModule, StringRef TempDir,
WriteBitcodeToFile(&TheModule, OS, true, false);
}
bool IsFirstDefinitionForLinker(const GlobalValueInfoList &GVInfo,
const ModuleSummaryIndex &Index,
StringRef ModulePath) {
// Get the first *linker visible* definition for this global in the summary
// list.
auto FirstDefForLinker = llvm::find_if(
GVInfo, [](const std::unique_ptr<GlobalValueInfo> &FuncInfo) {
auto Linkage = FuncInfo->summary()->linkage();
return !GlobalValue::isAvailableExternallyLinkage(Linkage);
});
// If \p GV is not the first definition, give up...
if ((*FirstDefForLinker)->summary()->modulePath() != ModulePath)
return false;
// If there is any strong definition anywhere, do not bother emitting this.
if (llvm::any_of(
GVInfo, [](const std::unique_ptr<GlobalValueInfo> &FuncInfo) {
auto Linkage = FuncInfo->summary()->linkage();
return !GlobalValue::isAvailableExternallyLinkage(Linkage) &&
!GlobalValue::isWeakForLinker(Linkage);
}))
return false;
return true;
};
static void ResolveODR(GlobalValue &GV, const ModuleSummaryIndex &Index,
StringRef ModulePath) {
if (GV.isDeclaration())
return;
auto HasMultipleCopies =
[&](const GlobalValueInfoList &GVInfo) { return GVInfo.size() > 1; };
auto getGVInfo = [&](GlobalValue &GV) -> const GlobalValueInfoList *{
auto GUID = Function::getGlobalIdentifier(GV.getName(), GV.getLinkage(),
ModulePath);
auto It = Index.findGlobalValueInfoList(GV.getName());
if (It == Index.end())
return nullptr;
return &It->second;
};
switch (GV.getLinkage()) {
case GlobalValue::ExternalLinkage:
case GlobalValue::AvailableExternallyLinkage:
case GlobalValue::AppendingLinkage:
case GlobalValue::InternalLinkage:
case GlobalValue::PrivateLinkage:
case GlobalValue::ExternalWeakLinkage:
case GlobalValue::CommonLinkage:
case GlobalValue::LinkOnceAnyLinkage:
case GlobalValue::WeakAnyLinkage:
break;
case GlobalValue::LinkOnceODRLinkage:
case GlobalValue::WeakODRLinkage: {
auto *GVInfo = getGVInfo(GV);
if (!GVInfo)
break;
// We need to emit only one of these, the first module will keep
// it, but turned into a weak while the others will drop it.
if (!HasMultipleCopies(*GVInfo))
break;
if (IsFirstDefinitionForLinker(*GVInfo, Index, ModulePath))
GV.setLinkage(GlobalValue::WeakODRLinkage);
else
GV.setLinkage(GlobalValue::AvailableExternallyLinkage);
break;
}
}
}
/// Resolve LinkOnceODR and WeakODR.
///
/// We'd like to drop these function if they are no longer referenced in the
/// current module. However there is a chance that another module is still
/// referencing them because of the import. We make sure we always emit at least
/// one copy.
static void ResolveODR(Module &TheModule,
const ModuleSummaryIndex &Index) {
// We won't optimize the globals that are referenced by an alias for now
// Ideally we should turn the alias into a global and duplicate the definition
// when needed.
DenseSet<GlobalValue *> GlobalInvolvedWithAlias;
for (auto &GA : TheModule.aliases()) {
auto *GO = GA.getBaseObject();
if (auto *GV = dyn_cast<GlobalValue>(GO))
GlobalInvolvedWithAlias.insert(GV);
}
// Process functions and global now
for (auto &GV : TheModule) {
if (!GlobalInvolvedWithAlias.count(&GV))
ResolveODR(GV, Index, TheModule.getModuleIdentifier());
}
for (auto &GV : TheModule.globals()) {
if (!GlobalInvolvedWithAlias.count(&GV))
ResolveODR(GV, Index, TheModule.getModuleIdentifier());
}
}
static StringMap<MemoryBufferRef>
generateModuleMap(const std::vector<MemoryBufferRef> &Modules) {
StringMap<MemoryBufferRef> ModuleMap;
@ -205,6 +303,11 @@ ProcessThinLTOModule(Module &TheModule, const ModuleSummaryIndex &Index,
if (!SingleModule) {
promoteModule(TheModule, Index);
// Resolve the LinkOnce/Weak ODR, trying to turn them into
// "available_externally" when possible.
// This is a compile-time optimization.
ResolveODR(TheModule, Index);
// Save temps: after promotion.
saveTempBitcode(TheModule, SaveTempsDir, count, ".2.promoted.bc");
@ -326,6 +429,11 @@ std::unique_ptr<ModuleSummaryIndex> ThinLTOCodeGenerator::linkCombinedIndex() {
*/
void ThinLTOCodeGenerator::promote(Module &TheModule,
ModuleSummaryIndex &Index) {
// Resolve the LinkOnceODR, trying to turn them into "available_externally"
// where possible.
ResolveODR(TheModule, Index);
promoteModule(TheModule, Index);
}

View File

@ -0,0 +1,29 @@
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.11.0"
; Alias are not optimized
@linkoncealias = linkonce_odr alias void (), void ()* @linkonceodrfuncwithalias
; Function with an alias are not optimized
define linkonce_odr void @linkonceodrfuncwithalias() #0 {
entry:
ret void
}
define linkonce_odr void @linkonceodrfunc() #0 {
entry:
ret void
}
define linkonce void @linkoncefunc() #0 {
entry:
ret void
}
define weak_odr void @weakodrfunc() #0 {
entry:
ret void
}
define weak void @weakfunc() #0 {
entry:
ret void
}

View File

@ -0,0 +1,50 @@
; Do setup work for all below tests: generate bitcode and combined index
; RUN: llvm-as -module-summary %s -o %t.bc
; RUN: llvm-as -module-summary %p/Inputs/odr_resolution.ll -o %t2.bc
; RUN: llvm-lto -thinlto-action=thinlink -o %t3.bc %t.bc %t2.bc
; Verify that only one ODR is selected across modules, but non ODR are not affected.
; RUN: llvm-lto -thinlto-action=promote %t.bc -thinlto-index=%t3.bc -o - | llvm-dis -o - | FileCheck %s --check-prefix=MOD1
; RUN: llvm-lto -thinlto-action=promote %t2.bc -thinlto-index=%t3.bc -o - | llvm-dis -o - | FileCheck %s --check-prefix=MOD2
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.11.0"
; Alias are not optimized
; MOD1: @linkoncealias = linkonce_odr alias void (), void ()* @linkonceodrfuncwithalias
; MOD2: @linkoncealias = linkonce_odr alias void (), void ()* @linkonceodrfuncwithalias
@linkoncealias = linkonce_odr alias void (), void ()* @linkonceodrfuncwithalias
; Function with an alias are not optimized
; MOD1: define linkonce_odr void @linkonceodrfuncwithalias()
; MOD2: define linkonce_odr void @linkonceodrfuncwithalias()
define linkonce_odr void @linkonceodrfuncwithalias() #0 {
entry:
ret void
}
; MOD1: define weak_odr void @linkonceodrfunc()
; MOD2: define available_externally void @linkonceodrfunc()
define linkonce_odr void @linkonceodrfunc() #0 {
entry:
ret void
}
; MOD1: define linkonce void @linkoncefunc()
; MOD2: define linkonce void @linkoncefunc()
define linkonce void @linkoncefunc() #0 {
entry:
ret void
}
; MOD1: define weak_odr void @weakodrfunc()
; MOD2: define available_externally void @weakodrfunc()
define weak_odr void @weakodrfunc() #0 {
entry:
ret void
}
; MOD1: define weak void @weakfunc()
; MOD2: define weak void @weakfunc()
define weak void @weakfunc() #0 {
entry:
ret void
}