}
std::string elf::replaceThinLTOSuffix(StringRef path) {
- StringRef suffix = config->thinLTOObjectSuffixReplace.first;
- StringRef repl = config->thinLTOObjectSuffixReplace.second;
-
+ auto [suffix, repl] = config->thinLTOObjectSuffixReplace;
if (path.consume_back(suffix))
return (path + repl).str();
return std::string(path);
bool emitEncryptionInfo = false;
bool emitInitOffsets = false;
bool emitChainedFixups = false;
+ bool thinLTOEmitImportsFiles;
+ bool thinLTOEmitIndexFiles;
+ bool thinLTOIndexOnly;
bool timeTraceEnabled = false;
bool dataConst = false;
bool dedupStrings = true;
uint32_t ltoo = 2;
llvm::CachePruningPolicy thinLTOCachePolicy;
llvm::StringRef thinLTOCacheDir;
+ llvm::StringRef thinLTOIndexOnlyArg;
+ std::pair<llvm::StringRef, llvm::StringRef> thinLTOObjectSuffixReplace;
+ std::pair<llvm::StringRef, llvm::StringRef> thinLTOPrefixReplace;
bool deadStripDylibs = false;
bool demangle = false;
bool deadStrip = false;
return getenv("LLD_REPRODUCE");
}
+// Parse options of the form "old;new".
+static std::pair<StringRef, StringRef> getOldNewOptions(opt::InputArgList &args,
+ unsigned id) {
+ auto *arg = args.getLastArg(id);
+ if (!arg)
+ return {"", ""};
+
+ StringRef s = arg->getValue();
+ std::pair<StringRef, StringRef> 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);
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);
// 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();
: 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.
}
}
+std::string macho::replaceThinLTOSuffix(StringRef path) {
+ auto [suffix, repl] = config->thinLTOObjectSuffixReplace;
+ if (path.consume_back(suffix))
+ return (path + repl).str();
+ return std::string(path);
+}
+
void macho::extract(InputFile &file, StringRef reason) {
if (!file.lazy)
return;
return detail::findCommands<CommandType>(anyHdr, 0, types...);
}
+std::string replaceThinLTOSuffix(StringRef path);
} // namespace macho
std::string toString(const macho::InputFile *file);
#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"
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<raw_fd_ostream> openFile(StringRef file) {
+ std::error_code ec;
+ auto ret =
+ std::make_unique<raw_fd_ostream>(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();
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;
}
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<lto::LTO>(createConfig(), backend);
}
void BitcodeCompiler::add(BitcodeFile &f) {
- ArrayRef<lto::InputFile::Symbol> objSyms = f.obj->symbols();
+ lto::InputFile &obj = *f.obj;
+
+ if (config->thinLTOEmitIndexFiles)
+ thinIndices.insert(obj.getName());
+
+ ArrayRef<lto::InputFile::Symbol> objSyms = obj.symbols();
std::vector<lto::SymbolResolution> resols;
resols.reserve(objSyms.size());
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<StringRef> linkedBitCodeFiles;
+ for (InputFile *file : inputFiles)
+ if (auto *f = dyn_cast<BitcodeFile>(file))
+ if (!f->lazy)
+ linkedBitCodeFiles.insert(f->getName());
+
+ for (InputFile *file : inputFiles) {
+ if (auto *f = dyn_cast<BitcodeFile>(file)) {
+ if (!f->lazy)
+ continue;
+ if (linkedBitCodeFiles.contains(f->getName()))
+ continue;
+ std::string path =
+ replaceThinLTOSuffix(getThinLTOOutputFile(f->obj->getName()));
+ std::unique_ptr<raw_fd_ostream> 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<ObjFile *> BitcodeCompiler::compile() {
},
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.
}
}
+ 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<ObjFile *> ret;
for (unsigned i = 0; i < maxTasks; ++i) {
// Get the native object contents either from the cache or from memory. Do
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);
}
#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 <memory>
#include <vector>
std::unique_ptr<llvm::lto::LTO> ltoObj;
std::vector<llvm::SmallString<0>> buf;
std::vector<std::unique_ptr<llvm::MemoryBuffer>> files;
+ std::unique_ptr<llvm::raw_fd_ostream> indexFile;
+ llvm::DenseSet<StringRef> thinIndices;
};
} // namespace lld::macho
def threads_eq : Joined<["--"], "threads=">,
HelpText<"Number of threads. '1' disables multi-threading. By default all available hardware threads are used">,
Group<grp_lld>;
+def thinlto_emit_imports_files: Flag<["--"], "thinlto-emit-imports-files">,
+ Group<grp_lld>;
+def thinlto_emit_index_files: Flag<["--"], "thinlto-emit-index-files">,
+ Group<grp_lld>;
+def thinlto_index_only: Flag<["--"], "thinlto-index-only">,
+ Group<grp_lld>;
+def thinlto_index_only_eq: Joined<["--"], "thinlto-index-only=">,
+ Group<grp_lld>;
def thinlto_jobs_eq : Joined<["--"], "thinlto-jobs=">,
HelpText<"Number of ThinLTO jobs. Default to --threads=">,
Group<grp_lld>;
+def thinlto_object_suffix_replace_eq:
+ Joined<["--"], "thinlto-object-suffix-replace=">,
+ Group<grp_lld>;
+def thinlto_prefix_replace_eq: Joined<["--"], "thinlto-prefix-replace=">,
+ Group<grp_lld>;
def reproduce: Separate<["--"], "reproduce">,
Group<grp_lld>;
def reproduce_eq: Joined<["--"], "reproduce=">,
;; Ensure lld doesn't generates index files when --thinlto-index-only is not enabled.
; RUN: rm -f 1.o.thinlto.bc 2.o.thinlto.bc
-; RUN: ld.lld -shared 1.o 2.o -o 4
+; RUN: ld.lld -shared 1.o 2.o -o 5
; RUN: not ls 1.o.thinlto.bc
; RUN: not ls 2.o.thinlto.bc
;; Ensure lld generates an index and not a binary if requested.
-; RUN: rm -f 4
; RUN: ld.lld --plugin-opt=thinlto-index-only -shared 1.o 2.o -o 4
; RUN: llvm-bcanalyzer -dump 1.o.thinlto.bc | FileCheck %s --check-prefix=BACKEND1
; RUN: llvm-bcanalyzer -dump 2.o.thinlto.bc | FileCheck %s --check-prefix=BACKEND2
; RUN: not test -e 4
-;; Ensure lld generates an index even if the file is wrapped in --start-lib/--end-lib
+;; Ensure lld generates an index even if the file is wrapped in --start-lib/--end-lib.
; RUN: rm -f 2.o.thinlto.bc 4
; RUN: ld.lld --plugin-opt=thinlto-index-only -shared 1.o 3.o --start-lib 2.o --end-lib -o 4
; RUN: llvm-dis < 2.o.thinlto.bc | grep -q '\^0 = module:'
;; Ensure lld does not emit empty combined module in default.
; RUN: rm -fr objpath && mkdir objpath
-; RUN: ld.lld 1.bc 2.bc -o objpath/a.out --save-temps
+; RUN: ld.lld -shared 1.bc 2.bc -o objpath/a.out --save-temps
; RUN: ls objpath/a.out*.lto.* | count 2
; EMPTY: file format elf64-x86-64
; RUN: ld.lld --thinlto-index-only --thinlto-prefix-replace="%t/oldpath/;%t/newpath/" -shared %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
+; 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 ld.lld --plugin-opt=thinlto-index-only --plugin-opt=thinlto-prefix-replace=abc:def -shared %t/oldpath/thinlto_prefix_replace.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=ERR
; ERR: --plugin-opt=thinlto-prefix-replace= expects 'old;new' format, but got abc:def
; 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
;; 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
--- /dev/null
+; 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"
--- /dev/null
+; 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: <MODULE_STRTAB_BLOCK
+; BACKEND1-NEXT: <ENTRY {{.*}} record string = '[[P1]]'
+; BACKEND1-NEXT: <ENTRY {{.*}} record string = '[[P2]]'
+; BACKEND1-NEXT: </MODULE_STRTAB_BLOCK
+; BACKEND1: <GLOBALVAL_SUMMARY_BLOCK
+; BACKEND1: <VERSION
+; BACKEND1: <FLAGS
+; BACKEND1: <VALUE_GUID op0={{1|2}} op1={{-3706093650706652785|-5300342847281564238}}
+; BACKEND1: <VALUE_GUID op0={{1|2}} op1={{-3706093650706652785|-5300342847281564238}}
+; BACKEND1: <COMBINED
+; BACKEND1: <COMBINED
+; BACKEND1: </GLOBALVAL_SUMMARY_BLOCK
+
+;; The backend index for Input/thinlto.ll contains summaries from itself only,
+;; as it does not import anything.
+; BACKEND2: <MODULE_STRTAB_BLOCK
+; BACKEND2-NEXT: <ENTRY {{.*}} record string = '[[P2]]'
+; BACKEND2-NEXT: </MODULE_STRTAB_BLOCK
+; BACKEND2-NEXT: <GLOBALVAL_SUMMARY_BLOCK
+; BACKEND2-NEXT: <VERSION
+; BACKEND2-NEXT: <FLAGS
+; BACKEND2-NEXT: <VALUE_GUID op0=1 op1=-5300342847281564238
+; BACKEND2-NEXT: <COMBINED
+; BACKEND2-NEXT: <BLOCK_COUNT op0=2/>
+; BACKEND2-NEXT: </GLOBALVAL_SUMMARY_BLOCK
+
+
+;--- 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"
--- /dev/null
+; REQUIRES: x86
+; RUN: rm -rf %t; split-file %s %t
+
+; 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 writes linked files to linked objects file
+; RUN: %lld --thinlto-index-only=%t/1.txt -dylib %t/1.o %t/2.o %t/3.o -o /dev/null
+; RUN: FileCheck %s < %t/1.txt
+; CHECK: 1.o
+; CHECK: 2.o
+; CHECK: 3.o
+
+;--- 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"
--- /dev/null
+; REQUIRES: x86
+; RUN: rm -rf %t; split-file %s %t
+
+;; 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-index-only -dylib %t/1.o %t/2.o -o %t/3
+; RUN: ls %t/2.o.thinlto.bc
+; RUN: not test -e %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 doesn't generates index files when thinlto-index-only is not enabled.
+; RUN: rm -f %t/1.o.thinlto.bc %t/2.o.thinlto.bc %t/3.o.thinlto.bc
+; RUN: %lld -dylib %t/1.o %t/2.o %t/3.o -o %t/5
+; RUN: not ls %t/1.o.thinlto.bc
+; RUN: not ls %t/2.o.thinlto.bc
+; RUN: not ls %t/3.o.thinlto.bc
+
+;; Ensure lld generates an index and not a binary if requested.
+; RUN: %lld --thinlto-index-only -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: not test -e %t/4
+
+;; Ensure lld generates an index even if the file is wrapped in --start-lib/--end-lib.
+; RUN: rm -f %t/2.o.thinlto.bc %t/4
+; RUN: %lld --thinlto-index-only -dylib %t/1.o %t/3.o --start-lib %t/2.o --end-lib -o %t/4
+; RUN: llvm-dis < %t/2.o.thinlto.bc | grep -q '\^0 = module:'
+; RUN: not test -e %t/4
+
+;; Test that LLD generates an empty index even for lazy object file that is not added to link.
+;; Test LLD generates empty imports file either because of thinlto-emit-imports-files option.
+; RUN: rm -f %t/1.o.thinlto.bc %t/1.o.imports
+; RUN: %lld --thinlto-index-only -dylib %t/2.o --start-lib %t/1.o --end-lib \
+; RUN: --thinlto-emit-imports-files -o %t/3
+; 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-index-only -dylib dummy.o --start-lib %t/1.o --end-lib -o /dev/null
+; RUN: ls %t/1.o.thinlto.bc
+
+;; Ensure when the same bitcode object is given as both lazy and non-lazy,
+;; LLD does not generate an empty index for the lazy object.
+; RUN: rm -f %t/2.o.thinlto.bc
+; RUN: %lld --thinlto-index-only -dylib %t/1.o %t/2.o --start-lib %t/2.o --end-lib -o /dev/null
+; RUN: llvm-dis < %t/2.o.thinlto.bc | grep -q '\^0 = module:'
+; RUN: rm -f %t/2.o.thinlto.bc
+; RUN: %lld --thinlto-index-only -dylib --start-lib %t/2.o --end-lib %t/2.o %t/1.o -o /dev/null
+; RUN: llvm-dis < %t/2.o.thinlto.bc | grep -q '\^0 = module:'
+
+;; Ensure when the same lazy bitcode object is given multiple times,
+;; no empty index file is generated if one of the copies is linked.
+; RUN: rm -f %t/2.o.thinlto.bc
+; RUN: %lld --thinlto-index-only -dylib %t/1.o --start-lib %t/2.o --end-lib --start-lib %t/2.o --end-lib -o /dev/null
+; RUN: llvm-dis < %t/2.o.thinlto.bc | grep -q '\^0 = module:'
+
+; NM: T _f
+
+;; The backend index for this module contains summaries from itself and
+;; g.ll, as it imports from the latter.
+; BACKEND1: <MODULE_STRTAB_BLOCK
+; BACKEND1-NEXT: <ENTRY {{.*}} record string = '[[P1]]'
+; BACKEND1-NEXT: <ENTRY {{.*}} record string = '[[P2]]'
+; BACKEND1-NEXT: </MODULE_STRTAB_BLOCK
+; BACKEND1: <GLOBALVAL_SUMMARY_BLOCK
+; BACKEND1: <VERSION
+; BACKEND1: <FLAGS
+; BACKEND1: <VALUE_GUID op0={{1|2}} op1={{-3706093650706652785|-5300342847281564238}}
+; BACKEND1: <VALUE_GUID op0={{1|2}} op1={{-3706093650706652785|-5300342847281564238}}
+; BACKEND1: <COMBINED
+; BACKEND1: <COMBINED
+; BACKEND1: </GLOBALVAL_SUMMARY_BLOCK
+
+;; The backend index for g.ll contains summaries from itself only,
+;; as it does not import anything.
+; BACKEND2: <MODULE_STRTAB_BLOCK
+; BACKEND2-NEXT: <ENTRY {{.*}} record string = '[[P2]]'
+; BACKEND2-NEXT: </MODULE_STRTAB_BLOCK
+; BACKEND2-NEXT: <GLOBALVAL_SUMMARY_BLOCK
+; BACKEND2-NEXT: <VERSION
+; BACKEND2-NEXT: <FLAGS
+; BACKEND2-NEXT: <VALUE_GUID op0=1 op1=-5300342847281564238
+; BACKEND2-NEXT: <COMBINED
+; BACKEND2-NEXT: <BLOCK_COUNT op0=2/>
+; BACKEND2-NEXT: </GLOBALVAL_SUMMARY_BLOCK
+
+;--- 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"
--- /dev/null
+; REQUIRES: x86
+; RUN: rm -rf %t; split-file %s %t
+
+; RUN: opt -module-summary %t/f.ll -o %t1.o
+; RUN: opt -module-summary %t/g.ll -o %t2.o
+
+;; Test to ensure that thinlto-index-only with obj-path creates the file.
+; RUN: rm -rf %t4
+; RUN: %lld --thinlto-index-only -object_path_lto %t4 -dylib %t1.o %t2.o -o /dev/null
+; RUN: llvm-readobj -h %t4/0.x86_64.lto.o | FileCheck %s
+; RUN: llvm-nm %t4/0.x86_64.lto.o 2>&1 | FileCheck --check-prefix=NM %s
+; RUN: llvm-readobj -h %t4/0.x86_64.lto.o | FileCheck %s
+
+;; Ensure lld emits empty combined module if specific obj-path.
+; RUN: rm -fr %t.dir/objpath && mkdir -p %t.dir/objpath
+; RUN: %lld -object_path_lto %t4.o -dylib %t1.o %t2.o -o %t.dir/objpath/a.out -save-temps
+; RUN: ls %t.dir/objpath/a.out*.lto.* | count 3
+
+;; Ensure lld does not emit empty combined module in default.
+; RUN: rm -fr %t.dir/objpath && mkdir -p %t.dir/objpath
+; RUN: %lld -dylib %t1.o %t2.o -o %t.dir/objpath/a.out -save-temps
+; RUN: ls %t.dir/objpath/a.out*.lto.* | count 2
+
+; NM: no symbols
+; CHECK: Format: Mach-O 64-bit x86-64
+
+;--- 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
+}
--- /dev/null
+; 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: %lld --thinlto-index-only -dylib 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: %lld --thinlto-index-only --thinlto-object-suffix-replace=".thinlink.bc;.o" \
+; RUN: -dylib 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 %lld --thinlto-index-only --thinlto-object-suffix-replace="abc:def" -dylib 1.thinlink.bc \
+; RUN: -o 3 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 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}
--- /dev/null
+; 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
+}