Index: lld/COFF/Config.h =================================================================== --- lld/COFF/Config.h +++ lld/COFF/Config.h @@ -225,9 +225,14 @@ // file paths written to the response file given in the // --thinlto-index-only=${response} option with // thinLTOPrefixReplaceNativeObject, instead of thinLTOPrefixReplaceNew. + // If thinLTOPrefixReplaceMinimizedIndex, replace the prefix of minimized + // index files, thinLTOPrefixReplaceMinimizedIndex, with + // thinLTOPrefixReplaceOld, allowing minimized index files to be in a + // different directory as the full bitcode files llvm::StringRef thinLTOPrefixReplaceOld; llvm::StringRef thinLTOPrefixReplaceNew; llvm::StringRef thinLTOPrefixReplaceNativeObject; + llvm::StringRef thinLTOPrefixReplaceMinimizedBitcode; // Used for /thinlto-object-suffix-replace: std::pair thinLTOObjectSuffixReplace; Index: lld/COFF/Driver.cpp =================================================================== --- lld/COFF/Driver.cpp +++ lld/COFF/Driver.cpp @@ -91,12 +91,13 @@ return ret; } -// Parse options of the form "old;new[;extra]". -static std::tuple +// Parse options of the form "old;new[;extra][;extra2]". +static std::tuple getOldNewOptionsExtra(opt::InputArgList &args, unsigned id) { auto [oldDir, second] = getOldNewOptions(args, id); - auto [newDir, extraDir] = second.split(';'); - return {oldDir, newDir, extraDir}; + auto [newDir, third] = second.split(';'); + auto [extraDir, extraDir2] = third.split(';'); + return {oldDir, newDir, extraDir, extraDir2}; } // Drop directory components and replace extension with @@ -1894,7 +1895,8 @@ config->thinLTOIndexOnlyArg = args.getLastArgValue(OPT_thinlto_index_only_arg); std::tie(config->thinLTOPrefixReplaceOld, config->thinLTOPrefixReplaceNew, - config->thinLTOPrefixReplaceNativeObject) = + config->thinLTOPrefixReplaceNativeObject, + config->thinLTOPrefixReplaceMinimizedBitcode) = getOldNewOptionsExtra(args, OPT_thinlto_prefix_replace); config->thinLTOObjectSuffixReplace = getOldNewOptions(args, OPT_thinlto_object_suffix_replace); Index: lld/COFF/InputFiles.cpp =================================================================== --- lld/COFF/InputFiles.cpp +++ lld/COFF/InputFiles.cpp @@ -996,10 +996,16 @@ bool lazy) : InputFile(ctx, BitcodeKind, mb, lazy) { std::string path = mb.getBufferIdentifier().str(); - if (ctx.config.thinLTOIndexOnly) + if (ctx.config.thinLTOIndexOnly) { path = replaceThinLTOSuffix(mb.getBufferIdentifier(), ctx.config.thinLTOObjectSuffixReplace.first, ctx.config.thinLTOObjectSuffixReplace.second); + if (!ctx.config.thinLTOPrefixReplaceMinimizedBitcode.empty()) { + path = lto::getThinLTOOutputFile( + path, ctx.config.thinLTOPrefixReplaceMinimizedBitcode.str(), + ctx.config.thinLTOPrefixReplaceOld.str()); + } + } // ThinLTO assumes that all MemoryBufferRefs given to it have a unique // name. If two archives define two members with the same name, this Index: lld/ELF/Config.h =================================================================== --- lld/ELF/Config.h +++ lld/ELF/Config.h @@ -180,6 +180,7 @@ llvm::StringRef thinLTOPrefixReplaceOld; llvm::StringRef thinLTOPrefixReplaceNew; llvm::StringRef thinLTOPrefixReplaceNativeObject; + llvm::StringRef thinLTOPrefixReplaceMinimizedBitcode; std::string rpath; llvm::SmallVector versionDefinitions; llvm::SmallVector auxiliaryList; Index: lld/ELF/Driver.cpp =================================================================== --- lld/ELF/Driver.cpp +++ lld/ELF/Driver.cpp @@ -1024,12 +1024,13 @@ return ret; } -// Parse options of the form "old;new[;extra]". -static std::tuple +// Parse options of the form "old;new[;extra][;extra2]". +static std::tuple getOldNewOptionsExtra(opt::InputArgList &args, unsigned id) { auto [oldDir, second] = getOldNewOptions(args, id); - auto [newDir, extraDir] = second.split(';'); - return {oldDir, newDir, extraDir}; + auto [newDir, third] = second.split(';'); + auto [extraDir, extraDir2] = third.split(';'); + return {oldDir, newDir, extraDir, extraDir2}; } // Parse the symbol ordering file and warn for any duplicate entries. @@ -1259,7 +1260,8 @@ config->thinLTOObjectSuffixReplace = getOldNewOptions(args, OPT_thinlto_object_suffix_replace_eq); std::tie(config->thinLTOPrefixReplaceOld, config->thinLTOPrefixReplaceNew, - config->thinLTOPrefixReplaceNativeObject) = + config->thinLTOPrefixReplaceNativeObject, + config->thinLTOPrefixReplaceMinimizedBitcode) = getOldNewOptionsExtra(args, OPT_thinlto_prefix_replace_eq); if (config->thinLTOEmitIndexFiles && !config->thinLTOIndexOnly) { if (args.hasArg(OPT_thinlto_object_suffix_replace_eq)) Index: lld/ELF/InputFiles.cpp =================================================================== --- lld/ELF/InputFiles.cpp +++ lld/ELF/InputFiles.cpp @@ -1581,8 +1581,14 @@ this->lazy = lazy; std::string path = mb.getBufferIdentifier().str(); - if (config->thinLTOIndexOnly) + if (config->thinLTOIndexOnly) { path = replaceThinLTOSuffix(mb.getBufferIdentifier()); + if (!config->thinLTOPrefixReplaceMinimizedBitcode.empty()) { + path = lto::getThinLTOOutputFile( + path, config->thinLTOPrefixReplaceMinimizedBitcode.str(), + config->thinLTOPrefixReplaceOld.str()); + } + } // ThinLTO assumes that all MemoryBufferRefs given to it have a unique // name. If two archives define two members with the same name, this Index: lld/MachO/Config.h =================================================================== --- lld/MachO/Config.h +++ lld/MachO/Config.h @@ -176,6 +176,7 @@ llvm::StringRef thinLTOPrefixReplaceOld; llvm::StringRef thinLTOPrefixReplaceNew; llvm::StringRef thinLTOPrefixReplaceNativeObject; + llvm::StringRef thinLTOPrefixReplaceMinimizedBitcode; bool deadStripDylibs = false; bool demangle = false; bool deadStrip = false; Index: lld/MachO/Driver.cpp =================================================================== --- lld/MachO/Driver.cpp +++ lld/MachO/Driver.cpp @@ -868,12 +868,13 @@ return ret; } -// Parse options of the form "old;new[;extra]". -static std::tuple +// Parse options of the form "old;new[;extra][;extra2]". +static std::tuple getOldNewOptionsExtra(opt::InputArgList &args, unsigned id) { auto [oldDir, second] = getOldNewOptions(args, id); - auto [newDir, extraDir] = second.split(';'); - return {oldDir, newDir, extraDir}; + auto [newDir, third] = second.split(';'); + auto [extraDir, extraDir2] = third.split(';'); + return {oldDir, newDir, extraDir, extraDir2}; } static void parseClangOption(StringRef opt, const Twine &msg) { @@ -1587,7 +1588,8 @@ config->thinLTOObjectSuffixReplace = getOldNewOptions(args, OPT_thinlto_object_suffix_replace_eq); std::tie(config->thinLTOPrefixReplaceOld, config->thinLTOPrefixReplaceNew, - config->thinLTOPrefixReplaceNativeObject) = + config->thinLTOPrefixReplaceNativeObject, + config->thinLTOPrefixReplaceMinimizedBitcode) = getOldNewOptionsExtra(args, OPT_thinlto_prefix_replace_eq); if (config->thinLTOEmitIndexFiles && !config->thinLTOIndexOnly) { if (args.hasArg(OPT_thinlto_object_suffix_replace_eq)) Index: lld/MachO/InputFiles.cpp =================================================================== --- lld/MachO/InputFiles.cpp +++ lld/MachO/InputFiles.cpp @@ -2194,8 +2194,14 @@ : InputFile(BitcodeKind, mb, lazy), forceHidden(forceHidden) { this->archiveName = std::string(archiveName); std::string path = mb.getBufferIdentifier().str(); - if (config->thinLTOIndexOnly) + if (config->thinLTOIndexOnly) { path = replaceThinLTOSuffix(mb.getBufferIdentifier()); + if (!config->thinLTOPrefixReplaceMinimizedBitcode.empty()) { + path = lto::getThinLTOOutputFile( + path, config->thinLTOPrefixReplaceMinimizedBitcode.str(), + config->thinLTOPrefixReplaceOld.str()); + } + } // ThinLTO assumes that all MemoryBufferRefs given to it have a unique // name. If two members with the same name are provided, this causes a Index: lld/test/COFF/thinlto-minimized-bitcode-prefix-replace.ll =================================================================== --- /dev/null +++ lld/test/COFF/thinlto-minimized-bitcode-prefix-replace.ll @@ -0,0 +1,38 @@ +; REQUIRES: x86 +;; Test to make sure the thinlto-prefix-replace option can replace the +;; prefix of minimized bitcode files +; RUN: rm -rf %t && mkdir %t && mkdir %t/minimized && mkdir %t/full && mkdir %t/indexing + +;; 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=%t/minimized/1.obj -o %t/full/1.obj + +;; First perform the thin link on the normal bitcode file, and save the +;; resulting index. +; RUN: lld-link -entry:main -thinlto-index-only -thinlto-prefix-replace:"%t/full;%t/indexing" %t/full/1.obj -out:%t/t.exe +; RUN: cp %t/indexing/1.obj.thinlto.bc %t/indexing/1.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. +;; Make sure it isn't inadvertently using the regular bitcode file. +; RUN: rm -f %t/full/1.obj +; RUN: lld-link -entry:main -thinlto-index-only -thinlto-prefix-replace:"%t/full;%t/indexing;;%t/minimized" \ +; RUN: %t/minimized/1.obj -out:%t/t.exe +; RUN: cmp %t/indexing/1.obj.thinlto.bc.orig %t/indexing/1.obj.thinlto.bc +;; Make sure the third string to thinlto-prefix-replace does not affect the test +; RUN: lld-link -entry:main -thinlto-index-only:"%t/unused" -thinlto-prefix-replace:"%t/full;%t/indexing;%t/unused;%t/minimized" \ +; RUN: %t/minimized/1.obj -out:%t/t.exe +; RUN: cmp %t/indexing/1.obj.thinlto.bc.orig %t/indexing/1.obj.thinlto.bc + +target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-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} \ No newline at end of file Index: lld/test/ELF/lto/thinlto-minimized-bitcode-prefix-replace.ll =================================================================== --- /dev/null +++ lld/test/ELF/lto/thinlto-minimized-bitcode-prefix-replace.ll @@ -0,0 +1,51 @@ +; 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: ld.lld --plugin-opt=thinlto-index-only -shared 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: ld.lld --plugin-opt=thinlto-index-only --plugin-opt=thinlto-object-suffix-replace=".thinlink.bc;.o" \ +; RUN: -shared 1.thinlink.bc -o 3 +; RUN: cmp 1.o.thinlto.bc.orig 1.o.thinlto.bc +;; Also check that this works without the --plugin-opt= prefix. +; RUN: ld.lld --thinlto-index-only --thinlto-object-suffix-replace=".thinlink.bc;.o" \ +; RUN: -shared 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 ld.lld --plugin-opt=thinlto-index-only --plugin-opt=thinlto-object-suffix-replace="abc:def" -shared 1.thinlink.bc \ +; RUN: -o 3 2>&1 | FileCheck %s --check-prefix=ERR1 +; ERR1: --plugin-opt=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: ld.lld --plugin-opt=thinlto-index-only --plugin-opt=thinlto-object-suffix-replace=".abc;.o" -shared 1.thinlink.bc -o /dev/null +; RUN: ls 1.thinlink.bc.thinlto.bc + +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" + +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-minimized-bitcode-prefix-replace.ll =================================================================== --- /dev/null +++ lld/test/MachO/thinlto-minimized-bitcode-prefix-replace.ll @@ -0,0 +1,38 @@ +; REQUIRES: x86 +;; Test to make sure the thinlto-prefix-replace option can replace the +;; prefix of minimized bitcode files +; RUN: rm -rf %t && mkdir %t && mkdir %t/minimized && mkdir %t/full && mkdir %t/indexing + +;; 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=%t/minimized/1.o -o %t/full/1.o + +;; First perform the thin link on the normal bitcode file, and save the +;; resulting index. +; RUN: %lld --thinlto-index-only --thinlto-prefix-replace="%t/full;%t/indexing" -dylib %t/full/1.o -o 3 +; RUN: cp %t/indexing/1.o.thinlto.bc %t/indexing/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. +;; Make sure it isn't inadvertently using the regular bitcode file. +; RUN: rm -f %t/full/1.o +; RUN: %lld --thinlto-index-only --thinlto-prefix-replace="%t/full;%t/indexing;;%t/minimized" \ +; RUN: -dylib %t/minimized/1.o -o 3 +; RUN: cmp %t/indexing/1.o.thinlto.bc.orig %t/indexing/1.o.thinlto.bc +;; Make sure the third string to thinlto-prefix-replace does not affect the test +; RUN: %lld --thinlto-index-only="%t/unused" --thinlto-prefix-replace="%t/full;%t/indexing;%t/unused;%t/minimized" \ +; RUN: -dylib %t/minimized/1.o -o 3 +; RUN: cmp %t/indexing/1.o.thinlto.bc.orig %t/indexing/1.o.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} \ No newline at end of file Index: llvm/tools/gold/gold-plugin.cpp =================================================================== --- llvm/tools/gold/gold-plugin.cpp +++ llvm/tools/gold/gold-plugin.cpp @@ -894,7 +894,7 @@ Backend = createWriteIndexesThinBackend( OldPrefix, NewPrefix, // TODO: Add support for optional native object path in - // thinlto_prefix_replace option to match lld. + // thinlto_prefix_replace option to match lld. /*NativeObjectPrefix=*/"", options::thinlto_emit_imports_files, LinkedObjectsFile, OnIndexWrite); } else { @@ -1053,6 +1053,8 @@ // will locate the full bitcode files for compiling/importing. std::string Identifier = getThinLTOObjectFileName(F.name, OldSuffix, NewSuffix); + // TODO: Add support for optional minimized bitcode file path in + // thinlto_prefix_replace option to match lld. auto ObjFilename = ObjectToIndexFileState.insert({Identifier, false}); assert(ObjFilename.second); if (const void *View = getSymbolsAndView(F))