diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -229,6 +229,7 @@ bool thinLTOEmitImportsFiles; bool thinLTOEmitIndexFiles; bool thinLTOIndexOnly; + bool thinLTOFullIndex; bool timeTraceEnabled; bool tocOptimize; bool pcRelOptimize; @@ -400,6 +401,7 @@ llvm::DenseMap> backwardReferences; + std::unique_ptr fullIndexFile; }; // The only instance of Ctx struct. diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -1199,6 +1199,7 @@ 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->thinLTOFullIndex = args.hasArg(OPT_thinlto_full_index); config->thinLTOObjectSuffixReplace = getOldNewOptions(args, OPT_thinlto_object_suffix_replace_eq); config->thinLTOPrefixReplace = @@ -2488,10 +2489,25 @@ // appended to the Files vector. { llvm::TimeTraceScope timeScope("Parse input files"); + if (!config->thinLTOIndexOnlyArg.empty() && config->thinLTOFullIndex) { + std::error_code ec; + ctx->fullIndexFile = std::make_unique( + (config->thinLTOIndexOnlyArg + ".full").str(), ec, + sys::fs::OpenFlags::OF_None); + if (ec) { + error("cannot open " + config->thinLTOIndexOnlyArg + + ".full: " + ec.message()); + return; + } + } for (size_t i = 0; i < files.size(); ++i) { llvm::TimeTraceScope timeScope("Parse input files", files[i]->getName()); parseFile(files[i]); } + if (ctx->fullIndexFile) { + ctx->fullIndexFile->close(); + ctx->fullIndexFile.release(); + } } // Now that we have every file, we can decide if we will need a diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -165,6 +165,8 @@ // Binary file if (auto *f = dyn_cast(file)) { ctx->binaryFiles.push_back(f); + if (ctx->fullIndexFile) + *(ctx->fullIndexFile) << file->getName() << "\n"; f->parse(); return; } @@ -185,6 +187,8 @@ // .so file if (auto *f = dyn_cast(file)) { + if (ctx->fullIndexFile) + *(ctx->fullIndexFile) << file->getName() << "\n"; f->parse(); return; } @@ -192,12 +196,21 @@ // LLVM bitcode file if (auto *f = dyn_cast(file)) { ctx->bitcodeFiles.push_back(f); + if (ctx->fullIndexFile) { + auto NewModulePath = lto::getThinLTOOutputFile( + std::string(f->obj->getName()), + std::string(config->thinLTOPrefixReplace.first), + std::string(config->thinLTOPrefixReplace.second)); + *(ctx->fullIndexFile) << NewModulePath << "\n"; + } f->parse(); return; } // Regular object file ctx->objectFiles.push_back(cast(file)); + if (ctx->fullIndexFile) + *(ctx->fullIndexFile) << file->getName() << "\n"; cast>(file)->parse(); } diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td --- a/lld/ELF/Options.td +++ b/lld/ELF/Options.td @@ -603,6 +603,7 @@ def thinlto_emit_index_files: FF<"thinlto-emit-index-files">; def thinlto_index_only: FF<"thinlto-index-only">; def thinlto_index_only_eq: JJ<"thinlto-index-only=">; +def thinlto_full_index: FF<"thinlto-full-index">; def thinlto_jobs: JJ<"thinlto-jobs=">, HelpText<"Number of ThinLTO jobs. Default to --threads=">; def thinlto_object_suffix_replace_eq: JJ<"thinlto-object-suffix-replace=">; @@ -664,6 +665,9 @@ def: J<"plugin-opt=thinlto-index-only=">, Alias, HelpText<"Alias for --thinlto-index-only=">; +def: F<"plugin-opt=thinlto-full-index">, + Alias, + HelpText<"Alias for --thinlto-full-index">; def: J<"plugin-opt=thinlto-object-suffix-replace=">, Alias, HelpText<"Alias for --thinlto-object-suffix-replace=">; diff --git a/lld/test/ELF/lto/thinlto-full-index.ll b/lld/test/ELF/lto/thinlto-full-index.ll new file mode 100644 --- /dev/null +++ b/lld/test/ELF/lto/thinlto-full-index.ll @@ -0,0 +1,74 @@ +; REQUIRES: x86 +;; Test --thinlto-full-index for distributed ThinLTO. + +; RUN: rm -rf %t && split-file %s %t && cd %t +; RUN: llvm-mc -filetype=obj -triple=x86_64 a.s -o a.o +; RUN: llvm-mc -filetype=obj -triple=x86_64 b.s -o b.o +; RUN: opt -module-summary c.ll -o c.bc +; RUN: opt -module-summary d.ll -o d.bc +; RUN: ld.lld --thinlto-index-only=thinlto.index --thinlto-full-index --thinlto-emit-imports-files --start-lib a.o --end-lib c.bc --start-lib b.o --end-lib d.bc -shared -o /dev/null +;; The index files and imports files are created. +; RUN: ls c.bc.thinlto.bc c.bc.imports d.bc.thinlto.bc d.bc.imports +;; Nothing to import. +; RUN: cat c.bc.imports | count 0 +; RUN: cat d.bc.imports | count 0 + +;; Check that index file has 2 bitcode objects. +; RUN: FileCheck %s --check-prefix=CHECK-INDEX < thinlto.index +; CHECK-INDEX: c.bc +; CHECK-INDEX-NEXT: d.bc + +;; Check that full index file has 2 bitcode objects plus 1 native object. +; RUN: FileCheck %s --check-prefix=CHECK-FULL < thinlto.index.full +; CHECK-FULL: c.bc +; CHECK-FULL-NEXT: b.o +; CHECK-FULL-NEXT: d.bc + +;; No need for importing. Compile both bitcode objects to native objects. +; RUN: llc c.bc --filetype=obj -o c.o +; RUN: llc d.bc --filetype=obj -o d.o +;; Run final link following thinlto.index. Because there is no indication of +;; where to place the native objects, they are moved to the end of inputs. +; RUN: not ld.lld c.o d.o --start-lib a.o --end-lib --start-lib b.o --end-lib -o /dev/null 2>&1 | FileCheck %s --check-prefix=CHECK-DUP +; CHECK-DUP: duplicate symbol: g +; CHECK-DUP-NEXT: defined at a.o +; CHECK-DUP-NEXT: defined at b.o + +;; Run final link following thinlto.index.full. +; RUN: ld.lld c.o b.o d.o -o /dev/null + +;--- a.s +.globl g +g: + ret + +;--- b.s +.globl g +g: + ret + +.globl f +f: + ret + +;--- c.ll +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 @f() + +define void @foo() { + call void () @f() + ret void +} + +;--- d.ll +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 @bar() { + call void () @g() + ret void +}