Index: include/llvm-c/lto.h =================================================================== --- include/llvm-c/lto.h +++ include/llvm-c/lto.h @@ -40,7 +40,7 @@ * @{ */ -#define LTO_API_VERSION 17 +#define LTO_API_VERSION 18 /** * \since prior to LTO_API_VERSION=3 @@ -91,6 +91,9 @@ /** opaque reference to a code generator */ typedef struct LLVMOpaqueLTOCodeGenerator *lto_code_gen_t; +/** opaque reference to a thin code generator */ +typedef struct LLVMOpaqueThinLTOCodeGenerator *thinlto_code_gen_t; + #ifdef __cplusplus extern "C" { #endif @@ -548,6 +551,142 @@ lto_codegen_set_should_embed_uselists(lto_code_gen_t cg, lto_bool_t ShouldEmbedUselists); +/** + * @} + * @defgroup LLVMCLTO ThinLTO + * @ingroup LLVMC + * + * @{ + */ + +/** + * Type to wrap a single object returned by ThinLTO. + * + * \since LTO_API_VERSION=18 + */ +typedef struct { + void *Buffer; + size_t Size; +} LTOObjectBuffer; + +/** + * Instantiates a ThinLTO code generator. + * Returns NULL on error (check lto_get_error_message() for details). + * + * \since LTO_API_VERSION=18 + */ +extern thinlto_code_gen_t thinlto_create_codegen(); + +/** + * Frees all code generator and all memory it internally allocated. + * Upon return the lto_code_gen_t is no longer valid. + * + * \since LTO_API_VERSION=18 + */ +extern void thinlto_codegen_dispose(thinlto_code_gen_t cg); + +/** + * Add a module to a ThinLTO code generator. Identifier has to be unique among + * all the modules in a code generator. The data buffer stay owned by the + * client, and is expected to be available for the entire lifetime of the + * thinlto_code_gen_t it is added to. + * + * On failure, returns NULL (check lto_get_error_message() for details). + * + * + * \since LTO_API_VERSION=18 + */ +extern void thinlto_codegen_add_module(thinlto_code_gen_t cg, + const char *Identifier, const char *Data, + int Length); + +/** + * Optimize and codegen all the modules added to the codegenerator using + * ThinLTO. Resulting objects are accessible using thinlto_module_get_object(). + * + * \since LTO_API_VERSION=18 + */ +extern void thinlto_codegen_process(thinlto_code_gen_t cg); + +/** + * Returns the number of object files produced by the ThinLTO CodeGenerator. + * + * It usually match the number of input files, but this is not a guarantee of + * the API and future implementation may change, so the client should not + * assume it. + * + * \since LTO_API_VERSION=18 + */ +extern unsigned int thinlto_module_get_num_objects(thinlto_code_gen_t cg); + +/** + * Returns the ith object file produced by the ThinLTO CodeGenerator. + * + * Client should use \p thinlto_module_get_num_objects() to get the number of + * available objects. + * + * \since LTO_API_VERSION=18 + */ +extern LTOObjectBuffer thinlto_module_get_object(thinlto_code_gen_t cg, + unsigned int index); + +/** + * Sets which PIC code model to generate. + * Returns true on error (check lto_get_error_message() for details). + * + * \since LTO_API_VERSION=18 + */ +extern lto_bool_t thinlto_codegen_set_pic_model(thinlto_code_gen_t cg, + lto_codegen_model); + +/** + * Sets the path to a directory to use as a cache storage for incremental build. + * + * \since LTO_API_VERSION=18 + */ +extern void thinlto_codegen_set_cache_dir(thinlto_code_gen_t cg, + const char *cache_dir); + +/** + * Sets the path to a directory to use as a storage for temporary bitcode files. + * The intention is to make available for debugging the bitcode files after + * optimizations and before the CodeGen. + * + * \since LTO_API_VERSION=18 + */ +extern void thinlto_codegen_set_savetemps_dir(thinlto_code_gen_t cg, + const char *save_temps_dir); + +/** + * Sets the cpu to generate code for. + * + * \since LTO_API_VERSION=18 + */ +extern void thinlto_codegen_set_cpu(thinlto_code_gen_t cg, const char *cpu); + +/** + * Parse -mllvm style debug options. + * + * \since LTO_API_VERSION=18 + */ +extern void thinlto_debug_options(const char *const *options, int number); + +/** + * Test if a module has support for ThinLTO linking. + */ +extern bool lto_module_is_thinlto(lto_module_t mod); + +/** + * Adds to a list of all global symbols that must exist in the final generated + * code. If a function is not listed there, it might be inlined into every usage + * and optimized away. For every single module, the functions referenced + * from another module or from code outside of the ThinLTO modules need to be + * added here. + */ +extern void thinlto_codegen_add_must_preserve_symbol(thinlto_code_gen_t cg, + const char *Name, + int Length); + #ifdef __cplusplus } #endif Index: include/llvm/ADT/STLExtras.h =================================================================== --- include/llvm/ADT/STLExtras.h +++ include/llvm/ADT/STLExtras.h @@ -386,6 +386,13 @@ return std::find(Range.begin(), Range.end(), val); } +/// Provide wrappers to std::find_if which take ranges instead of having to pass +/// begin/end explicitly. +template +auto find_if(R &&Range, const T &Pred) -> decltype(Range.begin()) { + return std::find_if(Range.begin(), Range.end(), Pred); +} + //===----------------------------------------------------------------------===// // Extra additions to //===----------------------------------------------------------------------===// Index: include/llvm/LTO/LTOModule.h =================================================================== --- include/llvm/LTO/LTOModule.h +++ include/llvm/LTO/LTOModule.h @@ -67,6 +67,9 @@ static bool isBitcodeFile(const void *mem, size_t length); static bool isBitcodeFile(const char *path); + /// Returns 'true' if the Module is produced for ThinLTO. + bool isThinLTO(); + /// Returns 'true' if the memory buffer is LLVM bitcode for the specified /// triple. static bool isBitcodeForTarget(MemoryBuffer *memBuffer, Index: include/llvm/LTO/ThinLTOCodeGenerator.h =================================================================== --- /dev/null +++ include/llvm/LTO/ThinLTOCodeGenerator.h @@ -0,0 +1,107 @@ +//===-LTOCodeGenerator.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 ThinLTOCodeGenerator class, similar to the +// LTOCodeGenerator but for the ThinLTO scheme. It provide an interface for +// linker plugin. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LTO_THINLTOCODEGENERATOR_H +#define LLVM_LTO_THINLTOCODEGENERATOR_H + +#include "llvm-c/lto.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Support/CodeGen.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Target/TargetOptions.h" + +#include + +namespace llvm { +class TargetMachine; + +/// Helper to gather options relevant to the target machine creation +struct TargetMachineBuilder { + Triple TheTriple; + std::string MCpu; + std::string MAttr; + TargetOptions Options; + Reloc::Model RelocModel = Reloc::Default; + CodeGenOpt::Level CGOptLevel = CodeGenOpt::Default; + + std::unique_ptr create() const; +}; + +/// This class define an interface similar to the LTOCodeGenerator, but adapted +/// for ThinLTO processing. +class ThinLTOCodeGenerator { +public: + /// Add given module to the code generator. + void addModule(StringRef Identifier, StringRef Data); + + /** + * Adds to a list of all global symbols that must exist in the final generated + * code. If a symbol is not listed there, it might be inlined into every usage + * and optimized away. + */ + void preserveSymbol(StringRef Name); + + /** + * Process all the modules that were added to the code generator in parallel. + * + * Client can access the resulting object files using getProducedBinaries() + */ + void run(); + + /** + * Return the "in memory" binaries produced by the code generator. + */ + std::vector> &getProducedBinaries() { + return ProducedBinaries; + } + + // Option setters + void setCacheDir(std::string Path) { CacheDir = std::move(Path); } + void setSaveTempsDir(std::string Path) { SaveTempsDir = std::move(Path); } + void setCpu(std::string Cpu) { TMBuilder.MCpu = std::move(Cpu); } + void setAttr(std::string MAttr) { TMBuilder.MAttr = std::move(MAttr); } + void setTargetOptions(TargetOptions Options) { + TMBuilder.Options = std::move(Options); + } + void setCodePICModel(Reloc::Model Model) { TMBuilder.RelocModel = Model; } + void setCodeGenOptLevel(CodeGenOpt::Level CGOptLevel) { + TMBuilder.CGOptLevel = CGOptLevel; + } + +private: + /// Helper factory to build a TargetMachine + TargetMachineBuilder TMBuilder; + + /// Vector holding the in-memory buffer containing the produced binaries. + std::vector> ProducedBinaries; + + /// Vector holding the input buffers containing the bitcode modules to + /// process. + std::vector Modules; + + /// Set of symbols that are cross-referenced between bitcode files, or needed + /// externally. + StringSet<> PreservedSymbols; + + /// Path to the cache directory for incremental build. + std::string CacheDir; + + /// Path to a directory to save the temporary bitcode files. + std::string SaveTempsDir; +}; +} +#endif Index: lib/LTO/CMakeLists.txt =================================================================== --- lib/LTO/CMakeLists.txt +++ lib/LTO/CMakeLists.txt @@ -1,6 +1,7 @@ add_llvm_library(LLVMLTO LTOModule.cpp LTOCodeGenerator.cpp + ThinLTOCodeGenerator.cpp ADDITIONAL_HEADER_DIRS ${LLVM_MAIN_INCLUDE_DIR}/llvm/LTO Index: lib/LTO/LTOModule.cpp =================================================================== --- lib/LTO/LTOModule.cpp +++ lib/LTO/LTOModule.cpp @@ -80,6 +80,18 @@ return bool(BCData); } +bool LTOModule::isThinLTO() { + // Right now the detection is only based on the summary presence. We may want + // to add a dedicated flag at some point. + return hasFunctionSummary(IRFile->getMemoryBufferRef(), + [](const DiagnosticInfo &DI) { + DiagnosticPrinterRawOStream DP(errs()); + DI.print(DP); + errs() << '\n'; + return; + }); +} + bool LTOModule::isBitcodeForTarget(MemoryBuffer *Buffer, StringRef TriplePrefix) { ErrorOr BCOrErr = Index: lib/LTO/ThinLTOCodeGenerator.cpp =================================================================== --- /dev/null +++ lib/LTO/ThinLTOCodeGenerator.cpp @@ -0,0 +1,492 @@ +//===-ThinLTOCodeGenerator.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/ThinLTOCodeGenerator.h" + +#include "llvm/Analysis/TargetLibraryInfo.h" +#include "llvm/Analysis/TargetTransformInfo.h" +#include "llvm/Bitcode/ReaderWriter.h" +#include "llvm/Bitcode/BitcodeWriterPass.h" +#include "llvm/ExecutionEngine/ObjectMemoryBuffer.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/DiagnosticPrinter.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/IR/Mangler.h" +#include "llvm/IRReader/IRReader.h" +#include "llvm/Linker/Linker.h" +#include "llvm/MC/SubtargetFeature.h" +#include "llvm/Object/FunctionIndexObjectFile.h" +#include "llvm/Support/raw_sha1_ostream.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/ThreadPool.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/Transforms/IPO.h" +#include "llvm/Transforms/IPO/FunctionImport.h" +#include "llvm/Transforms/IPO/PassManagerBuilder.h" +#include "llvm/Transforms/Utils/FunctionImportUtils.h" + +using namespace llvm; + +static void diagnosticHandler(const DiagnosticInfo &DI) { + DiagnosticPrinterRawOStream DP(errs()); + DI.print(DP); + errs() << '\n'; +} + +static std::unique_ptr loadModuleFromBuffer(MemoryBufferRef &Buffer, + LLVMContext &Context) { + SMDiagnostic Err; + auto Module = parseIR(Buffer, Err, Context); + if (!Module) { + Err.print("function-import", errs()); + report_fatal_error("Can't load module, abort."); + } + return Module; +} + +/// Produce the combined function index from all the bitcode files: "thin-link". +static std::unique_ptr +linkCombinedIndex(const std::vector &Modules) { + std::unique_ptr CombinedIndex; + uint64_t NextModuleId = 0; + for (auto &ModuleBuffer : Modules) { + ErrorOr> ObjOrErr = + object::FunctionIndexObjectFile::create(ModuleBuffer, diagnosticHandler, + false); + if (std::error_code EC = ObjOrErr.getError()) { + // FIXME diagnose + errs() << "error: can't create FunctionIndexObjectFile for buffer: " + << EC.message() << "\n"; + return nullptr; + } + auto Index = (*ObjOrErr)->takeIndex(); + if (CombinedIndex) { + CombinedIndex->mergeFrom(std::move(Index), ++NextModuleId); + } else { + CombinedIndex = std::move(Index); + } + } + return CombinedIndex; +} + +namespace { + +/// Provide a "loader" for the FunctionImporter to access function from other +/// modules. +class ModuleLoader { + /// The context that will be used for importing. + LLVMContext &Context; + + /// Map of Module Identifier to MemoryBuffer used to load a Module. + StringMap &PathToModules; + +public: + ModuleLoader(LLVMContext &Context, StringMap &PathToModules) + : Context(Context), PathToModules(PathToModules) {} + + /// Load a module on demand. + std::unique_ptr operator()(StringRef Identifier) { + SMDiagnostic Err; + auto BufferRef = PathToModules.find(Identifier); + if (BufferRef == PathToModules.end()) { + report_fatal_error( + std::string("ThinLTO lazy load error, unknown Module:") + Identifier); + } + auto LazyModule = + getLazyIRModule(MemoryBuffer::getMemBuffer(BufferRef->second, false), + Err, Context, /* ShouldLazyLoadMetadata */ false); + if (!LazyModule) { + Err.print("function-import", errs()); + report_fatal_error("Can't load module, abort."); + } + return LazyModule; + } +}; + +static std::string toHex(StringRef Input) { + static const char *const LUT = "0123456789ABCDEF"; + size_t Length = Input.size(); + + std::string Output; + Output.reserve(2 * Length); + for (size_t i = 0; i < Length; ++i) { + const unsigned char c = Input[i]; + Output.push_back(LUT[c >> 4]); + Output.push_back(LUT[c & 15]); + } + return Output; +} + +static std::unique_ptr +ProcessThinLTOModule(std::unique_ptr TheModule, + const FunctionInfoIndex &Index, + StringMap &PathToModules, + std::unique_ptr TM, StringRef CacheDir, + StringRef SaveTempPath) { + // Promote static to global + if (renameModuleForThinLTO(*TheModule, &Index)) { + report_fatal_error("renameModuleForThinLTO failed"); + } + + ModuleLoader Loader(TheModule->getContext(), PathToModules); + FunctionImporter Importer(Index, Loader); + Importer.importFunctions(*TheModule); + + std::string CachedFilename; + if (!CacheDir.empty()) { + // Compute the hash of the IR + raw_sha1_ostream HashStream; + WriteBitcodeToFile(TheModule.get(), HashStream); + auto Hash = toHex(HashStream.sha1()); + + // Check if this IR has already an object file in the cache + sys::fs::file_status Status; + CachedFilename = (Twine(CacheDir) + "/" + Hash + ".o").str(); + sys::fs::status(CachedFilename, Status); + if (sys::fs::exists(Status)) { + // Cache Hit! + auto FileLoaded = + MemoryBuffer::getFile(CachedFilename, Status.getSize(), false); + if (!FileLoaded) { + errs() << "ThinLTO: error opening the file '" << CachedFilename + << "': " << FileLoaded.getError().message() << "\n"; + report_fatal_error("FAILURE"); + } + return std::move(*FileLoaded); + } + // Cache miss, move on + } + + SmallVector OutputBuffer; + std::unique_ptr OptimizedBitcodeOS; + { + // Populate the PassManager + PassManagerBuilder PMB; + PMB.LibraryInfo = new TargetLibraryInfoImpl(TM->getTargetTriple()); + PMB.Inliner = createFunctionInliningPass(); + // FIXME: should get it from the bitcode? + PMB.OptLevel = CodeGenOpt::Aggressive; + PMB.LoopVectorize = true; + PMB.SLPVectorize = true; + PMB.VerifyInput = true; + PMB.VerifyOutput = false; + + legacy::PassManager PM; + + // Add the TTI (required to inform the vectorizer about register size for + // instance) + PM.add(createTargetTransformInfoWrapperPass(TM->getTargetIRAnalysis())); + + // Add optimizations + PMB.populateThinLTOPassManager(PM); + + if (!SaveTempPath.empty()) { + // User asked to save temps, let dump the optimized bitcode file. + // Write to a temporary to avoid race condition + std::error_code EC; + OptimizedBitcodeOS.reset( + new raw_fd_ostream(SaveTempPath, EC, sys::fs::F_None)); + if (EC) + report_fatal_error(Twine("Failed to open ") + SaveTempPath + + " to save optimized bitcode\n"); + PM.add(createBitcodeWriterPass(*OptimizedBitcodeOS, true)); + } + + // CodeGen + raw_svector_ostream OS(OutputBuffer); + if (TM->addPassesToEmitFile(PM, OS, TargetMachine::CGFT_ObjectFile, + /* DisableVerify */ true)) + report_fatal_error("Failed to setup codegen"); + + // Run optimize and codegen, possible dumping optimized bitcode at the same + // time. The resulting binary is in OutputBuffer. + PM.run(*TheModule); + } + + if (!CachedFilename.empty()) { + // Cache the Produced object file + + // Write to a temporary to avoid race condition + SmallString<128> TempFilename; + int TempFD; + std::error_code EC = + sys::fs::createTemporaryFile("Thin", "tmp.o", 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; + } + // Rename to final destination (hopefully race condition won't matter here) + sys::fs::rename(TempFilename, CachedFilename); + } + + // we leak OutputBuffer... :( + return make_unique(std::move(OutputBuffer)); +} + +/// Process a global adjusting its linkage, adding it to the "Keep" set if +/// needed. +/// \return true if the global will be internalized. +static bool InternalizeGlobal(Module &TheModule, GlobalValue &GV, + StringSet<> &Keep, const FunctionInfoIndex &Index, + const StringSet<> &PreservedSymbols, + bool IsFunction) { + Mangler Mang; + SmallString<128> Buffer; + + if (GV.hasAvailableExternallyLinkage()) { + Keep.insert(GV.getName()); + return false; + } + // There are no restrictions to apply to declarations. + if (GV.isDeclaration()) + return false; + + // There is nothing more restrictive than private linkage. + if (GV.hasPrivateLinkage()) + return false; + + auto IsFirstDefinitionForLinker = [&](GlobalValue &GV) { + assert(!GV.isStrongDefinitionForLinker() && "Expect weak or linkonce"); + auto It = Index.findFunctionInfoList(GV.getName()); + assert(It != Index.end()); + + // Get the first *linker visible* definition for this global in the summary + // list. + auto FirstDefForLinker = llvm::find_if( + It->second, [](const std::unique_ptr &FuncInfo) { + auto Linkage = FuncInfo->functionSummary()->getFunctionLinkage(); + return !GlobalValue::isAvailableExternallyLinkage(Linkage); + }); + // If \p GV is not the first definition, give up... + if ((*FirstDefForLinker)->functionSummary()->modulePath() != + TheModule.getModuleIdentifier()) + return false; + // If there is any strong definition anywhere, do not bother emitting this. + if (llvm::any_of( + It->second, [](const std::unique_ptr &FuncInfo) { + auto Linkage = FuncInfo->functionSummary()->getFunctionLinkage(); + return !GlobalValue::isWeakForLinker(Linkage); + })) + return false; + return true; + }; + auto Linkage = GV.getLinkage(); + + auto HasMultipleCopies = [&](GlobalValue &GV) { + return Index.findFunctionInfoList(GV.getName())->second.size() > 1; + }; + + if (Linkage == GlobalValue::LinkOnceAnyLinkage) { + // We need to emit only one of these, the first module will keep + // it, but turned into a weak while the others will drop it. + if (IsFunction && HasMultipleCopies(GV)) { + if (IsFirstDefinitionForLinker(GV)) + GV.setLinkage(GlobalValue::WeakAnyLinkage); + else + GV.setLinkage(GlobalValue::AvailableExternallyLinkage); + } + Keep.insert(GV.getName()); + return false; + } + if (Linkage == GlobalValue::LinkOnceODRLinkage) { + // We need to emit only one of these, the first module will keep + // it, but turned into a weak while the others will drop it. + if (IsFunction && HasMultipleCopies(GV)) { + if (IsFirstDefinitionForLinker(GV)) + GV.setLinkage(GlobalValue::WeakODRLinkage); + else + GV.setLinkage(GlobalValue::AvailableExternallyLinkage); + } + Keep.insert(GV.getName()); + return false; + } + if (Linkage == GlobalValue::WeakAnyLinkage) { + // We need to emit only one of these, the first module will keep + // it "as is" and the others will drop it + if (IsFunction && !IsFirstDefinitionForLinker(GV)) + GV.setLinkage(GlobalValue::AvailableExternallyLinkage); + Keep.insert(GV.getName()); + return false; + } + if (Linkage == GlobalValue::WeakODRLinkage) { + // We need to emit only one of these, the first module will keep + // it "as is" and the others will drop it + if (IsFunction && !IsFirstDefinitionForLinker(GV)) + GV.setLinkage(GlobalValue::AvailableExternallyLinkage); + Keep.insert(GV.getName()); + return false; + } + + // Check the list of supplied symbols to preserve because of cross reference + // or for symbols that need to be exported. + Buffer.clear(); + Mang.getNameWithPrefix(Buffer, &GV, false); + if (PreservedSymbols.count(Buffer)) { + Keep.insert(GV.getName()); + return false; + } + return true; +} + +// Helper function running internalize for ThinLTO +static void InternalizeModule(Module &TheModule, const FunctionInfoIndex &Index, + const StringSet<> &PreservedSymbols) { + + // Keep track of the original linkage, needed to be restored + // The issue is that even if a function is not accessed from outside the + // current module before ThinLTO, it can't be internalized because it + // potentially has a caller in the current module and this caller can be + // imported in another module. + StringMap> + OriginalLinkages; + + auto RememberOriginalLinkage = [&](GlobalValue &GV) { + OriginalLinkages[GV.getName()] = + std::make_pair(GV.getLinkage(), GV.getVisibility()); + }; + + // Set of symbol to preserve during Internalize + StringSet<> Keep; + + for (Function &GV : TheModule) { + if (InternalizeGlobal(TheModule, GV, Keep, Index, PreservedSymbols, true)) + RememberOriginalLinkage(GV); + } + for (GlobalVariable &GV : TheModule.globals()) { + if (InternalizeGlobal(TheModule, GV, Keep, Index, PreservedSymbols, false)) + RememberOriginalLinkage(GV); + } + for (GlobalAlias &GV : TheModule.aliases()) { + if (InternalizeGlobal(TheModule, GV, Keep, Index, PreservedSymbols, false)) + RememberOriginalLinkage(GV); + } + + { + // Run the internalize pass and some trivial optimizations + legacy::PassManager PM; + PM.add(createInternalizePass(std::move(Keep))); + PM.add(createGlobalDCEPass()); + PM.add(createIPSCCPPass()); // IP SCCP + PM.add(createGlobalOptimizerPass()); // Optimize out global vars + // Promote any localized global vars + // apply scope restrictions + PM.run(TheModule); + } + + // Restore the original linkage for function that haven't been eliminated + for (auto &GVInfo : OriginalLinkages) { + if (auto *GV = TheModule.getNamedValue(GVInfo.first())) { + GV->setLinkage(GVInfo.second.first); + GV->setVisibility(GVInfo.second.second); + } + } +} +} // end anonymous namespace + +void ThinLTOCodeGenerator::addModule(StringRef Identifier, StringRef Data) { + MemoryBufferRef Buffer(Data, Identifier); + if (Modules.empty()) { + // First module added, let initialize the triple and some options + LLVMContext Context; + Triple TheTriple(getBitcodeTargetTriple(Buffer, Context)); + + // Set a default CPU for Darwin triples (copied from LTOCodeGenerator). + 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); + } +#ifndef NDEBUG + else { + LLVMContext Context; + assert(TMBuilder.TheTriple.str() == + getBitcodeTargetTriple(Buffer, Context) && + "ThinLTO modules with different triple not supported"); + } +#endif + Modules.push_back(Buffer); +} + +void ThinLTOCodeGenerator::preserveSymbol(StringRef Name) { + PreservedSymbols.insert(Name); +} + +// TargetMachine factory +std::unique_ptr TargetMachineBuilder::create() const { + std::string ErrMsg; + const Target *TheTarget = + TargetRegistry::lookupTarget(TheTriple.str(), ErrMsg); + if (!TheTarget) { + report_fatal_error("Can't load target for this Triple"); + } + + // Use MAttr as the default set of features. + SubtargetFeatures Features(MAttr); + Features.getDefaultSubtargetFeatures(TheTriple); + std::string FeatureStr = Features.getString(); + return std::unique_ptr(TheTarget->createTargetMachine( + TheTriple.str(), MCpu, FeatureStr, Options, RelocModel, + CodeModel::Default, CGOptLevel)); +} + +// Main entry point for the ThinLTO processing +void ThinLTOCodeGenerator::run() { + // Sequential linking phase + auto Index = linkCombinedIndex(Modules); + + // Prepare the resulting object vector + ProducedBinaries.clear(); + ProducedBinaries.resize(Modules.size()); + + // Helper map to find a Module from its identifier + // !! Relies on having a unique getBufferIdentifier() !! + StringMap PathToModules; + for (auto &ModuleBuffer : Modules) { + assert(PathToModules.find(ModuleBuffer.getBufferIdentifier()) == + PathToModules.end()); + PathToModules[ModuleBuffer.getBufferIdentifier()] = ModuleBuffer; + } + + // Parallel optimizer + codegen + { + ThreadPool Pool; + int count = 0; + for (auto &ModuleBuffer : Modules) { + Pool.async([&](int count) { + + // Parse module now + LLVMContext Context; + auto TheModule = loadModuleFromBuffer(ModuleBuffer, Context); + + InternalizeModule(*TheModule, *Index, PreservedSymbols); + + ProducedBinaries[count] = + ProcessThinLTOModule(std::move(TheModule), *Index, PathToModules, + TMBuilder.create(), CacheDir, SaveTempsDir); + }, count); + count++; + } + } +} Index: tools/llvm-lto/llvm-lto.cpp =================================================================== --- tools/llvm-lto/llvm-lto.cpp +++ tools/llvm-lto/llvm-lto.cpp @@ -18,6 +18,7 @@ #include "llvm/IR/DiagnosticPrinter.h" #include "llvm/IR/LLVMContext.h" #include "llvm/LTO/LTOCodeGenerator.h" +#include "llvm/LTO/ThinLTOCodeGenerator.h" #include "llvm/LTO/LTOModule.h" #include "llvm/Object/FunctionIndexObjectFile.h" #include "llvm/Support/CommandLine.h" @@ -32,6 +33,11 @@ using namespace llvm; +static cl::opt ThinLTODestinationDirectory( + "thinlto-dir", cl::init(""), + cl::desc("Enable ThinLTO and produce the output to this directory"), + cl::value_desc("dirname")); + static cl::opt OptLevel("O", cl::desc("Optimization level. [-O0, -O1, -O2, or -O3] " @@ -263,6 +269,32 @@ return 0; } + if (!ThinLTODestinationDirectory.empty()) { + ThinLTOCodeGenerator CodeGen; + for (auto &Filename : InputFilenames) { + std::string ErrorMsg; + // auto Module = + // std::unique_ptr(LTOModule::createFromFile(Filename.c_str(), + // InitTargetOptionsFromCodeGenFlags(), ErrorMsg)); + // Can't do that, we want to leak the buffer instead, ld64 style! + ErrorOr> BufferOrErr = + MemoryBuffer::getFile(Filename); + if (std::error_code EC = BufferOrErr.getError()) { + errs() << "Can't open file: " << EC.message() << "\n"; + return 1; + } + // YEAH: memory leak + MemoryBuffer *Buffer = BufferOrErr->release(); + CodeGen.addModule(Filename, StringRef(Buffer->getBufferStart(), + Buffer->getBufferSize())); + } + // CodeGen.linkProfile(ThinLTODestinationDirectory + "summary.bc"); + // CodeGen.optimize(ThinLTODestinationDirectory); + // CodeGen.codegen(ThinLTODestinationDirectory); + errs() << "\n===\nThinLTO done\n"; + return 0; + } + if (ThinLTO) { createCombinedFunctionIndex(); return 0; Index: tools/lto/lto.cpp =================================================================== --- tools/lto/lto.cpp +++ tools/lto/lto.cpp @@ -20,6 +20,7 @@ #include "llvm/IR/LLVMContext.h" #include "llvm/LTO/LTOCodeGenerator.h" #include "llvm/LTO/LTOModule.h" +#include "llvm/LTO/ThinLTOCodeGenerator.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Signals.h" #include "llvm/Support/TargetSelect.h" @@ -134,6 +135,7 @@ } DEFINE_SIMPLE_CONVERSION_FUNCTIONS(LibLTOCodeGenerator, lto_code_gen_t) +DEFINE_SIMPLE_CONVERSION_FUNCTIONS(ThinLTOCodeGenerator, thinlto_code_gen_t) DEFINE_SIMPLE_CONVERSION_FUNCTIONS(LTOModule, lto_module_t) // Convert the subtarget features into a string to pass to LTOCodeGenerator. @@ -434,3 +436,86 @@ lto_bool_t ShouldEmbedUselists) { unwrap(cg)->setShouldEmbedUselists(ShouldEmbedUselists); } + +// ThinLTO API below + +thinlto_code_gen_t thinlto_create_codegen() { + lto_initialize(); + ThinLTOCodeGenerator *CodeGen = new ThinLTOCodeGenerator(); + CodeGen->setTargetOptions(InitTargetOptionsFromCodeGenFlags()); + + return wrap(CodeGen); +} + +void thinlto_codegen_dispose(thinlto_code_gen_t cg) { delete unwrap(cg); } + +void thinlto_codegen_add_module(thinlto_code_gen_t cg, const char *Identifier, + const char *Data, int Length) { + unwrap(cg)->addModule(Identifier, StringRef(Data, Length)); +} + +void thinlto_codegen_process(thinlto_code_gen_t cg) { unwrap(cg)->run(); } + +unsigned int thinlto_module_get_num_objects(thinlto_code_gen_t cg) { + return unwrap(cg)->getProducedBinaries().size(); +} +LTOObjectBuffer thinlto_module_get_object(thinlto_code_gen_t cg, + unsigned int index) { + assert(index < unwrap(cg)->getProducedBinaries().size() && "Index overflow"); + auto &MemBuffer = unwrap(cg)->getProducedBinaries()[index]; + return LTOObjectBuffer{(void *)MemBuffer->getBufferStart(), + MemBuffer->getBufferSize()}; +} + +void thinlto_debug_options(const char *const *options, int number) { + // if options were requested, set them + if (number && options) { + std::vector CodegenArgv(1, "libLTO"); + for (auto Arg : ArrayRef(options, number)) + CodegenArgv.push_back(Arg); + cl::ParseCommandLineOptions(CodegenArgv.size(), CodegenArgv.data()); + } +} + +bool lto_module_is_thinlto(lto_module_t mod) { + return unwrap(mod)->isThinLTO(); +} + +void thinlto_codegen_add_must_preserve_symbol(thinlto_code_gen_t cg, + const char *Name, int Length) { + unwrap(cg)->preserveSymbol(StringRef(Name, Length)); +} + +void thinlto_codegen_set_cpu(thinlto_code_gen_t cg, const char *cpu) { + return unwrap(cg)->setCpu(cpu); +} + +void thinlto_codegen_set_cache_dir(thinlto_code_gen_t cg, + const char *cache_dir) { + return unwrap(cg)->setCacheDir(cache_dir); +} + +void thinlto_codegen_set_savetemps_dir(thinlto_code_gen_t cg, + const char *save_temps_dir) { + return unwrap(cg)->setSaveTempsDir(save_temps_dir); +} + +lto_bool_t thinlto_codegen_set_pic_model(thinlto_code_gen_t cg, + lto_codegen_model model) { + switch (model) { + case LTO_CODEGEN_PIC_MODEL_STATIC: + unwrap(cg)->setCodePICModel(Reloc::Static); + return false; + case LTO_CODEGEN_PIC_MODEL_DYNAMIC: + unwrap(cg)->setCodePICModel(Reloc::PIC_); + return false; + case LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC: + unwrap(cg)->setCodePICModel(Reloc::DynamicNoPIC); + return false; + case LTO_CODEGEN_PIC_MODEL_DEFAULT: + unwrap(cg)->setCodePICModel(Reloc::Default); + return false; + } + sLastErrorString = "Unknown PIC model"; + return true; +} Index: tools/lto/lto.exports =================================================================== --- tools/lto/lto.exports +++ tools/lto/lto.exports @@ -45,3 +45,16 @@ LLVMDisasmDispose LLVMDisasmInstruction LLVMSetDisasmOptions +thinlto_create_codegen +thinlto_codegen_dispose +thinlto_codegen_add_module +thinlto_codegen_process +thinlto_module_get_num_objects +thinlto_module_get_object +thinlto_codegen_set_pic_model +thinlto_codegen_set_cache_dir +thinlto_codegen_set_savetemps_dir +thinlto_codegen_set_cpu +thinlto_debug_options +lto_module_is_thinlto +thinlto_codegen_add_must_preserve_symbol \ No newline at end of file