diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -116,6 +116,7 @@ llvm::StringRef sysroot; llvm::StringRef thinLTOCacheDir; llvm::StringRef thinLTOIndexOnlyArg; + llvm::StringRef thinLTOSingleModule; llvm::StringRef ltoBasicBlockSections; std::pair thinLTOObjectSuffixReplace; std::pair thinLTOPrefixReplace; diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -986,6 +986,7 @@ getOldNewOptions(args, OPT_thinlto_object_suffix_replace_eq); config->thinLTOPrefixReplace = getOldNewOptions(args, OPT_thinlto_prefix_replace_eq); + config->thinLTOSingleModule = args.getLastArgValue(OPT_thinlto_single_module); config->timeTraceEnabled = args.hasArg(OPT_time_trace); config->timeTraceGranularity = args::getInteger(args, OPT_time_trace_granularity, 500); @@ -1955,7 +1956,10 @@ // Likewise, --plugin-opt=emit-llvm and --plugin-opt=emit-asm are the // options to create output files in bitcode or assembly code // repsectively. No object files are generated. - if (config->thinLTOIndexOnly || config->emitLLVM || config->ltoEmitAsm) + // Also bail out here when only one thinLTO module is specified for + // compilation. The intermediate object file is the expected output. + if (config->thinLTOIndexOnly || config->emitLLVM || config->ltoEmitAsm || + !config->thinLTOSingleModule.empty()) return; // Apply symbol renames for -wrap. diff --git a/lld/ELF/LTO.cpp b/lld/ELF/LTO.cpp --- a/lld/ELF/LTO.cpp +++ b/lld/ELF/LTO.cpp @@ -139,6 +139,8 @@ c.HasWholeProgramVisibility = config->ltoWholeProgramVisibility; c.AlwaysEmitRegularLTOObj = !config->ltoObjPath.empty(); + c.ThinLTOSingleModule = std::string(config->thinLTOSingleModule); + c.TimeTraceEnabled = config->timeTraceEnabled; c.TimeTraceGranularity = config->timeTraceGranularity; diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td --- a/lld/ELF/Options.td +++ b/lld/ELF/Options.td @@ -553,6 +553,8 @@ HelpText<"Number of ThinLTO jobs. Default to --threads=">; def thinlto_object_suffix_replace_eq: JJ<"thinlto-object-suffix-replace=">; def thinlto_prefix_replace_eq: JJ<"thinlto-prefix-replace=">; +def thinlto_single_module: JJ<"thinlto-single-module=">, + HelpText<"Specific a single module to compile in ThinLTO mode, for debugging only">; def: J<"plugin-opt=O">, Alias, HelpText<"Alias for --lto-O">; def: F<"plugin-opt=debug-pass-manager">, diff --git a/lld/test/ELF/lto/thinlto-single-module.ll b/lld/test/ELF/lto/thinlto-single-module.ll new file mode 100644 --- /dev/null +++ b/lld/test/ELF/lto/thinlto-single-module.ll @@ -0,0 +1,68 @@ +; REQUIRES: x86 +; RUN: rm -fr %t && mkdir %t && cd %t +; RUN: opt -thinlto-bc -o main.o %s +; RUN: opt -thinlto-bc -o thin1.o %S/Inputs/thin1.ll +; RUN: opt -thinlto-bc -o thin2.o %S/Inputs/thin2.ll +; RUN: llvm-ar qcT thin.a thin1.o thin2.o +; RUN: ld.lld main.o thin.a --thinlto-single-module=main.o --lto-obj-path=single1.o +; RUN: llvm-readobj --symbols single1.o | FileCheck %s -check-prefix=CHECK-DEFAULT +; RUN: llvm-readobj --symbols single1.o1 | FileCheck %s -check-prefix=CHECK-MAIN +; RUN: ld.lld main.o thin.a --thinlto-single-module=thin.a --lto-obj-path=single2.o +; RUN: llvm-readobj --symbols single2.o | FileCheck %s -check-prefix=CHECK-DEFAULT +; RUN: llvm-readobj --symbols single2.o1 | FileCheck %s -check-prefix=CHECK-FOO +; RUN: llvm-readobj --symbols single2.o2 | FileCheck %s -check-prefix=CHECK-BLAH +; RUN: ld.lld main.o thin.a --thinlto-single-module=thin1.o --lto-obj-path=single3.o +; RUN: llvm-readobj --symbols single3.o | FileCheck %s -check-prefix=CHECK-DEFAULT +; RUN: llvm-readobj --symbols single3.o1 | FileCheck %s -check-prefix=CHECK-FOO + +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-scei-ps4" + +declare i32 @blah(i32 %meh) +declare i32 @foo(i32 %goo) + +define i32 @_start() { + call i32 @foo(i32 0) + call i32 @blah(i32 0) + ret i32 0 +} + +; CHECK-DEFAULT: Symbol { +; CHECK-DEFAULT: Name: ld-temp.o +; CHECK-DEFAULT-NEXT: Value: +; CHECK-DEFAULT-NEXT: Size: +; CHECK-DEFAULT-NEXT: Binding: Local +; CHECK-DEFAULT-NEXT: Type: File +; CHECK-DEFAULT-NEXT: Other +; CHECK-DEFAULT-NEXT: Section: Absolute +; CHECK-DEFAULT-NEXT: } + +; CHECK-MAIN: Symbol { +; CHECK-MAIN: Name: _start +; CHECK-MAIN-NEXT: Value: +; CHECK-MAIN-NEXT: Size: +; CHECK-MAIN-NEXT: Binding: Global +; CHECK-MAIN-NEXT: Type: Function +; CHECK-MAIN-NEXT: Other +; CHECK-MAIN-NEXT: Section: .text +; CHECK-MAIN-NEXT: } + +; CHECK-FOO: Symbol { +; CHECK-FOO: Name: foo +; CHECK-FOO-NEXT: Value: +; CHECK-FOO-NEXT: Size: +; CHECK-FOO-NEXT: Binding: Global +; CHECK-FOO-NEXT: Type: Function +; CHECK-FOO-NEXT: Other +; CHECK-FOO-NEXT: Section: .text +; CHECK-FOO-NEXT: } + +; CHECK-BLAH: Symbol { +; CHECK-BLAH: Name: blah +; CHECK-BLAH-NEXT: Value: +; CHECK-BLAH-NEXT: Size: +; CHECK-BLAH-NEXT: Binding: Global +; CHECK-BLAH-NEXT: Type: Function +; CHECK-BLAH-NEXT: Other +; CHECK-BLAH-NEXT: Section: .text +; CHECK-BLAH-NEXT: } diff --git a/llvm/include/llvm/LTO/Config.h b/llvm/include/llvm/LTO/Config.h --- a/llvm/include/llvm/LTO/Config.h +++ b/llvm/include/llvm/LTO/Config.h @@ -130,6 +130,9 @@ /// Statistics output file path. std::string StatsFile; + /// A single thinLTO module to compile. + std::string ThinLTOSingleModule; + /// Time trace enabled. bool TimeTraceEnabled = false; diff --git a/llvm/include/llvm/LTO/LTO.h b/llvm/include/llvm/LTO/LTO.h --- a/llvm/include/llvm/LTO/LTO.h +++ b/llvm/include/llvm/LTO/LTO.h @@ -17,6 +17,7 @@ #include "llvm/ADT/MapVector.h" #include "llvm/ADT/StringMap.h" +#include "llvm/Bitcode/BitcodeReader.h" #include "llvm/IR/ModuleSummaryIndex.h" #include "llvm/LTO/Config.h" #include "llvm/Object/IRSymtab.h" @@ -26,7 +27,6 @@ namespace llvm { -class BitcodeModule; class Error; class IRMover; class LLVMContext; @@ -330,12 +330,15 @@ bool EmptyCombinedModule = true; } RegularLTO; + using ModuleMapType = MapVector; + struct ThinLTOState { ThinLTOState(ThinBackend Backend); ThinBackend Backend; ModuleSummaryIndex CombinedIndex; - MapVector ModuleMap; + ModuleMapType ModuleMap; + Optional ModulesToCompile; DenseMap PrevailingModuleForGUID; } ThinLTO; diff --git a/llvm/lib/LTO/LTO.cpp b/llvm/lib/LTO/LTO.cpp --- a/llvm/lib/LTO/LTO.cpp +++ b/llvm/lib/LTO/LTO.cpp @@ -849,6 +849,19 @@ "Expected at most one ThinLTO module per bitcode file", inconvertibleErrorCode()); + if (!Conf.ThinLTOSingleModule.empty()) { + if (!ThinLTO.ModulesToCompile) + ThinLTO.ModulesToCompile = ModuleMapType(); + // This is a fuzzy name matching where only modules + // with name containing + // the specified switch value are goint to be compiled. + if (BM.getModuleIdentifier().contains(Conf.ThinLTOSingleModule)) { + ThinLTO.ModulesToCompile->insert({BM.getModuleIdentifier(), BM}); + llvm::errs() << "warning: [ThinLTO] Selecting " + << BM.getModuleIdentifier() << " to compile\n"; + } + } + return Error::success(); } @@ -1291,6 +1304,11 @@ if (ThinLTO.ModuleMap.empty()) return Error::success(); + if (ThinLTO.ModulesToCompile && ThinLTO.ModulesToCompile->empty()) { + llvm::errs() << "warning: [ThinLTO] No module compiled\n"; + return Error::success(); + } + if (Conf.CombinedIndexHook && !Conf.CombinedIndexHook(ThinLTO.CombinedIndex, GUIDPreservedSymbols)) return Error::success(); @@ -1395,10 +1413,13 @@ ThinLTO.Backend(Conf, ThinLTO.CombinedIndex, ModuleToDefinedGVSummaries, AddStream, Cache); + auto &ModuleMap = + ThinLTO.ModulesToCompile ? *ThinLTO.ModulesToCompile : ThinLTO.ModuleMap; + // Tasks 0 through ParallelCodeGenParallelismLevel-1 are reserved for combined // module and parallel code generation partitions. unsigned Task = RegularLTO.ParallelCodeGenParallelismLevel; - for (auto &Mod : ThinLTO.ModuleMap) { + for (auto &Mod : ModuleMap) { if (Error E = BackendProc->start(Task, Mod.second, ImportLists[Mod.first], ExportLists[Mod.first], ResolvedODR[Mod.first], ThinLTO.ModuleMap))