diff --git a/lld/COFF/Config.h b/lld/COFF/Config.h --- a/lld/COFF/Config.h +++ b/lld/COFF/Config.h @@ -182,6 +182,12 @@ // Used for /thinlto-index-only: llvm::StringRef ThinLTOIndexOnlyArg; + // Used for /thinlto-object-prefix-replace: + std::pair ThinLTOPrefixReplace; + + // Used for /thinlto-object-suffix-replace: + std::pair ThinLTOObjectSuffixReplace; + uint64_t ImageBase = -1; uint64_t FileAlign = 512; uint64_t StackReserve = 1024 * 1024; diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp --- a/lld/COFF/Driver.cpp +++ b/lld/COFF/Driver.cpp @@ -86,6 +86,20 @@ return !errorCount(); } +// 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; +} + // Drop directory components and replace extension with ".exe" or ".dll". static std::string getOutputPath(StringRef Path) { auto P = Path.find_last_of("\\/"); @@ -1446,6 +1460,11 @@ Args.hasArg(OPT_thinlto_index_only_arg); Config->ThinLTOIndexOnlyArg = Args.getLastArgValue(OPT_thinlto_index_only_arg); + Config->ThinLTOPrefixReplace = + getOldNewOptions(Args, OPT_thinlto_prefix_replace); + Config->ThinLTOObjectSuffixReplace = + getOldNewOptions(Args, OPT_thinlto_object_suffix_replace); + // Handle miscellaneous boolean flags. Config->AllowBind = Args.hasFlag(OPT_allowbind, OPT_allowbind_no, true); Config->AllowIsolation = diff --git a/lld/COFF/InputFiles.h b/lld/COFF/InputFiles.h --- a/lld/COFF/InputFiles.h +++ b/lld/COFF/InputFiles.h @@ -311,6 +311,8 @@ std::vector Symbols; }; + +std::string replaceThinLTOSuffix(StringRef Path); } // namespace coff std::string toString(const coff::InputFile *File); diff --git a/lld/COFF/InputFiles.cpp b/lld/COFF/InputFiles.cpp --- a/lld/COFF/InputFiles.cpp +++ b/lld/COFF/InputFiles.cpp @@ -783,6 +783,8 @@ uint64_t OffsetInArchive) : InputFile(BitcodeKind, MB) { 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 archives define two members with the same name, this @@ -849,6 +851,15 @@ return IMAGE_FILE_MACHINE_UNKNOWN; } } + +std::string replaceThinLTOSuffix(StringRef Path) { + StringRef Suffix = Config->ThinLTOObjectSuffixReplace.first; + StringRef Repl = Config->ThinLTOObjectSuffixReplace.second; + + if (Path.consume_back(Suffix)) + return (Path + Repl).str(); + return Path; +} } // namespace coff } // namespace lld diff --git a/lld/COFF/LTO.cpp b/lld/COFF/LTO.cpp --- a/lld/COFF/LTO.cpp +++ b/lld/COFF/LTO.cpp @@ -55,7 +55,9 @@ } static std::string getThinLTOOutputFile(StringRef ModulePath) { - return ModulePath; + return lto::getThinLTOOutputFile(ModulePath, + Config->ThinLTOPrefixReplace.first, + Config->ThinLTOPrefixReplace.second); } static lto::Config createConfig() { @@ -97,7 +99,8 @@ if (Config->ThinLTOIndexOnly) { auto OnIndexWrite = [&](StringRef S) { ThinIndices.erase(S); }; Backend = lto::createWriteIndexesThinBackend( - "", "", Config->ThinLTOEmitImportsFiles, IndexFile.get(), OnIndexWrite); + Config->ThinLTOPrefixReplace.first, Config->ThinLTOPrefixReplace.second, + Config->ThinLTOEmitImportsFiles, IndexFile.get(), OnIndexWrite); } else if (Config->ThinLTOJobs != 0) { Backend = lto::createInProcessThinBackend(Config->ThinLTOJobs); } diff --git a/lld/COFF/Options.td b/lld/COFF/Options.td --- a/lld/COFF/Options.td +++ b/lld/COFF/Options.td @@ -185,6 +185,12 @@ def thinlto_index_only_arg : P< "thinlto-index-only", "-thinlto-index-only and also write native module names to file">; +def thinlto_object_suffix_replace : P< + "thinlto-object-suffix-replace", + "'old;new' replace old suffix with new suffix in ThinLTO index">; +def thinlto_prefix_replace: P< + "thinlto-prefix-replace", + "'old;new' replace old prefix with new prefix in ThinLTO outputs">; def dash_dash_version : Flag<["--"], "version">, HelpText<"Print version information">; defm threads: B<"threads", diff --git a/lld/test/COFF/thinlto-object-suffix-replace.ll b/lld/test/COFF/thinlto-object-suffix-replace.ll new file mode 100644 --- /dev/null +++ b/lld/test/COFF/thinlto-object-suffix-replace.ll @@ -0,0 +1,50 @@ +; REQUIRES: x86 + +; Test to make sure the thinlto-object-suffix-replace option is handled +; correctly. + +; 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=%t1.thinlink.bc -o %t1.obj + +; First perform the thin link on the normal bitcode file, and save the +; resulting index. +; RUN: lld-link -thinlto-index-only -entry:main %t1.obj -out:%t3.exe +; RUN: cp %t1.obj.thinlto.bc %t1.obj.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 %t1.obj.thinlto.bc +; Make sure it isn't inadvertently using the regular bitcode file. +; RUN: rm -f %t1.obj +; RUN: lld-link -entry:main -thinlto-index-only \ +; RUN: -thinlto-object-suffix-replace:".thinlink.bc;.obj" \ +; RUN: %t1.thinlink.bc -out:%t3.exe +; RUN: diff %t1.obj.thinlto.bc.orig %t1.obj.thinlto.bc + +; Ensure lld generates error if suffix replace option not in 'old;new' format. +; RUN: rm -f %t1.obj.thinlto.bc +; RUN: not lld-link -entry:main -thinlto-index-only \ +; RUN: -thinlto-object-suffix-replace:"abc:def" %t1.thinlink.bc \ +; RUN: -out:%t3.exe 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 %t1.thinlink.bc.thinlto.bc +; RUN: lld-link -entry:main -thinlto-index-only \ +; RUN: -thinlto-object-suffix-replace:".abc;.obj" %t1.thinlink.bc -out:%t3.exe +; RUN: ls %t1.thinlink.bc.thinlto.bc + +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc19.0.24215" + +define void @main() { +entry: + ret void +} + +!llvm.dbg.cu = !{} + +!1 = !{i32 2, !"Debug Info Version", i32 3} +!llvm.module.flags = !{!1} diff --git a/lld/test/COFF/thinlto-prefix-replace.ll b/lld/test/COFF/thinlto-prefix-replace.ll new file mode 100644 --- /dev/null +++ b/lld/test/COFF/thinlto-prefix-replace.ll @@ -0,0 +1,26 @@ +; 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/t.obj + +; 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/t.obj.thinlto.bc +; RUN: lld-link -entry:main -thinlto-index-only \ +; RUN: -thinlto-prefix-replace:"%t/oldpath/;%t/newpath/" %t/oldpath/t.obj \ +; RUN: -out:%t/t.exe +; RUN: ls %t/newpath/t.obj.thinlto.bc + +; Ensure that lld errors if prefix replace option is not in 'old;new' format. +; RUN: rm -f %t/newpath/t.obj.thinlto.bc +; RUN: not lld-link -entry:main -thinlto-index-only \ +; RUN: -thinlto-prefix-replace:"abc:def" %t/oldpath/t.obj \ +; RUN: -out:%t/t.exe 2>&1 | FileCheck --check-prefix=ERR %s +; ERR: -thinlto-prefix-replace: expects 'old;new' format, but got abc:def + +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc19.0.24215" + +define void @main() { + ret void +}