Rewrite the gold plugin to fix pr19901.

There is a fundamental difference between how the gold API and lib/LTO view
the LTO process.

The gold API talks about a particular symbol in a particular file. The lib/LTO
API talks about a symbol in the merged module.

The merged module is then defined in terms of the IR semantics. In particular,
a linkonce_odr GV is only copied if it is used, since it is valid to drop
unused linkonce_odr GVs.

In the testcase in pr19901 both properties collide. What happens is that gold
asks us to keep a particular linkonce_odr symbol, but the IR linker doesn't
copy it to the merged module and we never have a chance to ask lib/LTO to keep
it.

This patch fixes it by having a more direct implementation of the gold API. If
it asks us to keep a symbol, we change the linkage so it is not linkonce. If it
says we can drop a symbol, we do so. All of this before we even send the module
to lib/Linker.

Since now we don't have to produce LTO_SYMBOL_SCOPE_DEFAULT_CAN_BE_HIDDEN,
during symbol resolution we can use a temporary LLVMContext and do lazy
module loading. This allows us to keep the minimum possible amount of
allocated memory around. This should also allow as much parallelism as
we want, since there is no shared context.

llvm-svn: 216215
This commit is contained in:
Rafael Espindola 2014-08-21 20:28:55 +00:00
parent 70b9c01bd4
commit 33466a745e
9 changed files with 450 additions and 158 deletions

View File

@ -0,0 +1 @@
@a = global i32 42

View File

@ -0,0 +1,4 @@
target triple = "x86_64-unknown-linux-gnu"
define linkonce_odr hidden void @f() {
ret void
}

View File

@ -0,0 +1,11 @@
; RUN: llvm-as %s -o %t.o
; RUN: llvm-as %p/Inputs/alias-1.ll -o %t2.o
; RUN: ld -shared -o %t3.o -plugin %llvmshlibdir/LLVMgold.so %t2.o %t.o \
; RUN: -plugin-opt=emit-llvm
; RUN: llvm-dis %t3.o -o - | FileCheck %s
; CHECK: @a = global i32 42
; CHECK: @b = global i32 1
@a = weak alias i32* @b
@b = global i32 1

View File

@ -55,7 +55,11 @@ define linkonce_odr void @f6() unnamed_addr {
@g6 = global void()* @f6
; API: f3
; API: f5
; API: g5
; API: g6
; API: f1 PREVAILING_DEF_IRONLY
; API: f2 PREVAILING_DEF_IRONLY
; API: f3 PREVAILING_DEF_IRONLY_EXP
; API: f4 PREVAILING_DEF_IRONLY_EXP
; API: f5 PREVAILING_DEF_IRONLY_EXP
; API: f6 PREVAILING_DEF_IRONLY_EXP
; API: g5 PREVAILING_DEF_IRONLY_EXP
; API: g6 PREVAILING_DEF_IRONLY_EXP

View File

@ -1,7 +1,13 @@
; RUN: llvm-as %s -o %t.o
; RUN: ld -plugin %llvmshlibdir/LLVMgold.so -m elf32ppc \
; RUN: -plugin-opt=mtriple=powerpc-linux-gnu \
; RUN: -shared %t.o -o %t2.o
; RUN: llvm-readobj %t2.o | FileCheck %s
; RUN: -plugin-opt=obj-path=%t3.o \
; RUN: -shared %t.o -o %t2
; RUN: llvm-readobj --file-headers %t2 | FileCheck --check-prefix=DSO %s
; RUN: llvm-readobj --file-headers %t3.o | FileCheck --check-prefix=REL %s
; CHECK: Format: ELF32-ppc
; REL: Type: Relocatable
; REL-NEXT: Machine: EM_PPC
; DSO: Type: SharedObject
; DSO-NEXT: Machine: EM_PPC

View File

@ -0,0 +1,17 @@
; RUN: llc %s -o %t.o -filetype=obj -relocation-model=pic
; RUN: llvm-as %p/Inputs/pr19901-1.ll -o %t2.o
; RUN: ld -shared -o %t.so -plugin %llvmshlibdir/LLVMgold.so %t2.o %t.o
; RUN: llvm-objdump -d -symbolize %t.so | FileCheck %s
; CHECK: g:
; CHECK-NEXT: push
; CHECK-NEXT: callq f
target triple = "x86_64-unknown-linux-gnu"
define i32 @g() {
call void @f()
ret i32 0
}
define linkonce_odr hidden void @f() {
ret void
}

View File

@ -16,7 +16,9 @@ else()
set(LLVM_LINK_COMPONENTS
${LLVM_TARGETS_TO_BUILD}
LTO
Linker
BitWriter
IPO
)
add_llvm_loadable_module(LLVMgold

View File

@ -20,7 +20,7 @@ EXPORTED_SYMBOL_FILE = $(PROJ_SRC_DIR)/gold.exports
# early so we can set up LINK_COMPONENTS before including Makefile.rules
include $(LEVEL)/Makefile.config
LINK_COMPONENTS := $(TARGETS_TO_BUILD) LTO
LINK_COMPONENTS := $(TARGETS_TO_BUILD) Linker BitWriter IPO
# Because off_t is used in the public API, the largefile parts are required for
# ABI compatibility.

View File

@ -13,13 +13,27 @@
//===----------------------------------------------------------------------===//
#include "llvm/Config/config.h" // plugin-api.h requires HAVE_STDINT_H
#include "llvm-c/lto.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/Bitcode/ReaderWriter.h"
#include "llvm/CodeGen/Analysis.h"
#include "llvm/CodeGen/CommandFlags.h"
#include "llvm/LTO/LTOCodeGenerator.h"
#include "llvm/LTO/LTOModule.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Verifier.h"
#include "llvm/Linker/Linker.h"
#include "llvm/MC/SubtargetFeature.h"
#include "llvm/Object/IRObjectFile.h"
#include "llvm/PassManager.h"
#include "llvm/Support/FormattedStream.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Target/TargetLibraryInfo.h"
#include "llvm/Transforms/IPO.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
#include "llvm/Transforms/Utils/GlobalStatus.h"
#include "llvm/Transforms/Utils/ModuleUtils.h"
#include <list>
#include <plugin-api.h>
#include <system_error>
@ -46,18 +60,18 @@ static ld_plugin_status discard_message(int level, const char *format, ...) {
abort();
}
static ld_plugin_get_input_file get_input_file = nullptr;
static ld_plugin_release_input_file release_input_file = nullptr;
static ld_plugin_add_symbols add_symbols = nullptr;
static ld_plugin_get_symbols get_symbols = nullptr;
static ld_plugin_add_input_file add_input_file = nullptr;
static ld_plugin_set_extra_library_path set_extra_library_path = nullptr;
static ld_plugin_get_view get_view = nullptr;
static ld_plugin_message message = discard_message;
static lto_codegen_model output_type = LTO_CODEGEN_PIC_MODEL_STATIC;
static Reloc::Model RelocationModel = Reloc::Default;
static std::string output_name = "";
static std::list<claimed_file> Modules;
static std::vector<std::string> Cleanup;
static LTOCodeGenerator *CodeGen = nullptr;
static StringSet<> CannotBeHidden;
static llvm::TargetOptions TargetOpts;
namespace options {
@ -108,6 +122,11 @@ namespace options {
}
} else {
// Save this option to pass to the code generator.
// ParseCommandLineOptions() expects argv[0] to be program name. Lazily
// add that.
if (extra.empty())
extra.push_back("LLVMgold");
extra.push_back(opt_);
}
}
@ -145,10 +164,10 @@ ld_plugin_status onload(ld_plugin_tv *tv) {
case LDPO_REL: // .o
case LDPO_DYN: // .so
case LDPO_PIE: // position independent executable
output_type = LTO_CODEGEN_PIC_MODEL_DYNAMIC;
RelocationModel = Reloc::PIC_;
break;
case LDPO_EXEC: // .exe
output_type = LTO_CODEGEN_PIC_MODEL_STATIC;
RelocationModel = Reloc::Static;
break;
default:
message(LDPL_ERROR, "Unknown output file type %d", tv->tv_u.tv_val);
@ -183,6 +202,12 @@ ld_plugin_status onload(ld_plugin_tv *tv) {
if (callback(cleanup_hook) != LDPS_OK)
return LDPS_ERR;
} break;
case LDPT_GET_INPUT_FILE:
get_input_file = tv->tv_u.tv_get_input_file;
break;
case LDPT_RELEASE_INPUT_FILE:
release_input_file = tv->tv_u.tv_release_input_file;
break;
case LDPT_ADD_SYMBOLS:
add_symbols = tv->tv_u.tv_add_symbols;
break;
@ -218,28 +243,15 @@ ld_plugin_status onload(ld_plugin_tv *tv) {
if (!RegisteredAllSymbolsRead)
return LDPS_OK;
CodeGen = new LTOCodeGenerator();
// Pass through extra options to the code generator.
if (!options::extra.empty()) {
for (const char *Opt : options::extra)
CodeGen->setCodeGenDebugOptions(Opt);
if (!get_input_file) {
message(LDPL_ERROR, "get_input_file not passed to LLVMgold.");
return LDPS_ERR;
}
CodeGen->parseCodeGenDebugOptions();
if (MAttrs.size()) {
std::string Attrs;
for (unsigned I = 0; I < MAttrs.size(); ++I) {
if (I > 0)
Attrs.append(",");
Attrs.append(MAttrs[I]);
}
CodeGen->setAttr(Attrs.c_str());
if (!release_input_file) {
message(LDPL_ERROR, "relesase_input_file not passed to LLVMgold.");
return LDPS_ERR;
}
TargetOpts = InitTargetOptionsFromCodeGenFlags();
CodeGen->setTargetOptions(TargetOpts);
return LDPS_OK;
}
@ -248,13 +260,16 @@ ld_plugin_status onload(ld_plugin_tv *tv) {
/// possible.
static ld_plugin_status claim_file_hook(const ld_plugin_input_file *file,
int *claimed) {
const void *view;
LLVMContext Context;
std::unique_ptr<MemoryBuffer> buffer;
if (get_view) {
const void *view;
if (get_view(file->handle, &view) != LDPS_OK) {
message(LDPL_ERROR, "Failed to get a view of %s", file->name);
return LDPS_ERR;
}
buffer.reset(MemoryBuffer::getMemBuffer(
StringRef((char *)view, file->filesize), "", false));
} else {
int64_t offset = 0;
// Gold has found what might be IR part-way inside of a file, such as
@ -270,95 +285,89 @@ static ld_plugin_status claim_file_hook(const ld_plugin_input_file *file,
return LDPS_ERR;
}
buffer = std::move(BufferOrErr.get());
view = buffer->getBufferStart();
}
if (!LTOModule::isBitcodeFile(view, file->filesize))
ErrorOr<object::IRObjectFile *> ObjOrErr =
object::IRObjectFile::createIRObjectFile(buffer->getMemBufferRef(),
Context);
std::error_code EC = ObjOrErr.getError();
if (EC == BitcodeError::InvalidBitcodeSignature)
return LDPS_OK;
*claimed = 1;
std::string Error;
LTOModule *M =
LTOModule::createFromBuffer(view, file->filesize, TargetOpts, Error);
if (!M) {
if (EC) {
message(LDPL_ERROR, "LLVM gold plugin has failed to create LTO module: %s",
Error.c_str());
EC.message().c_str());
return LDPS_ERR;
}
std::unique_ptr<object::IRObjectFile> Obj(ObjOrErr.get());
Modules.resize(Modules.size() + 1);
claimed_file &cf = Modules.back();
if (!options::triple.empty())
M->setTargetTriple(options::triple.c_str());
cf.handle = file->handle;
unsigned sym_count = M->getSymbolCount();
cf.syms.reserve(sym_count);
for (unsigned i = 0; i != sym_count; ++i) {
lto_symbol_attributes attrs = M->getSymbolAttributes(i);
if ((attrs & LTO_SYMBOL_SCOPE_MASK) == LTO_SYMBOL_SCOPE_INTERNAL)
for (auto &Sym : Obj->symbols()) {
uint32_t Symflags = Sym.getFlags();
if (!(Symflags & object::BasicSymbolRef::SF_Global))
continue;
if (Symflags & object::BasicSymbolRef::SF_FormatSpecific)
continue;
cf.syms.push_back(ld_plugin_symbol());
ld_plugin_symbol &sym = cf.syms.back();
sym.name = strdup(M->getSymbolName(i));
sym.version = nullptr;
int scope = attrs & LTO_SYMBOL_SCOPE_MASK;
bool CanBeHidden = scope == LTO_SYMBOL_SCOPE_DEFAULT_CAN_BE_HIDDEN;
if (!CanBeHidden)
CannotBeHidden.insert(sym.name);
switch (scope) {
case LTO_SYMBOL_SCOPE_HIDDEN:
sym.visibility = LDPV_HIDDEN;
break;
case LTO_SYMBOL_SCOPE_PROTECTED:
sym.visibility = LDPV_PROTECTED;
break;
case 0: // extern
case LTO_SYMBOL_SCOPE_DEFAULT:
case LTO_SYMBOL_SCOPE_DEFAULT_CAN_BE_HIDDEN:
SmallString<64> Name;
{
raw_svector_ostream OS(Name);
Sym.printName(OS);
}
sym.name = strdup(Name.c_str());
const GlobalValue *GV = Obj->getSymbolGV(Sym.getRawDataRefImpl());
sym.visibility = LDPV_DEFAULT;
if (GV) {
switch (GV->getVisibility()) {
case GlobalValue::DefaultVisibility:
sym.visibility = LDPV_DEFAULT;
break;
default:
message(LDPL_ERROR, "Unknown scope attribute: %d", scope);
return LDPS_ERR;
case GlobalValue::HiddenVisibility:
sym.visibility = LDPV_HIDDEN;
break;
case GlobalValue::ProtectedVisibility:
sym.visibility = LDPV_PROTECTED;
break;
}
}
int definition = attrs & LTO_SYMBOL_DEFINITION_MASK;
sym.comdat_key = nullptr;
switch (definition) {
case LTO_SYMBOL_DEFINITION_REGULAR:
sym.def = LDPK_DEF;
break;
case LTO_SYMBOL_DEFINITION_UNDEFINED:
sym.def = LDPK_UNDEF;
break;
case LTO_SYMBOL_DEFINITION_TENTATIVE:
sym.def = LDPK_COMMON;
break;
case LTO_SYMBOL_DEFINITION_WEAK:
sym.comdat_key = sym.name;
sym.def = LDPK_WEAKDEF;
break;
case LTO_SYMBOL_DEFINITION_WEAKUNDEF:
if (Symflags & object::BasicSymbolRef::SF_Undefined) {
sym.def = LDPK_UNDEF;
if (GV && GV->hasExternalWeakLinkage())
sym.def = LDPK_WEAKUNDEF;
break;
default:
message(LDPL_ERROR, "Unknown definition attribute: %d", definition);
return LDPS_ERR;
} else {
sym.def = LDPK_DEF;
if (GV) {
assert(!GV->hasExternalWeakLinkage() &&
!GV->hasAvailableExternallyLinkage() && "Not a declaration!");
if (GV->hasCommonLinkage())
sym.def = LDPK_COMMON;
else if (GV->isWeakForLinker())
sym.def = LDPK_WEAKDEF;
}
}
sym.size = 0;
sym.comdat_key = nullptr;
if (GV && (GV->hasWeakLinkage() || GV->hasLinkOnceLinkage()))
sym.comdat_key = sym.name;
sym.resolution = LDPR_UNKNOWN;
}
cf.syms.reserve(cf.syms.size());
if (!cf.syms.empty()) {
if (add_symbols(cf.handle, cf.syms.size(), &cf.syms[0]) != LDPS_OK) {
message(LDPL_ERROR, "Unable to add symbols!");
@ -366,51 +375,311 @@ static ld_plugin_status claim_file_hook(const ld_plugin_input_file *file,
}
}
if (CodeGen) {
std::string Error;
if (!CodeGen->addModule(M, Error)) {
message(LDPL_ERROR, "Error linking module: %s", Error.c_str());
return LDPS_ERR;
}
}
delete M;
return LDPS_OK;
}
static bool mustPreserve(ld_plugin_symbol &Sym) {
if (Sym.resolution == LDPR_PREVAILING_DEF)
static void keepGlobalValue(GlobalValue &GV) {
assert(!GV.hasLocalLinkage());
switch (GV.getLinkage()) {
default:
break;
case GlobalValue::LinkOnceAnyLinkage:
GV.setLinkage(GlobalValue::WeakAnyLinkage);
break;
case GlobalValue::LinkOnceODRLinkage:
GV.setLinkage(GlobalValue::WeakODRLinkage);
break;
}
assert(!GV.isDiscardableIfUnused());
}
static bool isDeclaration(const GlobalValue &V) {
if (V.hasAvailableExternallyLinkage())
return true;
if (Sym.resolution == LDPR_PREVAILING_DEF_IRONLY_EXP)
return CannotBeHidden.count(Sym.name);
return false;
if (V.isMaterializable())
return false;
return V.isDeclaration();
}
static void internalize(GlobalValue &GV) {
if (isDeclaration(GV))
return; // We get here if there is a matching asm definition.
if (!GV.hasLocalLinkage())
GV.setLinkage(GlobalValue::InternalLinkage);
}
static void drop(GlobalValue &GV) {
if (auto *F = dyn_cast<Function>(&GV)) {
F->deleteBody();
return;
}
if (auto *Var = dyn_cast<GlobalVariable>(&GV)) {
Var->setInitializer(nullptr);
Var->setLinkage(GlobalValue::ExternalLinkage);
return;
}
auto &Alias = cast<GlobalAlias>(GV);
Module &M = *Alias.getParent();
PointerType &Ty = *cast<PointerType>(Alias.getType());
GlobalValue::LinkageTypes L = Alias.getLinkage();
auto *Var =
new GlobalVariable(M, Ty.getElementType(), /*isConstant*/ false, L,
/*Initializer*/ nullptr);
Var->takeName(&Alias);
Alias.replaceAllUsesWith(Var);
}
static const char *getResolutionName(ld_plugin_symbol_resolution R) {
switch (R) {
case LDPR_UNKNOWN:
return "UNKNOWN";
case LDPR_UNDEF:
return "UNDEF";
case LDPR_PREVAILING_DEF:
return "PREVAILING_DEF";
case LDPR_PREVAILING_DEF_IRONLY:
return "PREVAILING_DEF_IRONLY";
case LDPR_PREEMPTED_REG:
return "PREEMPTED_REG";
case LDPR_PREEMPTED_IR:
return "PREEMPTED_IR";
case LDPR_RESOLVED_IR:
return "RESOLVED_IR";
case LDPR_RESOLVED_EXEC:
return "RESOLVED_EXEC";
case LDPR_RESOLVED_DYN:
return "RESOLVED_DYN";
case LDPR_PREVAILING_DEF_IRONLY_EXP:
return "PREVAILING_DEF_IRONLY_EXP";
}
}
static std::unique_ptr<Module>
getModuleForFile(LLVMContext &Context, claimed_file &F, raw_fd_ostream *ApiFile,
StringSet<> &Internalize, StringSet<> &Maybe) {
ld_plugin_input_file File;
if (get_input_file(F.handle, &File) != LDPS_OK)
message(LDPL_FATAL, "Failed to get file information");
if (get_symbols(F.handle, F.syms.size(), &F.syms[0]) != LDPS_OK)
message(LDPL_FATAL, "Failed to get symbol information");
const void *View;
if (get_view(F.handle, &View) != LDPS_OK)
message(LDPL_FATAL, "Failed to get a view of file");
std::unique_ptr<MemoryBuffer> Buffer(MemoryBuffer::getMemBuffer(
StringRef((char *)View, File.filesize), "", false));
if (release_input_file(F.handle) != LDPS_OK)
message(LDPL_FATAL, "Failed to release file information");
ErrorOr<Module *> MOrErr = getLazyBitcodeModule(Buffer.get(), Context);
if (std::error_code EC = MOrErr.getError())
message(LDPL_FATAL, "Could not read bitcode from file : %s",
EC.message().c_str());
Buffer.release();
std::unique_ptr<Module> M(MOrErr.get());
SmallPtrSet<GlobalValue *, 8> Used;
collectUsedGlobalVariables(*M, Used, /*CompilerUsed*/ false);
std::vector<GlobalValue *> Drop;
for (ld_plugin_symbol &Sym : F.syms) {
ld_plugin_symbol_resolution Resolution =
(ld_plugin_symbol_resolution)Sym.resolution;
if (options::generate_api_file)
*ApiFile << Sym.name << ' ' << getResolutionName(Resolution) << '\n';
GlobalValue *GV = M->getNamedValue(Sym.name);
if (!GV)
continue; // Asm symbol.
switch (Resolution) {
case LDPR_UNKNOWN:
llvm_unreachable("Unexpected resolution");
case LDPR_RESOLVED_IR:
case LDPR_RESOLVED_EXEC:
case LDPR_RESOLVED_DYN:
case LDPR_UNDEF:
assert(isDeclaration(*GV));
break;
case LDPR_PREVAILING_DEF_IRONLY: {
if (!Used.count(GV)) {
// Since we use the regular lib/Linker, we cannot just internalize GV
// now or it will not be copied to the merged module. Instead we force
// it to be copied and then internalize it.
keepGlobalValue(*GV);
Internalize.insert(Sym.name);
}
break;
}
case LDPR_PREVAILING_DEF:
keepGlobalValue(*GV);
break;
case LDPR_PREEMPTED_REG:
case LDPR_PREEMPTED_IR:
Drop.push_back(GV);
break;
case LDPR_PREVAILING_DEF_IRONLY_EXP: {
// We can only check for address uses after we merge the modules. The
// reason is that this GV might have a copy in another module
// and in that module the address might be significant, but that
// copy will be LDPR_PREEMPTED_IR.
if (GV->hasLinkOnceODRLinkage())
Maybe.insert(Sym.name);
keepGlobalValue(*GV);
break;
}
}
free(Sym.name);
Sym.name = nullptr;
Sym.comdat_key = nullptr;
}
if (!Drop.empty()) {
// This is horrible. Given how lazy loading is implemented, dropping
// the body while there is a materializer present doesn't work, the
// linker will just read the body back.
M->materializeAllPermanently();
for (auto *GV : Drop)
drop(*GV);
}
return M;
}
static void runLTOPasses(Module &M, TargetMachine &TM) {
PassManager passes;
PassManagerBuilder PMB;
PMB.LibraryInfo = new TargetLibraryInfo(Triple(TM.getTargetTriple()));
PMB.Inliner = createFunctionInliningPass();
PMB.VerifyInput = true;
PMB.VerifyOutput = true;
PMB.populateLTOPassManager(passes, &TM);
passes.run(M);
}
static void codegen(Module &M) {
const std::string &TripleStr = M.getTargetTriple();
Triple TheTriple(TripleStr);
std::string ErrMsg;
const Target *TheTarget = TargetRegistry::lookupTarget(TripleStr, ErrMsg);
if (!TheTarget)
message(LDPL_FATAL, "Target not found: %s", ErrMsg.c_str());
if (unsigned NumOpts = options::extra.size())
cl::ParseCommandLineOptions(NumOpts, &options::extra[0]);
SubtargetFeatures Features;
Features.getDefaultSubtargetFeatures(TheTriple);
for (const std::string &A : MAttrs)
Features.AddFeature(A);
TargetOptions Options = InitTargetOptionsFromCodeGenFlags();
std::unique_ptr<TargetMachine> TM(TheTarget->createTargetMachine(
TripleStr, options::mcpu, Features.getString(), Options, RelocationModel,
CodeModel::Default, CodeGenOpt::Aggressive));
runLTOPasses(M, *TM);
PassManager CodeGenPasses;
CodeGenPasses.add(new DataLayoutPass(&M));
SmallString<128> Filename;
int FD;
if (options::obj_path.empty()) {
std::error_code EC =
sys::fs::createTemporaryFile("lto-llvm", "o", FD, Filename);
if (EC)
message(LDPL_FATAL, "Could not create temorary file: %s",
EC.message().c_str());
} else {
Filename = options::obj_path;
std::error_code EC =
sys::fs::openFileForWrite(Filename.c_str(), FD, sys::fs::F_None);
if (EC)
message(LDPL_FATAL, "Could not open file: %s", EC.message().c_str());
}
{
raw_fd_ostream OS(FD, true);
formatted_raw_ostream FOS(OS);
if (TM->addPassesToEmitFile(CodeGenPasses, FOS,
TargetMachine::CGFT_ObjectFile))
message(LDPL_FATAL, "Failed to setup codegen");
CodeGenPasses.run(M);
}
if (add_input_file(Filename.c_str()) != LDPS_OK)
message(LDPL_FATAL,
"Unable to add .o file to the link. File left behind in: %s",
Filename.c_str());
if (options::obj_path.empty())
Cleanup.push_back(Filename.c_str());
}
/// gold informs us that all symbols have been read. At this point, we use
/// get_symbols to see if any of our definitions have been overridden by a
/// native object file. Then, perform optimization and codegen.
static ld_plugin_status allSymbolsReadHook(raw_fd_ostream *apiFile) {
assert(CodeGen);
static ld_plugin_status allSymbolsReadHook(raw_fd_ostream *ApiFile) {
if (Modules.empty())
return LDPS_OK;
LLVMContext Context;
std::unique_ptr<Module> Combined(new Module("ld-temp.o", Context));
Linker L(Combined.get());
std::string DefaultTriple = sys::getDefaultTargetTriple();
StringSet<> Internalize;
StringSet<> Maybe;
for (claimed_file &F : Modules) {
if (F.syms.empty())
continue;
get_symbols(F.handle, F.syms.size(), &F.syms[0]);
for (ld_plugin_symbol &Sym : F.syms) {
if (mustPreserve(Sym)) {
CodeGen->addMustPreserveSymbol(Sym.name);
if (options::generate_api_file)
(*apiFile) << Sym.name << "\n";
}
std::unique_ptr<Module> M =
getModuleForFile(Context, F, ApiFile, Internalize, Maybe);
if (!options::triple.empty())
M->setTargetTriple(options::triple.c_str());
else if (M->getTargetTriple().empty()) {
M->setTargetTriple(DefaultTriple);
}
std::string ErrMsg;
if (L.linkInModule(M.get(), &ErrMsg))
message(LDPL_FATAL, "Failed to link module: %s", ErrMsg.c_str());
}
CodeGen->setCodePICModel(output_type);
CodeGen->setDebugInfo(LTO_DEBUG_MODEL_DWARF);
if (!options::mcpu.empty())
CodeGen->setCpu(options::mcpu.c_str());
for (const auto &Name : Internalize) {
GlobalValue *GV = Combined->getNamedValue(Name.first());
if (GV)
internalize(*GV);
}
for (const auto &Name : Maybe) {
GlobalValue *GV = Combined->getNamedValue(Name.first());
if (!GV)
continue;
GV->setLinkage(GlobalValue::LinkOnceODRLinkage);
if (canBeOmittedFromSymbolTable(GV))
internalize(*GV);
}
if (options::generate_bc_file != options::BC_NO) {
std::string path;
@ -420,42 +689,22 @@ static ld_plugin_status allSymbolsReadHook(raw_fd_ostream *apiFile) {
path = options::bc_path;
else
path = output_name + ".bc";
std::string Error;
if (!CodeGen->writeMergedModules(path.c_str(), Error))
message(LDPL_FATAL, "Failed to write the output file.");
{
std::string Error;
raw_fd_ostream OS(path.c_str(), Error, sys::fs::OpenFlags::F_None);
if (!Error.empty())
message(LDPL_FATAL, "Failed to write the output file.");
WriteBitcodeToFile(L.getModule(), OS);
}
if (options::generate_bc_file == options::BC_ONLY)
return LDPS_OK;
}
std::string ObjPath;
{
const char *Temp;
std::string Error;
if (!CodeGen->compile_to_file(&Temp, /*DisableOpt*/ false, /*DisableInline*/
false, /*DisableGVNLoadPRE*/ false, Error))
message(LDPL_ERROR, "Could not produce a combined object file\n");
ObjPath = Temp;
}
for (claimed_file &F : Modules) {
for (ld_plugin_symbol &Sym : F.syms)
free(Sym.name);
}
if (add_input_file(ObjPath.c_str()) != LDPS_OK) {
message(LDPL_ERROR, "Unable to add .o file to the link.");
message(LDPL_ERROR, "File left behind in: %s", ObjPath.c_str());
return LDPS_ERR;
}
codegen(*L.getModule());
if (!options::extra_library_path.empty() &&
set_extra_library_path(options::extra_library_path.c_str()) != LDPS_OK) {
message(LDPL_ERROR, "Unable to set the extra library path.");
return LDPS_ERR;
}
if (options::obj_path.empty())
Cleanup.push_back(ObjPath);
set_extra_library_path(options::extra_library_path.c_str()) != LDPS_OK)
message(LDPL_FATAL, "Unable to set the extra library path.");
return LDPS_OK;
}
@ -466,15 +715,13 @@ static ld_plugin_status all_symbols_read_hook(void) {
Ret = allSymbolsReadHook(nullptr);
} else {
std::string Error;
raw_fd_ostream apiFile("apifile.txt", Error, sys::fs::F_None);
raw_fd_ostream ApiFile("apifile.txt", Error, sys::fs::F_None);
if (!Error.empty())
message(LDPL_FATAL, "Unable to open apifile.txt for writing: %s",
Error.c_str());
Ret = allSymbolsReadHook(&apiFile);
Ret = allSymbolsReadHook(&ApiFile);
}
delete CodeGen;
if (options::generate_bc_file == options::BC_ONLY)
exit(0);