diff options
author | Teresa Johnson <tejohnson@google.com> | 2016-08-11 12:56:40 +0000 |
---|---|---|
committer | Teresa Johnson <tejohnson@google.com> | 2016-08-11 12:56:40 +0000 |
commit | 9fd977036c9687b02368a52cf72beaa74c93427d (patch) | |
tree | d9604ff1330294554438b03d019ac4e88fdf20dc /tools/gold | |
parent | 452ae8eb6961de3b6baa1d2450262b8559017767 (diff) |
Resolution-based LTO API.
Summary:
This introduces a resolution-based LTO API. The main advantage of this API over
existing APIs is that it allows the linker to supply a resolution for each
symbol in each object, rather than the combined object as a whole. This will
become increasingly important for use cases such as ThinLTO which require us
to process symbol resolutions in a more complicated way than just adjusting
linkage.
Patch by Peter Collingbourne.
Reviewers: rafael, tejohnson, mehdi_amini
Subscribers: lhames, tejohnson, mehdi_amini, llvm-commits
Differential Revision: https://reviews.llvm.org/D20268
Address review comments
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@278330 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'tools/gold')
-rw-r--r-- | tools/gold/gold-plugin.cpp | 1171 |
1 files changed, 227 insertions, 944 deletions
diff --git a/tools/gold/gold-plugin.cpp b/tools/gold/gold-plugin.cpp index c8b02e6fa14b..eb7f7aa8d05b 100644 --- a/tools/gold/gold-plugin.cpp +++ b/tools/gold/gold-plugin.cpp @@ -12,44 +12,22 @@ // //===----------------------------------------------------------------------===// -#include "llvm/ADT/StringSet.h" -#include "llvm/Analysis/TargetLibraryInfo.h" -#include "llvm/Analysis/TargetTransformInfo.h" #include "llvm/Bitcode/ReaderWriter.h" -#include "llvm/CodeGen/Analysis.h" #include "llvm/CodeGen/CommandFlags.h" -#include "llvm/CodeGen/ParallelCG.h" #include "llvm/Config/config.h" // plugin-api.h requires HAVE_STDINT_H -#include "llvm/IR/AutoUpgrade.h" #include "llvm/IR/Constants.h" -#include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/DiagnosticPrinter.h" -#include "llvm/IR/LLVMContext.h" -#include "llvm/IR/LegacyPassManager.h" -#include "llvm/IR/Module.h" -#include "llvm/IR/Verifier.h" #include "llvm/LTO/LTO.h" -#include "llvm/Linker/IRMover.h" -#include "llvm/MC/SubtargetFeature.h" -#include "llvm/Object/IRObjectFile.h" -#include "llvm/Object/ModuleSummaryIndexObjectFile.h" -#include "llvm/Support/Host.h" +#include "llvm/Support/CommandLine.h" #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" -#include "llvm/Support/TargetRegistry.h" #include "llvm/Support/TargetSelect.h" -#include "llvm/Support/ThreadPool.h" #include "llvm/Support/raw_ostream.h" -#include "llvm/Support/thread.h" -#include "llvm/Transforms/IPO.h" -#include "llvm/Transforms/IPO/FunctionImport.h" -#include "llvm/Transforms/IPO/PassManagerBuilder.h" -#include "llvm/Transforms/Utils/FunctionImportUtils.h" -#include "llvm/Transforms/Utils/GlobalStatus.h" -#include "llvm/Transforms/Utils/ValueMapper.h" #include <list> +#include <map> #include <plugin-api.h> +#include <string> #include <system_error> #include <utility> #include <vector> @@ -61,6 +39,7 @@ #define LDPT_GET_SYMBOLS_V3 28 using namespace llvm; +using namespace lto; static ld_plugin_status discard_message(int level, const char *format, ...) { // Die loudly. Recent versions of Gold pass ld_plugin_message as the first @@ -106,38 +85,17 @@ struct PluginInputFile { }; struct ResolutionInfo { - uint64_t CommonSize = 0; - unsigned CommonAlign = 0; - bool IsLinkonceOdr = true; - GlobalValue::UnnamedAddr UnnamedAddr = GlobalValue::UnnamedAddr::Global; - GlobalValue::VisibilityTypes Visibility = GlobalValue::DefaultVisibility; - bool CommonInternal = false; - bool UseCommon = false; + bool CanOmitFromDynSym = true; + bool DefaultVisibility = true; }; -/// Class to own information used by a task or during its cleanup for a -/// ThinLTO backend instantiation. -class ThinLTOTaskInfo { - /// The output stream the task will codegen into. - std::unique_ptr<raw_fd_ostream> OS; - - /// The file name corresponding to the output stream, used during cleanup. - std::string Filename; - - /// Flag indicating whether the output file is a temp file that must be - /// added to the cleanup list during cleanup. - bool TempOutFile; - -public: - ThinLTOTaskInfo(std::unique_ptr<raw_fd_ostream> OS, std::string Filename, - bool TempOutFile) - : OS(std::move(OS)), Filename(std::move(Filename)), - TempOutFile(TempOutFile) {} - - /// Performs task related cleanup activities that must be done - /// single-threaded (i.e. call backs to gold). - void cleanup(); +struct CommonResolution { + bool Prevailing = false; + bool VisibleToRegularObj = false; + uint64_t Size = 0; + unsigned Align = 0; }; + } static ld_plugin_add_symbols add_symbols = nullptr; @@ -145,14 +103,16 @@ 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 bool IsExecutable = false; static Optional<Reloc::Model> RelocationModel; static std::string output_name = ""; static std::list<claimed_file> Modules; static DenseMap<int, void *> FDToLeaderHandle; static StringMap<ResolutionInfo> ResInfo; +static std::map<std::string, CommonResolution> Commons; static std::vector<std::string> Cleanup; static llvm::TargetOptions TargetOpts; -static std::string DefaultTriple = sys::getDefaultTargetTriple(); +static size_t MaxTasks; namespace options { enum OutputType { @@ -161,7 +121,6 @@ namespace options { OT_BC_ONLY, OT_SAVE_TEMPS }; - static bool generate_api_file = false; static OutputType TheOutputType = OT_NORMAL; static unsigned OptLevel = 2; // Default parallelism of 0 used to indicate that user did not specify. @@ -213,8 +172,6 @@ namespace options { // 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_) @@ -223,9 +180,7 @@ namespace options { return; llvm::StringRef opt = opt_; - if (opt == "generate-api-file") { - generate_api_file = true; - } else if (opt.startswith("mcpu=")) { + 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=")); @@ -307,10 +262,15 @@ ld_plugin_status onload(ld_plugin_tv *tv) { switch (tv->tv_u.tv_val) { case LDPO_REL: // .o case LDPO_DYN: // .so + IsExecutable = false; + RelocationModel = Reloc::PIC_; + break; case LDPO_PIE: // position independent executable + IsExecutable = true; RelocationModel = Reloc::PIC_; break; case LDPO_EXEC: // .exe + IsExecutable = true; RelocationModel = Reloc::Static; break; default: @@ -404,20 +364,6 @@ ld_plugin_status onload(ld_plugin_tv *tv) { return LDPS_OK; } -static const GlobalObject *getBaseObject(const GlobalValue &GV) { - if (auto *GA = dyn_cast<GlobalAlias>(&GV)) - return GA->getBaseObject(); - return cast<GlobalObject>(&GV); -} - -static bool shouldSkip(uint32_t Symflags) { - if (!(Symflags & object::BasicSymbolRef::SF_Global)) - return true; - if (Symflags & object::BasicSymbolRef::SF_FormatSpecific) - return true; - return false; -} - static void diagnosticHandler(const DiagnosticInfo &DI) { if (const auto *BDI = dyn_cast<BitcodeDiagnosticInfo>(&DI)) { std::error_code EC = BDI->getError(); @@ -447,21 +393,18 @@ static void diagnosticHandler(const DiagnosticInfo &DI) { message(Level, "LLVM gold plugin: %s", ErrStorage.c_str()); } -static void diagnosticHandlerForContext(const DiagnosticInfo &DI, - void *Context) { - diagnosticHandler(DI); +static void check(Error E, std::string Msg = "LLVM gold plugin") { + handleAllErrors(std::move(E), [&](ErrorInfoBase &EIB) { + message(LDPL_FATAL, "%s: %s", Msg.c_str(), EIB.message().c_str()); + return Error::success(); + }); } -static GlobalValue::VisibilityTypes -getMinVisibility(GlobalValue::VisibilityTypes A, - GlobalValue::VisibilityTypes B) { - if (A == GlobalValue::HiddenVisibility) - return A; - if (B == GlobalValue::HiddenVisibility) - return B; - if (A == GlobalValue::ProtectedVisibility) - return A; - return B; +template <typename T> static T check(Expected<T> E) { + if (E) + return std::move(*E); + check(E.takeError()); + return T(); } /// Called by gold to see whether this file is one that our plugin can handle. @@ -469,7 +412,6 @@ getMinVisibility(GlobalValue::VisibilityTypes A, /// possible. static ld_plugin_status claim_file_hook(const ld_plugin_input_file *file, int *claimed) { - LLVMContext Context; MemoryBufferRef BufferRef; std::unique_ptr<MemoryBuffer> Buffer; if (get_view) { @@ -498,22 +440,25 @@ static ld_plugin_status claim_file_hook(const ld_plugin_input_file *file, BufferRef = Buffer->getMemBufferRef(); } - Context.setDiagnosticHandler(diagnosticHandlerForContext); - ErrorOr<std::unique_ptr<object::IRObjectFile>> ObjOrErr = - object::IRObjectFile::create(BufferRef, Context); - std::error_code EC = ObjOrErr.getError(); - if (EC == object::object_error::invalid_file_type || - EC == object::object_error::bitcode_section_not_found) - 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; + Expected<std::unique_ptr<InputFile>> ObjOrErr = InputFile::create(BufferRef); + if (!ObjOrErr) { + handleAllErrors(ObjOrErr.takeError(), [&](const ErrorInfoBase &EI) { + std::error_code EC = EI.convertToErrorCode(); + if (EC == object::object_error::invalid_file_type || + EC == object::object_error::bitcode_section_not_found) + *claimed = 0; + else + message(LDPL_ERROR, + "LLVM gold plugin has failed to create LTO module: %s", + EI.message().c_str()); + }); + + return *claimed ? LDPS_ERR : LDPS_OK; } - std::unique_ptr<object::IRObjectFile> Obj = std::move(*ObjOrErr); + + std::unique_ptr<InputFile> Obj = std::move(*ObjOrErr); Modules.resize(Modules.size() + 1); claimed_file &cf = Modules.back(); @@ -535,72 +480,52 @@ static ld_plugin_status claim_file_hook(const ld_plugin_input_file *file, cf.name = file->name; if (file->offset) cf.name += ".llvm." + std::to_string(file->offset) + "." + - sys::path::filename(Obj->getModule().getSourceFileName()).str(); + sys::path::filename(Obj->getSourceFileName()).str(); for (auto &Sym : Obj->symbols()) { uint32_t Symflags = Sym.getFlags(); - if (shouldSkip(Symflags)) - continue; cf.syms.push_back(ld_plugin_symbol()); ld_plugin_symbol &sym = cf.syms.back(); sym.version = nullptr; + StringRef Name = Sym.getName(); + sym.name = strdup(Name.str().c_str()); - SmallString<64> Name; - { - raw_svector_ostream OS(Name); - Sym.printName(OS); - } - sym.name = strdup(Name.c_str()); - - const GlobalValue *GV = Obj->getSymbolGV(Sym.getRawDataRefImpl()); + ResolutionInfo &Res = ResInfo[Name]; - ResolutionInfo &Res = ResInfo[sym.name]; + Res.CanOmitFromDynSym &= Sym.canBeOmittedFromSymbolTable(); sym.visibility = LDPV_DEFAULT; - if (GV) { - Res.UnnamedAddr = - GlobalValue::getMinUnnamedAddr(Res.UnnamedAddr, GV->getUnnamedAddr()); - Res.IsLinkonceOdr &= GV->hasLinkOnceLinkage(); - Res.Visibility = getMinVisibility(Res.Visibility, GV->getVisibility()); - switch (GV->getVisibility()) { - case GlobalValue::DefaultVisibility: - break; - case GlobalValue::HiddenVisibility: - sym.visibility = LDPV_HIDDEN; - break; - case GlobalValue::ProtectedVisibility: - sym.visibility = LDPV_PROTECTED; - break; - } + GlobalValue::VisibilityTypes Vis = Sym.getVisibility(); + if (Vis != GlobalValue::DefaultVisibility) + Res.DefaultVisibility = false; + switch (Vis) { + case GlobalValue::DefaultVisibility: + 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()) + if (Symflags & object::BasicSymbolRef::SF_Weak) sym.def = LDPK_WEAKUNDEF; - } else { + } else if (Symflags & object::BasicSymbolRef::SF_Common) + sym.def = LDPK_COMMON; + else if (Symflags & object::BasicSymbolRef::SF_Weak) + sym.def = LDPK_WEAKDEF; + 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) { - const GlobalObject *Base = getBaseObject(*GV); - if (!Base) - message(LDPL_FATAL, "Unable to determine comdat of alias!"); - const Comdat *C = Base->getComdat(); - if (C) - sym.comdat_key = strdup(C->getName().str().c_str()); - } + const Comdat *C = check(Sym.getComdat()); + if (C) + sym.comdat_key = strdup(C->getName().str().c_str()); sym.resolution = LDPR_UNKNOWN; } @@ -615,39 +540,6 @@ static ld_plugin_status claim_file_hook(const ld_plugin_input_file *file, return LDPS_OK; } -static void internalize(GlobalValue &GV) { - if (GV.isDeclarationForLinker()) - return; // We get here if there is a matching asm definition. - if (!GV.hasLocalLinkage()) - GV.setLinkage(GlobalValue::InternalLinkage); -} - -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"; - } - llvm_unreachable("Unknown resolution"); -} - static void freeSymName(ld_plugin_symbol &Sym) { free(Sym.name); free(Sym.comdat_key); @@ -671,120 +563,27 @@ static const void *getSymbolsAndView(claimed_file &F) { return View; } -static std::unique_ptr<ModuleSummaryIndex> -getModuleSummaryIndexForFile(claimed_file &F) { - const void *View = getSymbolsAndView(F); - if (!View) - return nullptr; - +static void addModule(LTO &Lto, claimed_file &F, const void *View) { MemoryBufferRef BufferRef(StringRef((const char *)View, F.filesize), F.name); + Expected<std::unique_ptr<InputFile>> ObjOrErr = InputFile::create(BufferRef); - // Don't bother trying to build an index if there is no summary information - // in this bitcode file. - if (!object::ModuleSummaryIndexObjectFile::hasGlobalValueSummaryInMemBuffer( - BufferRef, diagnosticHandler)) - return std::unique_ptr<ModuleSummaryIndex>(nullptr); - - ErrorOr<std::unique_ptr<object::ModuleSummaryIndexObjectFile>> ObjOrErr = - object::ModuleSummaryIndexObjectFile::create(BufferRef, - diagnosticHandler); - - if (std::error_code EC = ObjOrErr.getError()) - message(LDPL_FATAL, - "Could not read module summary index bitcode from file : %s", - EC.message().c_str()); - - object::ModuleSummaryIndexObjectFile &Obj = **ObjOrErr; - - return Obj.takeIndex(); -} - -static std::unique_ptr<Module> -getModuleForFile(LLVMContext &Context, claimed_file &F, const void *View, - StringRef Name, raw_fd_ostream *ApiFile, - StringSet<> &Internalize, std::vector<GlobalValue *> &Keep, - StringMap<unsigned> &Realign) { - MemoryBufferRef BufferRef(StringRef((const char *)View, F.filesize), Name); - ErrorOr<std::unique_ptr<object::IRObjectFile>> ObjOrErr = - object::IRObjectFile::create(BufferRef, Context); - - if (std::error_code EC = ObjOrErr.getError()) + if (!ObjOrErr) message(LDPL_FATAL, "Could not read bitcode from file : %s", - EC.message().c_str()); - - object::IRObjectFile &Obj = **ObjOrErr; - - Module &M = Obj.getModule(); + toString(ObjOrErr.takeError()).c_str()); - M.materializeMetadata(); - UpgradeDebugInfo(M); - - SmallPtrSet<GlobalValue *, 8> Used; - collectUsedGlobalVariables(M, Used, /*CompilerUsed*/ false); + InputFile &Obj = **ObjOrErr; unsigned SymNum = 0; + std::vector<SymbolResolution> Resols(F.syms.size()); for (auto &ObjSym : Obj.symbols()) { - GlobalValue *GV = Obj.getSymbolGV(ObjSym.getRawDataRefImpl()); - if (GV && GV->hasAppendingLinkage()) - Keep.push_back(GV); - - if (shouldSkip(ObjSym.getFlags())) - continue; ld_plugin_symbol &Sym = F.syms[SymNum]; + SymbolResolution &R = Resols[SymNum]; ++SymNum; ld_plugin_symbol_resolution Resolution = (ld_plugin_symbol_resolution)Sym.resolution; - if (options::generate_api_file) - *ApiFile << Sym.name << ' ' << getResolutionName(Resolution) << '\n'; - - if (!GV) { - freeSymName(Sym); - continue; // Asm symbol. - } - ResolutionInfo &Res = ResInfo[Sym.name]; - if (Resolution == LDPR_PREVAILING_DEF_IRONLY_EXP && !Res.IsLinkonceOdr) - Resolution = LDPR_PREVAILING_DEF; - - GV->setUnnamedAddr(Res.UnnamedAddr); - GV->setVisibility(Res.Visibility); - - // Override gold's resolution for common symbols. We want the largest - // one to win. - if (GV->hasCommonLinkage()) { - if (Resolution == LDPR_PREVAILING_DEF_IRONLY) - Res.CommonInternal = true; - - if (Resolution == LDPR_PREVAILING_DEF_IRONLY || - Resolution == LDPR_PREVAILING_DEF) - Res.UseCommon = true; - - const DataLayout &DL = GV->getParent()->getDataLayout(); - uint64_t Size = DL.getTypeAllocSize(GV->getType()->getElementType()); - unsigned Align = GV->getAlignment(); - - if (Res.UseCommon && Size >= Res.CommonSize) { - // Take GV. - if (Res.CommonInternal) - Resolution = LDPR_PREVAILING_DEF_IRONLY; - else - Resolution = LDPR_PREVAILING_DEF; - cast<GlobalVariable>(GV)->setAlignment( - std::max(Res.CommonAlign, Align)); - } else { - // Do not take GV, it's smaller than what we already have in the - // combined module. - Resolution = LDPR_PREEMPTED_IR; - if (Align > Res.CommonAlign) - // Need to raise the alignment though. - Realign[Sym.name] = Align; - } - - Res.CommonSize = std::max(Res.CommonSize, Size); - Res.CommonAlign = std::max(Res.CommonAlign, Align); - } switch (Resolution) { case LDPR_UNKNOWN: @@ -795,57 +594,48 @@ getModuleForFile(LLVMContext &Context, claimed_file &F, const void *View, case LDPR_RESOLVED_DYN: case LDPR_PREEMPTED_IR: case LDPR_PREEMPTED_REG: - break; - case LDPR_UNDEF: - if (!GV->isDeclarationForLinker()) - assert(GV->hasComdat()); break; - case LDPR_PREVAILING_DEF_IRONLY: { - Keep.push_back(GV); - // The IR linker has to be able to map this value to a declaration, - // so we can only internalize after linking. - if (!Used.count(GV)) - Internalize.insert(GV->getName()); + case LDPR_PREVAILING_DEF_IRONLY: + R.Prevailing = true; break; - } case LDPR_PREVAILING_DEF: - Keep.push_back(GV); - // There is a non IR use, so we have to force optimizations to keep this. - switch (GV->getLinkage()) { - default: - break; - case GlobalValue::LinkOnceAnyLinkage: - GV->setLinkage(GlobalValue::WeakAnyLinkage); - break; - case GlobalValue::LinkOnceODRLinkage: - GV->setLinkage(GlobalValue::WeakODRLinkage); - break; - } + R.Prevailing = true; + R.VisibleToRegularObj = true; break; - case LDPR_PREVAILING_DEF_IRONLY_EXP: { - Keep.push_back(GV); - if (canBeOmittedFromSymbolTable(GV)) - Internalize.insert(GV->getName()); + case LDPR_PREVAILING_DEF_IRONLY_EXP: + R.Prevailing = true; + if (!Res.CanOmitFromDynSym) + R.VisibleToRegularObj = true; break; } + + if (Resolution != LDPR_RESOLVED_DYN && Resolution != LDPR_UNDEF && + (IsExecutable || !Res.DefaultVisibility)) + R.FinalDefinitionInLinkageUnit = true; + + if (ObjSym.getFlags() & object::BasicSymbolRef::SF_Common) { + // We ignore gold's resolution for common symbols. A common symbol with + // the correct size and alignment is added to the module by the pre-opt + // module hook if any common symbol prevailed. + CommonResolution &CommonRes = Commons[ObjSym.getIRName()]; + if (R.Prevailing) { + CommonRes.Prevailing = true; + CommonRes.VisibleToRegularObj = R.VisibleToRegularObj; + } + CommonRes.Size = std::max(CommonRes.Size, ObjSym.getCommonSize()); + CommonRes.Align = std::max(CommonRes.Align, ObjSym.getCommonAlignment()); + R.Prevailing = false; } freeSymName(Sym); } - return Obj.takeModule(); -} - -static void saveBCFile(StringRef Path, Module &M) { - std::error_code EC; - raw_fd_ostream OS(Path, EC, sys::fs::OpenFlags::F_None); - if (EC) - message(LDPL_FATAL, "Failed to write the output file."); - WriteBitcodeToFile(&M, OS, /* ShouldPreserveUseListOrder */ false); + check(Lto.add(std::move(*ObjOrErr), Resols), + std::string("Failed to link module ") + F.name); } static void recordFile(std::string Filename, bool TempOutFile) { @@ -857,190 +647,6 @@ static void recordFile(std::string Filename, bool TempOutFile) { Cleanup.push_back(Filename.c_str()); } -void ThinLTOTaskInfo::cleanup() { - // Close the output file descriptor before we pass it to gold. - OS->close(); - - recordFile(Filename, TempOutFile); -} - -namespace { -/// Class to manage optimization and code generation for a module, possibly -/// in a thread (ThinLTO). -class CodeGen { - /// The module for which this will generate code. - std::unique_ptr<llvm::Module> M; - - /// The output stream to generate code into. - raw_fd_ostream *OS; - - /// The task ID when this was invoked in a thread (ThinLTO). - int TaskID; - - /// The module summary index for ThinLTO tasks. - const ModuleSummaryIndex *CombinedIndex; - - /// The target machine for generating code for this module. - std::unique_ptr<TargetMachine> TM; - - /// Filename to use as base when save-temps is enabled, used to get - /// a unique and identifiable save-temps output file for each ThinLTO backend. - std::string SaveTempsFilename; - - /// Map from a module name to the corresponding buffer holding a view of the - /// bitcode provided via the get_view gold callback. - StringMap<MemoryBufferRef> *ModuleMap; - - // Functions to import into this module. - FunctionImporter::ImportMapTy *ImportList; - - // Map of globals defined in this module to their summary. - std::map<GlobalValue::GUID, GlobalValueSummary *> *DefinedGlobals; - -public: - /// Constructor used by full LTO. - CodeGen(std::unique_ptr<llvm::Module> M) - : M(std::move(M)), OS(nullptr), TaskID(-1), CombinedIndex(nullptr), - ModuleMap(nullptr) { - initTargetMachine(); - } - /// Constructor used by ThinLTO. - CodeGen(std::unique_ptr<llvm::Module> M, raw_fd_ostream *OS, int TaskID, - const ModuleSummaryIndex *CombinedIndex, std::string Filename, - StringMap<MemoryBufferRef> *ModuleMap, - FunctionImporter::ImportMapTy *ImportList, - std::map<GlobalValue::GUID, GlobalValueSummary *> *DefinedGlobals) - : M(std::move(M)), OS(OS), TaskID(TaskID), CombinedIndex(CombinedIndex), - SaveTempsFilename(std::move(Filename)), ModuleMap(ModuleMap), - ImportList(ImportList), DefinedGlobals(DefinedGlobals) { - assert(options::thinlto == !!CombinedIndex && - "Expected module summary index iff performing ThinLTO"); - initTargetMachine(); - } - - /// Invoke LTO passes and the code generator for the module. - void runAll(); - - /// Invoke the actual code generation to emit Module's object to file. - void runCodegenPasses(); - -private: - const Target *TheTarget; - std::string TripleStr; - std::string FeaturesString; - TargetOptions Options; - - /// Create a target machine for the module. Must be unique for each - /// module/task. - void initTargetMachine(); - - std::unique_ptr<TargetMachine> createTargetMachine(); - - /// Run all LTO passes on the module. - void runLTOPasses(); - - /// Sets up output files necessary to perform optional multi-threaded - /// split code generation, and invokes the code generation implementation. - /// If BCFileName is not empty, saves bitcode for module partitions into - /// {BCFileName}0 .. {BCFileName}N. - void runSplitCodeGen(const SmallString<128> &BCFilename); -}; -} - -static SubtargetFeatures getFeatures(Triple &TheTriple) { - SubtargetFeatures Features; - Features.getDefaultSubtargetFeatures(TheTriple); - for (const std::string &A : MAttrs) - Features.AddFeature(A); - return Features; -} - -static CodeGenOpt::Level getCGOptLevel() { - switch (options::OptLevel) { - case 0: - return CodeGenOpt::None; - case 1: - return CodeGenOpt::Less; - case 2: - return CodeGenOpt::Default; - case 3: - return CodeGenOpt::Aggressive; - } - llvm_unreachable("Invalid optimization level"); -} - -void CodeGen::initTargetMachine() { - TripleStr = M->getTargetTriple(); - Triple TheTriple(TripleStr); - - std::string ErrMsg; - TheTarget = TargetRegistry::lookupTarget(TripleStr, ErrMsg); - if (!TheTarget) - message(LDPL_FATAL, "Target not found: %s", ErrMsg.c_str()); - - SubtargetFeatures Features = getFeatures(TheTriple); - FeaturesString = Features.getString(); - Options = InitTargetOptionsFromCodeGenFlags(); - - // Disable the new X86 relax relocations since gold might not support them. - // FIXME: Check the gold version or add a new option to enable them. - Options.RelaxELFRelocations = false; - - TM = createTargetMachine(); -} - -std::unique_ptr<TargetMachine> CodeGen::createTargetMachine() { - CodeGenOpt::Level CGOptLevel = getCGOptLevel(); - - return std::unique_ptr<TargetMachine>(TheTarget->createTargetMachine( - TripleStr, options::mcpu, FeaturesString, Options, RelocationModel, - CodeModel::Default, CGOptLevel)); -} - -void CodeGen::runLTOPasses() { - M->setDataLayout(TM->createDataLayout()); - - if (CombinedIndex) { - // Apply summary-based LinkOnce/Weak resolution decisions. - thinLTOResolveWeakForLinkerModule(*M, *DefinedGlobals); - - // Apply summary-based internalization decisions. Skip if there are no - // defined globals from the summary since not only is it unnecessary, but - // if this module did not have a summary section the internalizer will - // assert if it finds any definitions in this module that aren't in the - // DefinedGlobals set. - if (!DefinedGlobals->empty()) - thinLTOInternalizeModule(*M, *DefinedGlobals); - - // Create a loader that will parse the bitcode from the buffers - // in the ModuleMap. - ModuleLoader Loader(M->getContext(), *ModuleMap); - - // Perform function importing. - FunctionImporter Importer(*CombinedIndex, Loader); - Importer.importFunctions(*M, *ImportList); - } - - legacy::PassManager passes; - passes.add(createTargetTransformInfoWrapperPass(TM->getTargetIRAnalysis())); - - PassManagerBuilder PMB; - PMB.LibraryInfo = new TargetLibraryInfoImpl(Triple(TM->getTargetTriple())); - PMB.Inliner = createFunctionInliningPass(); - // Unconditionally verify input since it is not verified before this - // point and has unknown origin. - PMB.VerifyInput = true; - PMB.VerifyOutput = !options::DisableVerify; - PMB.LoopVectorize = true; - PMB.SLPVectorize = true; - PMB.OptLevel = options::OptLevel; - if (options::thinlto) - PMB.populateThinLTOPassManager(passes); - else - PMB.populateLTOPassManager(passes); - passes.run(*M); -} - /// Open a file and return the new file descriptor given a base input /// file name, a flag indicating whether a temp file should be generated, /// and an optional task id. The new filename generated is @@ -1066,220 +672,44 @@ static int openOutputFile(SmallString<128> InFilename, bool TempOutFile, return FD; } -void CodeGen::runCodegenPasses() { - assert(OS && "Output stream must be set before emitting to file"); - legacy::PassManager CodeGenPasses; - if (TM->addPassesToEmitFile(CodeGenPasses, *OS, - TargetMachine::CGFT_ObjectFile)) - report_fatal_error("Failed to setup codegen"); - CodeGenPasses.run(*M); -} - -void CodeGen::runSplitCodeGen(const SmallString<128> &BCFilename) { - SmallString<128> Filename; - // Note that openOutputFile will append a unique ID for each task - if (!options::obj_path.empty()) - Filename = options::obj_path; - else if (options::TheOutputType == options::OT_SAVE_TEMPS) - Filename = output_name + ".o"; - - // Note that the default parallelism is 1 instead of the - // hardware_concurrency, as there are behavioral differences between - // parallelism levels (e.g. symbol ordering will be different, and some uses - // of inline asm currently have issues with parallelism >1). - unsigned int MaxThreads = options::Parallelism ? options::Parallelism : 1; - - std::vector<SmallString<128>> Filenames(MaxThreads); - std::vector<SmallString<128>> BCFilenames(MaxThreads); - bool TempOutFile = Filename.empty(); - { - // Open a file descriptor for each backend task. This is done in a block - // so that the output file descriptors are closed before gold opens them. - std::list<llvm::raw_fd_ostream> OSs; - std::vector<llvm::raw_pwrite_stream *> OSPtrs(MaxThreads); - for (unsigned I = 0; I != MaxThreads; ++I) { - int FD = openOutputFile(Filename, TempOutFile, Filenames[I], - // Only append ID if there are multiple tasks. - MaxThreads > 1 ? I : -1); - OSs.emplace_back(FD, true); - OSPtrs[I] = &OSs.back(); - } - - std::list<llvm::raw_fd_ostream> BCOSs; - std::vector<llvm::raw_pwrite_stream *> BCOSPtrs; - if (!BCFilename.empty() && MaxThreads > 1) { - for (unsigned I = 0; I != MaxThreads; ++I) { - int FD = openOutputFile(BCFilename, false, BCFilenames[I], I); - BCOSs.emplace_back(FD, true); - BCOSPtrs.push_back(&BCOSs.back()); - } - } - - // Run backend tasks. - splitCodeGen(std::move(M), OSPtrs, BCOSPtrs, - [&]() { return createTargetMachine(); }); - } - - for (auto &Filename : Filenames) - recordFile(Filename.c_str(), TempOutFile); -} - -void CodeGen::runAll() { - runLTOPasses(); - - SmallString<128> OptFilename; - if (options::TheOutputType == options::OT_SAVE_TEMPS) { - OptFilename = output_name; - // If the CodeGen client provided a filename, use it. Always expect - // a provided filename if we are in a task (i.e. ThinLTO backend). - assert(!SaveTempsFilename.empty() || TaskID == -1); - if (!SaveTempsFilename.empty()) - OptFilename = SaveTempsFilename; - OptFilename += ".opt.bc"; - saveBCFile(OptFilename, *M); - } - - // If we are already in a thread (i.e. ThinLTO), just perform - // codegen passes directly. - if (TaskID >= 0) - runCodegenPasses(); - // Otherwise attempt split code gen. - else - runSplitCodeGen(OptFilename); -} - -/// Links the module in \p View from file \p F into the combined module -/// saved in the IRMover \p L. -static void linkInModule(LLVMContext &Context, IRMover &L, claimed_file &F, - const void *View, StringRef Name, - raw_fd_ostream *ApiFile, StringSet<> &Internalize, - bool SetName = false) { - std::vector<GlobalValue *> Keep; - StringMap<unsigned> Realign; - std::unique_ptr<Module> M = getModuleForFile(Context, F, View, Name, ApiFile, - Internalize, Keep, Realign); - if (!M.get()) - return; - if (!options::triple.empty()) - M->setTargetTriple(options::triple.c_str()); - else if (M->getTargetTriple().empty()) { - M->setTargetTriple(DefaultTriple); - } - - // For ThinLTO we want to propagate the source file name to ensure - // we can create the correct global identifiers matching those in the - // original module. - if (SetName) - L.getModule().setSourceFileName(M->getSourceFileName()); - - if (Error E = L.move(std::move(M), Keep, - [](GlobalValue &, IRMover::ValueAdder) {})) { - handleAllErrors(std::move(E), [&](const llvm::ErrorInfoBase &EIB) { - message(LDPL_FATAL, "Failed to link module %s: %s", Name.str().c_str(), - EIB.message().c_str()); - }); - } - - for (const auto &I : Realign) { - GlobalValue *Dst = L.getModule().getNamedValue(I.first()); - if (!Dst) +/// Add all required common symbols to M, which is expected to be the first +/// combined module. +static void addCommons(Module &M) { + for (auto &I : Commons) { + if (!I.second.Prevailing) continue; - cast<GlobalVariable>(Dst)->setAlignment(I.second); + ArrayType *Ty = + ArrayType::get(Type::getInt8Ty(M.getContext()), I.second.Size); + GlobalVariable *OldGV = M.getNamedGlobal(I.first); + auto *GV = new GlobalVariable(M, Ty, false, GlobalValue::CommonLinkage, + ConstantAggregateZero::get(Ty), ""); + GV->setAlignment(I.second.Align); + if (OldGV) { + OldGV->replaceAllUsesWith(ConstantExpr::getBitCast(GV, OldGV->getType())); + GV->takeName(OldGV); + OldGV->eraseFromParent(); + } else { + GV->setName(I.first); + } + // We may only internalize commons if there is a single LTO task because + // other native object files may require the common. + if (MaxTasks == 1 && !I.second.VisibleToRegularObj) + GV->setLinkage(GlobalValue::InternalLinkage); } } -/// Perform the ThinLTO backend on a single module, invoking the LTO and codegen -/// pipelines. -static void thinLTOBackendTask(claimed_file &F, const void *View, - StringRef Name, raw_fd_ostream *ApiFile, - const ModuleSummaryIndex &CombinedIndex, - raw_fd_ostream *OS, unsigned TaskID, - StringMap<MemoryBufferRef> &ModuleMap, - FunctionImporter::ImportMapTy &ImportList, - std::map<GlobalValue::GUID, GlobalValueSummary *> &DefinedGlobals) { - // Need to use a separate context for each task - LLVMContext Context; - Context.setDiscardValueNames(options::TheOutputType != - options::OT_SAVE_TEMPS); - Context.enableDebugTypeODRUniquing(); // Merge debug info types. - Context.setDiagnosticHandler(diagnosticHandlerForContext, nullptr, true); - - std::unique_ptr<llvm::Module> NewModule(new llvm::Module(Name, Context)); - IRMover L(*NewModule.get()); - - StringSet<> Dummy; - linkInModule(Context, L, F, View, Name, ApiFile, Dummy, true); - if (renameModuleForThinLTO(*NewModule, CombinedIndex)) - message(LDPL_FATAL, "Failed to rename module for ThinLTO"); - - CodeGen codeGen(std::move(NewModule), OS, TaskID, &CombinedIndex, Name, - &ModuleMap, &ImportList, &DefinedGlobals); - codeGen.runAll(); -} - -/// Launch each module's backend pipeline in a separate task in a thread pool. -static void -thinLTOBackends(raw_fd_ostream *ApiFile, - const ModuleSummaryIndex &CombinedIndex, - StringMap<MemoryBufferRef> &ModuleMap, - StringMap<FunctionImporter::ImportMapTy> &ImportLists, - StringMap<std::map<GlobalValue::GUID, GlobalValueSummary *>> - &ModuleToDefinedGVSummaries) { - unsigned TaskCount = 0; - std::vector<ThinLTOTaskInfo> Tasks; - Tasks.reserve(Modules.size()); - unsigned int MaxThreads = options::Parallelism - ? options::Parallelism - : thread::hardware_concurrency(); - - // Create ThreadPool in nested scope so that threads will be joined - // on destruction. - { - ThreadPool ThinLTOThreadPool(MaxThreads); - for (claimed_file &F : Modules) { - // Do all the gold callbacks in the main thread, since gold is not thread - // safe by default. - const void *View = getSymbolsAndView(F); - if (!View) - continue; - - SmallString<128> Filename; - if (!options::obj_path.empty()) - // Note that openOutputFile will append a unique ID for each task - Filename = options::obj_path; - else if (options::TheOutputType == options::OT_SAVE_TEMPS) { - // Use the input file name so that we get a unique and identifiable - // output file for each ThinLTO backend task. - Filename = F.name; - Filename += ".thinlto.o"; - } - bool TempOutFile = Filename.empty(); - - SmallString<128> NewFilename; - int FD = openOutputFile(Filename, TempOutFile, NewFilename, - // Only append the TaskID if we will use the - // non-unique obj_path. - !options::obj_path.empty() ? TaskCount : -1); - TaskCount++; - std::unique_ptr<raw_fd_ostream> OS = - llvm::make_unique<raw_fd_ostream>(FD, true); - - // Enqueue the task - ThinLTOThreadPool.async(thinLTOBackendTask, std::ref(F), View, F.name, - ApiFile, std::ref(CombinedIndex), OS.get(), - TaskCount, std::ref(ModuleMap), - std::ref(ImportLists[F.name]), - std::ref(ModuleToDefinedGVSummaries[F.name])); - - // Record the information needed by the task or during its cleanup - // to a ThinLTOTaskInfo instance. For information needed by the task - // the unique_ptr ownership is transferred to the ThinLTOTaskInfo. - Tasks.emplace_back(std::move(OS), NewFilename.c_str(), TempOutFile); - } +static CodeGenOpt::Level getCGOptLevel() { + switch (options::OptLevel) { + case 0: + return CodeGenOpt::None; + case 1: + return CodeGenOpt::Less; + case 2: + return CodeGenOpt::Default; + case 3: + return CodeGenOpt::Aggressive; } - - for (auto &Task : Tasks) - Task.cleanup(); + llvm_unreachable("Invalid optimization level"); } /// Parse the thinlto_prefix_replace option into the \p OldPrefix and @@ -1293,266 +723,130 @@ static void getThinLTOOldAndNewPrefix(std::string &OldPrefix, NewPrefix = Split.second.str(); } -/// Given the original \p Path to an output file, replace any path -/// prefix matching \p OldPrefix with \p NewPrefix. Also, create the -/// resulting directory if it does not yet exist. -static std::string getThinLTOOutputFile(const std::string &Path, - const std::string &OldPrefix, - const std::string &NewPrefix) { - if (OldPrefix.empty() && NewPrefix.empty()) - return Path; - SmallString<128> NewPath(Path); - llvm::sys::path::replace_path_prefix(NewPath, OldPrefix, NewPrefix); - StringRef ParentPath = llvm::sys::path::parent_path(NewPath.str()); - if (!ParentPath.empty()) { - // Make sure the new directory exists, creating it if necessary. - if (std::error_code EC = llvm::sys::fs::create_directories(ParentPath)) - llvm::errs() << "warning: could not create directory '" << ParentPath - << "': " << EC.message() << '\n'; - } - return NewPath.str(); -} +static std::unique_ptr<LTO> createLTO() { + Config Conf; + ThinBackend Backend; + unsigned ParallelCodeGenParallelismLevel = 1; -/// Perform ThinLTO link, which creates the combined index file. -/// Also, either launch backend threads or (under thinlto-index-only) -/// emit individual index files for distributed backends and exit. -static ld_plugin_status thinLTOLink(raw_fd_ostream *ApiFile) { - // Map from a module name to the corresponding buffer holding a view of the - // bitcode provided via the get_view gold callback. - StringMap<MemoryBufferRef> ModuleMap; - // Map to own RAII objects that manage the file opening and releasing - // interfaces with gold. - DenseMap<void *, std::unique_ptr<PluginInputFile>> HandleToInputFile; - - // Keep track of symbols that must not be internalized because they - // are referenced outside of a single IR module. - DenseSet<GlobalValue::GUID> Preserve; - - // Keep track of the prevailing copy for each GUID, for use in resolving - // weak linkages. - DenseMap<GlobalValue::GUID, const GlobalValueSummary *> PrevailingCopy; - - ModuleSummaryIndex CombinedIndex; - uint64_t NextModuleId = 0; - for (claimed_file &F : Modules) { - if (!HandleToInputFile.count(F.leader_handle)) - HandleToInputFile.insert(std::make_pair( - F.leader_handle, llvm::make_unique<PluginInputFile>(F.handle))); - // Pass this into getModuleSummaryIndexForFile - const void *View = getSymbolsAndView(F); - if (!View) - continue; + Conf.CPU = options::mcpu; + Conf.Options = InitTargetOptionsFromCodeGenFlags(); - MemoryBufferRef ModuleBuffer(StringRef((const char *)View, F.filesize), - F.name); - assert(ModuleMap.find(ModuleBuffer.getBufferIdentifier()) == - ModuleMap.end() && - "Expect unique Buffer Identifier"); - ModuleMap[ModuleBuffer.getBufferIdentifier()] = ModuleBuffer; - - std::unique_ptr<ModuleSummaryIndex> Index = getModuleSummaryIndexForFile(F); - - // Use gold's symbol resolution information to identify symbols referenced - // by more than a single IR module (i.e. referenced by multiple IR modules - // or by a non-IR module). Cross references introduced by importing are - // checked separately via the export lists. Also track the prevailing copy - // for later symbol resolution. - for (auto &Sym : F.syms) { - ld_plugin_symbol_resolution Resolution = - (ld_plugin_symbol_resolution)Sym.resolution; - GlobalValue::GUID SymGUID = GlobalValue::getGUID(Sym.name); - if (Resolution != LDPR_PREVAILING_DEF_IRONLY) - Preserve.insert(SymGUID); - - if (Index && (Resolution == LDPR_PREVAILING_DEF || - Resolution == LDPR_PREVAILING_DEF_IRONLY || - Resolution == LDPR_PREVAILING_DEF_IRONLY_EXP)) - PrevailingCopy[SymGUID] = Index->getGlobalValueSummary(SymGUID); - } - - // Skip files without a module summary. - if (Index) - CombinedIndex.mergeFrom(std::move(Index), ++NextModuleId); + // Disable the new X86 relax relocations since gold might not support them. + // FIXME: Check the gold version or add a new option to enable them. + Conf.Options.RelaxELFRelocations = false; + + Conf.MAttrs = MAttrs; + Conf.RelocModel = *RelocationModel; + Conf.CGOptLevel = getCGOptLevel(); + Conf.DisableVerify = options::DisableVerify; + Conf.OptLevel = options::OptLevel; + if (options::Parallelism) { + if (options::thinlto) + Backend = createInProcessThinBackend(options::Parallelism); + else + ParallelCodeGenParallelismLevel = options::Parallelism; } - - // Collect for each module the list of function it defines (GUID -> - // Summary). - StringMap<std::map<GlobalValue::GUID, GlobalValueSummary *>> - ModuleToDefinedGVSummaries(NextModuleId); - CombinedIndex.collectDefinedGVSummariesPerModule(ModuleToDefinedGVSummaries); - - StringMap<FunctionImporter::ImportMapTy> ImportLists(NextModuleId); - StringMap<FunctionImporter::ExportSetTy> ExportLists(NextModuleId); - ComputeCrossModuleImport(CombinedIndex, ModuleToDefinedGVSummaries, - ImportLists, ExportLists); - - auto isPrevailing = [&](GlobalValue::GUID GUID, const GlobalValueSummary *S) { - const auto &Prevailing = PrevailingCopy.find(GUID); - assert(Prevailing != PrevailingCopy.end()); - return Prevailing->second == S; - }; - - // Callback for internalization, to prevent internalization of symbols - // that were not candidates initially, and those that are being imported - // (which introduces new cross references). - auto isExported = [&](StringRef ModuleIdentifier, GlobalValue::GUID GUID) { - const auto &ExportList = ExportLists.find(ModuleIdentifier); - return (ExportList != ExportLists.end() && - ExportList->second.count(GUID)) || - Preserve.count(GUID); - }; - - thinLTOResolveWeakForLinkerInIndex( - CombinedIndex, isPrevailing, - [](StringRef ModuleIdentifier, GlobalValue::GUID GUID, - GlobalValue::LinkageTypes NewLinkage) {}); - - // 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. - thinLTOInternalizeAndPromoteInIndex(CombinedIndex, isExported); - - if (options::thinlto_emit_imports_files && !options::thinlto_index_only) - message(LDPL_WARNING, - "thinlto-emit-imports-files ignored unless thinlto-index-only"); - if (options::thinlto_index_only) { - // If the thinlto-prefix-replace option was specified, parse it and - // extract the old and new prefixes. std::string OldPrefix, NewPrefix; getThinLTOOldAndNewPrefix(OldPrefix, NewPrefix); + Backend = createWriteIndexesThinBackend( + OldPrefix, NewPrefix, options::thinlto_emit_imports_files, + options::thinlto_linked_objects_file); + } - // If the user requested a list of objects gold included in the link, - // create and open the requested file. - raw_fd_ostream *ObjFileOS = nullptr; - if (!options::thinlto_linked_objects_file.empty()) { - std::error_code EC; - ObjFileOS = new raw_fd_ostream(options::thinlto_linked_objects_file, EC, - sys::fs::OpenFlags::F_None); - if (EC) - message(LDPL_FATAL, "Unable to open %s for writing: %s", - options::thinlto_linked_objects_file.c_str(), - EC.message().c_str()); - } - // For each input bitcode file, generate an individual index that - // contains summaries only for its own global values, and for any that - // should be imported. - for (claimed_file &F : Modules) { - std::error_code EC; + Conf.OverrideTriple = options::triple; + Conf.DefaultTriple = sys::getDefaultTargetTriple(); - std::string NewModulePath = - getThinLTOOutputFile(F.name, OldPrefix, NewPrefix); - - if (!options::thinlto_linked_objects_file.empty()) { - // If gold included any symbols from ths file in the link, emit path - // to the final object file, which should be included in the final - // native link. - if (get_symbols(F.handle, F.syms.size(), F.syms.data()) != - LDPS_NO_SYMS) { - assert(ObjFileOS); - *ObjFileOS << NewModulePath << "\n"; - } - } + Conf.DiagHandler = diagnosticHandler; - raw_fd_ostream OS((Twine(NewModulePath) + ".thinlto.bc").str(), EC, - sys::fs::OpenFlags::F_None); - if (EC) - message(LDPL_FATAL, "Unable to open %s.thinlto.bc for writing: %s", - NewModulePath.c_str(), EC.message().c_str()); - // Build a map of module to the GUIDs and summary objects that should - // be written to its index. - std::map<std::string, GVSummaryMapTy> ModuleToSummariesForIndex; - gatherImportedSummariesForModule(F.name, ModuleToDefinedGVSummaries, - ImportLists, ModuleToSummariesForIndex); - WriteIndexToFile(CombinedIndex, OS, &ModuleToSummariesForIndex); - - if (options::thinlto_emit_imports_files) { - if ((EC = EmitImportsFiles(F.name, - (Twine(NewModulePath) + ".imports").str(), - ImportLists))) - message(LDPL_FATAL, "Unable to open %s.imports", - NewModulePath.c_str(), EC.message().c_str()); - } - } + Conf.PreOptModuleHook = [](size_t Task, Module &M) { + if (Task == 0) + addCommons(M); + return true; + }; - if (ObjFileOS) - ObjFileOS->close(); + switch (options::TheOutputType) { + case options::OT_NORMAL: + break; - cleanup_hook(); - exit(0); - } + case options::OT_DISABLE: + Conf.PreOptModuleHook = [](size_t Task, Module &M) { return false; }; + break; - // Create OS in nested scope so that it will be closed on destruction. - { - std::error_code EC; - raw_fd_ostream OS(output_name + ".thinlto.bc", EC, - sys::fs::OpenFlags::F_None); - if (EC) - message(LDPL_FATAL, "Unable to open %s.thinlto.bc for writing: %s", - output_name.data(), EC.message().c_str()); - WriteIndexToFile(CombinedIndex, OS); + case options::OT_BC_ONLY: + Conf.PostInternalizeModuleHook = [](size_t Task, Module &M) { + std::error_code EC; + raw_fd_ostream OS(output_name, EC, sys::fs::OpenFlags::F_None); + if (EC) + message(LDPL_FATAL, "Failed to write the output file."); + WriteBitcodeToFile(&M, OS, /* ShouldPreserveUseListOrder */ false); + return false; + }; + break; + + case options::OT_SAVE_TEMPS: + check(Conf.addSaveTemps(output_name)); + break; } - thinLTOBackends(ApiFile, CombinedIndex, ModuleMap, ImportLists, - ModuleToDefinedGVSummaries); - return LDPS_OK; + return make_unique<LTO>(std::move(Conf), Backend, + ParallelCodeGenParallelismLevel); } /// 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) { +static ld_plugin_status allSymbolsReadHook() { if (Modules.empty()) return LDPS_OK; if (unsigned NumOpts = options::extra.size()) cl::ParseCommandLineOptions(NumOpts, &options::extra[0]); - if (options::thinlto) - return thinLTOLink(ApiFile); - - LLVMContext Context; - Context.setDiscardValueNames(options::TheOutputType != - options::OT_SAVE_TEMPS); - Context.enableDebugTypeODRUniquing(); // Merge debug info types. - Context.setDiagnosticHandler(diagnosticHandlerForContext, nullptr, true); + std::unique_ptr<LTO> Lto = createLTO(); - std::unique_ptr<Module> Combined(new Module("ld-temp.o", Context)); - IRMover L(*Combined); - - StringSet<> Internalize; for (claimed_file &F : Modules) { - // RAII object to manage the file opening and releasing interfaces with - // gold. PluginInputFile InputFile(F.handle); const void *View = getSymbolsAndView(F); if (!View) continue; - linkInModule(Context, L, F, View, F.name, ApiFile, Internalize); + addModule(*Lto, F, View); } - for (const auto &Name : Internalize) { - GlobalValue *GV = Combined->getNamedValue(Name.first()); - if (GV) - internalize(*GV); - } + SmallString<128> Filename; + // Note that openOutputFile will append a unique ID for each task + if (!options::obj_path.empty()) + Filename = options::obj_path; + else if (options::TheOutputType == options::OT_SAVE_TEMPS) + Filename = output_name + ".o"; + bool SaveTemps = !Filename.empty(); + + MaxTasks = Lto->getMaxTasks(); + std::vector<uintptr_t> IsTemporary(MaxTasks); + std::vector<SmallString<128>> Filenames(MaxTasks); + + auto AddStream = [&](size_t Task) { + int FD = openOutputFile(Filename, /*TempOutFile=*/!SaveTemps, + Filenames[Task], MaxTasks > 1 ? Task : -1); + IsTemporary[Task] = !SaveTemps; + + return make_unique<llvm::raw_fd_ostream>(FD, true); + }; + + check(Lto->run(AddStream)); - if (options::TheOutputType == options::OT_DISABLE) + if (options::TheOutputType == options::OT_DISABLE || + options::TheOutputType == options::OT_BC_ONLY) return LDPS_OK; - if (options::TheOutputType != options::OT_NORMAL) { - std::string path; - if (options::TheOutputType == options::OT_BC_ONLY) - path = output_name; - else - path = output_name + ".bc"; - saveBCFile(path, *Combined); - if (options::TheOutputType == options::OT_BC_ONLY) - return LDPS_OK; + if (options::thinlto_index_only) { + cleanup_hook(); + exit(0); } - CodeGen codeGen(std::move(Combined)); - codeGen.runAll(); + for (unsigned I = 0; I != MaxTasks; ++I) + if (!Filenames[I].empty()) + recordFile(Filenames[I].str(), IsTemporary[I]); if (!options::extra_library_path.empty() && set_extra_library_path(options::extra_library_path.c_str()) != LDPS_OK) @@ -1562,18 +856,7 @@ static ld_plugin_status allSymbolsReadHook(raw_fd_ostream *ApiFile) { } static ld_plugin_status all_symbols_read_hook(void) { - ld_plugin_status Ret; - if (!options::generate_api_file) { - Ret = allSymbolsReadHook(nullptr); - } else { - std::error_code EC; - raw_fd_ostream ApiFile("apifile.txt", EC, sys::fs::F_None); - if (EC) - message(LDPL_FATAL, "Unable to open apifile.txt for writing: %s", - EC.message().c_str()); - Ret = allSymbolsReadHook(&ApiFile); - } - + ld_plugin_status Ret = allSymbolsReadHook(); llvm_shutdown(); if (options::TheOutputType == options::OT_BC_ONLY || |