Disable GC and ICF when /debug is present

ICF and GC impair debugging, so MSVC disables these optimizations when
/debug is passed. They are still on by default when no PDB is produced.

This change also makes /opt:ref enable ICF, which is consistent with
MSVC: https://msdn.microsoft.com/en-us/library/bxwfs976.aspx

We should consider making /opt:icf fold readonly data in the near
future. LLD used to do this, but we disabled it because it breaks too
many programs. MSVC only does this if the user explicitly passes
/opt:icf.

Reviewers: ruiu, pcc

Subscribers: llvm-commits

Differential Revision: https://reviews.llvm.org/D39885

llvm-svn: 318071
This commit is contained in:
Reid Kleckner 2017-11-13 18:38:25 +00:00
parent a9e47fd7d9
commit c2dcdd852b
5 changed files with 54 additions and 28 deletions

View File

@ -896,50 +896,51 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
if (auto *Arg = Args.getLastArg(OPT_implib)) if (auto *Arg = Args.getLastArg(OPT_implib))
Config->Implib = Arg->getValue(); Config->Implib = Arg->getValue();
// Handle /opt // Handle /opt.
bool DoGC = !Args.hasArg(OPT_debug);
unsigned ICFLevel = 1; // 0: off, 1: limited, 2: on
for (auto *Arg : Args.filtered(OPT_opt)) { for (auto *Arg : Args.filtered(OPT_opt)) {
std::string Str = StringRef(Arg->getValue()).lower(); std::string Str = StringRef(Arg->getValue()).lower();
SmallVector<StringRef, 1> Vec; SmallVector<StringRef, 1> Vec;
StringRef(Str).split(Vec, ','); StringRef(Str).split(Vec, ',');
for (StringRef S : Vec) { for (StringRef S : Vec) {
if (S == "noref") { if (S == "ref") {
Config->DoGC = false; DoGC = true;
Config->DoICF = false; } else if (S == "noref") {
continue; DoGC = false;
} } else if (S == "icf" || S.startswith("icf=")) {
if (S == "icf" || S.startswith("icf=")) { ICFLevel = 2;
Config->DoICF = true; } else if (S == "noicf") {
continue; ICFLevel = 0;
} } else if (S.startswith("lldlto=")) {
if (S == "noicf") {
Config->DoICF = false;
continue;
}
if (S.startswith("lldlto=")) {
StringRef OptLevel = S.substr(7); StringRef OptLevel = S.substr(7);
if (OptLevel.getAsInteger(10, Config->LTOOptLevel) || if (OptLevel.getAsInteger(10, Config->LTOOptLevel) ||
Config->LTOOptLevel > 3) Config->LTOOptLevel > 3)
error("/opt:lldlto: invalid optimization level: " + OptLevel); error("/opt:lldlto: invalid optimization level: " + OptLevel);
continue; } else if (S.startswith("lldltojobs=")) {
}
if (S.startswith("lldltojobs=")) {
StringRef Jobs = S.substr(11); StringRef Jobs = S.substr(11);
if (Jobs.getAsInteger(10, Config->LTOJobs) || Config->LTOJobs == 0) if (Jobs.getAsInteger(10, Config->LTOJobs) || Config->LTOJobs == 0)
error("/opt:lldltojobs: invalid job count: " + Jobs); error("/opt:lldltojobs: invalid job count: " + Jobs);
continue; } else if (S.startswith("lldltopartitions=")) {
}
if (S.startswith("lldltopartitions=")) {
StringRef N = S.substr(17); StringRef N = S.substr(17);
if (N.getAsInteger(10, Config->LTOPartitions) || if (N.getAsInteger(10, Config->LTOPartitions) ||
Config->LTOPartitions == 0) Config->LTOPartitions == 0)
error("/opt:lldltopartitions: invalid partition count: " + N); error("/opt:lldltopartitions: invalid partition count: " + N);
continue; } else if (S != "lbr" && S != "nolbr")
}
if (S != "ref" && S != "lbr" && S != "nolbr")
error("/opt: unknown option: " + S); error("/opt: unknown option: " + S);
} }
} }
// Limited ICF is enabled if GC is enabled and ICF was never mentioned
// explicitly.
// FIXME: LLD only implements "limited" ICF, i.e. it only merges identical
// code. If the user passes /OPT:ICF explicitly, LLD should merge identical
// comdat readonly data.
if (ICFLevel == 1 && !DoGC)
ICFLevel = 0;
Config->DoGC = DoGC;
Config->DoICF = ICFLevel > 0;
// Handle /lldsavetemps // Handle /lldsavetemps
if (Args.hasArg(OPT_lldsavetemps)) if (Args.hasArg(OPT_lldsavetemps))
Config->SaveTemps = true; Config->SaveTemps = true;

View File

@ -1,5 +1,5 @@
# RUN: yaml2obj < %s > %t.obj # RUN: yaml2obj < %s > %t.obj
# RUN: lld-link /entry:foo /out:%t.exe /subsystem:console /include:bar \ # RUN: lld-link /opt:icf /entry:foo /out:%t.exe /subsystem:console /include:bar \
# RUN: /debug /verbose %t.obj > %t.log 2>&1 # RUN: /debug /verbose %t.obj > %t.log 2>&1
# RUN: FileCheck %s < %t.log # RUN: FileCheck %s < %t.log

View File

@ -1,5 +1,5 @@
# RUN: yaml2obj < %s > %t.obj # RUN: yaml2obj < %s > %t.obj
# RUN: lld-link /entry:foo /out:%t.exe /subsystem:console /include:bar \ # RUN: lld-link /opt:icf /entry:foo /out:%t.exe /subsystem:console /include:bar \
# RUN: /verbose %t.obj > %t.log 2>&1 # RUN: /verbose %t.obj > %t.log 2>&1
# RUN: FileCheck -check-prefix=ICF %s < %t.log # RUN: FileCheck -check-prefix=ICF %s < %t.log
@ -13,6 +13,31 @@
# RUN: /verbose /opt:noref,noicf %t.obj > %t.log 2>&1 # RUN: /verbose /opt:noref,noicf %t.obj > %t.log 2>&1
# RUN: FileCheck -check-prefix=NOICF %s < %t.log # RUN: FileCheck -check-prefix=NOICF %s < %t.log
# ICF is on by default (no /opt: flags).
# RUN: lld-link /entry:foo /out:%t.exe /subsystem:console \
# RUN: /include:bar /verbose %t.obj > %t.log 2>&1
# RUN: FileCheck -check-prefix=ICF %s < %t.log
# /debug disables ICF.
# RUN: lld-link /debug /entry:foo /out:%t.exe /subsystem:console \
# RUN: /include:bar /verbose %t.obj > %t.log 2>&1
# RUN: FileCheck -check-prefix=NOICF %s < %t.log
# /opt:noref disables ICF.
# RUN: lld-link /opt:noref /entry:foo /out:%t.exe /subsystem:console \
# RUN: /include:bar /verbose %t.obj > %t.log 2>&1
# RUN: FileCheck -check-prefix=NOICF %s < %t.log
# /debug /opt:ref enables ICF.
# RUN: lld-link /debug /opt:ref /entry:foo /out:%t.exe /subsystem:console \
# RUN: /include:bar /verbose %t.obj > %t.log 2>&1
# RUN: FileCheck -check-prefix=ICF %s < %t.log
# /debug /opt:noicf,ref disables ICF.
# RUN: lld-link /debug /opt:noicf,ref /entry:foo /out:%t.exe /subsystem:console \
# RUN: /include:bar /verbose %t.obj > %t.log 2>&1
# RUN: FileCheck -check-prefix=NOICF %s < %t.log
# NOICF-NOT: Removed foo # NOICF-NOT: Removed foo
# NOICF-NOT: Removed bar # NOICF-NOT: Removed bar

View File

@ -1,7 +1,7 @@
# RUN: yaml2obj %s -o %t.obj # RUN: yaml2obj %s -o %t.obj
# RUN: llvm-mc %S/Inputs/pdb-global-gc.s -triple x86_64-windows-msvc -filetype=obj -o %t2.obj # RUN: llvm-mc %S/Inputs/pdb-global-gc.s -triple x86_64-windows-msvc -filetype=obj -o %t2.obj
# RUN: lld-link %t.obj %t2.obj -debug -entry:main \ # RUN: lld-link %t.obj %t2.obj -debug -entry:main \
# RUN: -nodefaultlib -debug -out:%t.exe -pdb:%t.pdb -verbose # RUN: -nodefaultlib -opt:ref -out:%t.exe -pdb:%t.pdb -verbose
# RUN: llvm-pdbutil dump -symbols -globals %t.pdb | FileCheck %s # RUN: llvm-pdbutil dump -symbols -globals %t.pdb | FileCheck %s
# This tests the case where an __imp_ chunk is discarded by linker GC. The debug # This tests the case where an __imp_ chunk is discarded by linker GC. The debug

View File

@ -1,6 +1,6 @@
# RUN: yaml2obj %s -o %t.obj # RUN: yaml2obj %s -o %t.obj
# RUN: lld-link %t.obj %S/Inputs/pdb-import-gc.lib -debug -entry:main \ # RUN: lld-link %t.obj %S/Inputs/pdb-import-gc.lib -debug -entry:main \
# RUN: -nodefaultlib -debug -out:%t.exe -pdb:%t.pdb # RUN: -nodefaultlib -opt:ref -out:%t.exe -pdb:%t.pdb
# RUN: llvm-pdbutil dump -globals -symbols %t.pdb | FileCheck %s # RUN: llvm-pdbutil dump -globals -symbols %t.pdb | FileCheck %s
# This tests the case where an __imp_ chunk is discarded by linker GC. The debug # This tests the case where an __imp_ chunk is discarded by linker GC. The debug