Index: include/llvm/LTO/legacy/ThinLTOCodeGenerator.h =================================================================== --- include/llvm/LTO/legacy/ThinLTOCodeGenerator.h +++ include/llvm/LTO/legacy/ThinLTOCodeGenerator.h @@ -19,11 +19,14 @@ #include "llvm-c/lto.h" #include "llvm/ADT/StringSet.h" #include "llvm/ADT/Triple.h" +#include "llvm/IR/DiagnosticInfo.h" +#include "llvm/IR/DiagnosticPrinter.h" #include "llvm/IR/ModuleSummaryIndex.h" #include "llvm/Support/CachePruning.h" #include "llvm/Support/CodeGen.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Target/TargetOptions.h" +#include "llvm/Transforms/IPO/FunctionImport.h" #include @@ -32,6 +35,16 @@ class LLVMContext; class TargetMachine; +class ThinLTODiagnosticInfo : public DiagnosticInfo { + const Twine &Msg; + +public: + ThinLTODiagnosticInfo(const Twine &DiagMsg, + DiagnosticSeverity Severity = DS_Error) + : DiagnosticInfo(DK_Linker, Severity), Msg(DiagMsg) {} + void print(DiagnosticPrinter &DP) const override { DP << Msg; } +}; + /// Wrapper around MemoryBufferRef, owning the identifier class ThinLTOBuffer { std::string OwnedIdentifier; @@ -47,6 +60,7 @@ } StringRef getBuffer() const { return Buffer; } StringRef getBufferIdentifier() const { return OwnedIdentifier; } + void setBufferIdentifier(StringRef Name) { OwnedIdentifier = Name; } }; /// Helper to gather options relevant to the target machine creation @@ -58,9 +72,41 @@ Optional RelocModel; CodeGenOpt::Level CGOptLevel = CodeGenOpt::Aggressive; + // Initialize the TargetMachine builder for a given Triple + void init(const Triple &TheTriple); std::unique_ptr create() const; }; +/// Manage caching for a single Module. +class ModuleCacheEntry { +protected: + SmallString<128> EntryPath; + +public: + // Create a cache entry. This compute a unique hash for the Module considering + // the current list of export/import, and offer an interface to query to + // access the content in the cache. + ModuleCacheEntry( + StringRef CachePath, const ModuleSummaryIndex &Index, StringRef ModuleID, + const FunctionImporter::ImportMapTy &ImportList, + const FunctionImporter::ExportSetTy &ExportList, + const std::map &ResolvedODR, + const GVSummaryMapTy &DefinedGVSummaries, unsigned OptLevel, + bool Freestanding, const TargetMachineBuilder &TMBuilder); + + // Access the path to this entry in the cache. + StringRef getEntryPath() { return EntryPath; } + + // Try loading the buffer for this cache entry. + ErrorOr> tryLoadingBuffer(); + + // Cache the Produced object file from MemoryBuffer. + void write(const MemoryBuffer &OutputBuffer); + + // Cache the Produced object file from file. + void write(StringRef FilePath); +}; + /// This class define an interface similar to the LTOCodeGenerator, but adapted /// for ThinLTO processing. /// The ThinLTOCodeGenerator is not intended to be reuse for multiple @@ -69,6 +115,7 @@ /// codegenerator. class ThinLTOCodeGenerator { public: + virtual ~ThinLTOCodeGenerator() = default; /// Add given module to the code generator. void addModule(StringRef Identifier, StringRef Data); @@ -94,7 +141,7 @@ * unless setGeneratedObjectsDirectory() has been called, in which case * results are available through getProducedBinaryFiles(). */ - void run(); + virtual void run(); /** * Return the "in memory" binaries produced by the code generator. This is @@ -141,7 +188,7 @@ */ struct CachingOptions { - std::string Path; // Path to the cache, empty to disable. + std::string Path; // Path to the cache, empty to disable. CachePruningPolicy Policy; }; @@ -153,7 +200,7 @@ /// negative value to disable pruning. A value of 0 will force pruning to /// occur. void setCachePruningInterval(int Interval) { - if(Interval < 0) + if (Interval < 0) CacheOptions.Policy.Interval.reset(); else CacheOptions.Policy.Interval = std::chrono::seconds(Interval); @@ -301,15 +348,74 @@ /**@}*/ -private: +protected: + /// Simple helper to save temporary files for debug. + void saveTempBitcode(const Module &TheModule, StringRef TempDir, + unsigned count, StringRef Suffix); + + StringMap + generateModuleMap(const std::vector &Modules); + + std::unique_ptr loadModuleFromBuffer(const MemoryBufferRef &Buffer, + LLVMContext &Context, bool Lazy, + bool IsImporting); + + void crossImportIntoModule(Module &TheModule, const ModuleSummaryIndex &Index, + StringMap &ModuleMap, + const FunctionImporter::ImportMapTy &ImportList); + + DenseSet + computeGUIDPreservedSymbols(const StringSet<> &PreservedSymbols, + const Triple &TheTriple); + + std::unique_ptr + ProcessThinLTOModule(Module &TheModule, ModuleSummaryIndex &Index, + StringMap &ModuleMap, TargetMachine &TM, + const FunctionImporter::ImportMapTy &ImportList, + const FunctionImporter::ExportSetTy &ExportList, + const DenseSet &GUIDPreservedSymbols, + const GVSummaryMapTy &DefinedGlobals, + const ThinLTOCodeGenerator::CachingOptions &CacheOptions, + bool DisableCodeGen, StringRef SaveTempsDir, + bool Freestanding, unsigned OptLevel, unsigned count); + + /// Resolve LinkOnce/Weak symbols. Record resolutions in the \p ResolvedODR map + /// for caching, and in the \p Index for application during the ThinLTO + /// backends. This is needed for correctness for exported symbols (ensure + /// at least one copy kept) and a compile-time optimization (to drop duplicate + /// copies when possible). + void resolvePrevailingInIndex( + ModuleSummaryIndex &Index, + StringMap> + &ResolvedODR); + + void internalizeAndPromoteInIndex( + const StringMap &ExportLists, + const DenseSet &GUIDPreservedSymbols, + ModuleSummaryIndex &Index); + + void computeDeadSymbolsInIndex( + ModuleSummaryIndex &Index, + const DenseSet &GUIDPreservedSymbols); + + std::unique_ptr codegen(Module &TheModule); + + /// Write out the generated object file, either from CacheEntryPath or from + /// OutputBuffer, preferring hard-link when possible. + /// Returns the path to the generated file in SavedObjectsDirectoryPath. + std::string writeGeneratedObject(int count, StringRef CacheEntryPath, + StringRef SavedObjectsDirectoryPath, + const MemoryBuffer &OutputBuffer); + /// Helper factory to build a TargetMachine TargetMachineBuilder TMBuilder; - /// Vector holding the in-memory buffer containing the produced binaries, when - /// SavedObjectsDirectoryPath isn't set. + /// Vector holding the in-memory buffer containing the produced binaries, + /// when SavedObjectsDirectoryPath isn't set. std::vector> ProducedBinaries; - /// Path to generated files in the supplied SavedObjectsDirectoryPath if any. + /// Path to generated files in the supplied SavedObjectsDirectoryPath if + /// any. std::vector ProducedBinaryFiles; /// Vector holding the input buffers containing the bitcode modules to @@ -332,20 +438,20 @@ /// Path to a directory to save the generated object files. std::string SavedObjectsDirectoryPath; - /// Flag to enable/disable CodeGen. When set to true, the process stops after - /// optimizations and a bitcode is produced. + /// Flag to enable/disable CodeGen. When set to true, the process stops + /// after optimizations and a bitcode is produced. bool DisableCodeGen = false; - /// Flag to indicate that only the CodeGen will be performed, no cross-module - /// importing or optimization. + /// Flag to indicate that only the CodeGen will be performed, no + /// cross-module importing or optimization. bool CodeGenOnly = false; - /// Flag to indicate that the optimizer should not assume builtins are present - /// on the target. + /// Flag to indicate that the optimizer should not assume builtins are + /// present on the target. bool Freestanding = false; /// IR Optimization Level [0-3]. unsigned OptLevel = 3; }; -} +} // namespace llvm #endif Index: include/llvm/LTO/legacy/ThinLTOOutOfProcessCodeGenerator.h =================================================================== --- /dev/null +++ include/llvm/LTO/legacy/ThinLTOOutOfProcessCodeGenerator.h @@ -0,0 +1,120 @@ +//===-ThinLTOOutOfProcessCodeGenerator.h - LLVM Link Time Optimizer +//-------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file declares the ThinLTOOutOfProcessCodeGenerator class, similar to the +// LTOCodeGenerator but for the ThinLTO scheme. It provides an interface for +// linker plugin. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LTO_THINLTOOUTOFPROCESSCODEGENERATOR_H +#define LLVM_LTO_THINLTOOUTOFPROCESSCODEGENERATOR_H + +#include "llvm-c/lto.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/ADT/Triple.h" +#include "llvm/IR/ModuleSummaryIndex.h" +#include "llvm/LTO/legacy/ThinLTOCodeGenerator.h" +#include "llvm/Support/CachePruning.h" +#include "llvm/Support/CodeGen.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Program.h" +#include "llvm/Support/ThreadPool.h" +#include "llvm/Target/TargetOptions.h" + +#include +#include + +#include + +namespace llvm { +class StringRef; +class LLVMContext; +class TargetMachine; + +/// Wrapper class to construct a clang invocation suitable for thinlto +struct ClangThinLTOInvocation { + using Callback = std::function; + llvm::ThinLTOBuffer InputBuffer; + llvm::StringRef InputTmpFileName; + llvm::SmallString IndexPath; + llvm::SmallString OutputPath; + std::vector ImportFileList; + + ClangThinLTOInvocation(llvm::ThinLTOBuffer const &InputModuleBuffer, + llvm::StringRef InputTmpFileName, + llvm::StringRef IndexPath, + std::vector &&ImportFileList); +}; + +class ThinLTOCodegenManager { +public: + virtual ~ThinLTOCodegenManager() {} + + void createInvocation(llvm::ThinLTOBuffer const &InputModuleBuffer, + llvm::StringRef InputTmpFileName, + llvm::StringRef IndexPath, + std::vector ImportFileList, + ClangThinLTOInvocation::Callback CompletionCallback); + + virtual void + setTargetMachineBuilder(const llvm::TargetMachineBuilder &Builder) { + TMBuilder = Builder; + } + + // Pure virtual function. Subclass needs to provide implementation. + virtual void materializeModule(llvm::ThinLTOBuffer &Module) = 0; + virtual void execute(const ClangThinLTOInvocation &Invocation, + ClangThinLTOInvocation::Callback Callback) = 0; + +protected: + llvm::TargetMachineBuilder TMBuilder; +}; + +class LocalProcessCodegenManager : public ThinLTOCodegenManager { +public: + void materializeModule(llvm::ThinLTOBuffer &Module) override; + + void execute(const ClangThinLTOInvocation &Invocation, + ClangThinLTOInvocation::Callback Callback) override; + +private: + StringRef getExecutablePath(); + static std::string ExecutablePath; +}; + +/// This class exposes the same interface as ThinLTOCodeGenerator, but performs +/// the code generation steps in a distributed way by delegating to an XPC +/// service +class ThinLTOOutOfProcessCodeGenerator : public ThinLTOCodeGenerator { +public: + ThinLTOOutOfProcessCodeGenerator(ThinLTOCodegenManager *Manager) { + CodegenManager.reset(Manager); + } + + /** + * Process all the modules that were added to the code generator in + * parallel. + * + * Client can access the resulting object files using getProducedBinaries(), + * unless setGeneratedObjectsDirectory() has been called, in which case + * results are available through getProducedBinaryFiles(). + */ + void run() override; + +private: + /// CodegenManager. + std::unique_ptr CodegenManager; + + /// Thread Lock. + std::mutex ThreadLock; +}; +} // namespace llvm +#endif Index: lib/LTO/CMakeLists.txt =================================================================== --- lib/LTO/CMakeLists.txt +++ lib/LTO/CMakeLists.txt @@ -6,6 +6,7 @@ LTOCodeGenerator.cpp UpdateCompilerUsed.cpp ThinLTOCodeGenerator.cpp + ThinLTOOutOfProcessCodeGenerator.cpp ADDITIONAL_HEADER_DIRS ${LLVM_MAIN_INCLUDE_DIR}/llvm/LTO Index: lib/LTO/ThinLTOCodeGenerator.cpp =================================================================== --- lib/LTO/ThinLTOCodeGenerator.cpp +++ lib/LTO/ThinLTOCodeGenerator.cpp @@ -71,28 +71,12 @@ extern cl::opt LTODiscardValueNames; extern cl::opt LTORemarksFilename; extern cl::opt LTOPassRemarksWithHotness; -} - -namespace { -static cl::opt - ThreadCount("threads", cl::init(llvm::heavyweight_hardware_concurrency())); - -// Simple helper to save temporary files for debug. -static void saveTempBitcode(const Module &TheModule, StringRef TempDir, - unsigned count, StringRef Suffix) { - if (TempDir.empty()) - return; - // User asked to save temps, let dump the bitcode file after import. - std::string SaveTempPath = (TempDir + llvm::Twine(count) + Suffix).str(); - std::error_code EC; - raw_fd_ostream OS(SaveTempPath, EC, sys::fs::F_None); - if (EC) - report_fatal_error(Twine("Failed to open ") + SaveTempPath + - " to save optimized bitcode\n"); - WriteBitcodeToFile(TheModule, OS, /* ShouldPreserveUseListOrder */ true); -} +cl::opt ThreadCount("threads", + cl::init(llvm::heavyweight_hardware_concurrency())); +} // namespace llvm +namespace { static const GlobalValueSummary * getFirstDefinitionForLinker(const GlobalValueSummaryList &GVSummaryList) { // If there is any strong definition anywhere, get it. @@ -134,34 +118,11 @@ } } -static StringMap -generateModuleMap(const std::vector &Modules) { - StringMap ModuleMap; - for (auto &ModuleBuffer : Modules) { - assert(ModuleMap.find(ModuleBuffer.getBufferIdentifier()) == - ModuleMap.end() && - "Expect unique Buffer Identifier"); - ModuleMap[ModuleBuffer.getBufferIdentifier()] = ModuleBuffer.getMemBuffer(); - } - return ModuleMap; -} - static void promoteModule(Module &TheModule, const ModuleSummaryIndex &Index) { if (renameModuleForThinLTO(TheModule, Index)) report_fatal_error("renameModuleForThinLTO failed"); } -namespace { -class ThinLTODiagnosticInfo : public DiagnosticInfo { - const Twine &Msg; -public: - ThinLTODiagnosticInfo(const Twine &DiagMsg, - DiagnosticSeverity Severity = DS_Error) - : DiagnosticInfo(DK_Linker, Severity), Msg(DiagMsg) {} - void print(DiagnosticPrinter &DP) const override { DP << Msg; } -}; -} - /// Verify the module and strip broken debug info. static void verifyLoadedModule(Module &TheModule) { bool BrokenDebugInfo = false; @@ -174,51 +135,6 @@ } } -static std::unique_ptr -loadModuleFromBuffer(const MemoryBufferRef &Buffer, LLVMContext &Context, - bool Lazy, bool IsImporting) { - SMDiagnostic Err; - Expected> ModuleOrErr = - Lazy - ? getLazyBitcodeModule(Buffer, Context, - /* ShouldLazyLoadMetadata */ true, IsImporting) - : parseBitcodeFile(Buffer, Context); - if (!ModuleOrErr) { - handleAllErrors(ModuleOrErr.takeError(), [&](ErrorInfoBase &EIB) { - SMDiagnostic Err = SMDiagnostic(Buffer.getBufferIdentifier(), - SourceMgr::DK_Error, EIB.message()); - Err.print("ThinLTO", errs()); - }); - report_fatal_error("Can't load module, abort."); - } - if (!Lazy) - verifyLoadedModule(*ModuleOrErr.get()); - return std::move(ModuleOrErr.get()); -} - -static void -crossImportIntoModule(Module &TheModule, const ModuleSummaryIndex &Index, - StringMap &ModuleMap, - const FunctionImporter::ImportMapTy &ImportList) { - auto Loader = [&](StringRef Identifier) { - return loadModuleFromBuffer(ModuleMap[Identifier], TheModule.getContext(), - /*Lazy=*/true, /*IsImporting*/ true); - }; - - FunctionImporter Importer(Index, Loader); - Expected Result = Importer.importFunctions(TheModule, ImportList); - if (!Result) { - handleAllErrors(Result.takeError(), [&](ErrorInfoBase &EIB) { - SMDiagnostic Err = SMDiagnostic(TheModule.getModuleIdentifier(), - SourceMgr::DK_Error, EIB.message()); - Err.print("ThinLTO", errs()); - }); - report_fatal_error("importFunctions failed"); - } - // Verify again after cross-importing. - verifyLoadedModule(TheModule); -} - static void optimizeModule(Module &TheModule, TargetMachine &TM, unsigned OptLevel, bool Freestanding) { // Populate the PassManager @@ -247,21 +163,7 @@ PM.run(TheModule); } -// Convert the PreservedSymbols map from "Name" based to "GUID" based. -static DenseSet -computeGUIDPreservedSymbols(const StringSet<> &PreservedSymbols, - const Triple &TheTriple) { - DenseSet GUIDPreservedSymbols(PreservedSymbols.size()); - for (auto &Entry : PreservedSymbols) { - StringRef Name = Entry.first(); - if (TheTriple.isOSBinFormatMachO() && Name.size() > 0 && Name[0] == '_') - Name = Name.drop_front(); - GUIDPreservedSymbols.insert(GlobalValue::getGUID(Name)); - } - return GUIDPreservedSymbols; -} - -std::unique_ptr codegenModule(Module &TheModule, +static std::unique_ptr codegenModule(Module &TheModule, TargetMachine &TM) { SmallVector OutputBuffer; @@ -270,8 +172,9 @@ raw_svector_ostream OS(OutputBuffer); legacy::PassManager PM; - // If the bitcode files contain ARC code and were compiled with optimization, - // the ObjCARCContractPass must be run, so do it unconditionally here. + // If the bitcode files contain ARC code and were compiled with + // optimization, the ObjCARCContractPass must be run, so do it + // unconditionally here. PM.add(createObjCARCContractPass()); // Setup the codegen now. @@ -284,102 +187,206 @@ } return make_unique(std::move(OutputBuffer)); } +} // end anonymous namespace -/// Manage caching for a single Module. -class ModuleCacheEntry { - SmallString<128> EntryPath; - -public: - // Create a cache entry. This compute a unique hash for the Module considering - // the current list of export/import, and offer an interface to query to - // access the content in the cache. - ModuleCacheEntry( - StringRef CachePath, const ModuleSummaryIndex &Index, StringRef ModuleID, - const FunctionImporter::ImportMapTy &ImportList, - const FunctionImporter::ExportSetTy &ExportList, - const std::map &ResolvedODR, - const GVSummaryMapTy &DefinedGVSummaries, unsigned OptLevel, - bool Freestanding, const TargetMachineBuilder &TMBuilder) { - if (CachePath.empty()) - return; - - if (!Index.modulePaths().count(ModuleID)) - // The module does not have an entry, it can't have a hash at all - return; - - if (all_of(Index.getModuleHash(ModuleID), - [](uint32_t V) { return V == 0; })) - // No hash entry, no caching! - return; - - llvm::lto::Config Conf; - Conf.OptLevel = OptLevel; - Conf.Options = TMBuilder.Options; - Conf.CPU = TMBuilder.MCpu; - Conf.MAttrs.push_back(TMBuilder.MAttr); - Conf.RelocModel = TMBuilder.RelocModel; - Conf.CGOptLevel = TMBuilder.CGOptLevel; - Conf.Freestanding = Freestanding; - SmallString<40> Key; - computeLTOCacheKey(Key, Conf, Index, ModuleID, ImportList, ExportList, - ResolvedODR, DefinedGVSummaries); - - // This choice of file name allows the cache to be pruned (see pruneCache() - // in include/llvm/Support/CachePruning.h). - sys::path::append(EntryPath, CachePath, "llvmcache-" + Key); - } +ModuleCacheEntry::ModuleCacheEntry( + StringRef CachePath, const ModuleSummaryIndex &Index, StringRef ModuleID, + const FunctionImporter::ImportMapTy &ImportList, + const FunctionImporter::ExportSetTy &ExportList, + const std::map &ResolvedODR, + const GVSummaryMapTy &DefinedGVSummaries, unsigned OptLevel, + bool Freestanding, const TargetMachineBuilder &TMBuilder) { + if (CachePath.empty()) + return; - // Access the path to this entry in the cache. - StringRef getEntryPath() { return EntryPath; } + if (!Index.modulePaths().count(ModuleID)) + // The module does not have an entry, it can't have a hash at all + return; + + if (all_of(Index.getModuleHash(ModuleID), [](uint32_t V) { return V == 0; })) + // No hash entry, no caching! + return; + + llvm::lto::Config Conf; + Conf.OptLevel = OptLevel; + Conf.Options = TMBuilder.Options; + Conf.CPU = TMBuilder.MCpu; + Conf.MAttrs.push_back(TMBuilder.MAttr); + Conf.RelocModel = TMBuilder.RelocModel; + Conf.CGOptLevel = TMBuilder.CGOptLevel; + Conf.Freestanding = Freestanding; + SmallString<40> Key; + computeLTOCacheKey(Key, Conf, Index, ModuleID, ImportList, ExportList, + ResolvedODR, DefinedGVSummaries); + + // This choice of file name allows the cache to be pruned (see pruneCache() + // in include/llvm/Support/CachePruning.h). + sys::path::append(EntryPath, CachePath, "llvmcache-" + Key); +} // Try loading the buffer for this cache entry. - ErrorOr> tryLoadingBuffer() { - if (EntryPath.empty()) - return std::error_code(); - int FD; - SmallString<64> ResultPath; - std::error_code EC = sys::fs::openFileForRead( - Twine(EntryPath), FD, sys::fs::OF_UpdateAtime, &ResultPath); - if (EC) - return EC; - ErrorOr> MBOrErr = - MemoryBuffer::getOpenFile(FD, EntryPath, - /*FileSize*/ -1, - /*RequiresNullTerminator*/ false); - close(FD); - return MBOrErr; - } +ErrorOr> ModuleCacheEntry::tryLoadingBuffer() { + if (EntryPath.empty()) + return std::error_code(); + int FD; + SmallString<64> ResultPath; + std::error_code EC = sys::fs::openFileForRead( + Twine(EntryPath), FD, sys::fs::OF_UpdateAtime, &ResultPath); + if (EC) + return EC; + ErrorOr> MBOrErr = + MemoryBuffer::getOpenFile(FD, EntryPath, + /*FileSize*/ -1, + /*RequiresNullTerminator*/ false); + close(FD); + return MBOrErr; +} + +// Cache the Produced object file +void ModuleCacheEntry::write(const MemoryBuffer &OutputBuffer) { + if (EntryPath.empty()) + return; - // Cache the Produced object file - void write(const MemoryBuffer &OutputBuffer) { - if (EntryPath.empty()) - return; - - // Write to a temporary to avoid race condition - SmallString<128> TempFilename; - SmallString<128> CachePath(EntryPath); - int TempFD; - llvm::sys::path::remove_filename(CachePath); - sys::path::append(TempFilename, CachePath, "Thin-%%%%%%.tmp.o"); - std::error_code EC = + // Write to a temporary to avoid race condition + SmallString<128> TempFilename; + SmallString<128> CachePath(EntryPath); + int TempFD; + llvm::sys::path::remove_filename(CachePath); + sys::path::append(TempFilename, CachePath, "Thin-%%%%%%.tmp.o"); + std::error_code EC = sys::fs::createUniqueFile(TempFilename, TempFD, TempFilename); - if (EC) { - errs() << "Error: " << EC.message() << "\n"; - report_fatal_error("ThinLTO: Can't get a temporary file"); - } - { - raw_fd_ostream OS(TempFD, /* ShouldClose */ true); - OS << OutputBuffer.getBuffer(); - } - // Rename temp file to final destination; rename is atomic - EC = sys::fs::rename(TempFilename, EntryPath); - if (EC) - sys::fs::remove(TempFilename); + if (EC) { + errs() << "Error: " << EC.message() << "\n"; + report_fatal_error("ThinLTO: Can't get a temporary file"); } -}; + { + raw_fd_ostream OS(TempFD, /* ShouldClose */ true); + OS << OutputBuffer.getBuffer(); + } + // Rename temp file to final destination; rename is atomic + EC = sys::fs::rename(TempFilename, EntryPath); + if (EC) + sys::fs::remove(TempFilename); +} + +void ModuleCacheEntry::write(StringRef FilePath) { + if (EntryPath.empty()) + return; + + // Create a hard link to the cache. It is ok to be failed because of the file + // can be generated by another process. Ignore the failure and keep going. + if (auto EC = sys::fs::create_hard_link(FilePath, EntryPath)) + LLVM_DEBUG( + dbgs() + << "Could not materialize the hard link to create a cache entry for" + << FilePath << " with error message " << EC.message() << "\n"); +} + +void TargetMachineBuilder::init(const Triple &Triple) { + // Set a default CPU for Darwin triples (copied from LTOCodeGenerator). + // FIXME this looks pretty terrible... + if (MCpu.empty() && Triple.isOSDarwin()) { + if (TheTriple.getArch() == llvm::Triple::x86_64) + MCpu = "core2"; + else if (Triple.getArch() == llvm::Triple::x86) + MCpu = "yonah"; + else if (Triple.getArchName() == "arm64e" || + Triple.getArchName() == "arm64_32") + MCpu = "vortex"; + else if (Triple.getArch() == llvm::Triple::aarch64) + MCpu = "cyclone"; + } + TheTriple = std::move(Triple); +} + +void ThinLTOCodeGenerator::saveTempBitcode(const Module &TheModule, + StringRef TempDir, unsigned count, + StringRef Suffix) { + if (TempDir.empty()) + return; + // User asked to save temps, let dump the bitcode file after import. + std::string SaveTempPath = (TempDir + llvm::Twine(count) + Suffix).str(); + std::error_code EC; + raw_fd_ostream OS(SaveTempPath, EC, sys::fs::F_None); + if (EC) + report_fatal_error(Twine("Failed to open ") + SaveTempPath + + " to save optimized bitcode\n"); + WriteBitcodeToFile(TheModule, OS, /* ShouldPreserveUseListOrder */ true); +} + +StringMap ThinLTOCodeGenerator::generateModuleMap( + const std::vector &Modules) { + StringMap ModuleMap; + for (auto &ModuleBuffer : Modules) { + assert(ModuleMap.find(ModuleBuffer.getBufferIdentifier()) == + ModuleMap.end() && + "Expect unique Buffer Identifier"); + ModuleMap[ModuleBuffer.getBufferIdentifier()] = ModuleBuffer.getMemBuffer(); + } + return ModuleMap; +} -static std::unique_ptr -ProcessThinLTOModule(Module &TheModule, ModuleSummaryIndex &Index, +std::unique_ptr +ThinLTOCodeGenerator::loadModuleFromBuffer(const MemoryBufferRef &Buffer, + LLVMContext &Context, bool Lazy, + bool IsImporting) { + SMDiagnostic Err; + Expected> ModuleOrErr = + Lazy + ? getLazyBitcodeModule(Buffer, Context, + /* ShouldLazyLoadMetadata */ true, IsImporting) + : parseBitcodeFile(Buffer, Context); + if (!ModuleOrErr) { + handleAllErrors(ModuleOrErr.takeError(), [&](ErrorInfoBase &EIB) { + SMDiagnostic Err = SMDiagnostic(Buffer.getBufferIdentifier(), + SourceMgr::DK_Error, EIB.message()); + Err.print("ThinLTO", errs()); + }); + report_fatal_error("Can't load module, abort."); + } + if (!Lazy) + verifyLoadedModule(*ModuleOrErr.get()); + return std::move(ModuleOrErr.get()); +} + +void ThinLTOCodeGenerator::crossImportIntoModule( + Module &TheModule, const ModuleSummaryIndex &Index, + StringMap &ModuleMap, + const FunctionImporter::ImportMapTy &ImportList) { + auto Loader = [&](StringRef Identifier) { + return loadModuleFromBuffer(ModuleMap[Identifier], TheModule.getContext(), + /*Lazy=*/true, /*IsImporting*/ true); + }; + + FunctionImporter Importer(Index, Loader); + Expected Result = Importer.importFunctions(TheModule, ImportList); + if (!Result) { + handleAllErrors(Result.takeError(), [&](ErrorInfoBase &EIB) { + SMDiagnostic Err = SMDiagnostic(TheModule.getModuleIdentifier(), + SourceMgr::DK_Error, EIB.message()); + Err.print("ThinLTO", errs()); + }); + report_fatal_error("importFunctions failed"); + } + // Verify again after cross-importing. + verifyLoadedModule(TheModule); +} + +// Convert the PreservedSymbols map from "Name" based to "GUID" based. +DenseSet ThinLTOCodeGenerator::computeGUIDPreservedSymbols( + const StringSet<> &PreservedSymbols, const Triple &TheTriple) { + DenseSet GUIDPreservedSymbols(PreservedSymbols.size()); + for (auto &Entry : PreservedSymbols) { + StringRef Name = Entry.first(); + if (TheTriple.isOSBinFormatMachO() && Name.size() > 0 && Name[0] == '_') + Name = Name.drop_front(); + GUIDPreservedSymbols.insert(GlobalValue::getGUID(Name)); + } + return GUIDPreservedSymbols; +} + +std::unique_ptr +ThinLTOCodeGenerator::ProcessThinLTOModule(Module &TheModule, ModuleSummaryIndex &Index, StringMap &ModuleMap, TargetMachine &TM, const FunctionImporter::ImportMapTy &ImportList, const FunctionImporter::ExportSetTy &ExportList, @@ -443,11 +450,10 @@ /// backends. This is needed for correctness for exported symbols (ensure /// at least one copy kept) and a compile-time optimization (to drop duplicate /// copies when possible). -static void resolvePrevailingInIndex( +void ThinLTOCodeGenerator::resolvePrevailingInIndex( ModuleSummaryIndex &Index, StringMap> &ResolvedODR) { - DenseMap PrevailingCopy; computePrevailingCopies(Index, PrevailingCopy); @@ -468,24 +474,6 @@ thinLTOResolvePrevailingInIndex(Index, isPrevailing, recordNewLinkage); } -// Initialize the TargetMachine builder for a given Triple -static void initTMBuilder(TargetMachineBuilder &TMBuilder, - const Triple &TheTriple) { - // Set a default CPU for Darwin triples (copied from LTOCodeGenerator). - // FIXME this looks pretty terrible... - if (TMBuilder.MCpu.empty() && TheTriple.isOSDarwin()) { - if (TheTriple.getArch() == llvm::Triple::x86_64) - TMBuilder.MCpu = "core2"; - else if (TheTriple.getArch() == llvm::Triple::x86) - TMBuilder.MCpu = "yonah"; - else if (TheTriple.getArch() == llvm::Triple::aarch64) - TMBuilder.MCpu = "cyclone"; - } - TMBuilder.TheTriple = std::move(TheTriple); -} - -} // end anonymous namespace - void ThinLTOCodeGenerator::addModule(StringRef Identifier, StringRef Data) { ThinLTOBuffer Buffer(Data, Identifier); LLVMContext Context; @@ -499,12 +487,13 @@ Triple TheTriple(TripleStr); if (Modules.empty()) - initTMBuilder(TMBuilder, Triple(TheTriple)); + TMBuilder.init(Triple(TheTriple)); else if (TMBuilder.TheTriple != TheTriple) { if (!TMBuilder.TheTriple.isCompatibleWith(TheTriple)) report_fatal_error("ThinLTO modules with incompatible triples not " "supported"); - initTMBuilder(TMBuilder, Triple(TMBuilder.TheTriple.merge(TheTriple))); + auto TheMergedTriple = Triple(TMBuilder.TheTriple.merge(TheTriple)); + TMBuilder.init(TheMergedTriple); } Modules.push_back(Buffer); @@ -561,7 +550,7 @@ return CombinedIndex; } -static void internalizeAndPromoteInIndex( +void ThinLTOCodeGenerator::internalizeAndPromoteInIndex( const StringMap &ExportLists, const DenseSet &GUIDPreservedSymbols, ModuleSummaryIndex &Index) { @@ -575,7 +564,7 @@ thinLTOInternalizeAndPromoteInIndex(Index, isExported); } -static void computeDeadSymbolsInIndex( +void ThinLTOCodeGenerator::computeDeadSymbolsInIndex( ModuleSummaryIndex &Index, const DenseSet &GUIDPreservedSymbols) { // We have no symbols resolution available. And can't do any better now in the @@ -730,7 +719,7 @@ */ void ThinLTOCodeGenerator::internalize(Module &TheModule, ModuleSummaryIndex &Index) { - initTMBuilder(TMBuilder, Triple(TheModule.getTargetTriple())); + TMBuilder.init(Triple(TheModule.getTargetTriple())); auto ModuleCount = Index.modulePaths().size(); auto ModuleIdentifier = TheModule.getModuleIdentifier(); @@ -767,18 +756,24 @@ * Perform post-importing ThinLTO optimizations. */ void ThinLTOCodeGenerator::optimize(Module &TheModule) { - initTMBuilder(TMBuilder, Triple(TheModule.getTargetTriple())); + TMBuilder.init(Triple(TheModule.getTargetTriple())); // Optimize now optimizeModule(TheModule, *TMBuilder.create(), OptLevel, Freestanding); } -/// Write out the generated object file, either from CacheEntryPath or from -/// OutputBuffer, preferring hard-link when possible. -/// Returns the path to the generated file in SavedObjectsDirectoryPath. -static std::string writeGeneratedObject(int count, StringRef CacheEntryPath, - StringRef SavedObjectsDirectoryPath, - const MemoryBuffer &OutputBuffer) { +/** + * Perform ThinLTO CodeGen. + */ +std::unique_ptr ThinLTOCodeGenerator::codegen(Module &TheModule) { + TMBuilder.init(Triple(TheModule.getTargetTriple())); + return codegenModule(TheModule, *TMBuilder.create()); +} + +std::string +ThinLTOCodeGenerator::writeGeneratedObject(int count, StringRef CacheEntryPath, + StringRef SavedObjectsDirectoryPath, + const MemoryBuffer &OutputBuffer) { SmallString<128> OutputPath(SavedObjectsDirectoryPath); llvm::sys::path::append(OutputPath, Twine(count) + ".thinlto.o"); OutputPath.c_str(); // Ensure the string is null terminated. @@ -866,7 +861,6 @@ WriteIndexToFile(*Index, OS); } - // Prepare the module map. auto ModuleMap = generateModuleMap(Modules); auto ModuleCount = Modules.size(); @@ -977,9 +971,9 @@ } // Parse module now - auto TheModule = - loadModuleFromBuffer(ModuleBuffer.getMemBuffer(), Context, false, - /*IsImporting*/ false); + auto TheModule = loadModuleFromBuffer(ModuleBuffer.getMemBuffer(), + Context, false, + /*IsImporting*/ false); // Save temps: original file. saveTempBitcode(*TheModule, SaveTempsDir, count, ".0.original.bc"); @@ -1002,13 +996,15 @@ // Releasing the buffer from the heap and reloading it from the // cache file with mmap helps us to lower memory pressure. // The freed memory can be used for the next input file. - // The final binary link will read from the VFS cache (hopefully!) - // or from disk (if the memory pressure was too high). + // The final binary link will read from the VFS cache + // (hopefully!) or from disk (if the memory pressure was too + // high). auto ReloadedBufferOrErr = CacheEntry.tryLoadingBuffer(); if (auto EC = ReloadedBufferOrErr.getError()) { - // On error, keep the preexisting buffer and print a diagnostic. - errs() << "error: can't reload cached file '" << CacheEntryPath - << "': " << EC.message() << "\n"; + // On error, keep the preexisting buffer and print a + // diagnostic. + errs() << "error: can't reload cached file '" + << CacheEntryPath << "': " << EC.message() << "\n"; } else { OutputBuffer = std::move(*ReloadedBufferOrErr); } @@ -1016,8 +1012,9 @@ ProducedBinaries[count] = std::move(OutputBuffer); return; } - ProducedBinaryFiles[count] = writeGeneratedObject( - count, CacheEntryPath, SavedObjectsDirectoryPath, *OutputBuffer); + ProducedBinaryFiles[count] = + writeGeneratedObject(count, CacheEntryPath, + SavedObjectsDirectoryPath, *OutputBuffer); }, IndexCount); } } Index: lib/LTO/ThinLTOOutOfProcessCodeGenerator.cpp =================================================================== --- /dev/null +++ lib/LTO/ThinLTOOutOfProcessCodeGenerator.cpp @@ -0,0 +1,446 @@ +//===-ThinLTOOutOfProcessCodeGenerator.cpp - LLVM Link Time Optimizer +//-----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the Thin Link Time Optimization library. This library is +// intended to be used by linker to optimize code at link time. +// +//===----------------------------------------------------------------------===// + +#include "llvm/LTO/legacy/ThinLTOOutOfProcessCodeGenerator.h" + +#include "llvm/ADT/Statistic.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Analysis/ModuleSummaryAnalysis.h" +#include "llvm/Analysis/ProfileSummaryInfo.h" +#include "llvm/Analysis/TargetLibraryInfo.h" +#include "llvm/Analysis/TargetTransformInfo.h" +#include "llvm/Bitcode/BitcodeReader.h" +#include "llvm/Bitcode/BitcodeWriter.h" +#include "llvm/Bitcode/BitcodeWriterPass.h" +#include "llvm/Config/llvm-config.h" +#include "llvm/IR/DebugInfo.h" +#include "llvm/IR/DiagnosticPrinter.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/IR/Mangler.h" +#include "llvm/IR/PassTimingInfo.h" +#include "llvm/IR/Verifier.h" +#include "llvm/IRReader/IRReader.h" +#include "llvm/LTO/LTO.h" +#include "llvm/MC/SubtargetFeature.h" +#include "llvm/Object/IRObjectFile.h" +#include "llvm/Support/CachePruning.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Program.h" +#include "llvm/Support/SHA1.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/SmallVectorMemoryBuffer.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/ThreadPool.h" +#include "llvm/Support/Threading.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Support/VCSRevision.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/Transforms/IPO.h" +#include "llvm/Transforms/IPO/FunctionImport.h" +#include "llvm/Transforms/IPO/Internalize.h" +#include "llvm/Transforms/IPO/PassManagerBuilder.h" +#include "llvm/Transforms/ObjCARC.h" +#include "llvm/Transforms/Utils/FunctionImportUtils.h" + +#include +#include + +using namespace llvm; + +#define DEBUG_TYPE "thinlto-outofprocess" + +namespace llvm { +// Flags -discard-value-names, defined in LTOCodeGenerator.cpp +extern cl::opt LTODiscardValueNames; +extern cl::opt LTORemarksFilename; +extern cl::opt LTOPassRemarksWithHotness; + +// Defined in ThinLTOCodeGenerator.cpp +extern cl::opt ThreadCount; +} // namespace llvm + +void ThinLTOCodegenManager::createInvocation( + const llvm::ThinLTOBuffer &InputModuleBuffer, + llvm::StringRef InputTmpFileName, llvm::StringRef IndexPath, + std::vector ImportFileList, + ClangThinLTOInvocation::Callback CompletionCallback) { + + ClangThinLTOInvocation Entry(InputModuleBuffer, InputTmpFileName, IndexPath, + std::move(ImportFileList)); + this->execute(Entry, CompletionCallback); +} + +void LocalProcessCodegenManager::materializeModule(ThinLTOBuffer &Module) { + SmallString Path; + auto EC = sys::fs::createTemporaryFile( + llvm::sys::path::stem(Module.getBufferIdentifier()), "bc", Path); + if (EC) + report_fatal_error("Could not create temporary file to materialize module"); + raw_fd_ostream OS(Path, EC, sys::fs::F_None); + if (EC) + report_fatal_error(Twine("Failed to open ") + Path + + " to save module bitcode\n"); + OS << Module.getBuffer(); + // Update the buffer identifier and pointing it to the temp file. + Module.setBufferIdentifier(Path); + // Register the temp file to be removed on signal. + llvm::sys::RemoveFileOnSignal(Path); +} + +void LocalProcessCodegenManager::execute( + const llvm::ClangThinLTOInvocation &Invocation, + ClangThinLTOInvocation::Callback Callback) { + auto IndexPathArg = llvm::Twine("-fthinlto-index=") + Invocation.IndexPath; + llvm::SmallString IndexPathArgData; + auto IndexPathArgRef = IndexPathArg.toStringRef(IndexPathArgData); + + auto OptLevelArg = llvm::Twine("-O") + llvm::Twine(TMBuilder.CGOptLevel); + llvm::SmallString<8> OptLevelArgData; + llvm::StringRef OptLevelArgRef = OptLevelArg.toStringRef(OptLevelArgData); + + llvm::SmallString<32> RelocModelArgString; + + switch (*TMBuilder.RelocModel) { + case llvm::Reloc::Static: + RelocModelArgString += "static"; + break; + case llvm::Reloc::PIC_: + RelocModelArgString += "pic"; + break; + case llvm::Reloc::DynamicNoPIC: + RelocModelArgString += "dynamic-no-pic"; + break; + case llvm::Reloc::ROPI: + RelocModelArgString += "ropi"; + break; + case llvm::Reloc::RWPI: + RelocModelArgString += "rwpi"; + break; + case llvm::Reloc::ROPI_RWPI: + RelocModelArgString += "ropi-rwpi"; + break; + } + + std::vector Arguments = { + {getExecutablePath(), OptLevelArgRef, "-x", "ir", + Invocation.InputTmpFileName, "-c", "-Xclang", "-mrelocation-model", + "-Xclang", RelocModelArgString, IndexPathArgRef, "-o", + Invocation.OutputPath}}; + + if (llvm::sys::ExecuteAndWait(Arguments[0], Arguments, llvm::None)) + llvm::report_fatal_error("Clang Invocation failed for Input file: " + + Invocation.InputTmpFileName + "\n"); + Callback(Invocation); +} + +std::string LocalProcessCodegenManager::ExecutablePath = ""; + +StringRef LocalProcessCodegenManager::getExecutablePath() { + if (ExecutablePath.empty()) { + if (llvm::ErrorOr P = llvm::sys::findProgramByName("clang")) { + ExecutablePath = *P; + LLVM_DEBUG(llvm::dbgs() << "Using clang executable: " + << ExecutablePath << "\n"); + } else + llvm::report_fatal_error("Could not find clang executable!"); + } + return ExecutablePath; +} + +ClangThinLTOInvocation::ClangThinLTOInvocation( + llvm::ThinLTOBuffer const &InputModuleBuffer, + llvm::StringRef InputTmpFileName, llvm::StringRef IndexPath, + std::vector &&ImportFileList) + : InputBuffer(InputModuleBuffer), InputTmpFileName(InputTmpFileName), + IndexPath(IndexPath), ImportFileList(std::move(ImportFileList)) { + auto EC = llvm::sys::fs::createTemporaryFile( + llvm::sys::path::stem(InputModuleBuffer.getBufferIdentifier()), + "thinlto.o", OutputPath); + LLVM_DEBUG(llvm::dbgs() << "Just created the following file to store outputs " + << OutputPath << "\n"); + if (EC) + llvm::report_fatal_error("Could not create temporary file to store thinlto " + "codegen output"); +} + +// Main entry point for the ThinLTO processing +void ThinLTOOutOfProcessCodeGenerator::run() { + LLVM_DEBUG( + dbgs() << "Out of process thinlto optimzation and codegen path!\n"); + + CodegenManager->setTargetMachineBuilder(TMBuilder); + + // Prepare the resulting object vector + assert(ProducedBinaries.empty() && "The generator should not be reused"); + if (SavedObjectsDirectoryPath.empty()) + ProducedBinaries.resize(Modules.size()); + else { + sys::fs::create_directories(SavedObjectsDirectoryPath); + bool IsDir; + sys::fs::is_directory(SavedObjectsDirectoryPath, IsDir); + if (!IsDir) + report_fatal_error("Unexistent dir: '" + SavedObjectsDirectoryPath + "'"); + ProducedBinaryFiles.resize(Modules.size()); + } + + if (CodeGenOnly) { + // Perform only parallel codegen and return. + ThreadPool Pool; + int count = 0; + for (auto &ModuleBuffer : Modules) { + Pool.async( + [&](int count) { + LLVMContext Context; + Context.setDiscardValueNames(LTODiscardValueNames); + + // Parse module now + auto TheModule = loadModuleFromBuffer(ModuleBuffer.getMemBuffer(), + Context, false, + /*IsImporting*/ false); + + // CodeGen + auto OutputBuffer = codegen(*TheModule); + if (SavedObjectsDirectoryPath.empty()) + ProducedBinaries[count] = std::move(OutputBuffer); + else + ProducedBinaryFiles[count] = writeGeneratedObject( + count, "", SavedObjectsDirectoryPath, *OutputBuffer); + }, + count++); + } + + return; + } + + // Materialize the file. + for (auto &Module : Modules) + CodegenManager->materializeModule(Module); + + // Sequential linking phase + auto Index = linkCombinedIndex(); + + // Save temps: index. + if (!SaveTempsDir.empty()) { + auto SaveTempPath = SaveTempsDir + "index.bc"; + std::error_code EC; + raw_fd_ostream OS(SaveTempPath, EC, sys::fs::F_None); + if (EC) + report_fatal_error(Twine("Failed to open ") + SaveTempPath + + " to save optimized bitcode\n"); + WriteIndexToFile(*Index, OS); + } + + // Prepare the module map. + auto ModuleMap = generateModuleMap(Modules); + auto ModuleCount = Modules.size(); + + // Collect for each module the list of function it defines (GUID -> + // Summary). + StringMap ModuleToDefinedGVSummaries(ModuleCount); + Index->collectDefinedGVSummariesPerModule(ModuleToDefinedGVSummaries); + + // Convert the preserved symbols set from string to GUID, this is needed + // for computing the caching hash and the internalization. + auto GUIDPreservedSymbols = + computeGUIDPreservedSymbols(PreservedSymbols, TMBuilder.TheTriple); + + // Compute "dead" symbols, we don't want to import/export these! + computeDeadSymbolsInIndex(*Index, GUIDPreservedSymbols); + + // Collect the import/export lists for all modules from the call-graph in + // the combined index. + StringMap ImportLists(ModuleCount); + StringMap ExportLists(ModuleCount); + ComputeCrossModuleImport(*Index, ModuleToDefinedGVSummaries, ImportLists, + ExportLists); + + // We use a std::map here to be able to have a defined ordering when + // producing a hash for the cache entry. + // FIXME: we should be able to compute the caching hash for the entry + // based on the index, and nuke this map. + StringMap> ResolvedODR; + + // Resolve LinkOnce/Weak symbols, this has to be computed early because it + // impacts the caching. + resolvePrevailingInIndex(*Index, ResolvedODR); + + // 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. + internalizeAndPromoteInIndex(ExportLists, GUIDPreservedSymbols, *Index); + + // Make sure that every module has an entry in the ExportLists and + // ResolvedODR maps to enable threaded access to these maps below. + for (auto &DefinedGVSummaries : ModuleToDefinedGVSummaries) { + ExportLists[DefinedGVSummaries.first()]; + ResolvedODR[DefinedGVSummaries.first()]; + } + + // Compute the ordering we will process the inputs: the rough heuristic + // here is to sort them per size so that the largest module get schedule + // as soon as possible. This is purely a compile-time optimization. + std::vector ModulesOrdering; + ModulesOrdering.resize(Modules.size()); + std::iota(ModulesOrdering.begin(), ModulesOrdering.end(), 0); + llvm::sort(ModulesOrdering.begin(), ModulesOrdering.end(), + [&](int LeftIndex, int RightIndex) { + auto LSize = Modules[LeftIndex].getBuffer().size(); + auto RSize = Modules[RightIndex].getBuffer().size(); + return LSize > RSize; + }); + + // Parallel optimizer + codegen + { + ThreadPool Pool(ThreadCount); + for (auto IndexCount : ModulesOrdering) { + Pool.async( + [&](int ModuleIndex) { + auto &ModuleBuffer = Modules[ModuleIndex]; + auto ModuleIdentifier = ModuleBuffer.getBufferIdentifier(); + auto &ExportList = ExportLists[ModuleIdentifier]; + + auto &DefinedFunctions = + ModuleToDefinedGVSummaries[ModuleIdentifier]; + + // The module may be cached, this helps handling it. + ModuleCacheEntry CacheEntry( + CacheOptions.Path, *Index, ModuleIdentifier, + ImportLists[ModuleIdentifier], ExportList, + ResolvedODR[ModuleIdentifier], DefinedFunctions, OptLevel, + Freestanding, TMBuilder); + + auto CacheEntryPath = CacheEntry.getEntryPath(); + { + auto ErrOrBuffer = CacheEntry.tryLoadingBuffer(); + LLVM_DEBUG(dbgs() + << "Cache " << (ErrOrBuffer ? "hit" : "miss") << " '" + << CacheEntryPath << "' for buffer " << ModuleIndex + << " " << ModuleIdentifier << "\n"); + + if (ErrOrBuffer) { + // Cache Hit! + if (SavedObjectsDirectoryPath.empty()) + ProducedBinaries[ModuleIndex] = std::move(ErrOrBuffer.get()); + else + ProducedBinaryFiles[ModuleIndex] = writeGeneratedObject( + ModuleIndex, CacheEntryPath, SavedObjectsDirectoryPath, + *ErrOrBuffer.get()); + return; + } + } + + LLVMContext Context; + Context.setDiscardValueNames(LTODiscardValueNames); + Context.enableDebugTypeODRUniquing(); + auto DiagFileOrErr = lto::setupOptimizationRemarks( + Context, LTORemarksFilename, LTOPassRemarksWithHotness, + ModuleIndex); + if (!DiagFileOrErr) { + errs() << "Error: " << toString(DiagFileOrErr.takeError()) + << "\n"; + report_fatal_error("ThinLTO: Can't get an output file for the " + "remarks"); + } + + std::map ModuleToSummariesForIndex; + llvm::gatherImportedSummariesForModule( + ModuleIdentifier, ModuleToDefinedGVSummaries, + ImportLists[ModuleIdentifier], ModuleToSummariesForIndex); + SmallString IndexFilePath; + auto EC = sys::fs::createTemporaryFile(llvm::Twine("index-") + + llvm::Twine(ModuleIndex), + "bc", IndexFilePath); + if (EC) + report_fatal_error( + "could not create temporary file for thinlto codegen " + "output"); + { + raw_fd_ostream OS(IndexFilePath, EC, sys::fs::F_None); + if (EC) + report_fatal_error( + "could not open temporary file to write thinlto " + "codegen output"); + WriteIndexToFile(*Index, OS, &ModuleToSummariesForIndex); + // Remove the temporary index file on signal. + { + std::lock_guard LockGuard(ThreadLock); + llvm::sys::RemoveFileOnSignal(IndexFilePath); + } + } + + std::vector ImportFileList; + for (auto &ILI : ModuleToSummariesForIndex) { + // We don't want to include the current module in it's imports + // list + if (ILI.first != ModuleBuffer.getBufferIdentifier()) + ImportFileList.emplace_back(ILI.first); + } + + CodegenManager->createInvocation( + ModuleBuffer, ModuleIdentifier, + IndexFilePath, std::move(ImportFileList), + [=](const ClangThinLTOInvocation &Invocation) mutable { + CacheEntry.write(Invocation.OutputPath); + + if (SavedObjectsDirectoryPath.empty()) { + // We need to generate a memory buffer for the linker. + auto ErrorOrOutputBuffer = + MemoryBuffer::getFile(Invocation.OutputPath); + if (auto EC = ErrorOrOutputBuffer.getError()) + report_fatal_error( + Twine("Could not load output memory buffer for ") + + Invocation.OutputPath + "': " + EC.message() + "\n"); + else + ProducedBinaries[ModuleIndex] = + std::move(*ErrorOrOutputBuffer); + + sys::fs::remove(Invocation.OutputPath); + } else { + // Create a hard link from the temporary output file to + // the correct location in SavedObjectsDirectoryPath. + SmallString SavedObjectPath( + SavedObjectsDirectoryPath); + sys::path::append(SavedObjectPath, + llvm::Twine(ModuleIndex) + + llvm::Twine("thinlto.bc")); + if (auto EC = sys::fs::rename(Invocation.OutputPath, + SavedObjectPath)) + report_fatal_error( + Twine("Could not write output to output " + "file directory for") + + Invocation.OutputPath + ": " + EC.message() + "\n"); + + ProducedBinaryFiles[ModuleIndex] = SavedObjectPath.str(); + } + }); + }, + IndexCount); + } + } + + pruneCache(CacheOptions.Path, CacheOptions.Policy); + + // Run interrupt handler to remove all the temp files. + llvm::sys::RunInterruptHandlers(); + + // If statistics were requested, print them out now. + if (llvm::AreStatisticsEnabled()) + llvm::PrintStatistics(); + reportAndResetTimings(); +} Index: tools/lto/lto.cpp =================================================================== --- tools/lto/lto.cpp +++ tools/lto/lto.cpp @@ -22,6 +22,7 @@ #include "llvm/LTO/legacy/LTOCodeGenerator.h" #include "llvm/LTO/legacy/LTOModule.h" #include "llvm/LTO/legacy/ThinLTOCodeGenerator.h" +#include "llvm/LTO/legacy/ThinLTOOutOfProcessCodeGenerator.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Signals.h" #include "llvm/Support/TargetSelect.h" @@ -62,6 +63,21 @@ "disable-llvm-verifier", cl::init(!VerifyByDefault), cl::desc("Don't run the LLVM verifier during the optimization pipeline")); +namespace { +enum ThinLTOMode { + Thread, + OutOfProcess +}; +} // end anonymous namespace. + +static cl::opt ThinLTOCGMode( + "thinlto-mode", cl::init(Thread), + cl::desc( + "ThinLTO Code Generation Mode. Options: thread, out-of-process, xpc"), + cl::values(clEnumValN(Thread, "thread", "Threaded codegen"), + clEnumValN(OutOfProcess, "out-of-process", + "Local out of process using clang"))); + // Holds most recent error string. // *** Not thread safe *** static std::string sLastErrorString; @@ -144,7 +160,7 @@ std::unique_ptr OwnedContext; }; -} +} // namespace DEFINE_SIMPLE_CONVERSION_FUNCTIONS(LibLTOCodeGenerator, lto_code_gen_t) DEFINE_SIMPLE_CONVERSION_FUNCTIONS(ThinLTOCodeGenerator, thinlto_code_gen_t) @@ -472,7 +488,17 @@ thinlto_code_gen_t thinlto_create_codegen(void) { lto_initialize(); - ThinLTOCodeGenerator *CodeGen = new ThinLTOCodeGenerator(); + ThinLTOCodeGenerator *CodeGen = nullptr; + switch(ThinLTOCGMode) { + case Thread: + CodeGen = new ThinLTOCodeGenerator(); + break; + case OutOfProcess: + CodeGen = + new ThinLTOOutOfProcessCodeGenerator(new LocalProcessCodegenManager()); + break; + } + CodeGen->setTargetOptions(InitTargetOptionsFromCodeGenFlags()); CodeGen->setFreestanding(EnableFreestanding);