From aa0883b59ae17e5465906dad51b5561b5292a28d Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Sun, 20 Nov 2022 11:59:16 -0500 Subject: [PATCH] [lld/mac] Add support for distributed ThinLTO Adds support for the following flags: * --thinlto-index-only, --thinlto-index-only= * --thinlto-emit-imports-files * --thinlto-emit-index-files * --thinlto-object-suffix-replace= * --thinlto-prefix-replace= See https://blog.llvm.org/2016/06/thinlto-scalable-and-incremental-lto.html for some words on --thinlto-index-only. I don't really need the other flags, but they were in the vicinity and _someone_ might need them, so I figured I'd add them too. `-object_path_lto` now sets `c.AlwaysEmitRegularLTOObj` as in the other ports, which means it can now only point to a filename for non-thin LTO. I think that was the intent of D129705 anyways, so update test/MachO/lto-object-path.ll to use a non-thin bitcode file for that test. Differential Revision: https://reviews.llvm.org/D138451 --- lld/ELF/InputFiles.cpp | 4 +- lld/MachO/Config.h | 6 ++ lld/MachO/Driver.cpp | 44 +++++++- lld/MachO/InputFiles.cpp | 10 ++ lld/MachO/InputFiles.h | 1 + lld/MachO/LTO.cpp | 127 ++++++++++++++++++++++-- lld/MachO/LTO.h | 5 + lld/MachO/Options.td | 13 +++ lld/test/ELF/lto/thinlto-index-only.ll | 5 +- lld/test/ELF/lto/thinlto-obj-path.ll | 2 +- lld/test/ELF/lto/thinlto-prefix-replace.ll | 2 +- lld/test/MachO/lto-object-path.ll | 3 +- lld/test/MachO/thinlto-emit-imports.ll | 70 +++++++++++++ lld/test/MachO/thinlto-emit-index.ll | 122 +++++++++++++++++++++++ lld/test/MachO/thinlto-index-file.ll | 38 +++++++ lld/test/MachO/thinlto-index-only.ll | 120 ++++++++++++++++++++++ lld/test/MachO/thinlto-obj-path.ll | 46 +++++++++ lld/test/MachO/thinlto-object-suffix-replace.ll | 47 +++++++++ lld/test/MachO/thinlto-prefix-replace.ll | 24 +++++ 19 files changed, 668 insertions(+), 21 deletions(-) create mode 100644 lld/test/MachO/thinlto-emit-imports.ll create mode 100644 lld/test/MachO/thinlto-emit-index.ll create mode 100644 lld/test/MachO/thinlto-index-file.ll create mode 100644 lld/test/MachO/thinlto-index-only.ll create mode 100644 lld/test/MachO/thinlto-obj-path.ll create mode 100644 lld/test/MachO/thinlto-object-suffix-replace.ll create mode 100644 lld/test/MachO/thinlto-prefix-replace.ll diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp index 9ecc898..7dacdeb 100644 --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -1773,9 +1773,7 @@ bool InputFile::shouldExtractForCommon(StringRef name) { } std::string elf::replaceThinLTOSuffix(StringRef path) { - StringRef suffix = config->thinLTOObjectSuffixReplace.first; - StringRef repl = config->thinLTOObjectSuffixReplace.second; - + auto [suffix, repl] = config->thinLTOObjectSuffixReplace; if (path.consume_back(suffix)) return (path + repl).str(); return std::string(path); diff --git a/lld/MachO/Config.h b/lld/MachO/Config.h index 6586824..220fb99 100644 --- a/lld/MachO/Config.h +++ b/lld/MachO/Config.h @@ -133,6 +133,9 @@ struct Configuration { bool emitEncryptionInfo = false; bool emitInitOffsets = false; bool emitChainedFixups = false; + bool thinLTOEmitImportsFiles; + bool thinLTOEmitIndexFiles; + bool thinLTOIndexOnly; bool timeTraceEnabled = false; bool dataConst = false; bool dedupStrings = true; @@ -164,6 +167,9 @@ struct Configuration { uint32_t ltoo = 2; llvm::CachePruningPolicy thinLTOCachePolicy; llvm::StringRef thinLTOCacheDir; + llvm::StringRef thinLTOIndexOnlyArg; + std::pair thinLTOObjectSuffixReplace; + std::pair thinLTOPrefixReplace; bool deadStripDylibs = false; bool demangle = false; bool deadStrip = false; diff --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp index 356fcde..fe8540e 100644 --- a/lld/MachO/Driver.cpp +++ b/lld/MachO/Driver.cpp @@ -858,6 +858,20 @@ static const char *getReproduceOption(InputArgList &args) { return getenv("LLD_REPRODUCE"); } +// Parse options of the form "old;new". +static std::pair getOldNewOptions(opt::InputArgList &args, + unsigned id) { + auto *arg = args.getLastArg(id); + if (!arg) + return {"", ""}; + + StringRef s = arg->getValue(); + std::pair ret = s.split(';'); + if (ret.second.empty()) + error(arg->getSpelling() + " expects 'old;new' format, but got " + s); + return ret; +} + static void parseClangOption(StringRef opt, const Twine &msg) { std::string err; raw_string_ostream os(err); @@ -1549,6 +1563,25 @@ bool macho::link(ArrayRef argsArr, llvm::raw_ostream &stdoutOS, error("--lto-O: invalid optimization level: " + Twine(config->ltoo)); config->thinLTOCacheDir = args.getLastArgValue(OPT_cache_path_lto); config->thinLTOCachePolicy = getLTOCachePolicy(args); + config->thinLTOEmitImportsFiles = args.hasArg(OPT_thinlto_emit_imports_files); + config->thinLTOEmitIndexFiles = args.hasArg(OPT_thinlto_emit_index_files) || + args.hasArg(OPT_thinlto_index_only) || + args.hasArg(OPT_thinlto_index_only_eq); + config->thinLTOIndexOnly = args.hasArg(OPT_thinlto_index_only) || + args.hasArg(OPT_thinlto_index_only_eq); + config->thinLTOIndexOnlyArg = args.getLastArgValue(OPT_thinlto_index_only_eq); + config->thinLTOObjectSuffixReplace = + getOldNewOptions(args, OPT_thinlto_object_suffix_replace_eq); + config->thinLTOPrefixReplace = + getOldNewOptions(args, OPT_thinlto_prefix_replace_eq); + if (config->thinLTOEmitIndexFiles && !config->thinLTOIndexOnly) { + if (args.hasArg(OPT_thinlto_object_suffix_replace_eq)) + error("--thinlto-object-suffix-replace is not supported with " + "--thinlto-emit-index-files"); + else if (args.hasArg(OPT_thinlto_prefix_replace_eq)) + error("--thinlto-prefix-replace is not supported with " + "--thinlto-emit-index-files"); + } config->runtimePaths = args::getStrings(args, OPT_rpath); config->allLoad = args.hasFlag(OPT_all_load, OPT_noall_load, false); config->archMultiple = args.hasArg(OPT_arch_multiple); @@ -1834,11 +1867,20 @@ bool macho::link(ArrayRef argsArr, llvm::raw_ostream &stdoutOS, // explicitly exported. Do this before running LTO so that LTO can better // optimize. handleExplicitExports(); + + bool didCompileBitcodeFiles = compileBitcodeFiles(); + + // If --thinlto-index-only is given, we should create only "index + // files" and not object files. Index file creation is already done + // in compileBitcodeFiles, so we are done if that's the case. + if (config->thinLTOIndexOnly) + return errorCount() == 0; + // LTO may emit a non-hidden (extern) object file symbol even if the // corresponding bitcode symbol is hidden. In particular, this happens for // cross-module references to hidden symbols under ThinLTO. Thus, if we // compiled any bitcode files, we must redo the symbol hiding. - if (compileBitcodeFiles()) + if (didCompileBitcodeFiles) handleExplicitExports(); replaceCommonSymbols(); diff --git a/lld/MachO/InputFiles.cpp b/lld/MachO/InputFiles.cpp index bb32ba3..50c6987 100644 --- a/lld/MachO/InputFiles.cpp +++ b/lld/MachO/InputFiles.cpp @@ -2206,6 +2206,9 @@ BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName, : InputFile(BitcodeKind, mb, lazy), forceHidden(forceHidden) { this->archiveName = std::string(archiveName); std::string path = mb.getBufferIdentifier().str(); + if (config->thinLTOIndexOnly) + path = replaceThinLTOSuffix(mb.getBufferIdentifier()); + // ThinLTO assumes that all MemoryBufferRefs given to it have a unique // name. If two members with the same name are provided, this causes a // collision and ThinLTO can't proceed. @@ -2246,6 +2249,13 @@ void BitcodeFile::parseLazy() { } } +std::string macho::replaceThinLTOSuffix(StringRef path) { + auto [suffix, repl] = config->thinLTOObjectSuffixReplace; + if (path.consume_back(suffix)) + return (path + repl).str(); + return std::string(path); +} + void macho::extract(InputFile &file, StringRef reason) { if (!file.lazy) return; diff --git a/lld/MachO/InputFiles.h b/lld/MachO/InputFiles.h index 66cf92b..66d46e4 100644 --- a/lld/MachO/InputFiles.h +++ b/lld/MachO/InputFiles.h @@ -357,6 +357,7 @@ std::vector findCommands(const void *anyHdr, return detail::findCommands(anyHdr, 0, types...); } +std::string replaceThinLTOSuffix(StringRef path); } // namespace macho std::string toString(const macho::InputFile *file); diff --git a/lld/MachO/LTO.cpp b/lld/MachO/LTO.cpp index ba87688..565a66d 100644 --- a/lld/MachO/LTO.cpp +++ b/lld/MachO/LTO.cpp @@ -17,6 +17,7 @@ #include "lld/Common/CommonLinkerContext.h" #include "lld/Common/Strings.h" #include "lld/Common/TargetOptionsCommandFlags.h" +#include "llvm/Bitcode/BitcodeWriter.h" #include "llvm/LTO/Config.h" #include "llvm/LTO/LTO.h" #include "llvm/Support/Caching.h" @@ -31,6 +32,25 @@ using namespace llvm; using namespace llvm::MachO; using namespace llvm::sys; +// Creates an empty file to store a list of object files for final +// linking of distributed ThinLTO. +static std::unique_ptr openFile(StringRef file) { + std::error_code ec; + auto ret = + std::make_unique(file, ec, sys::fs::OpenFlags::OF_None); + if (ec) { + error("cannot open " + file + ": " + ec.message()); + return nullptr; + } + return ret; +} + +static std::string getThinLTOOutputFile(StringRef modulePath) { + return lto::getThinLTOOutputFile( + std::string(modulePath), std::string(config->thinLTOPrefixReplace.first), + std::string(config->thinLTOPrefixReplace.second)); +} + static lto::Config createConfig() { lto::Config c; c.Options = initTargetOptionsFromCodeGenFlags(); @@ -44,6 +64,9 @@ static lto::Config createConfig() { c.PreCodeGenPassesHook = [](legacy::PassManager &pm) { pm.add(createObjCARCContractPass()); }; + + c.AlwaysEmitRegularLTOObj = !config->ltoObjPath.empty(); + c.TimeTraceEnabled = config->timeTraceEnabled; c.TimeTraceGranularity = config->timeTraceGranularity; c.OptLevel = config->ltoo; @@ -67,13 +90,35 @@ static void saveOrHardlinkBuffer(StringRef buffer, const Twine &path, } BitcodeCompiler::BitcodeCompiler() { - lto::ThinBackend backend = lto::createInProcessThinBackend( - heavyweight_hardware_concurrency(config->thinLTOJobs)); + // Initialize indexFile. + if (!config->thinLTOIndexOnlyArg.empty()) + indexFile = openFile(config->thinLTOIndexOnlyArg); + + // Initialize ltoObj. + lto::ThinBackend backend; + auto onIndexWrite = [&](StringRef S) { thinIndices.erase(S); }; + if (config->thinLTOIndexOnly) { + backend = lto::createWriteIndexesThinBackend( + std::string(config->thinLTOPrefixReplace.first), + std::string(config->thinLTOPrefixReplace.second), + config->thinLTOEmitImportsFiles, indexFile.get(), onIndexWrite); + } else { + backend = lto::createInProcessThinBackend( + llvm::heavyweight_hardware_concurrency(config->thinLTOJobs), + onIndexWrite, config->thinLTOEmitIndexFiles, + config->thinLTOEmitImportsFiles); + } + ltoObj = std::make_unique(createConfig(), backend); } void BitcodeCompiler::add(BitcodeFile &f) { - ArrayRef objSyms = f.obj->symbols(); + lto::InputFile &obj = *f.obj; + + if (config->thinLTOEmitIndexFiles) + thinIndices.insert(obj.getName()); + + ArrayRef objSyms = obj.symbols(); std::vector resols; resols.reserve(objSyms.size()); @@ -117,6 +162,37 @@ void BitcodeCompiler::add(BitcodeFile &f) { checkError(ltoObj->add(std::move(f.obj), resols)); } +// If LazyObjFile has not been added to link, emit empty index files. +// This is needed because this is what GNU gold plugin does and we have a +// distributed build system that depends on that behavior. +static void thinLTOCreateEmptyIndexFiles() { + DenseSet linkedBitCodeFiles; + for (InputFile *file : inputFiles) + if (auto *f = dyn_cast(file)) + if (!f->lazy) + linkedBitCodeFiles.insert(f->getName()); + + for (InputFile *file : inputFiles) { + if (auto *f = dyn_cast(file)) { + if (!f->lazy) + continue; + if (linkedBitCodeFiles.contains(f->getName())) + continue; + std::string path = + replaceThinLTOSuffix(getThinLTOOutputFile(f->obj->getName())); + std::unique_ptr os = openFile(path + ".thinlto.bc"); + if (!os) + continue; + + ModuleSummaryIndex m(/*HaveGVs=*/false); + m.setSkipModuleByDistributedBackend(); + writeIndexToFile(m, *os); + if (config->thinLTOEmitImportsFiles) + openFile(path + ".imports"); + } + } +} + // Merge all the bitcode files we have seen, codegen the result // and return the resulting ObjectFile(s). std::vector BitcodeCompiler::compile() { @@ -142,8 +218,16 @@ std::vector BitcodeCompiler::compile() { }, cache)); - if (!config->thinLTOCacheDir.empty()) - pruneCache(config->thinLTOCacheDir, config->thinLTOCachePolicy, files); + // Emit empty index files for non-indexed files + for (StringRef s : thinIndices) { + std::string path = getThinLTOOutputFile(s); + openFile(path + ".thinlto.bc"); + if (config->thinLTOEmitImportsFiles) + openFile(path + ".imports"); + } + + if (config->thinLTOEmitIndexFiles) + thinLTOCreateEmptyIndexFiles(); // In ThinLTO mode, Clang passes a temporary directory in -object_path_lto, // while the argument is a single file in FullLTO mode. @@ -162,6 +246,32 @@ std::vector BitcodeCompiler::compile() { } } + auto outputFilePath = [objPathIsDir](int i) { + SmallString<261> filePath("/tmp/lto.tmp"); + if (!config->ltoObjPath.empty()) { + filePath = config->ltoObjPath; + if (objPathIsDir) + path::append(filePath, Twine(i) + "." + + getArchitectureName(config->arch()) + + ".lto.o"); + } + return filePath; + }; + + // ThinLTO with index only option is required to generate only the index + // files. After that, we exit from linker and ThinLTO backend runs in a + // distributed environment. + if (config->thinLTOIndexOnly) { + if (!config->ltoObjPath.empty()) + saveBuffer(buf[0], outputFilePath(0)); + if (indexFile) + indexFile->close(); + return {}; + } + + if (!config->thinLTOCacheDir.empty()) + pruneCache(config->thinLTOCacheDir, config->thinLTOCachePolicy, files); + std::vector ret; for (unsigned i = 0; i < maxTasks; ++i) { // Get the native object contents either from the cache or from memory. Do @@ -183,14 +293,9 @@ std::vector BitcodeCompiler::compile() { saveBuffer(objBuf, config->outputFile + ((i == 0) ? "" : Twine(i)) + ".lto.o"); - SmallString<261> filePath("/tmp/lto.tmp"); + auto filePath = outputFilePath(i); uint32_t modTime = 0; if (!config->ltoObjPath.empty()) { - filePath = config->ltoObjPath; - if (objPathIsDir) - path::append(filePath, Twine(i) + "." + - getArchitectureName(config->arch()) + - ".lto.o"); saveOrHardlinkBuffer(objBuf, filePath, cachePath); modTime = getModTime(filePath); } diff --git a/lld/MachO/LTO.h b/lld/MachO/LTO.h index 3e5f1a6..f07b1e3 100644 --- a/lld/MachO/LTO.h +++ b/lld/MachO/LTO.h @@ -9,8 +9,11 @@ #ifndef LLD_MACHO_LTO_H #define LLD_MACHO_LTO_H +#include "lld/Common/LLVM.h" +#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" #include #include @@ -34,6 +37,8 @@ private: std::unique_ptr ltoObj; std::vector> buf; std::vector> files; + std::unique_ptr indexFile; + llvm::DenseSet thinIndices; }; } // namespace lld::macho diff --git a/lld/MachO/Options.td b/lld/MachO/Options.td index b0ccf13..f05835d 100644 --- a/lld/MachO/Options.td +++ b/lld/MachO/Options.td @@ -28,9 +28,22 @@ def color_diagnostics_eq: Joined<["--"], "color-diagnostics=">, def threads_eq : Joined<["--"], "threads=">, HelpText<"Number of threads. '1' disables multi-threading. By default all available hardware threads are used">, Group; +def thinlto_emit_imports_files: Flag<["--"], "thinlto-emit-imports-files">, + Group; +def thinlto_emit_index_files: Flag<["--"], "thinlto-emit-index-files">, + Group; +def thinlto_index_only: Flag<["--"], "thinlto-index-only">, + Group; +def thinlto_index_only_eq: Joined<["--"], "thinlto-index-only=">, + Group; def thinlto_jobs_eq : Joined<["--"], "thinlto-jobs=">, HelpText<"Number of ThinLTO jobs. Default to --threads=">, Group; +def thinlto_object_suffix_replace_eq: + Joined<["--"], "thinlto-object-suffix-replace=">, + Group; +def thinlto_prefix_replace_eq: Joined<["--"], "thinlto-prefix-replace=">, + Group; def reproduce: Separate<["--"], "reproduce">, Group; def reproduce_eq: Joined<["--"], "reproduce=">, diff --git a/lld/test/ELF/lto/thinlto-index-only.ll b/lld/test/ELF/lto/thinlto-index-only.ll index b0124c4..dc84df9 100644 --- a/lld/test/ELF/lto/thinlto-index-only.ll +++ b/lld/test/ELF/lto/thinlto-index-only.ll @@ -18,18 +18,17 @@ ;; Ensure lld doesn't generates index files when --thinlto-index-only is not enabled. ; RUN: rm -f 1.o.thinlto.bc 2.o.thinlto.bc -; RUN: ld.lld -shared 1.o 2.o -o 4 +; RUN: ld.lld -shared 1.o 2.o -o 5 ; RUN: not ls 1.o.thinlto.bc ; RUN: not ls 2.o.thinlto.bc ;; Ensure lld generates an index and not a binary if requested. -; RUN: rm -f 4 ; RUN: ld.lld --plugin-opt=thinlto-index-only -shared 1.o 2.o -o 4 ; RUN: llvm-bcanalyzer -dump 1.o.thinlto.bc | FileCheck %s --check-prefix=BACKEND1 ; RUN: llvm-bcanalyzer -dump 2.o.thinlto.bc | FileCheck %s --check-prefix=BACKEND2 ; RUN: not test -e 4 -;; Ensure lld generates an index even if the file is wrapped in --start-lib/--end-lib +;; Ensure lld generates an index even if the file is wrapped in --start-lib/--end-lib. ; RUN: rm -f 2.o.thinlto.bc 4 ; RUN: ld.lld --plugin-opt=thinlto-index-only -shared 1.o 3.o --start-lib 2.o --end-lib -o 4 ; RUN: llvm-dis < 2.o.thinlto.bc | grep -q '\^0 = module:' diff --git a/lld/test/ELF/lto/thinlto-obj-path.ll b/lld/test/ELF/lto/thinlto-obj-path.ll index 20cfed7..4058dd0 100644 --- a/lld/test/ELF/lto/thinlto-obj-path.ll +++ b/lld/test/ELF/lto/thinlto-obj-path.ll @@ -47,7 +47,7 @@ ;; Ensure lld does not emit empty combined module in default. ; RUN: rm -fr objpath && mkdir objpath -; RUN: ld.lld 1.bc 2.bc -o objpath/a.out --save-temps +; RUN: ld.lld -shared 1.bc 2.bc -o objpath/a.out --save-temps ; RUN: ls objpath/a.out*.lto.* | count 2 ; EMPTY: file format elf64-x86-64 diff --git a/lld/test/ELF/lto/thinlto-prefix-replace.ll b/lld/test/ELF/lto/thinlto-prefix-replace.ll index dcdf93b..4a4cac3 100644 --- a/lld/test/ELF/lto/thinlto-prefix-replace.ll +++ b/lld/test/ELF/lto/thinlto-prefix-replace.ll @@ -14,7 +14,7 @@ ; RUN: ld.lld --thinlto-index-only --thinlto-prefix-replace="%t/oldpath/;%t/newpath/" -shared %t/oldpath/thinlto_prefix_replace.o -o %t/thinlto_prefix_replace ; RUN: ls %t/newpath/thinlto_prefix_replace.o.thinlto.bc -; Ensure that lld generates error if prefix replace option does not have 'old;new' format +; Ensure that lld generates error if prefix replace option does not have 'old;new' format. ; RUN: rm -f %t/newpath/thinlto_prefix_replace.o.thinlto.bc ; RUN: not ld.lld --plugin-opt=thinlto-index-only --plugin-opt=thinlto-prefix-replace=abc:def -shared %t/oldpath/thinlto_prefix_replace.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=ERR ; ERR: --plugin-opt=thinlto-prefix-replace= expects 'old;new' format, but got abc:def diff --git a/lld/test/MachO/lto-object-path.ll b/lld/test/MachO/lto-object-path.ll index 8903175..d36e536d 100644 --- a/lld/test/MachO/lto-object-path.ll +++ b/lld/test/MachO/lto-object-path.ll @@ -3,6 +3,7 @@ ; RUN: rm -rf %t; mkdir %t ; RUN: opt -thinlto-bc %s -o %t/test.o +; RUN: opt %s -o %t/test-nonthin.o ; RUN: %lld %t/test.o -o %t/test ; RUN: llvm-nm -pa %t/test | FileCheck %s --check-prefixes CHECK,NOOBJPATH @@ -28,7 +29,7 @@ ;; check that the object path can be an existing file ; RUN: touch %t/lto-tmp.o -; RUN: ZERO_AR_DATE=0 %lld %t/test.o -o %t/test -object_path_lto %t/lto-tmp.o +; RUN: ZERO_AR_DATE=0 %lld %t/test-nonthin.o -o %t/test -object_path_lto %t/lto-tmp.o ; RUN: llvm-nm -pa %t/test | FileCheck %s --check-prefixes CHECK,OBJPATH-FILE -DFILE=%t/lto-tmp.o diff --git a/lld/test/MachO/thinlto-emit-imports.ll b/lld/test/MachO/thinlto-emit-imports.ll new file mode 100644 index 0000000..47a612b --- /dev/null +++ b/lld/test/MachO/thinlto-emit-imports.ll @@ -0,0 +1,70 @@ +; REQUIRES: x86 +; RUN: rm -rf %t; split-file %s %t + +; Generate summary sections and test lld handling. +; RUN: opt -module-summary %t/f.ll -o %t1.o +; RUN: opt -module-summary %t/g.ll -o %t2.o + +; Include a file with an empty module summary index, to ensure that the expected +; output files are created regardless, for a distributed build system. +; RUN: opt -module-summary %t/empty.ll -o %t3.o + +; Ensure lld generates imports files if requested for distributed backends. +; RUN: rm -f %t3.o.imports %t3.o.thinlto.bc +; RUN: %lld --thinlto-index-only --thinlto-emit-imports-files -dylib %t1.o %t2.o %t3.o -o %t4 + +; The imports file for this module contains the bitcode file for %t/g.ll +; RUN: count 1 < %t1.o.imports +; RUN: FileCheck %s --check-prefix=IMPORTS1 < %t1.o.imports +; IMPORTS1: thinlto-emit-imports.ll.tmp2.o + +; The imports file for g.ll is empty as it does not import anything. +; RUN: count 0 < %t2.o.imports + +; The imports file for empty.ll is empty but should exist. +; RUN: count 0 < %t3.o.imports + +; The index file should be created even for the input with an empty summary. +; RUN: ls %t3.o.thinlto.bc + +; Ensure lld generates error if unable to write to imports file. +; RUN: rm -f %t3.o.imports +; RUN: touch %t3.o.imports +; RUN: chmod 400 %t3.o.imports +; RUN: not %lld --thinlto-index-only --thinlto-emit-imports-files -dylib %t1.o %t2.o %t3.o -o /dev/null 2>&1 \ +; RUN: | FileCheck -DMSG=%errc_EACCES %s --check-prefix=ERR +; ERR: cannot open {{.*}}3.o.imports: [[MSG]] + +; Ensure lld doesn't generate import files when thinlto-index-only is not enabled +; RUN: rm -f %t1.o.imports +; RUN: rm -f %t2.o.imports +; RUN: rm -f %t3.o.imports +; RUN: %lld --thinlto-emit-imports-files -dylib %t1.o %t2.o %t3.o -o %t4 +; RUN: not ls %t1.o.imports +; RUN: not ls %t2.o.imports +; RUN: not ls %t3.o.imports + +;--- f.ll +target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-darwin" + +declare void @g(...) + +define void @f() { +entry: + call void (...) @g() + ret void +} + +;--- g.ll +target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-darwin" + +define void @g() { +entry: + ret void +} + +;--- empty.ll +target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-darwin" diff --git a/lld/test/MachO/thinlto-emit-index.ll b/lld/test/MachO/thinlto-emit-index.ll new file mode 100644 index 0000000..fa87609 --- /dev/null +++ b/lld/test/MachO/thinlto-emit-index.ll @@ -0,0 +1,122 @@ +; REQUIRES: x86 +; RUN: rm -rf %t; split-file %s %t + +;; Mostly copied/updated from thinlto-index-only.ll +;; First ensure that the ThinLTO handling in lld handles +;; bitcode without summary sections gracefully and generates index file. +; RUN: llvm-as %t/f.ll -o %t/1.o +; RUN: llvm-as %t/g.ll -o %t/2.o +; RUN: %lld --thinlto-emit-index-files -dylib %t/1.o %t/2.o -o %t/3 +; RUN: ls %t/2.o.thinlto.bc +; RUN: ls %t/3 +; RUN: %lld -dylib %t/1.o %t/2.o -o %t/3 +; RUN: llvm-nm %t/3 | FileCheck %s --check-prefix=NM + +;; Basic ThinLTO tests. +; RUN: opt -module-summary %t/f.ll -o %t/1.o +; RUN: opt -module-summary %t/g.ll -o %t/2.o +; RUN: opt -module-summary %t/empty.ll -o %t/3.o + +;; Ensure lld generates an index and also a binary if requested. +; RUN: %lld --thinlto-emit-index-files -dylib %t/1.o %t/2.o -o %t/4 +; RUN: llvm-bcanalyzer -dump %t/1.o.thinlto.bc | FileCheck %s -DP1=%t/1.o -DP2=%t/2.o --check-prefix=BACKEND1 +; RUN: llvm-bcanalyzer -dump %t/2.o.thinlto.bc | FileCheck %s -DP2=%t/2.o --check-prefix=BACKEND2 +; RUN: ls %t/4 + +;; Ensure lld generates an index and not a binary if both emit-index and index-only are present. +; RUN: %lld --thinlto-emit-index-files --thinlto-index-only -dylib %t/1.o %t/2.o -o %t/5 +; RUN: not ls %t/5 + +;; Ensure lld generates an index even if the file is wrapped in --start-lib/--end-lib +; RUN: rm -f %t/2.o.thinlto.bc +; RUN: %lld --thinlto-emit-index-files -dylib %t/1.o %t/3.o --start-lib %t/2.o --end-lib -o %t/6 +; RUN: llvm-dis < %t/2.o.thinlto.bc | grep -q '\^0 = module:' +; RUN: ls %t/6 + +;; Test that LLD generates an empty index even for lazy object file that is not added to link. +;; Test that LLD also generates empty imports file with the --thinlto-emit-imports-files option. +; RUN: rm -f %t/1.o.thinlto.bc %t/1.o.imports +; RUN: %lld --thinlto-emit-index-files -dylib %t/2.o --start-lib %t/1.o --end-lib \ +; RUN: --thinlto-emit-imports-files -o %t/7 +; RUN: ls %t/7 +; RUN: ls %t/1.o.thinlto.bc +; RUN: ls %t/1.o.imports + +;; Ensure LLD generates an empty index for each bitcode file even if all bitcode files are lazy. +; RUN: rm -f %t/1.o.thinlto.bc +; RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin /dev/null -o dummy.o +; RUN: %lld --thinlto-emit-index-files -dylib dummy.o --start-lib %t/1.o --end-lib -o %t/8 +; RUN: ls %t/8 +; RUN: ls %t/1.o.thinlto.bc + +;; Test that LLD errors out when run with suffix replacement, or prefix replacement +; RUN: not %lld --thinlto-emit-index-files -dylib %t/2.o --start-lib %t/1.o --end-lib \ +; RUN: --thinlto-prefix-replace="abc;xyz" 2>&1 | FileCheck %s --check-prefix=ERR1 +; ERR1: --thinlto-prefix-replace is not supported with --thinlto-emit-index-files + +; RUN: not %lld --thinlto-emit-index-files -dylib %t/2.o --start-lib %t/1.o --end-lib \ +; RUN: --thinlto-object-suffix-replace="abc;xyz" 2>&1 | FileCheck %s --check-prefix=ERR2 +; ERR2: --thinlto-object-suffix-replace is not supported with --thinlto-emit-index-files + +;; But not when passed with index only as well +; RUN: %lld --thinlto-emit-index-files -dylib %t/2.o --start-lib %t/1.o --end-lib \ +; RUN: --thinlto-prefix-replace="abc;xyz" --thinlto-index-only + +; RUN: %lld --thinlto-emit-index-files -dylib %t/2.o --start-lib %t/1.o --end-lib \ +; RUN: --thinlto-object-suffix-replace="abc;xyz" --thinlto-index-only + +; NM: T _f + +;; The backend index for this module contains summaries from itself and +;; Inputs/thinlto.ll, as it imports from the latter. +; BACKEND1: +; BACKEND2-NEXT: +; BACKEND2-NEXT: &1 | FileCheck --check-prefix=NM %s +; RUN: llvm-readobj -h %t4/0.x86_64.lto.o | FileCheck %s + +;; Ensure lld emits empty combined module if specific obj-path. +; RUN: rm -fr %t.dir/objpath && mkdir -p %t.dir/objpath +; RUN: %lld -object_path_lto %t4.o -dylib %t1.o %t2.o -o %t.dir/objpath/a.out -save-temps +; RUN: ls %t.dir/objpath/a.out*.lto.* | count 3 + +;; Ensure lld does not emit empty combined module in default. +; RUN: rm -fr %t.dir/objpath && mkdir -p %t.dir/objpath +; RUN: %lld -dylib %t1.o %t2.o -o %t.dir/objpath/a.out -save-temps +; RUN: ls %t.dir/objpath/a.out*.lto.* | count 2 + +; NM: no symbols +; CHECK: Format: Mach-O 64-bit x86-64 + +;--- f.ll +target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-darwin" + +declare void @g(...) + +define void @f() { +entry: + call void (...) @g() + ret void +} + +;--- g.ll +target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-darwin" + +define void @g() { +entry: + ret void +} diff --git a/lld/test/MachO/thinlto-object-suffix-replace.ll b/lld/test/MachO/thinlto-object-suffix-replace.ll new file mode 100644 index 0000000..83f3f9c8 --- /dev/null +++ b/lld/test/MachO/thinlto-object-suffix-replace.ll @@ -0,0 +1,47 @@ +; REQUIRES: x86 +;; Test to make sure the thinlto-object-suffix-replace option is handled +;; correctly. +; RUN: rm -rf %t && mkdir %t && cd %t + +;; Generate bitcode file with summary, as well as a minimized bitcode without +; the debug metadata for the thin link. +; RUN: opt --thinlto-bc %s -thin-link-bitcode-file=1.thinlink.bc -o 1.o + +;; First perform the thin link on the normal bitcode file, and save the +;; resulting index. +; RUN: %lld --thinlto-index-only -dylib 1.o -o 3 +; RUN: cp 1.o.thinlto.bc 1.o.thinlto.bc.orig + +;; Next perform the thin link on the minimized bitcode file, and compare dump +;; of the resulting index to the above dump to ensure they are identical. +; RUN: rm -f 1.o.thinlto.bc +;; Make sure it isn't inadvertently using the regular bitcode file. +; RUN: rm -f 1.o +; RUN: %lld --thinlto-index-only --thinlto-object-suffix-replace=".thinlink.bc;.o" \ +; RUN: -dylib 1.thinlink.bc -o 3 +; RUN: cmp 1.o.thinlto.bc.orig 1.o.thinlto.bc + +;; Ensure lld generates error if object suffix replace option does not have 'old;new' format +; RUN: rm -f 1.o.thinlto.bc +; RUN: not %lld --thinlto-index-only --thinlto-object-suffix-replace="abc:def" -dylib 1.thinlink.bc \ +; RUN: -o 3 2>&1 | FileCheck %s --check-prefix=ERR1 +; ERR1: --thinlto-object-suffix-replace= expects 'old;new' format, but got abc:def + +;; If filename does not end with old suffix, no suffix change should occur, +;; so ".thinlto.bc" will simply be appended to the input file name. +; RUN: rm -f 1.thinlink.bc.thinlto.bc +; RUN: %lld --thinlto-index-only --thinlto-object-suffix-replace=".abc;.o" -dylib 1.thinlink.bc -o /dev/null +; RUN: ls 1.thinlink.bc.thinlto.bc + +target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-darwin" + +define void @f() { +entry: + ret void +} + +!llvm.dbg.cu = !{} + +!1 = !{i32 2, !"Debug Info Version", i32 3} +!llvm.module.flags = !{!1} diff --git a/lld/test/MachO/thinlto-prefix-replace.ll b/lld/test/MachO/thinlto-prefix-replace.ll new file mode 100644 index 0000000..d46163f --- /dev/null +++ b/lld/test/MachO/thinlto-prefix-replace.ll @@ -0,0 +1,24 @@ +; REQUIRES: x86 + +;; Check that changing the output path via thinlto-prefix-replace works +; RUN: mkdir -p %t/oldpath +; RUN: opt -module-summary %s -o %t/oldpath/thinlto_prefix_replace.o + +;; Ensure that there is no existing file at the new path, so we properly +;; test the creation of the new file there. +; RUN: rm -f %t/newpath/thinlto_prefix_replace.o.thinlto.bc +; RUN: %lld --thinlto-index-only --thinlto-prefix-replace="%t/oldpath/;%t/newpath/" -dylib %t/oldpath/thinlto_prefix_replace.o -o %t/thinlto_prefix_replace +; RUN: ls %t/newpath/thinlto_prefix_replace.o.thinlto.bc + +;; Ensure that lld generates error if prefix replace option does not have 'old;new' format. +; RUN: rm -f %t/newpath/thinlto_prefix_replace.o.thinlto.bc +; RUN: not %lld --thinlto-index-only --thinlto-prefix-replace=abc:def -dylib %t/oldpath/thinlto_prefix_replace.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=ERR +; ERR: --thinlto-prefix-replace= expects 'old;new' format, but got abc:def + +target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-darwin" + +define void @f() { +entry: + ret void +} -- 2.7.4