Index: lld/MachO/Config.h =================================================================== --- lld/MachO/Config.h +++ lld/MachO/Config.h @@ -133,6 +133,9 @@ bool emitEncryptionInfo = false; bool emitInitOffsets = false; bool emitChainedFixups = false; + bool thinLTOEmitImportsFiles; + bool thinLTOEmitIndexFiles; + bool thinLTOIndexOnly; bool timeTraceEnabled = false; bool dataConst = false; bool dedupLiterals = true; @@ -164,6 +167,9 @@ 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; Index: lld/MachO/Driver.cpp =================================================================== --- lld/MachO/Driver.cpp +++ lld/MachO/Driver.cpp @@ -841,6 +841,20 @@ 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); @@ -1513,6 +1527,25 @@ 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); @@ -1799,11 +1832,20 @@ // 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(); Index: lld/MachO/InputFiles.h =================================================================== --- lld/MachO/InputFiles.h +++ lld/MachO/InputFiles.h @@ -355,6 +355,7 @@ return detail::findCommands(anyHdr, 0, types...); } +std::string replaceThinLTOSuffix(StringRef path); } // namespace macho std::string toString(const macho::InputFile *file); Index: lld/MachO/InputFiles.cpp =================================================================== --- lld/MachO/InputFiles.cpp +++ lld/MachO/InputFiles.cpp @@ -2171,6 +2171,9 @@ : 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. @@ -2211,6 +2214,15 @@ } } +std::string macho::replaceThinLTOSuffix(StringRef path) { + StringRef suffix = config->thinLTOObjectSuffixReplace.first; + StringRef repl = config->thinLTOObjectSuffixReplace.second; + + if (path.consume_back(suffix)) + return (path + repl).str(); + return std::string(path); +} + void macho::extract(InputFile &file, StringRef reason) { assert(file.lazy); file.lazy = false; Index: lld/MachO/LTO.h =================================================================== --- lld/MachO/LTO.h +++ 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 @@ std::unique_ptr ltoObj; std::vector> buf; std::vector> files; + std::unique_ptr indexFile; + llvm::DenseSet thinIndices; }; } // namespace lld::macho Index: lld/MachO/LTO.cpp =================================================================== --- lld/MachO/LTO.cpp +++ 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::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 @@ 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 @@ } 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 @@ 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 @@ }, 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 @@ } } + 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 @@ 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); } Index: lld/MachO/Options.td =================================================================== --- lld/MachO/Options.td +++ lld/MachO/Options.td @@ -28,9 +28,22 @@ 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=">, Index: lld/test/ELF/lto/thinlto-obj-path.ll =================================================================== --- lld/test/ELF/lto/thinlto-obj-path.ll +++ lld/test/ELF/lto/thinlto-obj-path.ll @@ -21,7 +21,7 @@ ;; Ensure lld does not emit empty combined module in default. ; RUN: rm -fr %t.dir/objpath && mkdir -p %t.dir/objpath -; RUN: ld.lld %t1.o %t2.o -o %t.dir/objpath/a.out --save-temps +; RUN: ld.lld -shared %t1.o %t2.o -o %t.dir/objpath/a.out --save-temps ; RUN: ls %t.dir/objpath/a.out*.lto.* | count 2 ; CHECK: Format: elf64-x86-64 Index: lld/test/MachO/lto-object-path.ll =================================================================== --- lld/test/MachO/lto-object-path.ll +++ 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 Index: lld/test/MachO/thinlto-emit-imports.ll =================================================================== --- /dev/null +++ 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" Index: lld/test/MachO/thinlto-emit-index.ll =================================================================== --- /dev/null +++ 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 %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} Index: lld/test/MachO/thinlto-prefix-replace.ll =================================================================== --- /dev/null +++ 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 +}