//===- llvm-lto: a simple command-line program to link modules with LTO ---===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This program takes in a list of bitcode files, links them, performs link-time // optimization, and outputs an object file. // //===----------------------------------------------------------------------===// #include "llvm-c/lto.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSet.h" #include "llvm/ADT/Twine.h" #include "llvm/Bitcode/BitcodeReader.h" #include "llvm/Bitcode/BitcodeWriter.h" #include "llvm/CodeGen/CommandFlags.h" #include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/DiagnosticPrinter.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" #include "llvm/IR/ModuleSummaryIndex.h" #include "llvm/IR/Verifier.h" #include "llvm/IRReader/IRReader.h" #include "llvm/LTO/legacy/LTOCodeGenerator.h" #include "llvm/LTO/legacy/LTOModule.h" #include "llvm/LTO/legacy/ThinLTOCodeGenerator.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/Casting.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Error.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/ToolOutputFile.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Target/TargetOptions.h" #include #include #include #include #include #include #include #include #include #include #include #include using namespace llvm; static codegen::RegisterCodeGenFlags CGF; static cl::opt OptLevel("O", cl::desc("Optimization level. [-O0, -O1, -O2, or -O3] " "(default = '-O2')"), cl::Prefix, cl::ZeroOrMore, cl::init('2')); static cl::opt IndexStats("thinlto-index-stats", cl::desc("Print statistic for the index in every input files"), cl::init(false)); static cl::opt DisableVerify( "disable-verify", cl::init(false), cl::desc("Do not run the verifier during the optimization pipeline")); static cl::opt EnableFreestanding( "lto-freestanding", cl::init(false), cl::desc("Enable Freestanding (disable builtins / TLI) during LTO")); static cl::opt UseDiagnosticHandler( "use-diagnostic-handler", cl::init(false), cl::desc("Use a diagnostic handler to test the handler interface")); static cl::opt ThinLTO("thinlto", cl::init(false), cl::desc("Only write combined global index for ThinLTO backends")); enum ThinLTOModes { THINLINK, THINDISTRIBUTE, THINEMITIMPORTS, THINPROMOTE, THINIMPORT, THININTERNALIZE, 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(THINDISTRIBUTE, "distributedindexes", "Produces individual indexes for distributed backends."), clEnumValN(THINEMITIMPORTS, "emitimports", "Emit imports files for distributed backends."), clEnumValN(THINPROMOTE, "promote", "Perform pre-import promotion (requires -thinlto-index)."), clEnumValN(THINIMPORT, "import", "Perform both promotion and " "cross-module importing (requires " "-thinlto-index)."), clEnumValN(THININTERNALIZE, "internalize", "Perform internalization driven by -exported-symbol " "(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"))); 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 ThinLTOPrefixReplace( "thinlto-prefix-replace", cl::desc("Control where files for distributed backends are " "created. Expects 'oldprefix;newprefix' and if path " "prefix of output file is oldprefix it will be " "replaced with newprefix.")); static cl::opt ThinLTOModuleId( "thinlto-module-id", cl::desc("For the module ID for the file to process, useful to " "match what is in the index.")); static cl::opt ThinLTOCacheDir("thinlto-cache-dir", cl::desc("Enable ThinLTO caching.")); static cl::opt ThinLTOCachePruningInterval("thinlto-cache-pruning-interval", cl::init(1200), cl::desc("Set ThinLTO cache pruning interval.")); static cl::opt ThinLTOCacheMaxSizeBytes( "thinlto-cache-max-size-bytes", cl::desc("Set ThinLTO cache pruning directory maximum size in bytes.")); static cl::opt ThinLTOCacheMaxSizeFiles("thinlto-cache-max-size-files", cl::init(1000000), cl::desc("Set ThinLTO cache pruning directory maximum number of files.")); static cl::opt ThinLTOCacheEntryExpiration("thinlto-cache-entry-expiration", cl::init(604800) /* 1w */, cl::desc("Set ThinLTO cache entry expiration time.")); static cl::opt ThinLTOSaveTempsPrefix( "thinlto-save-temps", cl::desc("Save ThinLTO temp files using filenames created by adding " "suffixes to the given file path prefix.")); static cl::opt ThinLTOGeneratedObjectsDir( "thinlto-save-objects", cl::desc("Save ThinLTO generated object files using filenames created in " "the given directory.")); static cl::opt SaveLinkedModuleFile( "save-linked-module", cl::init(false), cl::desc("Write linked LTO module to file before optimize")); static cl::opt SaveModuleFile("save-merged-module", cl::init(false), cl::desc("Write merged LTO module to file before CodeGen")); static cl::list InputFilenames(cl::Positional, cl::OneOrMore, cl::desc("")); static cl::opt OutputFilename("o", cl::init(""), cl::desc("Override output filename"), cl::value_desc("filename")); static cl::list ExportedSymbols( "exported-symbol", cl::desc("List of symbols to export from the resulting object file"), cl::ZeroOrMore); static cl::list DSOSymbols("dso-symbol", cl::desc("Symbol to put in the symtab in the resulting dso"), cl::ZeroOrMore); static cl::opt ListSymbolsOnly( "list-symbols-only", cl::init(false), cl::desc("Instead of running LTO, list the symbols in each IR file")); static cl::opt ListDependentLibrariesOnly( "list-dependent-libraries-only", cl::init(false), cl::desc("Instead of running LTO, list the dependent libraries in each IR file")); static cl::opt SetMergedModule( "set-merged-module", cl::init(false), cl::desc("Use the first input module as the merged module")); static cl::opt Parallelism("j", cl::Prefix, cl::init(1), cl::desc("Number of backend threads")); static cl::opt RestoreGlobalsLinkage( "restore-linkage", cl::init(false), cl::desc("Restore original linkage of globals prior to CodeGen")); static cl::opt CheckHasObjC( "check-for-objc", cl::init(false), cl::desc("Only check if the module has objective-C defined in it")); static cl::opt PrintMachOCPUOnly( "print-macho-cpu-only", cl::init(false), cl::desc("Instead of running LTO, print the mach-o cpu in each IR file")); namespace { struct ModuleInfo { std::vector CanBeHidden; }; } // end anonymous namespace static void handleDiagnostics(lto_codegen_diagnostic_severity_t Severity, const char *Msg, void *) { errs() << "llvm-lto: "; switch (Severity) { case LTO_DS_NOTE: errs() << "note: "; break; case LTO_DS_REMARK: errs() << "remark: "; break; case LTO_DS_ERROR: errs() << "error: "; break; case LTO_DS_WARNING: errs() << "warning: "; break; } errs() << Msg << "\n"; } static std::string CurrentActivity; namespace { struct LLVMLTODiagnosticHandler : public DiagnosticHandler { bool handleDiagnostics(const DiagnosticInfo &DI) override { raw_ostream &OS = errs(); OS << "llvm-lto: "; switch (DI.getSeverity()) { case DS_Error: OS << "error"; break; case DS_Warning: OS << "warning"; break; case DS_Remark: OS << "remark"; break; case DS_Note: OS << "note"; break; } if (!CurrentActivity.empty()) OS << ' ' << CurrentActivity; OS << ": "; DiagnosticPrinterRawOStream DP(OS); DI.print(DP); OS << '\n'; if (DI.getSeverity() == DS_Error) exit(1); return true; } }; } static void error(const Twine &Msg) { errs() << "llvm-lto: " << Msg << '\n'; exit(1); } static void error(std::error_code EC, const Twine &Prefix) { if (EC) error(Prefix + ": " + EC.message()); } template static void error(const ErrorOr &V, const Twine &Prefix) { error(V.getError(), Prefix); } static void maybeVerifyModule(const Module &Mod) { if (!DisableVerify && verifyModule(Mod, &errs())) error("Broken Module"); } static std::unique_ptr getLocalLTOModule(StringRef Path, std::unique_ptr &Buffer, const TargetOptions &Options) { ErrorOr> BufferOrErr = MemoryBuffer::getFile(Path); error(BufferOrErr, "error loading file '" + Path + "'"); Buffer = std::move(BufferOrErr.get()); CurrentActivity = ("loading file '" + Path + "'").str(); std::unique_ptr Context = std::make_unique(); Context->setDiagnosticHandler(std::make_unique(), true); ErrorOr> Ret = LTOModule::createInLocalContext( std::move(Context), Buffer->getBufferStart(), Buffer->getBufferSize(), Options, Path); CurrentActivity = ""; maybeVerifyModule((*Ret)->getModule()); return std::move(*Ret); } /// Print some statistics on the index for each input files. static void printIndexStats() { for (auto &Filename : InputFilenames) { ExitOnError ExitOnErr("llvm-lto: error loading file '" + Filename + "': "); std::unique_ptr Index = ExitOnErr(getModuleSummaryIndexForFile(Filename)); // Skip files without a module summary. if (!Index) report_fatal_error(Filename + " does not contain an index"); unsigned Calls = 0, Refs = 0, Functions = 0, Alias = 0, Globals = 0; for (auto &Summaries : *Index) { for (auto &Summary : Summaries.second.SummaryList) { Refs += Summary->refs().size(); if (auto *FuncSummary = dyn_cast(Summary.get())) { Functions++; Calls += FuncSummary->calls().size(); } else if (isa(Summary.get())) Alias++; else Globals++; } } outs() << "Index " << Filename << " contains " << (Alias + Globals + Functions) << " nodes (" << Functions << " functions, " << Alias << " alias, " << Globals << " globals) and " << (Calls + Refs) << " edges (" << Refs << " refs and " << Calls << " calls)\n"; } } /// List symbols in each IR file. /// /// The main point here is to provide lit-testable coverage for the LTOModule /// functionality that's exposed by the C API to list symbols. Moreover, this /// provides testing coverage for modules that have been created in their own /// contexts. static void listSymbols(const TargetOptions &Options) { for (auto &Filename : InputFilenames) { std::unique_ptr Buffer; std::unique_ptr Module = getLocalLTOModule(Filename, Buffer, Options); // List the symbols. outs() << Filename << ":\n"; for (int I = 0, E = Module->getSymbolCount(); I != E; ++I) outs() << Module->getSymbolName(I) << "\n"; } } static std::unique_ptr loadFile(StringRef Filename) { ExitOnError ExitOnErr("llvm-lto: error loading file '" + Filename.str() + "': "); return ExitOnErr(errorOrToExpected(MemoryBuffer::getFileOrSTDIN(Filename))); } static void listDependentLibraries() { for (auto &Filename : InputFilenames) { auto Buffer = loadFile(Filename); std::string E; std::unique_ptr Input(LTOModule::createInputFile( Buffer->getBufferStart(), Buffer->getBufferSize(), Filename.c_str(), E)); if (!Input) error(E); // List the dependent libraries. outs() << Filename << ":\n"; for (size_t I = 0, C = LTOModule::getDependentLibraryCount(Input.get()); I != C; ++I) { size_t L = 0; const char *S = LTOModule::getDependentLibrary(Input.get(), I, &L); assert(S); outs() << StringRef(S, L) << "\n"; } } } static void printMachOCPUOnly() { LLVMContext Context; Context.setDiagnosticHandler(std::make_unique(), true); TargetOptions Options = codegen::InitTargetOptionsFromCodeGenFlags(Triple()); for (auto &Filename : InputFilenames) { ErrorOr> ModuleOrErr = LTOModule::createFromFile(Context, Filename, Options); if (!ModuleOrErr) error(ModuleOrErr, "llvm-lto: "); Expected CPUType = (*ModuleOrErr)->getMachOCPUType(); Expected CPUSubType = (*ModuleOrErr)->getMachOCPUSubType(); if (!CPUType) error("Error while printing mach-o cputype: " + toString(CPUType.takeError())); if (!CPUSubType) error("Error while printing mach-o cpusubtype: " + toString(CPUSubType.takeError())); outs() << llvm::format("%s:\ncputype: %u\ncpusubtype: %u\n", Filename.c_str(), *CPUType, *CPUSubType); } } /// Create a combined index file from the input IR files and write it. /// /// This is meant to enable testing of ThinLTO combined index generation, /// currently available via the gold plugin via -thinlto. static void createCombinedModuleSummaryIndex() { ModuleSummaryIndex CombinedIndex(/*HaveGVs=*/false); uint64_t NextModuleId = 0; for (auto &Filename : InputFilenames) { ExitOnError ExitOnErr("llvm-lto: error loading file '" + Filename + "': "); std::unique_ptr MB = ExitOnErr(errorOrToExpected(MemoryBuffer::getFileOrSTDIN(Filename))); ExitOnErr(readModuleSummaryIndex(*MB, CombinedIndex, NextModuleId++)); } std::error_code EC; assert(!OutputFilename.empty()); raw_fd_ostream OS(OutputFilename + ".thinlto.bc", EC, sys::fs::OpenFlags::OF_None); error(EC, "error opening the file '" + OutputFilename + ".thinlto.bc'"); WriteIndexToFile(CombinedIndex, OS); OS.close(); } /// Parse the thinlto_prefix_replace option into the \p OldPrefix and /// \p NewPrefix strings, if it was specified. static void getThinLTOOldAndNewPrefix(std::string &OldPrefix, std::string &NewPrefix) { assert(ThinLTOPrefixReplace.empty() || ThinLTOPrefixReplace.find(';') != StringRef::npos); StringRef PrefixReplace = ThinLTOPrefixReplace; std::pair Split = PrefixReplace.split(";"); OldPrefix = Split.first.str(); NewPrefix = Split.second.str(); } /// Given the original \p Path to an output file, replace any path /// prefix matching \p OldPrefix with \p NewPrefix. Also, create the /// resulting directory if it does not yet exist. static std::string getThinLTOOutputFile(const std::string &Path, const std::string &OldPrefix, const std::string &NewPrefix) { if (OldPrefix.empty() && NewPrefix.empty()) return Path; SmallString<128> NewPath(Path); llvm::sys::path::replace_path_prefix(NewPath, OldPrefix, NewPrefix); StringRef ParentPath = llvm::sys::path::parent_path(NewPath.str()); if (!ParentPath.empty()) { // Make sure the new directory exists, creating it if necessary. if (std::error_code EC = llvm::sys::fs::create_directories(ParentPath)) error(EC, "error creating the directory '" + ParentPath + "'"); } return std::string(NewPath.str()); } namespace thinlto { std::vector> loadAllFilesForIndex(const ModuleSummaryIndex &Index) { std::vector> InputBuffers; for (auto &ModPath : Index.modulePaths()) { const auto &Filename = ModPath.first(); std::string CurrentActivity = ("loading file '" + Filename + "'").str(); 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"); ExitOnError ExitOnErr("llvm-lto: error loading file '" + ThinLTOIndex + "': "); return ExitOnErr(getModuleSummaryIndexForFile(ThinLTOIndex)); } static std::unique_ptr loadInputFile(MemoryBufferRef Buffer) { ExitOnError ExitOnErr("llvm-lto: error loading input '" + Buffer.getBufferIdentifier().str() + "': "); return ExitOnErr(lto::InputFile::create(Buffer)); } static std::unique_ptr loadModuleFromInput(lto::InputFile &File, LLVMContext &CTX) { auto &Mod = File.getSingleBitcodeModule(); auto ModuleOrErr = Mod.parseModule(CTX); if (!ModuleOrErr) { handleAllErrors(ModuleOrErr.takeError(), [&](ErrorInfoBase &EIB) { SMDiagnostic Err = SMDiagnostic(Mod.getModuleIdentifier(), SourceMgr::DK_Error, EIB.message()); Err.print("llvm-lto", errs()); }); report_fatal_error("Can't load module, abort."); } maybeVerifyModule(**ModuleOrErr); if (ThinLTOModuleId.getNumOccurrences()) { if (InputFilenames.size() != 1) report_fatal_error("Can't override the module id for multiple files"); (*ModuleOrErr)->setModuleIdentifier(ThinLTOModuleId); } return std::move(*ModuleOrErr); } static void writeModuleToFile(Module &TheModule, StringRef Filename) { std::error_code EC; raw_fd_ostream OS(Filename, EC, sys::fs::OpenFlags::OF_None); error(EC, "error opening the file '" + Filename + "'"); maybeVerifyModule(TheModule); WriteBitcodeToFile(TheModule, OS, /* ShouldPreserveUseListOrder */ true); } class ThinLTOProcessing { public: ThinLTOCodeGenerator ThinGenerator; ThinLTOProcessing(const TargetOptions &Options) { ThinGenerator.setCodePICModel(codegen::getExplicitRelocModel()); ThinGenerator.setTargetOptions(Options); ThinGenerator.setCacheDir(ThinLTOCacheDir); ThinGenerator.setCachePruningInterval(ThinLTOCachePruningInterval); ThinGenerator.setCacheEntryExpiration(ThinLTOCacheEntryExpiration); ThinGenerator.setCacheMaxSizeFiles(ThinLTOCacheMaxSizeFiles); ThinGenerator.setCacheMaxSizeBytes(ThinLTOCacheMaxSizeBytes); ThinGenerator.setFreestanding(EnableFreestanding); // Add all the exported symbols to the table of symbols to preserve. for (unsigned i = 0; i < ExportedSymbols.size(); ++i) ThinGenerator.preserveSymbol(ExportedSymbols[i]); } void run() { switch (ThinLTOMode) { case THINLINK: return thinLink(); case THINDISTRIBUTE: return distributedIndexes(); case THINEMITIMPORTS: return emitImports(); case THINPROMOTE: return promote(); case THINIMPORT: return import(); case THININTERNALIZE: return internalize(); 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]; std::string 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(); if (!CombinedIndex) report_fatal_error("ThinLink didn't create an index"); std::error_code EC; raw_fd_ostream OS(OutputFilename, EC, sys::fs::OpenFlags::OF_None); error(EC, "error opening the file '" + OutputFilename + "'"); WriteIndexToFile(*CombinedIndex, OS); } /// Load the combined index from disk, then compute and generate /// individual index files suitable for ThinLTO distributed backend builds /// on the files mentioned on the command line (these must match the index /// content). void distributedIndexes() { 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."); std::string OldPrefix, NewPrefix; getThinLTOOldAndNewPrefix(OldPrefix, NewPrefix); auto Index = loadCombinedIndex(); for (auto &Filename : InputFilenames) { LLVMContext Ctx; auto Buffer = loadFile(Filename); auto Input = loadInputFile(Buffer->getMemBufferRef()); auto TheModule = loadModuleFromInput(*Input, Ctx); // Build a map of module to the GUIDs and summary objects that should // be written to its index. std::map ModuleToSummariesForIndex; ThinGenerator.gatherImportedSummariesForModule( *TheModule, *Index, ModuleToSummariesForIndex, *Input); std::string OutputName = OutputFilename; if (OutputName.empty()) { OutputName = Filename + ".thinlto.bc"; } OutputName = getThinLTOOutputFile(OutputName, OldPrefix, NewPrefix); std::error_code EC; raw_fd_ostream OS(OutputName, EC, sys::fs::OpenFlags::OF_None); error(EC, "error opening the file '" + OutputName + "'"); WriteIndexToFile(*Index, OS, &ModuleToSummariesForIndex); } } /// Load the combined index from disk, compute the imports, and emit /// the import file lists for each module to disk. void emitImports() { 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."); std::string OldPrefix, NewPrefix; getThinLTOOldAndNewPrefix(OldPrefix, NewPrefix); auto Index = loadCombinedIndex(); for (auto &Filename : InputFilenames) { LLVMContext Ctx; auto Buffer = loadFile(Filename); auto Input = loadInputFile(Buffer->getMemBufferRef()); auto TheModule = loadModuleFromInput(*Input, Ctx); std::string OutputName = OutputFilename; if (OutputName.empty()) { OutputName = Filename + ".imports"; } OutputName = getThinLTOOutputFile(OutputName, OldPrefix, NewPrefix); ThinGenerator.emitImports(*TheModule, OutputName, *Index, *Input); } } /// 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 Buffer = loadFile(Filename); auto Input = loadInputFile(Buffer->getMemBufferRef()); auto TheModule = loadModuleFromInput(*Input, Ctx); ThinGenerator.promote(*TheModule, *Index, *Input); 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 Buffer = loadFile(Filename); auto Input = loadInputFile(Buffer->getMemBufferRef()); auto TheModule = loadModuleFromInput(*Input, Ctx); ThinGenerator.crossModuleImport(*TheModule, *Index, *Input); std::string OutputName = OutputFilename; if (OutputName.empty()) { OutputName = Filename + ".thinlto.imported.bc"; } writeModuleToFile(*TheModule, OutputName); } } void internalize() { 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 (ExportedSymbols.empty()) errs() << "Warning: -internalize will not perform without " "-exported-symbol\n"; auto Index = loadCombinedIndex(); auto InputBuffers = loadAllFilesForIndex(*Index); for (auto &MemBuffer : InputBuffers) ThinGenerator.addModule(MemBuffer->getBufferIdentifier(), MemBuffer->getBuffer()); for (auto &Filename : InputFilenames) { LLVMContext Ctx; auto Buffer = loadFile(Filename); auto Input = loadInputFile(Buffer->getMemBufferRef()); auto TheModule = loadModuleFromInput(*Input, Ctx); ThinGenerator.internalize(*TheModule, *Index, *Input); std::string OutputName = OutputFilename; if (OutputName.empty()) { OutputName = Filename + ".thinlto.internalized.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 Buffer = loadFile(Filename); auto Input = loadInputFile(Buffer->getMemBufferRef()); auto TheModule = loadModuleFromInput(*Input, 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"; std::vector> InputBuffers; for (auto &Filename : InputFilenames) { LLVMContext Ctx; auto InputOrErr = MemoryBuffer::getFile(Filename); error(InputOrErr, "error " + CurrentActivity); InputBuffers.push_back(std::move(*InputOrErr)); ThinGenerator.addModule(Filename, InputBuffers.back()->getBuffer()); } ThinGenerator.setCodeGenOnly(true); ThinGenerator.run(); for (auto BinName : zip(ThinGenerator.getProducedBinaries(), InputFilenames)) { std::string OutputName = OutputFilename; if (OutputName.empty()) OutputName = std::get<1>(BinName) + ".thinlto.o"; else if (OutputName == "-") { outs() << std::get<0>(BinName)->getBuffer(); return; } std::error_code EC; raw_fd_ostream OS(OutputName, EC, sys::fs::OpenFlags::OF_None); error(EC, "error opening the file '" + OutputName + "'"); OS << std::get<0>(BinName)->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]; std::string 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()); } if (!ThinLTOSaveTempsPrefix.empty()) ThinGenerator.setSaveTempsDir(ThinLTOSaveTempsPrefix); if (!ThinLTOGeneratedObjectsDir.empty()) { ThinGenerator.setGeneratedObjectsDirectory(ThinLTOGeneratedObjectsDir); ThinGenerator.run(); return; } 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::OF_None); error(EC, "error opening the file '" + OutputName + "'"); OS << Binaries[BufID]->getBuffer(); } } /// Load the combined index from disk, then load every file referenced by }; } // end namespace thinlto int main(int argc, char **argv) { InitLLVM X(argc, argv); cl::ParseCommandLineOptions(argc, argv, "llvm LTO linker\n"); if (OptLevel < '0' || OptLevel > '3') error("optimization level must be between 0 and 3"); // Initialize the configured targets. InitializeAllTargets(); InitializeAllTargetMCs(); InitializeAllAsmPrinters(); InitializeAllAsmParsers(); // set up the TargetOptions for the machine TargetOptions Options = codegen::InitTargetOptionsFromCodeGenFlags(Triple()); if (ListSymbolsOnly) { listSymbols(Options); return 0; } if (ListDependentLibrariesOnly) { listDependentLibraries(); return 0; } if (IndexStats) { printIndexStats(); return 0; } if (CheckHasObjC) { for (auto &Filename : InputFilenames) { ExitOnError ExitOnErr(std::string(*argv) + ": error loading file '" + Filename + "': "); std::unique_ptr BufferOrErr = ExitOnErr(errorOrToExpected(MemoryBuffer::getFile(Filename))); auto Buffer = std::move(BufferOrErr.get()); if (ExitOnErr(isBitcodeContainingObjCCategory(*Buffer))) outs() << "Bitcode " << Filename << " contains ObjC\n"; else outs() << "Bitcode " << Filename << " does not contain ObjC\n"; } return 0; } if (PrintMachOCPUOnly) { printMachOCPUOnly(); 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) { createCombinedModuleSummaryIndex(); return 0; } unsigned BaseArg = 0; LLVMContext Context; Context.setDiagnosticHandler(std::make_unique(), true); LTOCodeGenerator CodeGen(Context); CodeGen.setDisableVerify(DisableVerify); if (UseDiagnosticHandler) CodeGen.setDiagnosticHandler(handleDiagnostics, nullptr); CodeGen.setCodePICModel(codegen::getExplicitRelocModel()); CodeGen.setFreestanding(EnableFreestanding); CodeGen.setDebugInfo(LTO_DEBUG_MODEL_DWARF); CodeGen.setTargetOptions(Options); CodeGen.setShouldRestoreGlobalsLinkage(RestoreGlobalsLinkage); StringSet DSOSymbolsSet; for (unsigned i = 0; i < DSOSymbols.size(); ++i) DSOSymbolsSet.insert(DSOSymbols[i]); std::vector KeptDSOSyms; for (unsigned i = BaseArg; i < InputFilenames.size(); ++i) { CurrentActivity = "loading file '" + InputFilenames[i] + "'"; ErrorOr> ModuleOrErr = LTOModule::createFromFile(Context, InputFilenames[i], Options); std::unique_ptr &Module = *ModuleOrErr; CurrentActivity = ""; unsigned NumSyms = Module->getSymbolCount(); for (unsigned I = 0; I < NumSyms; ++I) { StringRef Name = Module->getSymbolName(I); if (!DSOSymbolsSet.count(Name)) continue; lto_symbol_attributes Attrs = Module->getSymbolAttributes(I); unsigned Scope = Attrs & LTO_SYMBOL_SCOPE_MASK; if (Scope != LTO_SYMBOL_SCOPE_DEFAULT_CAN_BE_HIDDEN) KeptDSOSyms.push_back(std::string(Name)); } // We use the first input module as the destination module when // SetMergedModule is true. if (SetMergedModule && i == BaseArg) { // Transfer ownership to the code generator. CodeGen.setModule(std::move(Module)); } else if (!CodeGen.addModule(Module.get())) { // Print a message here so that we know addModule() did not abort. error("error adding file '" + InputFilenames[i] + "'"); } } // Add all the exported symbols to the table of symbols to preserve. for (unsigned i = 0; i < ExportedSymbols.size(); ++i) CodeGen.addMustPreserveSymbol(ExportedSymbols[i]); // Add all the dso symbols to the table of symbols to expose. for (unsigned i = 0; i < KeptDSOSyms.size(); ++i) CodeGen.addMustPreserveSymbol(KeptDSOSyms[i]); // Set cpu and attrs strings for the default target/subtarget. CodeGen.setCpu(codegen::getMCPU().c_str()); CodeGen.setOptLevel(OptLevel - '0'); CodeGen.setAttrs(codegen::getMAttrs()); if (auto FT = codegen::getExplicitFileType()) CodeGen.setFileType(FT.getValue()); if (!OutputFilename.empty()) { if (SaveLinkedModuleFile) { std::string ModuleFilename = OutputFilename; ModuleFilename += ".linked.bc"; std::string ErrMsg; if (!CodeGen.writeMergedModules(ModuleFilename)) error("writing linked module failed."); } if (!CodeGen.optimize()) { // Diagnostic messages should have been printed by the handler. error("error optimizing the code"); } if (SaveModuleFile) { std::string ModuleFilename = OutputFilename; ModuleFilename += ".merged.bc"; std::string ErrMsg; if (!CodeGen.writeMergedModules(ModuleFilename)) error("writing merged module failed."); } std::list OSs; std::vector OSPtrs; for (unsigned I = 0; I != Parallelism; ++I) { std::string PartFilename = OutputFilename; if (Parallelism != 1) PartFilename += "." + utostr(I); std::error_code EC; OSs.emplace_back(PartFilename, EC, sys::fs::OF_None); if (EC) error("error opening the file '" + PartFilename + "': " + EC.message()); OSPtrs.push_back(&OSs.back().os()); } if (!CodeGen.compileOptimized(OSPtrs)) // Diagnostic messages should have been printed by the handler. error("error compiling the code"); for (ToolOutputFile &OS : OSs) OS.keep(); } else { if (Parallelism != 1) error("-j must be specified together with -o"); if (SaveModuleFile) error(": -save-merged-module must be specified with -o"); const char *OutputName = nullptr; if (!CodeGen.compile_to_file(&OutputName)) error("error compiling the code"); // Diagnostic messages should have been printed by the handler. outs() << "Wrote native object file '" << OutputName << "'\n"; } return 0; }