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 @@ -252,7 +255,6 @@ extern void lto_module_set_target_triple(lto_module_t mod, const char *triple); - /** * Returns the number of symbols in the object module. * @@ -548,6 +550,103 @@ lto_codegen_set_should_embed_uselists(lto_code_gen_t cg, lto_bool_t ShouldEmbedUselists); +/** + * @} + * @defgroup LLVMCLTO ThinLTO + * @ingroup LLVMC + * + * @{ + */ + +/** + * Type to wrap returned objects to the linker for ThinLTO. + * + * \since LTO_API_VERSION=18 + */ +typedef struct { + void *Buffer; + size_t Size; +} LTOObjectBuffer; + +/** + * Type to wrap returned objects to the linker for ThinLTO. + * + * \since LTO_API_VERSION=18 + */ +typedef struct { + LTOObjectBuffer *Objects; + size_t Size; +} LTOObjectBuffers; + +/** + * Instantiates a ThinLTO code generator. + * Returns NULL on error (check lto_get_error_message() for details). + * + * \since prior to LTO_API_VERSION=18 + */ +thinlto_code_gen_t thinlto_create_codegen(); + +/** + * Add a module to a ThinLTO code generator. Identifier has to be unique among + * all the modules in a code generator. + * + * \since prior to LTO_API_VERSION=18 + */ +void thinlto_codegen_add_module(thinlto_code_gen_t cg, const char *Identifier, + const char *Data, int Length); + +/** + * 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 cpu to generate code for. + * + * \since LTO_API_VERSION=18 + */ +extern void thinlto_codegen_set_cpu(thinlto_code_gen_t cg, const char *cpu); + +/** + * Optimize and codegen all the modules added to the codegenerator using + * ThinLTO. + * + * \since prior to LTO_API_VERSION=18 + */ +LTOObjectBuffers thinlto_codegen_process(thinlto_code_gen_t cg); + +/** + * Parse -mllvm style debug options. + * + * \since prior to LTO_API_VERSION=18 + */ +void thinlto_debug_options(const char *const *options, int number); + +/** + * Test if a module was produced for ThinLTO linking. + */ +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. + */ +void thinlto_codegen_add_must_preserve_symbol(thinlto_code_gen_t cg, + const char *Name, int Length); + #ifdef __cplusplus } #endif 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,87 @@ +//===-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; +}; + +class ThinLTOCodeGenerator { +public: + /// Add given module. + 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. + * \return a LTOObjectBuffers structure that contains the object files in + * memory. + */ + LTOObjectBuffers run(); + + // Option setters + void setCacheDir(std::string Path) { CacheDir = 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: + void setTriple(Triple TheTriple) { + TMBuilder.TheTriple = std::move(TheTriple); + } + TargetMachineBuilder TMBuilder; + + std::vector Modules; + StringSet<> PreservedSymbols; + /// Cache for incremental build. + std::string CacheDir; +}; +} +#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,450 @@ +//===-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/Bitcode/ReaderWriter.h" +#include "llvm/Support/ThreadPool.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/Target/TargetMachine.h" +#include "llvm/Transforms/IPO.h" +#include "llvm/Transforms/IPO/FunctionImport.h" +#include "llvm/Transforms/IPO/PassManagerBuilder.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); // FIXME Should it be lazy? + 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 LTOObjectBuffer ProcessThinLTOModule( + std::unique_ptr TheModule, const FunctionInfoIndex &Index, + StringMap &PathToModules, + std::unique_ptr TM, const std::string &CacheDir) { + // 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"); + } + auto MemBuffer = FileLoaded->release(); // Leak + return LTOObjectBuffer{(void *)MemBuffer->getBufferStart(), + MemBuffer->getBufferSize()}; + } + // Cache miss, move on + } + + auto OutputBuffer = new SmallVector(); + { + // Optimize and codegen + PassManagerBuilder PMB; + PMB.LibraryInfo = new TargetLibraryInfoImpl(TM->getTargetTriple()); + PMB.Inliner = createFunctionInliningPass(); + // PMB.OptLevel = OptLevel; + PMB.VerifyInput = false; + PMB.VerifyOutput = false; + PMB.SLPVectorize = true; + legacy::PassManager PM; + PMB.populateLTOPassManager(PM); + + raw_svector_ostream OS(*OutputBuffer); + if (TM->addPassesToEmitFile(PM, OS, TargetMachine::CGFT_ObjectFile, + /* DisableVerify */ true)) + report_fatal_error("Failed to setup codegen"); + + 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 LTOObjectBuffer{OutputBuffer->begin(), OutputBuffer->size()}; +} + +/// 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 IsFirstDefinition = [&](GlobalValue &GV) { + auto It = Index.findFunctionInfoList(GV.getName()); + assert(It != Index.end()); + return ((*It->second.begin())->functionSummary()->modulePath() == + TheModule.getModuleIdentifier()); + }; + + auto HasMultipleCopies = [&](GlobalValue &GV) { + return Index.findFunctionInfoList(GV.getName())->second.size() > 1; + }; + + auto Linkage = GV.getLinkage(); + if (Linkage == GlobalValue::AvailableExternallyLinkage) { + Keep.insert(GV.getName()); + return false; + } + 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 (IsFirstDefinition(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 (IsFirstDefinition(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 && !IsFirstDefinition(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 && !IsFirstDefinition(GV)) + ; // We need to have "IsFirstWeakODRDefinition() instead. + // In case of extern template the first definition might be + // an "available externally" one. + // GV.setLinkage(GlobalValue::AvailableExternallyLinkage); + Keep.insert(GV.getName()); + return false; + } + 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()); + // 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"; + } + + setTriple(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 +LTOObjectBuffers ThinLTOCodeGenerator::run() { + // Sequential linking phase + auto Index = linkCombinedIndex(Modules); + + // Prepare the resulting object to return to the linker + LTOObjectBuffers Result; + Result.Objects = + (LTOObjectBuffer *)malloc(sizeof(LTOObjectBuffer) * Modules.size()); + Result.Size = 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); + + Result.Objects[count] = + ProcessThinLTOModule(std::move(TheModule), *Index, PathToModules, + TMBuilder.create(), CacheDir); + }, count); + count++; + } + } + return Result; +} 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. @@ -276,6 +278,10 @@ return unwrap(mod)->setTargetTriple(triple); } +void lto_module_set_preserved(lto_module_t mod, const char *symbol) { + return; // FIXME unwrap(mod)->addPreservedSymbols(symbol); +} + unsigned int lto_module_get_num_symbols(lto_module_t mod) { return unwrap(mod)->getSymbolCount(); } @@ -434,3 +440,70 @@ 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_add_module(thinlto_code_gen_t cg, const char *Identifier, + const char *Data, int Length) { + unwrap(cg)->addModule(Identifier, StringRef(Data, Length)); +} + +LTOObjectBuffers thinlto_codegen_process(thinlto_code_gen_t cg) { + return unwrap(cg)->run(); +} + +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); +} + +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,12 @@ LLVMDisasmDispose LLVMDisasmInstruction LLVMSetDisasmOptions +lto_module_is_thinlto +thinlto_codegen_add_module +thinlto_codegen_add_must_preserve_symbol +thinlto_codegen_process +thinlto_codegen_set_cache_dir +thinlto_codegen_set_cpu +thinlto_codegen_set_pic_model +thinlto_create_codegen +thinlto_debug_options