[LTO] Restore original linkage of externals prior to splitting

Summary:
This is a companion patch for http://reviews.llvm.org/D16124.

Internalized symbols increase the size of strongly-connected components in
SCC-based module splitting and thus reduce the amount of parallelism. This
patch records the original linkage of non-local symbols prior to
internalization and then restores it just before splitting/CodeGen. This is
also useful for cases where the linker requires symbols to remain external, for
instance, so they can be placed according to linker script rules.

It's currently under its own flag (-restore-globals) but should eventually
share a common flag with D16124.

Reviewers: joker.eph, pcc

Subscribers: slarin, llvm-commits, joker.eph

Differential Revision: http://reviews.llvm.org/D16229

llvm-svn: 258100
This commit is contained in:
Tobias Edler von Koch 2016-01-18 23:24:54 +00:00
parent 82a3dcbbfd
commit c19c96e06f
3 changed files with 85 additions and 2 deletions

View File

@ -39,6 +39,7 @@
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/IR/GlobalValue.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetOptions.h"
#include <string>
@ -47,7 +48,6 @@
namespace llvm {
class LLVMContext;
class DiagnosticInfo;
class GlobalValue;
class Linker;
class Mangler;
class MemoryBuffer;
@ -86,6 +86,22 @@ struct LTOCodeGenerator {
void setShouldInternalize(bool Value) { ShouldInternalize = Value; }
void setShouldEmbedUselists(bool Value) { ShouldEmbedUselists = Value; }
/// Restore linkage of globals
///
/// When set, the linkage of globals will be restored prior to code
/// generation. That is, a global symbol that had external linkage prior to
/// LTO will be emitted with external linkage again; and a local will remain
/// local. Note that this option only affects the end result - globals may
/// still be internalized in the process of LTO and may be modified and/or
/// deleted where legal.
///
/// The default behavior will internalize globals (unless on the preserve
/// list) and, if parallel code generation is enabled, will externalize
/// all locals.
void setShouldRestoreGlobalsLinkage(bool Value) {
ShouldRestoreGlobalsLinkage = Value;
}
void addMustPreserveSymbol(StringRef Sym) { MustPreserveSymbols[Sym] = 1; }
/// Pass options to the driver and optimization passes.
@ -154,6 +170,7 @@ private:
void initializeLTOPasses();
bool compileOptimizedToFile(const char **Name);
void restoreLinkageForExternals();
void applyScopeRestrictions();
void applyRestriction(GlobalValue &GV, ArrayRef<StringRef> Libcalls,
std::vector<const char *> &MustPreserveList,
@ -178,6 +195,7 @@ private:
Reloc::Model RelocModel = Reloc::Default;
StringSet MustPreserveSymbols;
StringSet AsmUndefinedRefs;
StringMap<GlobalValue::LinkageTypes> ExternalSymbols;
std::vector<std::string> CodegenOptions;
std::string FeatureStr;
std::string MCpu;
@ -190,6 +208,7 @@ private:
void *DiagContext = nullptr;
bool ShouldInternalize = true;
bool ShouldEmbedUselists = false;
bool ShouldRestoreGlobalsLinkage = false;
TargetMachine::CodeGenFileType FileType = TargetMachine::CGFT_ObjectFile;
};
}

View File

@ -347,6 +347,12 @@ applyRestriction(GlobalValue &GV,
if (isa<Function>(GV) &&
std::binary_search(Libcalls.begin(), Libcalls.end(), GV.getName()))
AsmUsed.insert(&GV);
// Record the linkage type of non-local symbols so they can be restored prior
// to module splitting.
if (ShouldRestoreGlobalsLinkage && !GV.hasAvailableExternallyLinkage() &&
!GV.hasLocalLinkage() && GV.hasName())
ExternalSymbols.insert(std::make_pair(GV.getName(), GV.getLinkage()));
}
static void findUsedValues(GlobalVariable *LLVMUsed,
@ -454,6 +460,35 @@ void LTOCodeGenerator::applyScopeRestrictions() {
ScopeRestrictionsDone = true;
}
/// Restore original linkage for symbols that may have been internalized
void LTOCodeGenerator::restoreLinkageForExternals() {
if (!ShouldInternalize || !ShouldRestoreGlobalsLinkage)
return;
assert(ScopeRestrictionsDone &&
"Cannot externalize without internalization!");
if (ExternalSymbols.empty())
return;
auto externalize = [this](GlobalValue &GV) {
if (!GV.hasLocalLinkage() || !GV.hasName())
return;
auto I = ExternalSymbols.find(GV.getName());
if (I == ExternalSymbols.end())
return;
GV.setLinkage(I->second);
};
std::for_each(MergedModule->begin(), MergedModule->end(), externalize);
std::for_each(MergedModule->global_begin(), MergedModule->global_end(),
externalize);
std::for_each(MergedModule->alias_begin(), MergedModule->alias_end(),
externalize);
}
/// Optimize merged modules using various IPO passes
bool LTOCodeGenerator::optimize(bool DisableVerify, bool DisableInline,
bool DisableGVNLoadPRE,
@ -504,6 +539,10 @@ bool LTOCodeGenerator::compileOptimized(ArrayRef<raw_pwrite_stream *> Out) {
preCodeGenPasses.add(createObjCARCContractPass());
preCodeGenPasses.run(*MergedModule);
// Re-externalize globals that may have been internalized to increase scope
// for splitting
restoreLinkageForExternals();
// Do code generation. We need to preserve the module in case the client calls
// writeMergedModules() after compilation, but we only need to allow this at
// parallelism level 1. This is achieved by having splitCodeGen return the
@ -511,7 +550,8 @@ bool LTOCodeGenerator::compileOptimized(ArrayRef<raw_pwrite_stream *> Out) {
// MergedModule.
MergedModule =
splitCodeGen(std::move(MergedModule), Out, MCpu, FeatureStr, Options,
RelocModel, CodeModel::Default, CGOptLevel, FileType);
RelocModel, CodeModel::Default, CGOptLevel, FileType,
ShouldRestoreGlobalsLinkage);
return true;
}

View File

@ -0,0 +1,24 @@
; Check that "internalizedfn" is re-externalized prior to CodeGen when
; setShouldRestoreGlobalsLinkage is enabled.
;
; RUN: llvm-as < %s > %t1
; RUN: llvm-lto -exported-symbol=preservedfn -restore-linkage -filetype=asm -o - %t1 | FileCheck %s
;
; CHECK: .globl internalizedfn
target triple = "x86_64-unknown-linux-gnu"
declare void @f()
define void @internalizedfn() noinline {
entry:
call void @f()
ret void
}
define void @preservedfn() {
entry:
call void @internalizedfn()
ret void
}