if (auto *Arg = Args.getLastArg(OPT_implib))
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)) {
std::string Str = StringRef(Arg->getValue()).lower();
SmallVector<StringRef, 1> Vec;
StringRef(Str).split(Vec, ',');
for (StringRef S : Vec) {
- if (S == "noref") {
- Config->DoGC = false;
- Config->DoICF = false;
- continue;
- }
- if (S == "icf" || S.startswith("icf=")) {
- Config->DoICF = true;
- continue;
- }
- if (S == "noicf") {
- Config->DoICF = false;
- continue;
- }
- if (S.startswith("lldlto=")) {
+ if (S == "ref") {
+ DoGC = true;
+ } else if (S == "noref") {
+ DoGC = false;
+ } else if (S == "icf" || S.startswith("icf=")) {
+ ICFLevel = 2;
+ } else if (S == "noicf") {
+ ICFLevel = 0;
+ } else if (S.startswith("lldlto=")) {
StringRef OptLevel = S.substr(7);
if (OptLevel.getAsInteger(10, Config->LTOOptLevel) ||
Config->LTOOptLevel > 3)
error("/opt:lldlto: invalid optimization level: " + OptLevel);
- continue;
- }
- if (S.startswith("lldltojobs=")) {
+ } else if (S.startswith("lldltojobs=")) {
StringRef Jobs = S.substr(11);
if (Jobs.getAsInteger(10, Config->LTOJobs) || Config->LTOJobs == 0)
error("/opt:lldltojobs: invalid job count: " + Jobs);
- continue;
- }
- if (S.startswith("lldltopartitions=")) {
+ } else if (S.startswith("lldltopartitions=")) {
StringRef N = S.substr(17);
if (N.getAsInteger(10, Config->LTOPartitions) ||
Config->LTOPartitions == 0)
error("/opt:lldltopartitions: invalid partition count: " + N);
- continue;
- }
- if (S != "ref" && S != "lbr" && S != "nolbr")
+ } else if (S != "lbr" && S != "nolbr")
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
if (Args.hasArg(OPT_lldsavetemps))
Config->SaveTemps = true;
# 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: FileCheck -check-prefix=ICF %s < %t.log
# RUN: /verbose /opt:noref,noicf %t.obj > %t.log 2>&1
# 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 bar