Index: lld/trunk/COFF/Driver.cpp =================================================================== --- lld/trunk/COFF/Driver.cpp +++ lld/trunk/COFF/Driver.cpp @@ -896,50 +896,51 @@ 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 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; Index: lld/trunk/test/COFF/icf-associative.test =================================================================== --- lld/trunk/test/COFF/icf-associative.test +++ lld/trunk/test/COFF/icf-associative.test @@ -1,5 +1,5 @@ # 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: FileCheck %s < %t.log Index: lld/trunk/test/COFF/icf-simple.test =================================================================== --- lld/trunk/test/COFF/icf-simple.test +++ lld/trunk/test/COFF/icf-simple.test @@ -1,5 +1,5 @@ # 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 @@ -13,6 +13,31 @@ # 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 Index: lld/trunk/test/COFF/pdb-global-gc.yaml =================================================================== --- lld/trunk/test/COFF/pdb-global-gc.yaml +++ lld/trunk/test/COFF/pdb-global-gc.yaml @@ -1,7 +1,7 @@ # 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: 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 # This tests the case where an __imp_ chunk is discarded by linker GC. The debug Index: lld/trunk/test/COFF/pdb-import-gc.yaml =================================================================== --- lld/trunk/test/COFF/pdb-import-gc.yaml +++ lld/trunk/test/COFF/pdb-import-gc.yaml @@ -1,6 +1,6 @@ # RUN: yaml2obj %s -o %t.obj # 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 # This tests the case where an __imp_ chunk is discarded by linker GC. The debug