summaryrefslogtreecommitdiff
path: root/lld
diff options
context:
space:
mode:
authorIvan Tadeu Ferreira Antunes Filho <antunesi@google.com>2023-04-04 09:57:53 -0700
committerTeresa Johnson <tejohnson@google.com>2023-04-04 11:24:51 -0700
commit73fd9d310fff4d7d2b95e0509aafdf28bd3c713c (patch)
tree4e2ca6a50f12c40734fbaeb033514d4c2f2bd664 /lld
parenta401e10f12ba555eefc3da607fa5168603caa9c6 (diff)
downloadllvm-73fd9d310fff4d7d2b95e0509aafdf28bd3c713c.tar.gz
[lld] Support separate native object file path in --thinlto-prefix-replace
Currently, the --thinlto-prefix-replace="oldpath;newpath" option is used during distributed ThinLTO thin links to specify the mapping of the input bitcode object files' directory tree (oldpath) to the directory tree (newpath) used for both: 1) the output files of the thin link itself (the .thinlto.bc index files and the optional .imports files) 2) the specified object file paths written to the response file given in the --thinlto-index-only=${response} option, which is used by the final native link and must match the paths of the native object files that will be produced by ThinLTO backend compiles. This patch expands the --thinlto-prefix-replace option to allow a separate directory tree mapping to be specified for the object file paths written to the response file (number 2 above). This is important to support builds and build systems where the same output directory may not be written by multiple build actions (e.g. the thin link and the ThinLTO backend compiles). The new format is: --thinlto-prefix-replace="origpath;outpath[;objpath]" This replaces the origpath directory tree of the thin link input files with outpath when writing the thin link index and imports outputs (number 1 above). If objpath is specified it replaces origpath of the input files with objpath when writing the response file (number 2 above), otherwise it falls back to the old behavior of using outpath for this as well. Reviewed By: tejohnson, MaskRay Differential Revision: https://reviews.llvm.org/D144596
Diffstat (limited to 'lld')
-rw-r--r--lld/COFF/Config.h12
-rw-r--r--lld/COFF/Driver.cpp14
-rw-r--r--lld/COFF/LTO.cpp9
-rw-r--r--lld/ELF/Config.h4
-rw-r--r--lld/ELF/Driver.cpp19
-rw-r--r--lld/ELF/LTO.cpp9
-rw-r--r--lld/MachO/Config.h4
-rw-r--r--lld/MachO/Driver.cpp18
-rw-r--r--lld/MachO/LTO.cpp9
-rw-r--r--lld/test/COFF/thinlto-index-file-object-prefix-replace.ll46
-rw-r--r--lld/test/ELF/lto/thinlto-index-file-object-prefix-replace.ll51
-rw-r--r--lld/test/MachO/thinlto-index-file-object-prefix-replace.ll67
12 files changed, 240 insertions, 22 deletions
diff --git a/lld/COFF/Config.h b/lld/COFF/Config.h
index 1c35acf25ed6..028cb9c13baf 100644
--- a/lld/COFF/Config.h
+++ b/lld/COFF/Config.h
@@ -218,8 +218,16 @@ struct Configuration {
// Used for /thinlto-index-only:
llvm::StringRef thinLTOIndexOnlyArg;
- // Used for /thinlto-object-prefix-replace:
- std::pair<llvm::StringRef, llvm::StringRef> thinLTOPrefixReplace;
+ // Used for /thinlto-prefix-replace:
+ // Replace the prefix in paths generated for ThinLTO, replacing
+ // thinLTOPrefixReplaceOld with thinLTOPrefixReplaceNew. If
+ // thinLTOPrefixReplaceNativeObject is defined, replace the prefix of object
+ // file paths written to the response file given in the
+ // --thinlto-index-only=${response} option with
+ // thinLTOPrefixReplaceNativeObject, instead of thinLTOPrefixReplaceNew.
+ llvm::StringRef thinLTOPrefixReplaceOld;
+ llvm::StringRef thinLTOPrefixReplaceNew;
+ llvm::StringRef thinLTOPrefixReplaceNativeObject;
// Used for /thinlto-object-suffix-replace:
std::pair<llvm::StringRef, llvm::StringRef> thinLTOObjectSuffixReplace;
diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp
index 29219662e542..8d272c2cc6bf 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -53,6 +53,7 @@
#include <future>
#include <memory>
#include <optional>
+#include <tuple>
using namespace llvm;
using namespace llvm::object;
@@ -90,6 +91,14 @@ static std::pair<StringRef, StringRef> getOldNewOptions(opt::InputArgList &args,
return ret;
}
+// Parse options of the form "old;new[;extra]".
+static std::tuple<StringRef, StringRef, StringRef>
+getOldNewOptionsExtra(opt::InputArgList &args, unsigned id) {
+ auto [oldDir, second] = getOldNewOptions(args, id);
+ auto [newDir, extraDir] = second.split(';');
+ return {oldDir, newDir, extraDir};
+}
+
// Drop directory components and replace extension with
// ".exe", ".dll" or ".sys".
static std::string getOutputPath(StringRef path, bool isDll, bool isDriver) {
@@ -1884,8 +1893,9 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
args.hasArg(OPT_thinlto_index_only_arg);
config->thinLTOIndexOnlyArg =
args.getLastArgValue(OPT_thinlto_index_only_arg);
- config->thinLTOPrefixReplace =
- getOldNewOptions(args, OPT_thinlto_prefix_replace);
+ std::tie(config->thinLTOPrefixReplaceOld, config->thinLTOPrefixReplaceNew,
+ config->thinLTOPrefixReplaceNativeObject) =
+ getOldNewOptionsExtra(args, OPT_thinlto_prefix_replace);
config->thinLTOObjectSuffixReplace =
getOldNewOptions(args, OPT_thinlto_object_suffix_replace);
config->ltoObjPath = args.getLastArgValue(OPT_lto_obj_path);
diff --git a/lld/COFF/LTO.cpp b/lld/COFF/LTO.cpp
index cfb964b6aa3b..fb3ba2c6e9d7 100644
--- a/lld/COFF/LTO.cpp
+++ b/lld/COFF/LTO.cpp
@@ -56,8 +56,8 @@ static std::unique_ptr<raw_fd_ostream> openFile(StringRef file) {
std::string BitcodeCompiler::getThinLTOOutputFile(StringRef path) {
return lto::getThinLTOOutputFile(
- std::string(path), std::string(ctx.config.thinLTOPrefixReplace.first),
- std::string(ctx.config.thinLTOPrefixReplace.second));
+ std::string(path), std::string(ctx.config.thinLTOPrefixReplaceOld),
+ std::string(ctx.config.thinLTOPrefixReplaceNew));
}
lto::Config BitcodeCompiler::createConfig() {
@@ -114,8 +114,9 @@ BitcodeCompiler::BitcodeCompiler(COFFLinkerContext &c) : ctx(c) {
if (ctx.config.thinLTOIndexOnly) {
auto OnIndexWrite = [&](StringRef S) { thinIndices.erase(S); };
backend = lto::createWriteIndexesThinBackend(
- std::string(ctx.config.thinLTOPrefixReplace.first),
- std::string(ctx.config.thinLTOPrefixReplace.second),
+ std::string(ctx.config.thinLTOPrefixReplaceOld),
+ std::string(ctx.config.thinLTOPrefixReplaceNew),
+ std::string(ctx.config.thinLTOPrefixReplaceNativeObject),
ctx.config.thinLTOEmitImportsFiles, indexFile.get(), OnIndexWrite);
} else {
backend = lto::createInProcessThinBackend(
diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h
index 249fe3c30ece..a6e87511bcfb 100644
--- a/lld/ELF/Config.h
+++ b/lld/ELF/Config.h
@@ -177,7 +177,9 @@ struct Config {
StringRef zCetReport = "none";
llvm::StringRef ltoBasicBlockSections;
std::pair<llvm::StringRef, llvm::StringRef> thinLTOObjectSuffixReplace;
- std::pair<llvm::StringRef, llvm::StringRef> thinLTOPrefixReplace;
+ llvm::StringRef thinLTOPrefixReplaceOld;
+ llvm::StringRef thinLTOPrefixReplaceNew;
+ llvm::StringRef thinLTOPrefixReplaceNativeObject;
std::string rpath;
llvm::SmallVector<VersionDefinition, 0> versionDefinitions;
llvm::SmallVector<llvm::StringRef, 0> auxiliaryList;
diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index e0d2d466ec7d..8cc2d2005e39 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -65,6 +65,7 @@
#include "llvm/Support/TimeProfiler.h"
#include "llvm/Support/raw_ostream.h"
#include <cstdlib>
+#include <tuple>
#include <utility>
using namespace llvm;
@@ -1023,6 +1024,14 @@ static std::pair<StringRef, StringRef> getOldNewOptions(opt::InputArgList &args,
return ret;
}
+// Parse options of the form "old;new[;extra]".
+static std::tuple<StringRef, StringRef, StringRef>
+getOldNewOptionsExtra(opt::InputArgList &args, unsigned id) {
+ auto [oldDir, second] = getOldNewOptions(args, id);
+ auto [newDir, extraDir] = second.split(';');
+ return {oldDir, newDir, extraDir};
+}
+
// Parse the symbol ordering file and warn for any duplicate entries.
static SmallVector<StringRef, 0> getSymbolOrderingFile(MemoryBufferRef mb) {
SetVector<StringRef, SmallVector<StringRef, 0>> names;
@@ -1249,8 +1258,9 @@ static void readConfigs(opt::InputArgList &args) {
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->thinLTOPrefixReplaceOld, config->thinLTOPrefixReplaceNew,
+ config->thinLTOPrefixReplaceNativeObject) =
+ 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 "
@@ -1259,6 +1269,11 @@ static void readConfigs(opt::InputArgList &args) {
error("--thinlto-prefix-replace is not supported with "
"--thinlto-emit-index-files");
}
+ if (!config->thinLTOPrefixReplaceNativeObject.empty() &&
+ config->thinLTOIndexOnlyArg.empty()) {
+ error("--thinlto-prefix-replace=old_dir;new_dir;obj_dir must be used with "
+ "--thinlto-index-only=");
+ }
config->thinLTOModulesToCompile =
args::getStrings(args, OPT_thinlto_single_module_eq);
config->timeTraceEnabled = args.hasArg(OPT_time_trace_eq);
diff --git a/lld/ELF/LTO.cpp b/lld/ELF/LTO.cpp
index b51810dc91d7..8b5c23ee6a47 100644
--- a/lld/ELF/LTO.cpp
+++ b/lld/ELF/LTO.cpp
@@ -68,8 +68,8 @@ static std::unique_ptr<raw_fd_ostream> openLTOOutputFile(StringRef file) {
static std::string getThinLTOOutputFile(StringRef modulePath) {
return lto::getThinLTOOutputFile(
- std::string(modulePath), std::string(config->thinLTOPrefixReplace.first),
- std::string(config->thinLTOPrefixReplace.second));
+ std::string(modulePath), std::string(config->thinLTOPrefixReplaceOld),
+ std::string(config->thinLTOPrefixReplaceNew));
}
static lto::Config createConfig() {
@@ -196,8 +196,9 @@ BitcodeCompiler::BitcodeCompiler() {
auto onIndexWrite = [&](StringRef s) { thinIndices.erase(s); };
if (config->thinLTOIndexOnly) {
backend = lto::createWriteIndexesThinBackend(
- std::string(config->thinLTOPrefixReplace.first),
- std::string(config->thinLTOPrefixReplace.second),
+ std::string(config->thinLTOPrefixReplaceOld),
+ std::string(config->thinLTOPrefixReplaceNew),
+ std::string(config->thinLTOPrefixReplaceNativeObject),
config->thinLTOEmitImportsFiles, indexFile.get(), onIndexWrite);
} else {
backend = lto::createInProcessThinBackend(
diff --git a/lld/MachO/Config.h b/lld/MachO/Config.h
index 328aea49d77c..feb502d0630d 100644
--- a/lld/MachO/Config.h
+++ b/lld/MachO/Config.h
@@ -173,7 +173,9 @@ struct Configuration {
llvm::StringRef thinLTOCacheDir;
llvm::StringRef thinLTOIndexOnlyArg;
std::pair<llvm::StringRef, llvm::StringRef> thinLTOObjectSuffixReplace;
- std::pair<llvm::StringRef, llvm::StringRef> thinLTOPrefixReplace;
+ llvm::StringRef thinLTOPrefixReplaceOld;
+ llvm::StringRef thinLTOPrefixReplaceNew;
+ llvm::StringRef thinLTOPrefixReplaceNativeObject;
bool deadStripDylibs = false;
bool demangle = false;
bool deadStrip = false;
diff --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp
index 49f665195016..093538f7da64 100644
--- a/lld/MachO/Driver.cpp
+++ b/lld/MachO/Driver.cpp
@@ -868,6 +868,14 @@ static std::pair<StringRef, StringRef> getOldNewOptions(opt::InputArgList &args,
return ret;
}
+// Parse options of the form "old;new[;extra]".
+static std::tuple<StringRef, StringRef, StringRef>
+getOldNewOptionsExtra(opt::InputArgList &args, unsigned id) {
+ auto [oldDir, second] = getOldNewOptions(args, id);
+ auto [newDir, extraDir] = second.split(';');
+ return {oldDir, newDir, extraDir};
+}
+
static void parseClangOption(StringRef opt, const Twine &msg) {
std::string err;
raw_string_ostream os(err);
@@ -1578,8 +1586,9 @@ bool macho::link(ArrayRef<const char *> argsArr, llvm::raw_ostream &stdoutOS,
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->thinLTOPrefixReplaceOld, config->thinLTOPrefixReplaceNew,
+ config->thinLTOPrefixReplaceNativeObject) =
+ 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 "
@@ -1588,6 +1597,11 @@ bool macho::link(ArrayRef<const char *> argsArr, llvm::raw_ostream &stdoutOS,
error("--thinlto-prefix-replace is not supported with "
"--thinlto-emit-index-files");
}
+ if (!config->thinLTOPrefixReplaceNativeObject.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);
diff --git a/lld/MachO/LTO.cpp b/lld/MachO/LTO.cpp
index af15336b21ea..481ac9444ef0 100644
--- a/lld/MachO/LTO.cpp
+++ b/lld/MachO/LTO.cpp
@@ -47,8 +47,8 @@ static std::unique_ptr<raw_fd_ostream> openFile(StringRef file) {
static std::string getThinLTOOutputFile(StringRef modulePath) {
return lto::getThinLTOOutputFile(
- std::string(modulePath), std::string(config->thinLTOPrefixReplace.first),
- std::string(config->thinLTOPrefixReplace.second));
+ std::string(modulePath), std::string(config->thinLTOPrefixReplaceOld),
+ std::string(config->thinLTOPrefixReplaceNew));
}
static lto::Config createConfig() {
@@ -99,8 +99,9 @@ BitcodeCompiler::BitcodeCompiler() {
auto onIndexWrite = [&](StringRef S) { thinIndices.erase(S); };
if (config->thinLTOIndexOnly) {
backend = lto::createWriteIndexesThinBackend(
- std::string(config->thinLTOPrefixReplace.first),
- std::string(config->thinLTOPrefixReplace.second),
+ std::string(config->thinLTOPrefixReplaceOld),
+ std::string(config->thinLTOPrefixReplaceNew),
+ std::string(config->thinLTOPrefixReplaceNativeObject),
config->thinLTOEmitImportsFiles, indexFile.get(), onIndexWrite);
} else {
backend = lto::createInProcessThinBackend(
diff --git a/lld/test/COFF/thinlto-index-file-object-prefix-replace.ll b/lld/test/COFF/thinlto-index-file-object-prefix-replace.ll
new file mode 100644
index 000000000000..efe03d62b945
--- /dev/null
+++ b/lld/test/COFF/thinlto-index-file-object-prefix-replace.ll
@@ -0,0 +1,46 @@
+; REQUIRES: x86
+; RUN: rm -rf %t && mkdir %t
+; RUN: mkdir -p %t/old/subdir
+; RUN: opt -module-summary %s -o %t/old/subdir/1.obj
+; RUN: opt -module-summary %p/Inputs/thinlto.ll -o %t/old/subdir/2.obj
+; RUN: opt -module-summary %p/Inputs/thinlto-empty.ll -o %t/old/3.obj
+
+;; Ensure lld writes linked files to linked objects file.
+; RUN: lld-link -entry:main -thinlto-index-only:%t/1.txt %t/old/subdir/1.obj %t/old/subdir/2.obj %t/old/3.obj -out:%t/t.exe
+; RUN: ls %t/old/subdir/1.obj.thinlto.bc
+; RUN: ls %t/old/subdir/2.obj.thinlto.bc
+; RUN: ls %t/old/3.obj.thinlto.bc
+; RUN: FileCheck --check-prefix=CHECK-NO-REPLACE %s < %t/1.txt
+; CHECK-NO-REPLACE: old/subdir/1.obj
+; CHECK-NO-REPLACE-NEXT: old/subdir/2.obj
+; CHECK-NO-REPLACE-NEXT: old/3.obj
+
+;; Check that this also works with thinlto-prefix-replace.
+; RUN: lld-link -entry:main -thinlto-index-only:%t/2.txt -thinlto-prefix-replace:"%t/old/;%t/new/" %t/old/subdir/1.obj %t/old/subdir/2.obj %t/old/3.obj -out:%t/t.exe
+; RUN: ls %t/new/subdir/1.obj.thinlto.bc
+; RUN: ls %t/new/subdir/2.obj.thinlto.bc
+; RUN: ls %t/new/3.obj.thinlto.bc
+; RUN: FileCheck --check-prefix=CHECK-REPLACE-PREFIX %s < %t/2.txt
+; CHECK-REPLACE-PREFIX: new/subdir/1.obj
+; CHECK-REPLACE-PREFIX-NEXT: new/subdir/2.obj
+; CHECK-REPLACE-PREFIX-NEXT: new/3.obj
+
+;; Check that this also works with replacing the prefix of linked objects.
+; RUN: lld-link -entry:main -thinlto-index-only:%t/3.txt -thinlto-prefix-replace:"%t/old/;%t/new/;%t/obj/" %t/old/subdir/1.obj %t/old/subdir/2.obj %t/old/3.obj -out:%t/t.exe
+; RUN: ls %t/new/subdir/1.obj.thinlto.bc
+; RUN: ls %t/new/subdir/2.obj.thinlto.bc
+; RUN: ls %t/new/3.obj.thinlto.bc
+; RUN: FileCheck --check-prefix=CHECK-REPLACE-OBJECT-PREFIX %s < %t/3.txt
+; CHECK-REPLACE-OBJECT-PREFIX: obj/subdir/1.obj
+; CHECK-REPLACE-OBJECT-PREFIX-NEXT: obj/subdir/2.obj
+; CHECK-REPLACE-OBJECT-PREFIX-NEXT: obj/3.obj
+
+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"
+
+declare void @g(...)
+
+define void @main() {
+ call void (...) @g()
+ ret void
+}
diff --git a/lld/test/ELF/lto/thinlto-index-file-object-prefix-replace.ll b/lld/test/ELF/lto/thinlto-index-file-object-prefix-replace.ll
new file mode 100644
index 000000000000..ccbd15323dff
--- /dev/null
+++ b/lld/test/ELF/lto/thinlto-index-file-object-prefix-replace.ll
@@ -0,0 +1,51 @@
+; REQUIRES: x86
+; RUN: rm -rf %t && mkdir %t && cd %t
+; RUN: mkdir -p old/subdir
+; RUN: opt -module-summary %s -o old/subdir/1.o
+; RUN: opt -module-summary %p/Inputs/thinlto.ll -o old/subdir/2.o
+; RUN: opt -module-summary %p/Inputs/thinlto_empty.ll -o old/3.o
+
+;; Ensure lld writes linked files to linked objects file.
+; RUN: ld.lld --thinlto-index-only=1.txt -shared old/subdir/1.o old/subdir/2.o old/3.o -o /dev/null
+; RUN: ls old/subdir/1.o.thinlto.bc
+; RUN: ls old/subdir/2.o.thinlto.bc
+; RUN: ls old/3.o.thinlto.bc
+; RUN: FileCheck --match-full-lines --check-prefix=CHECK-NO-REPLACE %s < 1.txt
+; CHECK-NO-REPLACE: old/subdir/1.o
+; CHECK-NO-REPLACE-NEXT: old/subdir/2.o
+; CHECK-NO-REPLACE-NEXT: old/3.o
+
+;; Check that this also works with thinlto-prefix-replace.
+; RUN: ld.lld --thinlto-index-only=2.txt --thinlto-prefix-replace="old/;new/" -shared old/subdir/1.o old/subdir/2.o old/3.o -o /dev/null
+; RUN: ls new/subdir/1.o.thinlto.bc
+; RUN: ls new/subdir/2.o.thinlto.bc
+; RUN: ls new/3.o.thinlto.bc
+; RUN: FileCheck --match-full-lines --check-prefix=CHECK-REPLACE-PREFIX %s < 2.txt
+; CHECK-REPLACE-PREFIX: new/subdir/1.o
+; CHECK-REPLACE-PREFIX-NEXT: new/subdir/2.o
+; CHECK-REPLACE-PREFIX-NEXT: new/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="old/;new/;obj/" -shared old/subdir/1.o old/subdir/2.o old/3.o -o /dev/null
+; RUN: ls new/subdir/1.o.thinlto.bc
+; RUN: ls new/subdir/2.o.thinlto.bc
+; RUN: ls new/3.o.thinlto.bc
+; RUN: FileCheck --match-full-lines --check-prefix=CHECK-REPLACE-OBJECT-PREFIX %s < 3.txt
+; CHECK-REPLACE-OBJECT-PREFIX: obj/subdir/1.o
+; CHECK-REPLACE-OBJECT-PREFIX-NEXT: obj/subdir/2.o
+; CHECK-REPLACE-OBJECT-PREFIX-NEXT: obj/3.o
+
+; Create an 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="old/;new/;obj/" -shared old/subdir/1.o old/subdir/2.o old/3.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=ERROR
+; ERROR: error: --thinlto-prefix-replace=old_dir;new_dir;obj_dir must be used with --thinlto-index-only=
+
+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
+}
diff --git a/lld/test/MachO/thinlto-index-file-object-prefix-replace.ll b/lld/test/MachO/thinlto-index-file-object-prefix-replace.ll
new file mode 100644
index 000000000000..7f4ce2ec2e4b
--- /dev/null
+++ b/lld/test/MachO/thinlto-index-file-object-prefix-replace.ll
@@ -0,0 +1,67 @@
+; REQUIRES: x86
+; RUN: rm -rf %t && split-file %s %t
+; RUN: mkdir -p %t/old/subdir
+
+; RUN: opt -module-summary %t/f.ll -o %t/old/subdir/1.o
+; RUN: opt -module-summary %t/g.ll -o %t/old/subdir/2.o
+; RUN: opt -module-summary %t/empty.ll -o %t/old/3.o
+
+;; Ensure lld writes linked files to linked objects file.
+; RUN: %lld --thinlto-index-only=%t/1.txt -dylib %t/old/subdir/1.o %t/old/subdir/2.o %t/old/3.o -o /dev/null
+; RUN: ls %t/old/subdir/1.o.thinlto.bc
+; RUN: ls %t/old/subdir/2.o.thinlto.bc
+; RUN: ls %t/old/3.o.thinlto.bc
+; RUN: FileCheck --check-prefix=CHECK-NO-REPLACE %s < %t/1.txt
+; CHECK-NO-REPLACE: old/subdir/1.o
+; CHECK-NO-REPLACE-NEXT: old/subdir/2.o
+; CHECK-NO-REPLACE-NEXT: old/3.o
+
+;; Check that this also works with thinlto-prefix-replace.
+; RUN: %lld --thinlto-index-only=%t/2.txt --thinlto-prefix-replace="%t/old/;%t/new/" -dylib %t/old/subdir/1.o %t/old/subdir/2.o %t/old/3.o -o /dev/null
+; RUN: ls %t/new/subdir/1.o.thinlto.bc
+; RUN: ls %t/new/subdir/2.o.thinlto.bc
+; RUN: ls %t/new/3.o.thinlto.bc
+; RUN: FileCheck --check-prefix=CHECK-REPLACE-PREFIX %s < %t/2.txt
+; CHECK-REPLACE-PREFIX: new/subdir/1.o
+; CHECK-REPLACE-PREFIX-NEXT: new/subdir/2.o
+; CHECK-REPLACE-PREFIX-NEXT: new/3.o
+
+
+;; Check that this also works with replacing the prefix of linked objects.
+; RUN: %lld --thinlto-index-only=%t/3.txt --thinlto-prefix-replace="%t/old/;%t/new/;%t/obj/" -dylib %t/old/subdir/1.o %t/old/subdir/2.o %t/old/3.o -o /dev/null
+; RUN: ls %t/new/subdir/1.o.thinlto.bc
+; RUN: ls %t/new/subdir/2.o.thinlto.bc
+; RUN: ls %t/new/3.o.thinlto.bc
+; RUN: FileCheck --check-prefix=CHECK-REPLACE-OBJECT-PREFIX %s < %t/3.txt
+; CHECK-REPLACE-OBJECT-PREFIX: obj/subdir/1.o
+; CHECK-REPLACE-OBJECT-PREFIX-NEXT: obj/subdir/2.o
+; CHECK-REPLACE-OBJECT-PREFIX-NEXT: obj/3.o
+
+; Create an 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 %lld --thinlto-prefix-replace="%t/old/;%t/new/;%t/obj/" -dylib %t/old/subdir/1.o %t/old/subdir/2.o %t/old/3.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=ERROR
+; ERROR: error: --thinlto-prefix-replace=old_dir;new_dir;obj_dir must be used with --thinlto-index-only=
+
+;--- 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"