Index: include/llvm-c/lto.h =================================================================== --- include/llvm-c/lto.h +++ include/llvm-c/lto.h @@ -40,8 +40,7 @@ * @{ */ -#define LTO_API_VERSION 17 - +#define LTO_API_VERSION 18 /** * \since prior to LTO_API_VERSION=3 */ @@ -91,6 +90,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 +550,221 @@ lto_codegen_set_should_embed_uselists(lto_code_gen_t cg, lto_bool_t ShouldEmbedUselists); +/** + * @} + * @defgroup LLVMCTLTO 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). + * + * + * The ThinLTOCodeGenerator is not intended to be reuse for multiple + * compilation: the model is that the client adds modules to the generator and + * ask to perform the ThinLTO optimizations / codegen, and finally destroys the + * codegenerator. + * + * \since LTO_API_VERSION=18 + */ +extern thinlto_code_gen_t thinlto_create_codegen(); + +/** + * Frees the generator and all memory it internally allocated. + * Upon return the thinlto_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 stays 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 matches the number of input files, but this is not a guarantee of + * the API and may change in future implementation, 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 a reference to 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); + +/** + * @} + * @defgroup LLVMCTLTO_CACHING ThinLTO Cache Control + * @ingroup LLVMCTLTO + * + * These entry points control the ThinLTO cache. The cache is intended to + * support incremental build, and thus needs to be persistent accross build. + * The client enabled the cache by supplying a path to an existing directory. + * The code generator will use this to store objects files that may be reused + * during a subsequent build. + * To avoid filling the disk space, a few knobs are provided: + * - The pruning interval limit the frequency at which the garbage collector + * will try to scan the cache directory to prune it from expired entries. + * Setting to -1 disable the pruning (default). + * - The pruning expiration time indicates to the garbage collector how old an + * entry needs to be to be removed. + * - Finally, the garbage collector can be instructed to prune the cache till + * the occupied space goes below a threshold. + * @{ + */ + +/** + * 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 cache pruning interval (in seconds). A negative value disable the + * pruning (default). + * + * \since LTO_API_VERSION=18 + */ +extern void thinlto_codegen_set_cache_pruning_interval(thinlto_code_gen_t cg, + int interval); + +/** + * Sets the maximum cache size that can be persistent across build, in term of + * percentage of the available space on the the disk. Set to 100 to indicate + * no limit, 50 to indicate that the cache size will not be left over the + * available space. A value over 100 will be reduced to 100. + * + * The formula looks like: + * AvailableSpace = FreeSpace + ExistingCacheSize + * NewCacheSize = (100*P)/AvailableSpace + * + * \since LTO_API_VERSION=18 + */ +extern void thinlto_codegen_set_final_cache_size_relative_to_available_space( + thinlto_code_gen_t cg, unsigned percentage); + +/** + * Sets the expiration (in seconds) for an entry in the cache. + * + * \since LTO_API_VERSION=18 + */ +extern void thinlto_codegen_set_cache_entry_expiration(thinlto_code_gen_t cg, + unsigned expiration); + +/** + * @} + */ + +/** + * Sets the path to a directory to use as a storage for temporary bitcode files. + * The intention is to make the bitcode files available for debugging at various + * stage of the pipeline. + * + * \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. + * + * \since LTO_API_VERSION=18 + */ +extern bool lto_module_is_thinlto(lto_module_t mod); + +/** + * Adds a symbol to the list of 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 code outside of the ThinLTO modules need to be added here. + * + * \since LTO_API_VERSION=18 + */ +extern void thinlto_codegen_add_must_preserve_symbol(thinlto_code_gen_t cg, + const char *name, + int length); + +/** + * Adds a symbol to the list of global symbols that are cross-referenced between + * ThinLTO files. If the ThinLTO CodeGenerator can ensure that every + * references from a ThinLTO module to this symbol is optimized away, then + * the symbol can be discarded. + * + * \since LTO_API_VERSION=18 + */ +extern void thinlto_codegen_add_cross_referenced_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 @@ -65,6 +65,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,233 @@ +//===-ThinLTOCodeGenerator.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 provides 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 FunctionInfoIndex; +class LLVMContext; +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. +/// The ThinLTOCodeGenerator is not intended to be reuse for multiple +/// compilation: the model is that the client adds modules to the generator and +/// ask to perform the ThinLTO optimizations / codegen, and finally destroys the +/// codegenerator. +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 will be optimized away if it is + * inlined into every usage. + */ + void preserveSymbol(StringRef Name); + + /** + * Adds to a list of all global symbols that are cross-referenced between + * ThinLTO files. If the ThinLTO CodeGenerator can ensure that every + * references from a ThinLTO module to this symbol is optimized away, then + * the symbol can be discarded. + */ + void crossReferenceSymbol(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; + } + + /** + * \defgroup Options setters + * @{ + */ + + /** + * \defgroup Cache controlling options + * + * These entry points control the ThinLTO cache. The cache is intended to + * support incremental build, and thus needs to be persistent accross build. + * The client enabled the cache by supplying a path to an existing directory. + * The code generator will use this to store objects files that may be reused + * during a subsequent build. + * To avoid filling the disk space, a few knobs are provided: + * - The pruning interval limit the frequency at which the garbage collector + * will try to scan the cache directory to prune it from expired entries. + * Setting to -1 disable the pruning (default). + * - The pruning expiration time indicates to the garbage collector how old + *an + * entry needs to be to be removed. + * - Finally, the garbage collector can be instructed to prune the cache till + * the occupied space goes below a threshold. + * @{ + */ + + struct CachingOptions { + std::string Path; + int PruningInterval = -1; // seconds, -1 to disable pruning + unsigned int Expiration; // seconds. + unsigned MaxPercentageOfAvailableSpace; // percentage. + }; + + /// Provide a path to a directory where to store the cached files for + /// incremental build. + void setCacheDir(std::string Path) { CacheOptions.Path = std::move(Path); } + + /// Cache policy: interval (seconds) between two prune of the cache. Set to a + /// negative value (default) to disable pruning. + void setCachePruningInterval(int Interval) { + CacheOptions.PruningInterval = Interval; + } + + /// Cache policy: expiration (in seconds) for an entry. + void setCacheEntryExpiration(unsigned Expiration) { + CacheOptions.Expiration = Expiration; + } + + /** + * Sets the maximum cache size that can be persistent across build, in term of + * percentage of the available space on the the disk. Set to 100 to indicate + * no limit, 50 to indicate that the cache size will not be left over the + * available space. A value over 100 will be reduced to 100. + * + * The formula looks like: + * AvailableSpace = FreeSpace + ExistingCacheSize + * NewCacheSize = (100*P)/AvailableSpace + */ + void setMaxCacheSizeRelativeToAvailableSpace(unsigned Percentage) { + CacheOptions.MaxPercentageOfAvailableSpace = Percentage; + } + + /**@}*/ + + /// Set the path to a directory where to save temporaries at various stages of + /// the processing. + void setSaveTempsDir(std::string Path) { SaveTempsDir = std::move(Path); } + + /// CPU to use to initialize the TargetMachine + void setCpu(std::string Cpu) { TMBuilder.MCpu = std::move(Cpu); } + + /// Subtarget attributes + void setAttr(std::string MAttr) { TMBuilder.MAttr = std::move(MAttr); } + + /// TargetMachine options + void setTargetOptions(TargetOptions Options) { + TMBuilder.Options = std::move(Options); + } + + /// CodeModel + void setCodePICModel(Reloc::Model Model) { TMBuilder.RelocModel = Model; } + + /// CodeGen optimization level + void setCodeGenOptLevel(CodeGenOpt::Level CGOptLevel) { + TMBuilder.CGOptLevel = CGOptLevel; + } + + /**@}*/ + + /** + * \defgroup Set of APIs to run individual stages in isolation. + * @{ + */ + + /** + * Produce the combined function index from all the bitcode files: + * "thin-link". + */ + std::unique_ptr linkCombinedIndex(); + + /** + * Perform promotion and renaming of exported internal functions. + */ + void promote(Module &Module, FunctionInfoIndex &Index); + + /** + * Perform cross-module importing for the module identified by + * ModuleIdentifier. + */ + void crossModuleImport(Module &Module, FunctionInfoIndex &Index); + + /** + * Perform post-importing ThinLTO optimizations. + */ + void optimize(Module &Module); + + /** + * Perform ThinLTO CodeGen. + */ + std::unique_ptr codegen(Module &Module); + + /**@}*/ + +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 need to be preserved outside of the set of bitcode + /// files. + StringSet<> PreservedSymbols; + + /// Set of symbols that are cross-referenced between bitcode files. + StringSet<> CrossReferencedSymbols; + + /// Control the caching behavior. + CachingOptions CacheOptions; + + /// 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,9 +1,10 @@ add_llvm_library(LLVMLTO LTOModule.cpp LTOCodeGenerator.cpp + ThinLTOCodeGenerator.cpp ADDITIONAL_HEADER_DIRS ${LLVM_MAIN_INCLUDE_DIR}/llvm/LTO - ) +) add_dependencies(LLVMLTO intrinsics_gen) Index: lib/LTO/LLVMBuild.txt =================================================================== --- lib/LTO/LLVMBuild.txt +++ lib/LTO/LLVMBuild.txt @@ -34,3 +34,4 @@ Scalar Support Target + TransformUtils \ No newline at end of file Index: lib/LTO/LTOModule.cpp =================================================================== --- lib/LTO/LTOModule.cpp +++ lib/LTO/LTOModule.cpp @@ -75,6 +75,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,384 @@ +//===-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/ADT/StringExtras.h" +#include "llvm/ADT/Statistic.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/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/ObjCARC.h" +#include "llvm/Transforms/Utils/FunctionImportUtils.h" + +using namespace llvm; + +namespace { + +static cl::opt ThreadCount("threads", + cl::init(std::thread::hardware_concurrency())); + +static void diagnosticHandler(const DiagnosticInfo &DI) { + DiagnosticPrinterRawOStream DP(errs()); + DI.print(DP); + errs() << '\n'; +} + +// Simple helper to load a module from bitcode +static std::unique_ptr +loadModuleFromBuffer(const MemoryBufferRef &Buffer, LLVMContext &Context, + bool Lazy) { + SMDiagnostic Err; + ErrorOr> ModuleOrErr(nullptr); + if (Lazy) { + ModuleOrErr = + getLazyBitcodeModule(MemoryBuffer::getMemBuffer(Buffer, false), Context, + /* ShouldLazyLoadMetadata */ Lazy); + } else { + ModuleOrErr = parseBitcodeFile(Buffer, Context); + } + if (std::error_code EC = ModuleOrErr.getError()) { + Err = SMDiagnostic(Buffer.getBufferIdentifier(), SourceMgr::DK_Error, + EC.message()); + Err.print("ThinLTO", errs()); + report_fatal_error("Can't load module, abort."); + } + return std::move(ModuleOrErr.get()); +} + +// 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. + auto SaveTempPath = TempDir + llvm::utostr(count) + Suffix; + std::error_code EC; + raw_fd_ostream OS(SaveTempPath.str(), EC, sys::fs::F_None); + if (EC) + report_fatal_error(Twine("Failed to open ") + SaveTempPath + + " to save optimized bitcode\n"); + WriteBitcodeToFile(&TheModule, OS, true, false); +} + +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; + } + return ModuleMap; +} + +/// 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 from Module identifier to MemoryBuffer. Used by clients like the + /// FunctionImported to request loading a Module. + StringMap &ModuleMap; + +public: + ModuleLoader(LLVMContext &Context, StringMap &ModuleMap) + : Context(Context), ModuleMap(ModuleMap) {} + + /// Load a module on demand. + std::unique_ptr operator()(StringRef Identifier) { + return loadModuleFromBuffer(ModuleMap[Identifier], Context, /*Lazy*/ true); + } +}; + +static void promoteModule(Module &TheModule, const FunctionInfoIndex &Index) { + if (renameModuleForThinLTO(TheModule, &Index)) + report_fatal_error("renameModuleForThinLTO failed"); +} + +static void crossImportIntoModule(Module &TheModule, + const FunctionInfoIndex &Index, + StringMap &ModuleMap) { + ModuleLoader Loader(TheModule.getContext(), ModuleMap); + FunctionImporter Importer(Index, Loader); + Importer.importFunctions(TheModule); +} + +static void optimizeModule(Module &TheModule, TargetMachine &TM) { + // Populate the PassManager + PassManagerBuilder PMB; + PMB.LibraryInfo = new TargetLibraryInfoImpl(TM.getTargetTriple()); + PMB.Inliner = createFunctionInliningPass(); + // FIXME: should get it from the bitcode? + PMB.OptLevel = 3; + 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); + PM.add(createObjCARCContractPass()); + + PM.run(TheModule); +} + +std::unique_ptr codegenModule(Module &TheModule, + TargetMachine &TM) { + SmallVector OutputBuffer; + + // CodeGen + { + raw_svector_ostream OS(OutputBuffer); + legacy::PassManager PM; + if (TM.addPassesToEmitFile(PM, OS, TargetMachine::CGFT_ObjectFile, + /* DisableVerify */ true)) + report_fatal_error("Failed to setup codegen"); + + // Run codegen now. resulting binary is in OutputBuffer. + PM.run(TheModule); + } + return make_unique(std::move(OutputBuffer)); +} + +static std::unique_ptr +ProcessThinLTOModule(Module &TheModule, const FunctionInfoIndex &Index, + StringMap &ModuleMap, TargetMachine &TM, + ThinLTOCodeGenerator::CachingOptions CacheOptions, + StringRef SaveTempsDir, unsigned count) { + + // Save temps: after IPO. + saveTempBitcode(TheModule, SaveTempsDir, count, ".1.IPO.bc"); + + // "Benchmark"-like optimization: single-source case + bool SingleModule = (ModuleMap.size() == 1); + + if (!SingleModule) { + promoteModule(TheModule, Index); + + // Save temps: after promotion. + saveTempBitcode(TheModule, SaveTempsDir, count, ".2.promoted.bc"); + + crossImportIntoModule(TheModule, Index, ModuleMap); + + // Save temps: after cross-module import. + saveTempBitcode(TheModule, SaveTempsDir, count, ".3.imported.bc"); + } + + optimizeModule(TheModule, TM); + + saveTempBitcode(TheModule, SaveTempsDir, count, ".3.opt.bc"); + + return codegenModule(TheModule, TM); +} + +// 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) { + MemoryBufferRef Buffer(Data, Identifier); + if (Modules.empty()) { + // First module added, so initialize the triple and some options + LLVMContext Context; + Triple TheTriple(getBitcodeTargetTriple(Buffer, Context)); + initTMBuilder(TMBuilder, Triple(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); +} + +void ThinLTOCodeGenerator::crossReferenceSymbol(StringRef Name) { + CrossReferencedSymbols.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: " + ErrMsg); + } + + // 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)); +} + +/** + * Produce the combined function index from all the bitcode files: + * "thin-link". + */ +std::unique_ptr ThinLTOCodeGenerator::linkCombinedIndex() { + 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; +} + +/** + * Perform promotion and renaming of exported internal functions. + */ +void ThinLTOCodeGenerator::promote(Module &TheModule, + FunctionInfoIndex &Index) { + promoteModule(TheModule, Index); +} + +/** + * Perform cross-module importing for the module identified by ModuleIdentifier. + */ +void ThinLTOCodeGenerator::crossModuleImport(Module &TheModule, + FunctionInfoIndex &Index) { + auto ModuleMap = generateModuleMap(Modules); + crossImportIntoModule(TheModule, Index, ModuleMap); +} + +/** + * Perform post-importing ThinLTO optimizations. + */ +void ThinLTOCodeGenerator::optimize(Module &TheModule) { + initTMBuilder(TMBuilder, Triple(TheModule.getTargetTriple())); + optimizeModule(TheModule, *TMBuilder.create()); +} + +/** + * Perform ThinLTO CodeGen. + */ +std::unique_ptr ThinLTOCodeGenerator::codegen(Module &TheModule) { + initTMBuilder(TMBuilder, Triple(TheModule.getTargetTriple())); + return codegenModule(TheModule, *TMBuilder.create()); +} + +// Main entry point for the ThinLTO processing +void ThinLTOCodeGenerator::run() { + // 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"); + WriteFunctionSummaryToFile(*Index, OS); + } + + // Prepare the resulting object vector + assert(ProducedBinaries.empty() && "The generator should not be reused"); + ProducedBinaries.resize(Modules.size()); + + // Prepare the module map. + auto ModuleMap = generateModuleMap(Modules); + + // Parallel optimizer + codegen + { + ThreadPool Pool(ThreadCount); + int count = 0; + for (auto &ModuleBuffer : Modules) { + Pool.async([&](int count) { + LLVMContext Context; + + // Parse module now + auto TheModule = loadModuleFromBuffer(ModuleBuffer, Context, false); + + // Save temps: original file. + if (!SaveTempsDir.empty()) { + saveTempBitcode(*TheModule, SaveTempsDir, count, ".0.original.bc"); + } + + ProducedBinaries[count] = ProcessThinLTOModule( + *TheModule, *Index, ModuleMap, *TMBuilder.create(), CacheOptions, + SaveTempsDir, count); + }, count); + count++; + } + } + + // If statistics were requested, print them out now. + if (llvm::AreStatisticsEnabled()) + llvm::PrintStatistics(); +} Index: test/ThinLTO/Inputs/funcimport.ll =================================================================== --- /dev/null +++ test/ThinLTO/Inputs/funcimport.ll @@ -0,0 +1,32 @@ +target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-macosx10.11.0" + + +define i32 @main() #0 { +entry: + call void (...) @weakalias() + call void (...) @analias() + %call = call i32 (...) @referencestatics() + %call1 = call i32 (...) @referenceglobals() + %call2 = call i32 (...) @referencecommon() + call void (...) @setfuncptr() + call void (...) @callfuncptr() + call void (...) @callweakfunc() + ret i32 0 +} + +declare void @weakalias(...) #1 + +declare void @analias(...) #1 + +declare i32 @referencestatics(...) #1 + +declare i32 @referenceglobals(...) #1 + +declare i32 @referencecommon(...) #1 + +declare void @setfuncptr(...) #1 + +declare void @callfuncptr(...) #1 + +declare void @callweakfunc(...) #1 Index: test/ThinLTO/funcimport.ll =================================================================== --- /dev/null +++ test/ThinLTO/funcimport.ll @@ -0,0 +1,139 @@ +; Do setup work for all below tests: generate bitcode and combined index +; RUN: llvm-as -function-summary %s -o %t.bc +; RUN: llvm-as -function-summary %p/Inputs/funcimport.ll -o %t2.bc +; RUN: llvm-lto -thinlto-action=thinlink -o %t3.bc %t.bc %t2.bc + +; Ensure statics are promoted/renamed correctly from this file (all but +; constant variable need promotion). +; RUN: llvm-lto -thinlto-action=promote %t.bc -thinlto-index=%t3.bc -o - | llvm-dis -o - | FileCheck %s --check-prefix=EXPORTSTATIC +; EXPORTSTATIC-DAG: @staticvar.llvm.0 = hidden global +; EXPORTSTATIC-DAG: @staticconstvar = internal unnamed_addr constant +; EXPORTSTATIC-DAG: @P.llvm.0 = hidden global void ()* null +; EXPORTSTATIC-DAG: define hidden i32 @staticfunc.llvm.0 +; EXPORTSTATIC-DAG: define hidden void @staticfunc2.llvm.0 + +; Ensure that both weak alias to an imported function and strong alias to a +; non-imported function are correctly turned into declarations. +; Also ensures that alias to a linkonce function is turned into a declaration +; and that the associated linkonce function is not in the output, as it is +; lazily linked and never referenced/materialized. +; RUN: llvm-lto -thinlto-action=import %t2.bc -thinlto-index=%t3.bc -o - | llvm-dis -o - | FileCheck %s --check-prefix=IMPORTGLOB1 +; IMPORTGLOB1-DAG: define available_externally void @globalfunc1 +; IMPORTGLOB1-DAG: declare void @weakalias +; IMPORTGLOB1-DAG: declare void @analias +; IMPORTGLOB1-NOT: @linkoncealias +; IMPORTGLOB1-NOT: @linkoncefunc +; IMPORTGLOB1-NOT: declare void @globalfunc2 + +; Verify that the optimizer run +; RUN: llvm-lto -thinlto-action=optimize %t2.bc -o - | llvm-dis -o - | FileCheck %s --check-prefix=OPTIMIZED +; OPTIMIZED: define i32 @main() + +; Verify that the codegen run +; RUN: llvm-lto -thinlto-action=codegen %t2.bc -o - | llvm-nm -o - | FileCheck %s --check-prefix=CODEGEN +; CODEGEN: T _main + +; Verify that all run together +; RUN: llvm-lto -thinlto-action=run %t2.bc %t.bc +; RUN: llvm-nm -o - < %t.bc.thinlto.o | FileCheck %s --check-prefix=ALL +; RUN: llvm-nm -o - < %t2.bc.thinlto.o | FileCheck %s --check-prefix=ALL2 +; ALL: T _callfuncptr +; ALL2: T _main + +target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-macosx10.11.0" + +@globalvar_in_section = global i32 1, align 4 +@globalvar = global i32 1, align 4 +@staticvar = internal global i32 1, align 4 +@staticvar2 = internal global i32 1, align 4 +@staticconstvar = internal unnamed_addr constant [2 x i32] [i32 10, i32 20], align 4 +@commonvar = common global i32 0, align 4 +@P = internal global void ()* null, align 8 + +@weakalias = weak alias void (...), bitcast (void ()* @globalfunc1 to void (...)*) +@analias = alias void (...), bitcast (void ()* @globalfunc2 to void (...)*) +@linkoncealias = alias void (...), bitcast (void ()* @linkoncefunc to void (...)*) + +define void @globalfunc1() #0 { +entry: + ret void +} + +define void @globalfunc2() #0 { +entry: + ret void +} + +define linkonce_odr void @linkoncefunc() #0 { +entry: + ret void +} + +define i32 @referencestatics(i32 %i) #0 { +entry: + %i.addr = alloca i32, align 4 + store i32 %i, i32* %i.addr, align 4 + %call = call i32 @staticfunc() + %0 = load i32, i32* @staticvar, align 4 + %add = add nsw i32 %call, %0 + %1 = load i32, i32* %i.addr, align 4 + %idxprom = sext i32 %1 to i64 + %arrayidx = getelementptr inbounds [2 x i32], [2 x i32]* @staticconstvar, i64 0, i64 %idxprom + %2 = load i32, i32* %arrayidx, align 4 + %add1 = add nsw i32 %add, %2 + ret i32 %add1 +} + +define i32 @referenceglobals(i32 %i) #0 { +entry: + %i.addr = alloca i32, align 4 + store i32 %i, i32* %i.addr, align 4 + call void @globalfunc1() + %0 = load i32, i32* @globalvar, align 4 + ret i32 %0 +} + +define i32 @referencecommon(i32 %i) #0 { +entry: + %i.addr = alloca i32, align 4 + store i32 %i, i32* %i.addr, align 4 + %0 = load i32, i32* @commonvar, align 4 + ret i32 %0 +} + +define void @setfuncptr() #0 { +entry: + store void ()* @staticfunc2, void ()** @P, align 8 + ret void +} + +define void @callfuncptr() #0 { +entry: + %0 = load void ()*, void ()** @P, align 8 + call void %0() + ret void +} + +@weakvar = weak global i32 1, align 4 +define weak void @weakfunc() #0 { +entry: + ret void +} + +define void @callweakfunc() #0 { +entry: + call void @weakfunc() + ret void +} + +define internal i32 @staticfunc() #0 { +entry: + ret i32 1 +} + +define internal void @staticfunc2() #0 { +entry: + %0 = load i32, i32* @staticvar2, align 4 + ret void +} Index: tools/llvm-lto/llvm-lto.cpp =================================================================== --- tools/llvm-lto/llvm-lto.cpp +++ tools/llvm-lto/llvm-lto.cpp @@ -17,7 +17,9 @@ #include "llvm/CodeGen/CommandFlags.h" #include "llvm/IR/DiagnosticPrinter.h" #include "llvm/IR/LLVMContext.h" +#include "llvm/IRReader/IRReader.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" @@ -25,6 +27,7 @@ #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/Signals.h" +#include "llvm/Support/SourceMgr.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/ToolOutputFile.h" #include "llvm/Support/raw_ostream.h" @@ -64,6 +67,36 @@ ThinLTO("thinlto", cl::init(false), cl::desc("Only write combined global index for ThinLTO backends")); +enum ThinLTOModes { + THINLINK, + THINPROMOTE, + THINIMPORT, + THINOPT, + THINCODEGEN, + THINALL +}; + +cl::opt ThinLTOMode( + "thinlto-action", cl::desc("Perform a single ThinLTO stage:"), + cl::values( + clEnumValN( + THINLINK, "thinlink", + "ThinLink: produces the index by linking only the summaries."), + clEnumValN(THINPROMOTE, "promote", + "Perform pre-import promotion (requires -thinlto-index)."), + clEnumValN(THINIMPORT, "import", "Perform both promotion and " + "cross-module importing (requires " + "-thinlto-index)."), + clEnumValN(THINOPT, "optimize", "Perform ThinLTO optimizations."), + clEnumValN(THINCODEGEN, "codegen", "CodeGen (expected to match llc)"), + clEnumValN(THINALL, "run", "Perform ThinLTO end-to-end"), + clEnumValEnd)); + +static cl::opt + ThinLTOIndex("thinlto-index", + cl::desc("Provide the index produced by a ThinLink, required " + "to perform the promotion and/or importing.")); + static cl::opt SaveModuleFile("save-merged-module", cl::init(false), cl::desc("Write merged LTO module to file before CodeGen")); @@ -241,6 +274,255 @@ OS.close(); } +namespace thinlto { + +std::vector> +loadAllFilesForIndex(const FunctionInfoIndex &Index) { + std::vector> InputBuffers; + + for (auto &ModPath : Index.modPathStringEntries()) { + const auto &Filename = ModPath.first(); + auto CurrentActivity = "loading file '" + Filename + "'"; + auto InputOrErr = MemoryBuffer::getFile(Filename); + error(InputOrErr, "error " + CurrentActivity); + InputBuffers.push_back(std::move(*InputOrErr)); + } + return InputBuffers; +} + +std::unique_ptr loadCombinedIndex() { + if (ThinLTOIndex.empty()) + report_fatal_error("Missing -thinlto-index for ThinLTO promotion stage"); + auto CurrentActivity = "loading file '" + ThinLTOIndex + "'"; + ErrorOr> IndexOrErr = + llvm::getFunctionIndexForFile(ThinLTOIndex, diagnosticHandler); + error(IndexOrErr, "error " + CurrentActivity); + return std::move(IndexOrErr.get()); +} + +static std::unique_ptr loadModule(StringRef Filename, + LLVMContext &Ctx) { + SMDiagnostic Err; + std::unique_ptr M(parseIRFile(Filename, Err, Ctx)); + if (!M) { + Err.print("llvm-lto", errs()); + report_fatal_error("Can't load module for file " + Filename); + } + return M; +} + +static void writeModuleToFile(Module &TheModule, StringRef Filename) { + std::error_code EC; + raw_fd_ostream OS(Filename, EC, sys::fs::OpenFlags::F_None); + error(EC, "error opening the file '" + Filename + "'"); + WriteBitcodeToFile(&TheModule, OS, true, false); +} + +class ThinLTOProcessing { +public: + ThinLTOCodeGenerator ThinGenerator; + + ThinLTOProcessing(const TargetOptions &Options) { + ThinGenerator.setCodePICModel(RelocModel); + ThinGenerator.setTargetOptions(Options); + } + + void run() { + switch (ThinLTOMode) { + case THINLINK: + return thinLink(); + case THINPROMOTE: + return promote(); + case THINIMPORT: + return import(); + case THINOPT: + return optimize(); + case THINCODEGEN: + return codegen(); + case THINALL: + return runAll(); + } + } + +private: + /// Load the input files, create the combined index, and write it out. + void thinLink() { + // Perform "ThinLink": just produce the index + if (OutputFilename.empty()) + report_fatal_error( + "OutputFilename is necessary to store the combined index.\n"); + + LLVMContext Ctx; + std::vector> InputBuffers; + for (unsigned i = 0; i < InputFilenames.size(); ++i) { + auto &Filename = InputFilenames[i]; + StringRef CurrentActivity = "loading file '" + Filename + "'"; + auto InputOrErr = MemoryBuffer::getFile(Filename); + error(InputOrErr, "error " + CurrentActivity); + InputBuffers.push_back(std::move(*InputOrErr)); + ThinGenerator.addModule(Filename, InputBuffers.back()->getBuffer()); + } + + auto CombinedIndex = ThinGenerator.linkCombinedIndex(); + std::error_code EC; + raw_fd_ostream OS(OutputFilename, EC, sys::fs::OpenFlags::F_None); + error(EC, "error opening the file '" + OutputFilename + "'"); + WriteFunctionSummaryToFile(*CombinedIndex, OS); + return; + } + + /// Load the combined index from disk, then load every file referenced by + /// the index and add them to the generator, finally perform the promotion + /// on the files mentioned on the command line (these must match the index + /// content). + void promote() { + if (InputFilenames.size() != 1 && !OutputFilename.empty()) + report_fatal_error("Can't handle a single output filename and multiple " + "input files, do not provide an output filename and " + "the output files will be suffixed from the input " + "ones."); + + auto Index = loadCombinedIndex(); + for (auto &Filename : InputFilenames) { + LLVMContext Ctx; + auto TheModule = loadModule(Filename, Ctx); + + ThinGenerator.promote(*TheModule, *Index); + + std::string OutputName = OutputFilename; + if (OutputName.empty()) { + OutputName = Filename + ".thinlto.promoted.bc"; + } + writeModuleToFile(*TheModule, OutputName); + } + } + + /// Load the combined index from disk, then load every file referenced by + /// the index and add them to the generator, then performs the promotion and + /// cross module importing on the files mentioned on the command line + /// (these must match the index content). + void import() { + if (InputFilenames.size() != 1 && !OutputFilename.empty()) + report_fatal_error("Can't handle a single output filename and multiple " + "input files, do not provide an output filename and " + "the output files will be suffixed from the input " + "ones."); + + auto Index = loadCombinedIndex(); + auto InputBuffers = loadAllFilesForIndex(*Index); + for (auto &MemBuffer : InputBuffers) + ThinGenerator.addModule(MemBuffer->getBufferIdentifier(), + MemBuffer->getBuffer()); + + for (auto &Filename : InputFilenames) { + LLVMContext Ctx; + auto TheModule = loadModule(Filename, Ctx); + + ThinGenerator.crossModuleImport(*TheModule, *Index); + + std::string OutputName = OutputFilename; + if (OutputName.empty()) { + OutputName = Filename + ".thinlto.imported.bc"; + } + writeModuleToFile(*TheModule, OutputName); + } + } + + void optimize() { + if (InputFilenames.size() != 1 && !OutputFilename.empty()) + report_fatal_error("Can't handle a single output filename and multiple " + "input files, do not provide an output filename and " + "the output files will be suffixed from the input " + "ones."); + if (!ThinLTOIndex.empty()) + errs() << "Warning: -thinlto-index ignored for optimize stage"; + + for (auto &Filename : InputFilenames) { + LLVMContext Ctx; + auto TheModule = loadModule(Filename, Ctx); + + ThinGenerator.optimize(*TheModule); + + std::string OutputName = OutputFilename; + if (OutputName.empty()) { + OutputName = Filename + ".thinlto.imported.bc"; + } + writeModuleToFile(*TheModule, OutputName); + } + } + + void codegen() { + if (InputFilenames.size() != 1 && !OutputFilename.empty()) + report_fatal_error("Can't handle a single output filename and multiple " + "input files, do not provide an output filename and " + "the output files will be suffixed from the input " + "ones."); + if (!ThinLTOIndex.empty()) + errs() << "Warning: -thinlto-index ignored for codegen stage"; + + for (auto &Filename : InputFilenames) { + LLVMContext Ctx; + auto TheModule = loadModule(Filename, Ctx); + + auto Buffer = ThinGenerator.codegen(*TheModule); + std::string OutputName = OutputFilename; + if (OutputName.empty()) { + OutputName = Filename + ".thinlto.o"; + } + if (OutputName == "-") { + outs() << Buffer->getBuffer(); + return; + } + + std::error_code EC; + raw_fd_ostream OS(OutputName, EC, sys::fs::OpenFlags::F_None); + error(EC, "error opening the file '" + OutputName + "'"); + OS << Buffer->getBuffer(); + } + } + + /// Full ThinLTO process + void runAll() { + if (!OutputFilename.empty()) + report_fatal_error("Do not provide an output filename for ThinLTO " + " processing, the output files will be suffixed from " + "the input ones."); + + if (!ThinLTOIndex.empty()) + errs() << "Warning: -thinlto-index ignored for full ThinLTO process"; + + LLVMContext Ctx; + std::vector> InputBuffers; + for (unsigned i = 0; i < InputFilenames.size(); ++i) { + auto &Filename = InputFilenames[i]; + StringRef CurrentActivity = "loading file '" + Filename + "'"; + auto InputOrErr = MemoryBuffer::getFile(Filename); + error(InputOrErr, "error " + CurrentActivity); + InputBuffers.push_back(std::move(*InputOrErr)); + ThinGenerator.addModule(Filename, InputBuffers.back()->getBuffer()); + } + + ThinGenerator.run(); + + auto &Binaries = ThinGenerator.getProducedBinaries(); + if (Binaries.size() != InputFilenames.size()) + report_fatal_error("Number of output objects does not match the number " + "of inputs"); + + for (unsigned BufID = 0; BufID < Binaries.size(); ++BufID) { + auto OutputName = InputFilenames[BufID] + ".thinlto.o"; + std::error_code EC; + raw_fd_ostream OS(OutputName, EC, sys::fs::OpenFlags::F_None); + error(EC, "error opening the file '" + OutputName + "'"); + OS << Binaries[BufID]->getBuffer(); + } + } + + /// Load the combined index from disk, then load every file referenced by +}; + +} // namespace thinlto + int main(int argc, char **argv) { // Print a stack trace if we signal out. sys::PrintStackTraceOnErrorSignal(); @@ -266,6 +548,14 @@ return 0; } + if (ThinLTOMode.getNumOccurrences()) { + if (ThinLTOMode.getNumOccurrences() > 1) + report_fatal_error("You can't specify more than one -thinlto-action"); + thinlto::ThinLTOProcessing ThinLTOProcessor(Options); + ThinLTOProcessor.run(); + 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. @@ -440,3 +442,106 @@ 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_add_cross_referenced_symbol(thinlto_code_gen_t cg, + const char *Name, int Length) { + unwrap(cg)->crossReferenceSymbol(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_cache_pruning_interval(thinlto_code_gen_t cg, + int interval) { + return unwrap(cg)->setCachePruningInterval(interval); +} + +void thinlto_codegen_set_cache_entry_expiration(thinlto_code_gen_t cg, + unsigned expiration) { + return unwrap(cg)->setCacheEntryExpiration(expiration); +} + +void thinlto_codegen_set_final_cache_size_relative_to_available_space( + thinlto_code_gen_t cg, unsigned Percentage) { + return unwrap(cg)->setMaxCacheSizeRelativeToAvailableSpace(Percentage); +} + +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,20 @@ 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_cache_pruning_interval +thinlto_codegen_set_cache_entry_expiration +thinlto_codegen_set_savetemps_dir +thinlto_codegen_set_cpu +thinlto_debug_options +lto_module_is_thinlto +thinlto_codegen_add_must_preserve_symbol +thinlto_codegen_add_cross_referenced_symbol +thinlto_codegen_set_final_cache_size_relative_to_available_space \ No newline at end of file