mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2024-12-13 20:32:21 +00:00
3fd7e37b34
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. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@216215 91177308-0d34-0410-b5e6-96231b3b80d8
741 lines
22 KiB
C++
741 lines
22 KiB
C++
//===-- gold-plugin.cpp - Plugin to gold for Link Time Optimization ------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This is a gold plugin for LLVM. It provides an LLVM implementation of the
|
|
// interface described in http://gcc.gnu.org/wiki/whopr/driver .
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/Config/config.h" // plugin-api.h requires HAVE_STDINT_H
|
|
#include "llvm/ADT/StringSet.h"
|
|
#include "llvm/Bitcode/ReaderWriter.h"
|
|
#include "llvm/CodeGen/Analysis.h"
|
|
#include "llvm/CodeGen/CommandFlags.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>
|
|
#include <vector>
|
|
|
|
#ifndef LDPO_PIE
|
|
// FIXME: remove this declaration when we stop maintaining Ubuntu Quantal and
|
|
// Precise and Debian Wheezy (binutils 2.23 is required)
|
|
# define LDPO_PIE 3
|
|
#endif
|
|
|
|
using namespace llvm;
|
|
|
|
namespace {
|
|
struct claimed_file {
|
|
void *handle;
|
|
std::vector<ld_plugin_symbol> syms;
|
|
};
|
|
}
|
|
|
|
static ld_plugin_status discard_message(int level, const char *format, ...) {
|
|
// Die loudly. Recent versions of Gold pass ld_plugin_message as the first
|
|
// callback in the transfer vector. This should never be called.
|
|
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 Reloc::Model RelocationModel = Reloc::Default;
|
|
static std::string output_name = "";
|
|
static std::list<claimed_file> Modules;
|
|
static std::vector<std::string> Cleanup;
|
|
static llvm::TargetOptions TargetOpts;
|
|
|
|
namespace options {
|
|
enum generate_bc { BC_NO, BC_ALSO, BC_ONLY };
|
|
static bool generate_api_file = false;
|
|
static generate_bc generate_bc_file = BC_NO;
|
|
static std::string bc_path;
|
|
static std::string obj_path;
|
|
static std::string extra_library_path;
|
|
static std::string triple;
|
|
static std::string mcpu;
|
|
// Additional options to pass into the code generator.
|
|
// Note: This array will contain all plugin options which are not claimed
|
|
// as plugin exclusive to pass to the code generator.
|
|
// For example, "generate-api-file" and "as"options are for the plugin
|
|
// use only and will not be passed.
|
|
static std::vector<const char *> extra;
|
|
|
|
static void process_plugin_option(const char* opt_)
|
|
{
|
|
if (opt_ == nullptr)
|
|
return;
|
|
llvm::StringRef opt = opt_;
|
|
|
|
if (opt == "generate-api-file") {
|
|
generate_api_file = true;
|
|
} else if (opt.startswith("mcpu=")) {
|
|
mcpu = opt.substr(strlen("mcpu="));
|
|
} else if (opt.startswith("extra-library-path=")) {
|
|
extra_library_path = opt.substr(strlen("extra_library_path="));
|
|
} else if (opt.startswith("mtriple=")) {
|
|
triple = opt.substr(strlen("mtriple="));
|
|
} else if (opt.startswith("obj-path=")) {
|
|
obj_path = opt.substr(strlen("obj-path="));
|
|
} else if (opt == "emit-llvm") {
|
|
generate_bc_file = BC_ONLY;
|
|
} else if (opt == "also-emit-llvm") {
|
|
generate_bc_file = BC_ALSO;
|
|
} else if (opt.startswith("also-emit-llvm=")) {
|
|
llvm::StringRef path = opt.substr(strlen("also-emit-llvm="));
|
|
generate_bc_file = BC_ALSO;
|
|
if (!bc_path.empty()) {
|
|
message(LDPL_WARNING, "Path to the output IL file specified twice. "
|
|
"Discarding %s",
|
|
opt_);
|
|
} else {
|
|
bc_path = path;
|
|
}
|
|
} 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_);
|
|
}
|
|
}
|
|
}
|
|
|
|
static ld_plugin_status claim_file_hook(const ld_plugin_input_file *file,
|
|
int *claimed);
|
|
static ld_plugin_status all_symbols_read_hook(void);
|
|
static ld_plugin_status cleanup_hook(void);
|
|
|
|
extern "C" ld_plugin_status onload(ld_plugin_tv *tv);
|
|
ld_plugin_status onload(ld_plugin_tv *tv) {
|
|
InitializeAllTargetInfos();
|
|
InitializeAllTargets();
|
|
InitializeAllTargetMCs();
|
|
InitializeAllAsmParsers();
|
|
InitializeAllAsmPrinters();
|
|
|
|
// We're given a pointer to the first transfer vector. We read through them
|
|
// until we find one where tv_tag == LDPT_NULL. The REGISTER_* tagged values
|
|
// contain pointers to functions that we need to call to register our own
|
|
// hooks. The others are addresses of functions we can use to call into gold
|
|
// for services.
|
|
|
|
bool registeredClaimFile = false;
|
|
bool RegisteredAllSymbolsRead = false;
|
|
|
|
for (; tv->tv_tag != LDPT_NULL; ++tv) {
|
|
switch (tv->tv_tag) {
|
|
case LDPT_OUTPUT_NAME:
|
|
output_name = tv->tv_u.tv_string;
|
|
break;
|
|
case LDPT_LINKER_OUTPUT:
|
|
switch (tv->tv_u.tv_val) {
|
|
case LDPO_REL: // .o
|
|
case LDPO_DYN: // .so
|
|
case LDPO_PIE: // position independent executable
|
|
RelocationModel = Reloc::PIC_;
|
|
break;
|
|
case LDPO_EXEC: // .exe
|
|
RelocationModel = Reloc::Static;
|
|
break;
|
|
default:
|
|
message(LDPL_ERROR, "Unknown output file type %d", tv->tv_u.tv_val);
|
|
return LDPS_ERR;
|
|
}
|
|
break;
|
|
case LDPT_OPTION:
|
|
options::process_plugin_option(tv->tv_u.tv_string);
|
|
break;
|
|
case LDPT_REGISTER_CLAIM_FILE_HOOK: {
|
|
ld_plugin_register_claim_file callback;
|
|
callback = tv->tv_u.tv_register_claim_file;
|
|
|
|
if (callback(claim_file_hook) != LDPS_OK)
|
|
return LDPS_ERR;
|
|
|
|
registeredClaimFile = true;
|
|
} break;
|
|
case LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK: {
|
|
ld_plugin_register_all_symbols_read callback;
|
|
callback = tv->tv_u.tv_register_all_symbols_read;
|
|
|
|
if (callback(all_symbols_read_hook) != LDPS_OK)
|
|
return LDPS_ERR;
|
|
|
|
RegisteredAllSymbolsRead = true;
|
|
} break;
|
|
case LDPT_REGISTER_CLEANUP_HOOK: {
|
|
ld_plugin_register_cleanup callback;
|
|
callback = tv->tv_u.tv_register_cleanup;
|
|
|
|
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;
|
|
case LDPT_GET_SYMBOLS_V2:
|
|
get_symbols = tv->tv_u.tv_get_symbols;
|
|
break;
|
|
case LDPT_ADD_INPUT_FILE:
|
|
add_input_file = tv->tv_u.tv_add_input_file;
|
|
break;
|
|
case LDPT_SET_EXTRA_LIBRARY_PATH:
|
|
set_extra_library_path = tv->tv_u.tv_set_extra_library_path;
|
|
break;
|
|
case LDPT_GET_VIEW:
|
|
get_view = tv->tv_u.tv_get_view;
|
|
break;
|
|
case LDPT_MESSAGE:
|
|
message = tv->tv_u.tv_message;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!registeredClaimFile) {
|
|
message(LDPL_ERROR, "register_claim_file not passed to LLVMgold.");
|
|
return LDPS_ERR;
|
|
}
|
|
if (!add_symbols) {
|
|
message(LDPL_ERROR, "add_symbols not passed to LLVMgold.");
|
|
return LDPS_ERR;
|
|
}
|
|
|
|
if (!RegisteredAllSymbolsRead)
|
|
return LDPS_OK;
|
|
|
|
if (!get_input_file) {
|
|
message(LDPL_ERROR, "get_input_file not passed to LLVMgold.");
|
|
return LDPS_ERR;
|
|
}
|
|
if (!release_input_file) {
|
|
message(LDPL_ERROR, "relesase_input_file not passed to LLVMgold.");
|
|
return LDPS_ERR;
|
|
}
|
|
|
|
return LDPS_OK;
|
|
}
|
|
|
|
/// Called by gold to see whether this file is one that our plugin can handle.
|
|
/// We'll try to open it and register all the symbols with add_symbol if
|
|
/// possible.
|
|
static ld_plugin_status claim_file_hook(const ld_plugin_input_file *file,
|
|
int *claimed) {
|
|
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
|
|
// an .a archive.
|
|
if (file->offset) {
|
|
offset = file->offset;
|
|
}
|
|
ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =
|
|
MemoryBuffer::getOpenFileSlice(file->fd, file->name, file->filesize,
|
|
offset);
|
|
if (std::error_code EC = BufferOrErr.getError()) {
|
|
message(LDPL_ERROR, EC.message().c_str());
|
|
return LDPS_ERR;
|
|
}
|
|
buffer = std::move(BufferOrErr.get());
|
|
}
|
|
|
|
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;
|
|
|
|
if (EC) {
|
|
message(LDPL_ERROR, "LLVM gold plugin has failed to create LTO module: %s",
|
|
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();
|
|
|
|
cf.handle = file->handle;
|
|
|
|
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.version = nullptr;
|
|
|
|
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;
|
|
case GlobalValue::HiddenVisibility:
|
|
sym.visibility = LDPV_HIDDEN;
|
|
break;
|
|
case GlobalValue::ProtectedVisibility:
|
|
sym.visibility = LDPV_PROTECTED;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Symflags & object::BasicSymbolRef::SF_Undefined) {
|
|
sym.def = LDPK_UNDEF;
|
|
if (GV && GV->hasExternalWeakLinkage())
|
|
sym.def = LDPK_WEAKUNDEF;
|
|
} 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;
|
|
}
|
|
|
|
if (!cf.syms.empty()) {
|
|
if (add_symbols(cf.handle, cf.syms.size(), &cf.syms[0]) != LDPS_OK) {
|
|
message(LDPL_ERROR, "Unable to add symbols!");
|
|
return LDPS_ERR;
|
|
}
|
|
}
|
|
|
|
return LDPS_OK;
|
|
}
|
|
|
|
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 (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) {
|
|
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) {
|
|
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());
|
|
}
|
|
|
|
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;
|
|
if (options::generate_bc_file == options::BC_ONLY)
|
|
path = output_name;
|
|
else if (!options::bc_path.empty())
|
|
path = options::bc_path;
|
|
else
|
|
path = output_name + ".bc";
|
|
{
|
|
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;
|
|
}
|
|
|
|
codegen(*L.getModule());
|
|
|
|
if (!options::extra_library_path.empty() &&
|
|
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;
|
|
}
|
|
|
|
static ld_plugin_status all_symbols_read_hook(void) {
|
|
ld_plugin_status Ret;
|
|
if (!options::generate_api_file) {
|
|
Ret = allSymbolsReadHook(nullptr);
|
|
} else {
|
|
std::string Error;
|
|
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);
|
|
}
|
|
|
|
if (options::generate_bc_file == options::BC_ONLY)
|
|
exit(0);
|
|
|
|
return Ret;
|
|
}
|
|
|
|
static ld_plugin_status cleanup_hook(void) {
|
|
for (std::string &Name : Cleanup) {
|
|
std::error_code EC = sys::fs::remove(Name);
|
|
if (EC)
|
|
message(LDPL_ERROR, "Failed to delete '%s': %s", Name.c_str(),
|
|
EC.message().c_str());
|
|
}
|
|
|
|
return LDPS_OK;
|
|
}
|