diff options
author | Teresa Johnson <tejohnson@google.com> | 2016-08-11 13:03:56 +0000 |
---|---|---|
committer | Teresa Johnson <tejohnson@google.com> | 2016-08-11 13:03:56 +0000 |
commit | 6f0ab850313cb5a8eee3bf9767feb5e01e74d0f3 (patch) | |
tree | 80b294db21b4124491af8cfd861fc3d3c597550b /tools/gold | |
parent | 9fd977036c9687b02368a52cf72beaa74c93427d (diff) |
Revert "Resolution-based LTO API."
This reverts commit r278330.
I made a change to the save temps output that is causing issues with the
bots. Didn't realize this because I had older output files sitting on
disk in my test output directory.
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@278331 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'tools/gold')
-rw-r--r-- | tools/gold/gold-plugin.cpp | 1171 |
1 files changed, 944 insertions, 227 deletions
diff --git a/tools/gold/gold-plugin.cpp b/tools/gold/gold-plugin.cpp index eb7f7aa8d05b..c8b02e6fa14b 100644 --- a/tools/gold/gold-plugin.cpp +++ b/tools/gold/gold-plugin.cpp @@ -12,22 +12,44 @@ // //===----------------------------------------------------------------------===// +#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/Support/CommandLine.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/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> @@ -39,7 +61,6 @@ #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 @@ -85,17 +106,38 @@ struct PluginInputFile { }; struct ResolutionInfo { - bool CanOmitFromDynSym = true; - bool DefaultVisibility = true; + 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; }; -struct CommonResolution { - bool Prevailing = false; - bool VisibleToRegularObj = false; - uint64_t Size = 0; - unsigned Align = 0; -}; +/// 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(); +}; } static ld_plugin_add_symbols add_symbols = nullptr; @@ -103,16 +145,14 @@ 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 size_t MaxTasks; +static std::string DefaultTriple = sys::getDefaultTargetTriple(); namespace options { enum OutputType { @@ -121,6 +161,7 @@ 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. @@ -172,6 +213,8 @@ 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_) @@ -180,7 +223,9 @@ namespace options { return; llvm::StringRef opt = opt_; - if (opt.startswith("mcpu=")) { + 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=")); @@ -262,15 +307,10 @@ 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: @@ -364,6 +404,20 @@ 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(); @@ -393,18 +447,21 @@ static void diagnosticHandler(const DiagnosticInfo &DI) { message(Level, "LLVM gold plugin: %s", ErrStorage.c_str()); } -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 void diagnosticHandlerForContext(const DiagnosticInfo &DI, + void *Context) { + diagnosticHandler(DI); } -template <typename T> static T check(Expected<T> E) { - if (E) - return std::move(*E); - check(E.takeError()); - return T(); +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; } /// Called by gold to see whether this file is one that our plugin can handle. @@ -412,6 +469,7 @@ template <typename T> static T check(Expected<T> E) { /// 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) { @@ -440,25 +498,22 @@ static ld_plugin_status claim_file_hook(const ld_plugin_input_file *file, BufferRef = Buffer->getMemBufferRef(); } - *claimed = 1; + 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; - 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()); - }); + *claimed = 1; - return *claimed ? LDPS_ERR : LDPS_OK; + 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<InputFile> Obj = std::move(*ObjOrErr); + std::unique_ptr<object::IRObjectFile> Obj = std::move(*ObjOrErr); Modules.resize(Modules.size() + 1); claimed_file &cf = Modules.back(); @@ -480,52 +535,72 @@ 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->getSourceFileName()).str(); + sys::path::filename(Obj->getModule().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()); - ResolutionInfo &Res = ResInfo[Name]; + SmallString<64> Name; + { + raw_svector_ostream OS(Name); + Sym.printName(OS); + } + sym.name = strdup(Name.c_str()); + + const GlobalValue *GV = Obj->getSymbolGV(Sym.getRawDataRefImpl()); - Res.CanOmitFromDynSym &= Sym.canBeOmittedFromSymbolTable(); + ResolutionInfo &Res = ResInfo[sym.name]; sym.visibility = LDPV_DEFAULT; - 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 (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; + } } if (Symflags & object::BasicSymbolRef::SF_Undefined) { sym.def = LDPK_UNDEF; - if (Symflags & object::BasicSymbolRef::SF_Weak) + if (GV && GV->hasExternalWeakLinkage()) sym.def = LDPK_WEAKUNDEF; - } else if (Symflags & object::BasicSymbolRef::SF_Common) - sym.def = LDPK_COMMON; - else if (Symflags & object::BasicSymbolRef::SF_Weak) - sym.def = LDPK_WEAKDEF; - else + } 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; - const Comdat *C = check(Sym.getComdat()); - if (C) - sym.comdat_key = strdup(C->getName().str().c_str()); + 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()); + } sym.resolution = LDPR_UNKNOWN; } @@ -540,6 +615,39 @@ 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); @@ -563,27 +671,120 @@ static const void *getSymbolsAndView(claimed_file &F) { return View; } -static void addModule(LTO &Lto, claimed_file &F, const void *View) { +static std::unique_ptr<ModuleSummaryIndex> +getModuleSummaryIndexForFile(claimed_file &F) { + const void *View = getSymbolsAndView(F); + if (!View) + return nullptr; + MemoryBufferRef BufferRef(StringRef((const char *)View, F.filesize), F.name); - Expected<std::unique_ptr<InputFile>> ObjOrErr = InputFile::create(BufferRef); - if (!ObjOrErr) + // 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()) message(LDPL_FATAL, "Could not read bitcode from file : %s", - toString(ObjOrErr.takeError()).c_str()); + EC.message().c_str()); + + object::IRObjectFile &Obj = **ObjOrErr; + + Module &M = Obj.getModule(); - InputFile &Obj = **ObjOrErr; + M.materializeMetadata(); + UpgradeDebugInfo(M); + + SmallPtrSet<GlobalValue *, 8> Used; + collectUsedGlobalVariables(M, Used, /*CompilerUsed*/ false); 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: @@ -594,48 +795,57 @@ static void addModule(LTO &Lto, 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: - R.Prevailing = true; + 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()); break; + } case LDPR_PREVAILING_DEF: - R.Prevailing = true; - R.VisibleToRegularObj = true; + 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; + } break; - case LDPR_PREVAILING_DEF_IRONLY_EXP: - R.Prevailing = true; - if (!Res.CanOmitFromDynSym) - R.VisibleToRegularObj = true; + case LDPR_PREVAILING_DEF_IRONLY_EXP: { + Keep.push_back(GV); + if (canBeOmittedFromSymbolTable(GV)) + Internalize.insert(GV->getName()); 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); } - check(Lto.add(std::move(*ObjOrErr), Resols), - std::string("Failed to link module ") + F.name); + 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); } static void recordFile(std::string Filename, bool TempOutFile) { @@ -647,6 +857,190 @@ 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 @@ -672,44 +1066,220 @@ static int openOutputFile(SmallString<128> InFilename, bool TempOutFile, return FD; } -/// 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; - 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); +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()); + } } - // 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); + + // Run backend tasks. + splitCodeGen(std::move(M), OSPtrs, BCOSPtrs, + [&]() { return createTargetMachine(); }); } + + for (auto &Filename : Filenames) + recordFile(Filename.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; +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) + continue; + cast<GlobalVariable>(Dst)->setAlignment(I.second); } - llvm_unreachable("Invalid optimization level"); +} + +/// 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); + } + } + + for (auto &Task : Tasks) + Task.cleanup(); } /// Parse the thinlto_prefix_replace option into the \p OldPrefix and @@ -723,130 +1293,266 @@ static void getThinLTOOldAndNewPrefix(std::string &OldPrefix, NewPrefix = Split.second.str(); } -static std::unique_ptr<LTO> createLTO() { - Config Conf; - ThinBackend Backend; - unsigned ParallelCodeGenParallelismLevel = 1; +/// 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(); +} - Conf.CPU = options::mcpu; - Conf.Options = InitTargetOptionsFromCodeGenFlags(); +/// 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; - // 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; - } - if (options::thinlto_index_only) { - std::string OldPrefix, NewPrefix; - getThinLTOOldAndNewPrefix(OldPrefix, NewPrefix); - Backend = createWriteIndexesThinBackend( - OldPrefix, NewPrefix, options::thinlto_emit_imports_files, - options::thinlto_linked_objects_file); - } + 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); + } - Conf.OverrideTriple = options::triple; - Conf.DefaultTriple = sys::getDefaultTargetTriple(); + // Skip files without a module summary. + if (Index) + CombinedIndex.mergeFrom(std::move(Index), ++NextModuleId); + } - Conf.DiagHandler = diagnosticHandler; + // 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; + }; - Conf.PreOptModuleHook = [](size_t Task, Module &M) { - if (Task == 0) - addCommons(M); - return true; + // 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); }; - switch (options::TheOutputType) { - case options::OT_NORMAL: - break; + thinLTOResolveWeakForLinkerInIndex( + CombinedIndex, isPrevailing, + [](StringRef ModuleIdentifier, GlobalValue::GUID GUID, + GlobalValue::LinkageTypes NewLinkage) {}); - case options::OT_DISABLE: - Conf.PreOptModuleHook = [](size_t Task, Module &M) { return false; }; - break; + // 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); - case options::OT_BC_ONLY: - Conf.PostInternalizeModuleHook = [](size_t Task, Module &M) { + 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); + + // 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; - raw_fd_ostream OS(output_name, EC, sys::fs::OpenFlags::F_None); + ObjFileOS = new raw_fd_ostream(options::thinlto_linked_objects_file, 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; + 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; - case options::OT_SAVE_TEMPS: - check(Conf.addSaveTemps(output_name)); - break; + 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"; + } + } + + 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()); + } + } + + if (ObjFileOS) + ObjFileOS->close(); + + cleanup_hook(); + exit(0); + } + + // 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); } - return make_unique<LTO>(std::move(Conf), Backend, - ParallelCodeGenParallelismLevel); + thinLTOBackends(ApiFile, CombinedIndex, ModuleMap, ImportLists, + ModuleToDefinedGVSummaries); + return LDPS_OK; } /// 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() { +static ld_plugin_status allSymbolsReadHook(raw_fd_ostream *ApiFile) { if (Modules.empty()) return LDPS_OK; if (unsigned NumOpts = options::extra.size()) cl::ParseCommandLineOptions(NumOpts, &options::extra[0]); - std::unique_ptr<LTO> Lto = createLTO(); + 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<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; - addModule(*Lto, F, View); + linkInModule(Context, L, F, View, F.name, ApiFile, Internalize); } - 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)); + for (const auto &Name : Internalize) { + GlobalValue *GV = Combined->getNamedValue(Name.first()); + if (GV) + internalize(*GV); + } - if (options::TheOutputType == options::OT_DISABLE || - options::TheOutputType == options::OT_BC_ONLY) + if (options::TheOutputType == options::OT_DISABLE) return LDPS_OK; - if (options::thinlto_index_only) { - cleanup_hook(); - exit(0); + 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; } - for (unsigned I = 0; I != MaxTasks; ++I) - if (!Filenames[I].empty()) - recordFile(Filenames[I].str(), IsTemporary[I]); + CodeGen codeGen(std::move(Combined)); + codeGen.runAll(); if (!options::extra_library_path.empty() && set_extra_library_path(options::extra_library_path.c_str()) != LDPS_OK) @@ -856,7 +1562,18 @@ static ld_plugin_status allSymbolsReadHook() { } static ld_plugin_status all_symbols_read_hook(void) { - ld_plugin_status Ret = allSymbolsReadHook(); + 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); + } + llvm_shutdown(); if (options::TheOutputType == options::OT_BC_ONLY || |