Index: lld/COFF/Config.h =================================================================== --- lld/COFF/Config.h +++ lld/COFF/Config.h @@ -218,8 +218,9 @@ // Used for /thinlto-index-only: llvm::StringRef thinLTOIndexOnlyArg; - // Used for /thinlto-object-prefix-replace: + // Used for /thinlto-prefix-replace: std::pair thinLTOPrefixReplace; + llvm::StringRef thinLTOIndexFileObjectPrefixReplace; // Used for /thinlto-object-suffix-replace: std::pair thinLTOObjectSuffixReplace; Index: lld/COFF/LTO.cpp =================================================================== --- lld/COFF/LTO.cpp +++ lld/COFF/LTO.cpp @@ -116,6 +116,7 @@ backend = lto::createWriteIndexesThinBackend( std::string(ctx.config.thinLTOPrefixReplace.first), std::string(ctx.config.thinLTOPrefixReplace.second), + std::string(ctx.config.thinLTOIndexFileObjectPrefixReplace), ctx.config.thinLTOEmitImportsFiles, indexFile.get(), OnIndexWrite); } else { backend = lto::createInProcessThinBackend( Index: lld/ELF/Config.h =================================================================== --- lld/ELF/Config.h +++ lld/ELF/Config.h @@ -171,6 +171,7 @@ llvm::StringRef ltoBasicBlockSections; std::pair thinLTOObjectSuffixReplace; std::pair thinLTOPrefixReplace; + llvm::StringRef thinLTOIndexFileObjectPrefixReplace; std::string rpath; llvm::SmallVector versionDefinitions; llvm::SmallVector auxiliaryList; Index: lld/ELF/Driver.cpp =================================================================== --- lld/ELF/Driver.cpp +++ lld/ELF/Driver.cpp @@ -1020,6 +1020,13 @@ return names.takeVector(); } +static std::pair, StringRef> +getOldNewOptionsExtra(opt::InputArgList &args, unsigned id) { + std::pair ret = getOldNewOptions(args, id); + std::pair newOptions = ret.second.split(';'); + return {{ret.first, newOptions.first}, newOptions.second}; +} + static bool getIsRela(opt::InputArgList &args) { // If -z rel or -z rela is specified, use the last option. for (auto *arg : args.filtered_reverse(OPT_z)) { @@ -1236,8 +1243,9 @@ 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); + std::tie(config->thinLTOPrefixReplace, + config->thinLTOIndexFileObjectPrefixReplace) = + getOldNewOptionsExtra(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 " @@ -1246,6 +1254,11 @@ error("--thinlto-prefix-replace is not supported with " "--thinlto-emit-index-files"); } + if (!config->thinLTOIndexFileObjectPrefixReplace.empty() && + config->thinLTOIndexOnlyArg.empty()) { + error("--thinlto-prefix-replace=old_dir;new_dir;obj_dir only supported" + " when thinlto-index-only specifies an output file"); + } config->thinLTOModulesToCompile = args::getStrings(args, OPT_thinlto_single_module_eq); config->timeTraceEnabled = args.hasArg(OPT_time_trace_eq); Index: lld/ELF/LTO.cpp =================================================================== --- lld/ELF/LTO.cpp +++ lld/ELF/LTO.cpp @@ -198,6 +198,7 @@ backend = lto::createWriteIndexesThinBackend( std::string(config->thinLTOPrefixReplace.first), std::string(config->thinLTOPrefixReplace.second), + std::string(config->thinLTOIndexFileObjectPrefixReplace), config->thinLTOEmitImportsFiles, indexFile.get(), onIndexWrite); } else { backend = lto::createInProcessThinBackend( Index: lld/MachO/Config.h =================================================================== --- lld/MachO/Config.h +++ lld/MachO/Config.h @@ -175,6 +175,7 @@ llvm::StringRef thinLTOIndexOnlyArg; std::pair thinLTOObjectSuffixReplace; std::pair thinLTOPrefixReplace; + llvm::StringRef thinLTOIndexFileObjectPrefixReplace; bool deadStripDylibs = false; bool demangle = false; bool deadStrip = false; Index: lld/MachO/Driver.cpp =================================================================== --- lld/MachO/Driver.cpp +++ lld/MachO/Driver.cpp @@ -872,6 +872,13 @@ return ret; } +static std::pair, StringRef> +getOldNewOptionsExtra(opt::InputArgList &args, unsigned id) { + std::pair ret = getOldNewOptions(args, id); + std::pair newOptions = ret.second.split(';'); + return {{ret.first, newOptions.first}, newOptions.second}; +} + static void parseClangOption(StringRef opt, const Twine &msg) { std::string err; raw_string_ostream os(err); @@ -1580,8 +1587,9 @@ 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); + std::tie(config->thinLTOPrefixReplace, + config->thinLTOIndexFileObjectPrefixReplace) = + getOldNewOptionsExtra(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 " @@ -1590,6 +1598,11 @@ error("--thinlto-prefix-replace is not supported with " "--thinlto-emit-index-files"); } + if (!config->thinLTOIndexFileObjectPrefixReplace.empty() && + config->thinLTOIndexOnlyArg.empty()) { + error("--thinlto-prefix-replace=old_dir;new_dir;obj_dir must be used with " + "--thinlto-index-only="); + } 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); Index: lld/MachO/LTO.cpp =================================================================== --- lld/MachO/LTO.cpp +++ lld/MachO/LTO.cpp @@ -101,6 +101,7 @@ backend = lto::createWriteIndexesThinBackend( std::string(config->thinLTOPrefixReplace.first), std::string(config->thinLTOPrefixReplace.second), + std::string(config->thinLTOIndexFileObjectPrefixReplace), config->thinLTOEmitImportsFiles, indexFile.get(), onIndexWrite); } else { backend = lto::createInProcessThinBackend( Index: lld/test/ELF/lto/thinlto-index-file-object-prefix-replace.ll =================================================================== --- /dev/null +++ lld/test/ELF/lto/thinlto-index-file-object-prefix-replace.ll @@ -0,0 +1,52 @@ +; REQUIRES: x86 +; RUN: rm -rf %t && mkdir %t && cd %t +; RUN: mkdir -p oldpath oldpath/prefix_replace +; RUN: opt -module-summary %s -o oldpath/prefix_replace/1.o +; RUN: opt -module-summary %p/Inputs/thinlto.ll -o oldpath/prefix_replace/2.o +; RUN: opt -module-summary %p/Inputs/thinlto_empty.ll -o oldpath/3.o + +;; Ensure lld writes linked files to linked objects file +; RUN: ld.lld --thinlto-index-only=1.txt -shared oldpath/prefix_replace/1.o oldpath/prefix_replace/2.o oldpath/3.o -o /dev/null +; RUN: ls oldpath/prefix_replace/1.o.thinlto.bc +; RUN: ls oldpath/prefix_replace/2.o.thinlto.bc +; RUN: ls oldpath/3.o.thinlto.bc +; RUN: FileCheck --match-full-lines --check-prefix=CHECK-NO-REPLACE %s < 1.txt +; CHECK-NO-REPLACE: oldpath/prefix_replace/1.o +; CHECK-NO-REPLACE: oldpath/prefix_replace/2.o +; CHECK-NO-REPLACE: oldpath/3.o + +;; Check that this also works with thinlto-prefix-replace +; RUN: ld.lld --thinlto-index-only=2.txt --thinlto-prefix-replace="oldpath/;newpath/" -shared oldpath/prefix_replace/1.o oldpath/prefix_replace/2.o oldpath/3.o -o /dev/null +; RUN: ls newpath/prefix_replace/1.o.thinlto.bc +; RUN: ls newpath/prefix_replace/2.o.thinlto.bc +; RUN: ls newpath/3.o.thinlto.bc +; RUN: FileCheck --match-full-lines --check-prefix=CHECK-REPLACE-PREFIX %s < 2.txt +; CHECK-REPLACE-PREFIX: newpath/prefix_replace/1.o +; CHECK-REPLACE-PREFIX: newpath/prefix_replace/2.o +; CHECK-REPLACE-PREFIX: newpath/3.o + + +;; Check that this also works with replacing the prefix of linked objects +; RUN: ld.lld --thinlto-index-only=3.txt --thinlto-prefix-replace="oldpath/;newpath/;objpath/" -shared oldpath/prefix_replace/1.o oldpath/prefix_replace/2.o oldpath/3.o -o /dev/null +; RUN: ls newpath/prefix_replace/1.o.thinlto.bc +; RUN: ls newpath/prefix_replace/2.o.thinlto.bc +; RUN: ls newpath/3.o.thinlto.bc +; RUN: FileCheck --match-full-lines --check-prefix=CHECK-REPLACE-OBJECT-PREFIX %s < 3.txt +; CHECK-REPLACE-OBJECT-PREFIX: objpath/prefix_replace/1.o +; CHECK-REPLACE-OBJECT-PREFIX: objpath/prefix_replace/2.o +; CHECK-REPLACE-OBJECT-PREFIX: objpath/3.o + +; Ensure that lld generates error if prefix replace option have 'old;new;obj' format but index file is not set. Ensure that the error is about thinlto-prefix-replace +; RUN: not ld.lld --thinlto-prefix-replace="oldpath/;newpath/;objpath/" -shared oldpath/prefix_replace/1.o oldpath/prefix_replace/2.o oldpath/3.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=ERR +; ERR: thinlto-prefix-replace + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +declare void @g(...) + +define void @f() { +entry: + call void (...) @g() + ret void +} \ No newline at end of file Index: lld/test/MachO/thinlto-index-file-object-prefix-replace.ll =================================================================== --- /dev/null +++ lld/test/MachO/thinlto-index-file-object-prefix-replace.ll @@ -0,0 +1,67 @@ +; REQUIRES: x86 +; RUN: rm -rf %t ; split-file %s %t && cd %t +; RUN: mkdir -p oldpath oldpath/prefix_replace + +; RUN: opt -module-summary f.ll -o oldpath/prefix_replace/1.o +; RUN: opt -module-summary g.ll -o oldpath/prefix_replace/2.o +; RUN: opt -module-summary empty.ll -o oldpath/3.o + +;; Ensure lld writes linked files to linked objects file +; RUN: ld.lld --thinlto-index-only=1.txt -shared oldpath/prefix_replace/1.o oldpath/prefix_replace/2.o oldpath/3.o -o /dev/null +; RUN: ls oldpath/prefix_replace/1.o.thinlto.bc +; RUN: ls oldpath/prefix_replace/2.o.thinlto.bc +; RUN: ls oldpath/3.o.thinlto.bc +; RUN: FileCheck --match-full-lines --check-prefix=CHECK-NO-REPLACE %s < 1.txt +; CHECK-NO-REPLACE: oldpath/prefix_replace/1.o +; CHECK-NO-REPLACE: oldpath/prefix_replace/2.o +; CHECK-NO-REPLACE: oldpath/3.o + +;; Check that this also works with thinlto-prefix-replace +; RUN: ld.lld --thinlto-index-only=2.txt --thinlto-prefix-replace="oldpath/;newpath/" -shared oldpath/prefix_replace/1.o oldpath/prefix_replace/2.o oldpath/3.o -o /dev/null +; RUN: ls newpath/prefix_replace/1.o.thinlto.bc +; RUN: ls newpath/prefix_replace/2.o.thinlto.bc +; RUN: ls newpath/3.o.thinlto.bc +; RUN: FileCheck --match-full-lines --check-prefix=CHECK-REPLACE-PREFIX %s < 2.txt +; CHECK-REPLACE-PREFIX: newpath/prefix_replace/1.o +; CHECK-REPLACE-PREFIX: newpath/prefix_replace/2.o +; CHECK-REPLACE-PREFIX: newpath/3.o + + +;; Check that this also works with replacing the prefix of linked objects +; RUN: ld.lld --thinlto-index-only=3.txt --thinlto-prefix-replace="oldpath/;newpath/;objpath/" -shared oldpath/prefix_replace/1.o oldpath/prefix_replace/2.o oldpath/3.o -o /dev/null +; RUN: ls newpath/prefix_replace/1.o.thinlto.bc +; RUN: ls newpath/prefix_replace/2.o.thinlto.bc +; RUN: ls newpath/3.o.thinlto.bc +; RUN: FileCheck --match-full-lines --check-prefix=CHECK-REPLACE-OBJECT-PREFIX %s < 3.txt +; CHECK-REPLACE-OBJECT-PREFIX: objpath/prefix_replace/1.o +; CHECK-REPLACE-OBJECT-PREFIX: objpath/prefix_replace/2.o +; CHECK-REPLACE-OBJECT-PREFIX: objpath/3.o + +; Ensure that lld generates error if prefix replace option have 'old;new;obj' format but index file is not set. Ensure that the error is about thinlto-prefix-replace +; RUN: not ld.lld --thinlto-prefix-replace="oldpath/;newpath/;objpath/" -shared oldpath/prefix_replace/1.o oldpath/prefix_replace/2.o oldpath/3.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=ERR +; ERR: thinlto-prefix-replace + +;--- 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: llvm/include/llvm/LTO/LTO.h =================================================================== --- llvm/include/llvm/LTO/LTO.h +++ llvm/include/llvm/LTO/LTO.h @@ -219,11 +219,14 @@ /// ShouldEmitImportsFiles is true it also writes a list of imported files to a /// similar path with ".imports" appended instead. /// LinkedObjectsFile is an output stream to write the list of object files for -/// the final ThinLTO linking. Can be nullptr. -/// OnWrite is callback which receives module identifier and notifies LTO user -/// that index file for the module (and optionally imports file) was created. +/// the final ThinLTO linking. Can be nullptr. If LinkedObjectsFile is not +/// nullptr and NewLinkObjectPrefix is not empty then it replaces the prefix of +/// the objects for NewLinkObjectPrefix instead of NewPrefix. OnWrite is +/// callback which receives module identifier and notifies LTO user that index +/// file for the module (and optionally imports file) was created. ThinBackend createWriteIndexesThinBackend(std::string OldPrefix, std::string NewPrefix, + std::string NewLinkObjectPrefix, bool ShouldEmitImportsFiles, raw_fd_ostream *LinkedObjectsFile, IndexWriteCallback OnWrite); Index: llvm/lib/LTO/LTO.cpp =================================================================== --- llvm/lib/LTO/LTO.cpp +++ llvm/lib/LTO/LTO.cpp @@ -1415,18 +1415,20 @@ namespace { class WriteIndexesThinBackend : public ThinBackendProc { - std::string OldPrefix, NewPrefix; + std::string OldPrefix, NewPrefix, NewLinkObjectPrefix; raw_fd_ostream *LinkedObjectsFile; public: WriteIndexesThinBackend( const Config &Conf, ModuleSummaryIndex &CombinedIndex, const StringMap &ModuleToDefinedGVSummaries, - std::string OldPrefix, std::string NewPrefix, bool ShouldEmitImportsFiles, + std::string OldPrefix, std::string NewPrefix, + std::string NewLinkObjectPrefix, bool ShouldEmitImportsFiles, raw_fd_ostream *LinkedObjectsFile, lto::IndexWriteCallback OnWrite) : ThinBackendProc(Conf, CombinedIndex, ModuleToDefinedGVSummaries, OnWrite, ShouldEmitImportsFiles), OldPrefix(OldPrefix), NewPrefix(NewPrefix), + NewLinkObjectPrefix(NewLinkObjectPrefix), LinkedObjectsFile(LinkedObjectsFile) {} Error start( @@ -1439,8 +1441,13 @@ std::string NewModulePath = getThinLTOOutputFile(std::string(ModulePath), OldPrefix, NewPrefix); - if (LinkedObjectsFile) - *LinkedObjectsFile << NewModulePath << '\n'; + if (LinkedObjectsFile) { + std::string ObjectPrefix = + NewLinkObjectPrefix.empty() ? NewPrefix : NewLinkObjectPrefix; + std::string LinkedObjectsFilePath = getThinLTOOutputFile( + std::string(ModulePath), OldPrefix, ObjectPrefix); + *LinkedObjectsFile << LinkedObjectsFilePath << '\n'; + } if (auto E = emitFiles(ImportList, ModulePath, NewModulePath)) return E; @@ -1459,14 +1466,16 @@ } // end anonymous namespace ThinBackend lto::createWriteIndexesThinBackend( - std::string OldPrefix, std::string NewPrefix, bool ShouldEmitImportsFiles, + std::string OldPrefix, std::string NewPrefix, + std::string NewLinkObjectPrefix, bool ShouldEmitImportsFiles, raw_fd_ostream *LinkedObjectsFile, IndexWriteCallback OnWrite) { return [=](const Config &Conf, ModuleSummaryIndex &CombinedIndex, const StringMap &ModuleToDefinedGVSummaries, AddStreamFn AddStream, FileCache Cache) { return std::make_unique( Conf, CombinedIndex, ModuleToDefinedGVSummaries, OldPrefix, NewPrefix, - ShouldEmitImportsFiles, LinkedObjectsFile, OnWrite); + NewLinkObjectPrefix, ShouldEmitImportsFiles, LinkedObjectsFile, + OnWrite); }; } Index: llvm/tools/llvm-lto2/llvm-lto2.cpp =================================================================== --- llvm/tools/llvm-lto2/llvm-lto2.cpp +++ llvm/tools/llvm-lto2/llvm-lto2.cpp @@ -320,11 +320,12 @@ ThinBackend Backend; if (ThinLTODistributedIndexes) - Backend = - createWriteIndexesThinBackend(/* OldPrefix */ "", - /* NewPrefix */ "", ThinLTOEmitImports, - /* LinkedObjectsFile */ nullptr, - /* OnWrite */ {}); + Backend = createWriteIndexesThinBackend(/* OldPrefix */ "", + /* NewPrefix */ "", + /* NewLinkObjectPrefix */ "", + ThinLTOEmitImports, + /* LinkedObjectsFile */ nullptr, + /* OnWrite */ {}); else Backend = createInProcessThinBackend( llvm::heavyweight_hardware_concurrency(Threads),