[LTO] Suppress emission of empty combined module by default

Summary:
That unless the user requested an output object (--lto-obj-path), the an
unused empty combined module is not emitted.

This changed is helpful for some target (ex. RISCV-V) which encoded the
ABI info in IR module flags (target-abi). Empty unused module has no ABI
info so the linker would get the linking error during merging
incompatible ABIs.

Reviewers: tejohnson, espindola, MaskRay

Subscribers: emaste, inglorion, arichardson, hiraditya, simoncook, MaskRay, steven_wu, dexonsmith, PkmX, dang, lenary, s.egerton, luismarques, llvm-commits

Tags: #llvm

Differential Revision: https://reviews.llvm.org/D78988
This commit is contained in:
Zakk Chen 2020-04-27 20:04:36 -07:00
parent 36183811fb
commit ad5fad0ac5
12 changed files with 58 additions and 19 deletions

View File

@ -81,6 +81,7 @@ static lto::Config createConfig() {
c.CPU = getCPUStr();
c.MAttrs = getMAttrs();
c.CGOptLevel = args::getCGOptLevel(config->ltoo);
c.AlwaysEmitRegularLTOObj = !config->ltoObjPath.empty();
if (config->saveTemps)
checkError(c.addSaveTemps(std::string(config->outputFile) + ".",

View File

@ -138,6 +138,7 @@ static lto::Config createConfig() {
c.DwoDir = std::string(config->dwoDir);
c.HasWholeProgramVisibility = config->ltoWholeProgramVisibility;
c.AlwaysEmitRegularLTOObj = !config->ltoObjPath.empty();
c.TimeTraceEnabled = config->timeTraceEnabled;
c.TimeTraceGranularity = config->timeTraceGranularity;
@ -327,7 +328,8 @@ std::vector<InputFile *> BitcodeCompiler::compile() {
}
if (config->saveTemps) {
saveBuffer(buf[0], config->outputFile + ".lto.o");
if (!buf[0].empty())
saveBuffer(buf[0], config->outputFile + ".lto.o");
for (unsigned i = 1; i != maxTasks; ++i)
saveBuffer(buf[i], config->outputFile + Twine(i) + ".lto.o");
}

View File

@ -11,6 +11,18 @@
; RUN: llvm-nm %t4.obj 2>&1 | FileCheck %s -check-prefix=SYMBOLS
; RUN: llvm-nm %t4.obj 2>&1 | count 1
;; Ensure lld emits empty combined module if specific obj-path.
; RUN: rm -fr %t.dir/objpath && mkdir -p %t.dir/objpath
; RUN: lld-link /out:%t.dir/objpath/a.exe -lto-obj-path:%t4.obj \
; RUN: -entry:main %t1.obj %t2.obj -lldsavetemps
; RUN: ls %t.dir/objpath/a.exe.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-link /out:%t.dir/objpath/a.exe \
; RUN: -entry:main %t1.obj %t2.obj -lldsavetemps
; RUN: ls %t.dir/objpath/a.exe.lto.* | count 2
; CHECK: Format: COFF-x86-64
; SYMBOLS: @feat.00

View File

@ -29,10 +29,8 @@ declare void @foo()
; CHECK: Modules
; CHECK: ============================================================
; CHECK: Mod 0000 | `{{.*}}main.exe.lto.obj`:
; CHECK: Obj: `{{.*}}main.exe.lto.obj`:
; CHECK: Mod 0001 | `{{.*}}main.exe.lto.1.obj`:
; CHECK: Mod 0000 | `{{.*}}main.exe.lto.1.obj`:
; CHECK: Obj: `{{.*}}main.exe.lto.1.obj`:
; CHECK: Mod 0002 | `{{.*}}main.exe.lto.2.obj`:
; CHECK: Mod 0001 | `{{.*}}main.exe.lto.2.obj`:
; CHECK: Obj: `{{.*}}main.exe.lto.2.obj`:
; CHECK: Mod 0003 | `* Linker *`:
; CHECK: Mod 0002 | `* Linker *`:

View File

@ -1,13 +1,13 @@
; REQUIRES: x86
; RUN: llvm-as %s -o %t.o
; RUN: rm -f %t2.*
; RUN: echo "foo = 1;" > %t.script
; RUN: ld.lld %t.o -o %t2 --script %t.script -save-temps
;; Combined module is not empty, but it will be empty after optimization.
;; Ensure lld still emits empty combined obj in this case.
; RUN: llvm-nm %t2.lto.o | count 0
; CHECK-NOT: bar
; CHECK-NOT: foo
; RUN: llvm-readobj --symbols %t2 | FileCheck %s --check-prefix=VAL
; VAL: Symbol {
; VAL: Name: foo

View File

@ -14,6 +14,16 @@
; RUN: ld.lld -thinlto-index-only -lto-obj-path=%t4.o -shared %t1.o %t2.o -o /dev/null
; RUN: llvm-readobj -h %t4.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: ld.lld --plugin-opt=obj-path=%t4.o -shared %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: ld.lld %t1.o %t2.o -o %t.dir/objpath/a.out --save-temps
; RUN: ls %t.dir/objpath/a.out*.lto.* | count 2
; CHECK: Format: elf64-x86-64
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"

View File

@ -66,6 +66,11 @@ struct Config {
/// link.
bool HasWholeProgramVisibility = false;
/// Always emit a Regular LTO object even when it is empty because no Regular
/// LTO modules were linked. This option is useful for some build system which
/// want to know a priori all possible output files.
bool AlwaysEmitRegularLTOObj = false;
/// If this field is set, the set of passes run in the middle-end optimizer
/// will be the one specified by the string. Only works with the new pass
/// manager as the old one doesn't have this ability.

View File

@ -331,6 +331,7 @@ private:
std::vector<GlobalValue *> Keep;
};
std::vector<AddedModule> ModsWithSummaries;
bool EmptyCombinedModule = true;
} RegularLTO;
struct ThinLTOState {

View File

@ -603,6 +603,7 @@ Error LTO::addModule(InputFile &Input, unsigned ModI,
if (LTOInfo->IsThinLTO)
return addThinLTO(BM, ModSyms, ResI, ResE);
RegularLTO.EmptyCombinedModule = false;
Expected<RegularLTOState::AddedModule> ModOrErr =
addRegularLTO(BM, ModSyms, ResI, ResE);
if (!ModOrErr)
@ -1026,10 +1027,13 @@ Error LTO::runRegularLTO(AddStreamFn AddStream) {
!Conf.PostInternalizeModuleHook(0, *RegularLTO.CombinedModule))
return Error::success();
}
if (Error Err =
backend(Conf, AddStream, RegularLTO.ParallelCodeGenParallelismLevel,
std::move(RegularLTO.CombinedModule), ThinLTO.CombinedIndex))
return Err;
if (!RegularLTO.EmptyCombinedModule || Conf.AlwaysEmitRegularLTOObj) {
if (Error Err = backend(
Conf, AddStream, RegularLTO.ParallelCodeGenParallelismLevel,
std::move(RegularLTO.CombinedModule), ThinLTO.CombinedIndex))
return Err;
}
return finalizeOptimizationRemarks(std::move(*DiagFileOrErr));
}

View File

@ -1,11 +1,11 @@
; RUN: opt -module-summary -o %t.bc %s
; RUN: rm -f %t2.0
; RUN: llvm-lto2 run %t.bc -r %t.bc,foo,pl -o %t2 -thinlto-distributed-indexes
; RUN: llvm-readobj -h %t2.0 | FileCheck %s
; RUN: llvm-nm %t2.0 2>&1 | count 0
; CHECK: Format: elf64-x86-64
; RUN: rm -f %t2.*
; RUN: llvm-lto2 run %t.bc -r %t.bc,foo,pl -o %t2 -thinlto-distributed-indexes -save-temps
; Ensure lto does not emit empty combined module.
; RUN: test ! -e %t2.0
; Ensure empty combined module has only 2 temp files.
; RUN: ls %t2.0.*.bc | count 2
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"

View File

@ -32,6 +32,7 @@
; Ensure gold generates an index as well as a binary with save-temps in ThinLTO mode.
; First force single-threaded mode
; RUN: rm -f %t4*
; RUN: %gold -plugin %llvmshlibdir/LLVMgold%shlibext \
; RUN: -m elf_x86_64 \
; RUN: --plugin-opt=save-temps \
@ -40,6 +41,8 @@
; RUN: -shared %t.o %t2.o -o %t4
; RUN: llvm-bcanalyzer -dump %t4.index.bc | FileCheck %s --check-prefix=COMBINED
; RUN: llvm-nm %t4 | FileCheck %s --check-prefix=NM
; Ensure ld does not emit empty combined module in default.
; RUN: ls %t4.o* | count 2
; Check with --no-map-whole-files
; RUN: %gold -plugin %llvmshlibdir/LLVMgold%shlibext \
@ -72,6 +75,8 @@
; RUN: -shared %t.o %t2.o -o %t4
; RUN: llvm-nm %t5.o1 | FileCheck %s --check-prefix=NM2
; RUN: llvm-nm %t5.o2 | FileCheck %s --check-prefix=NM2
; Ensure ld emits empty combined module if specific obj-path.
; RUN: ls %t5.o* | count 3
; Test to ensure that thinlto-index-only with obj-path creates the file.
; RUN: rm -f %t5.o %t5.o1

View File

@ -871,6 +871,7 @@ static std::unique_ptr<LTO> createLTO(IndexWriteCallback OnIndexWrite,
Conf.OptLevel = options::OptLevel;
Conf.PTO.LoopVectorization = options::OptLevel > 1;
Conf.PTO.SLPVectorization = options::OptLevel > 1;
Conf.AlwaysEmitRegularLTOObj = !options::obj_path.empty();
if (options::thinlto_index_only) {
std::string OldPrefix, NewPrefix;