Skip to content

Commit 5bdc5e7

Browse files
committed
[lld-link] Add safe icf mode to lld-link, which does safe icf for all sections.
Differential Revision: https://reviews.llvm.org/D97436
1 parent 3dfa861 commit 5bdc5e7

File tree

5 files changed

+72
-24
lines changed

5 files changed

+72
-24
lines changed

lld/COFF/Config.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,13 @@ enum class GuardCFLevel {
8080
Full, // Enable all protections.
8181
};
8282

83+
enum class ICFLevel {
84+
None,
85+
Safe, // Safe ICF for all sections.
86+
All, // Aggressive ICF for code, but safe ICF for data, similar to MSVC's
87+
// behavior.
88+
};
89+
8390
// Global configuration.
8491
struct Configuration {
8592
enum ManifestKind { SideBySide, Embed, No };
@@ -95,7 +102,7 @@ struct Configuration {
95102
std::string importName;
96103
bool demangle = true;
97104
bool doGC = true;
98-
bool doICF = true;
105+
ICFLevel doICF = ICFLevel::None;
99106
bool tailMerge;
100107
bool relocatable = true;
101108
bool forceMultiple = false;

lld/COFF/Driver.cpp

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1552,8 +1552,9 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
15521552

15531553
// Handle /opt.
15541554
bool doGC = debug == DebugKind::None || args.hasArg(OPT_profile);
1555-
unsigned icfLevel =
1556-
args.hasArg(OPT_profile) ? 0 : 1; // 0: off, 1: limited, 2: on
1555+
Optional<ICFLevel> icfLevel = None;
1556+
if (args.hasArg(OPT_profile))
1557+
icfLevel = ICFLevel::None;
15571558
unsigned tailMerge = 1;
15581559
bool ltoNewPM = LLVM_ENABLE_NEW_PASS_MANAGER;
15591560
bool ltoDebugPM = false;
@@ -1567,9 +1568,11 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
15671568
} else if (s == "noref") {
15681569
doGC = false;
15691570
} else if (s == "icf" || s.startswith("icf=")) {
1570-
icfLevel = 2;
1571+
icfLevel = ICFLevel::All;
1572+
} else if (s == "safeicf") {
1573+
icfLevel = ICFLevel::Safe;
15711574
} else if (s == "noicf") {
1572-
icfLevel = 0;
1575+
icfLevel = ICFLevel::None;
15731576
} else if (s == "lldtailmerge") {
15741577
tailMerge = 2;
15751578
} else if (s == "nolldtailmerge") {
@@ -1601,16 +1604,12 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
16011604
}
16021605
}
16031606

1604-
// Limited ICF is enabled if GC is enabled and ICF was never mentioned
1605-
// explicitly.
1606-
// FIXME: LLD only implements "limited" ICF, i.e. it only merges identical
1607-
// code. If the user passes /OPT:ICF explicitly, LLD should merge identical
1608-
// comdat readonly data.
1609-
if (icfLevel == 1 && !doGC)
1610-
icfLevel = 0;
1607+
if (!icfLevel)
1608+
icfLevel = doGC ? ICFLevel::All : ICFLevel::None;
16111609
config->doGC = doGC;
1612-
config->doICF = icfLevel > 0;
1613-
config->tailMerge = (tailMerge == 1 && config->doICF) || tailMerge == 2;
1610+
config->doICF = icfLevel.getValue();
1611+
config->tailMerge =
1612+
(tailMerge == 1 && config->doICF != ICFLevel::None) || tailMerge == 2;
16141613
config->ltoNewPassManager = ltoNewPM;
16151614
config->ltoDebugPassManager = ltoDebugPM;
16161615

@@ -1719,8 +1718,8 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
17191718
args.hasFlag(OPT_allowisolation, OPT_allowisolation_no, true);
17201719
config->incremental =
17211720
args.hasFlag(OPT_incremental, OPT_incremental_no,
1722-
!config->doGC && !config->doICF && !args.hasArg(OPT_order) &&
1723-
!args.hasArg(OPT_profile));
1721+
!config->doGC && config->doICF == ICFLevel::None &&
1722+
!args.hasArg(OPT_order) && !args.hasArg(OPT_profile));
17241723
config->integrityCheck =
17251724
args.hasFlag(OPT_integritycheck, OPT_integritycheck_no, false);
17261725
config->cetCompat = args.hasFlag(OPT_cetcompat, OPT_cetcompat_no, false);
@@ -1769,7 +1768,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
17691768
config->incremental = false;
17701769
}
17711770

1772-
if (config->incremental && config->doICF) {
1771+
if (config->incremental && config->doICF != ICFLevel::None) {
17731772
warn("ignoring '/incremental' because ICF is enabled; use '/opt:noicf' to "
17741773
"disable");
17751774
config->incremental = false;
@@ -2212,9 +2211,9 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
22122211
convertResources();
22132212

22142213
// Identify identical COMDAT sections to merge them.
2215-
if (config->doICF) {
2214+
if (config->doICF != ICFLevel::None) {
22162215
findKeepUniqueSections();
2217-
doICF(symtab->getChunks());
2216+
doICF(symtab->getChunks(), config->doICF);
22182217
}
22192218

22202219
// Write the result.

lld/COFF/ICF.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ static Timer icfTimer("ICF", Timer::root());
4040

4141
class ICF {
4242
public:
43+
ICF(ICFLevel icfLevel) : icfLevel(icfLevel){};
4344
void run(ArrayRef<Chunk *> v);
4445

4546
private:
@@ -62,6 +63,7 @@ class ICF {
6263
std::vector<SectionChunk *> chunks;
6364
int cnt = 0;
6465
std::atomic<bool> repeat = {false};
66+
ICFLevel icfLevel = ICFLevel::All;
6567
};
6668

6769
// Returns true if section S is subject of ICF.
@@ -81,8 +83,9 @@ bool ICF::isEligible(SectionChunk *c) {
8183
if (!c->isCOMDAT() || !c->live || writable)
8284
return false;
8385

84-
// Code sections are eligible.
85-
if (c->getOutputCharacteristics() & llvm::COFF::IMAGE_SCN_MEM_EXECUTE)
86+
// Under regular (not safe) ICF, all code sections are eligible.
87+
if ((icfLevel == ICFLevel::All) &&
88+
c->getOutputCharacteristics() & llvm::COFF::IMAGE_SCN_MEM_EXECUTE)
8689
return true;
8790

8891
// .pdata and .xdata unwind info sections are eligible.
@@ -314,7 +317,9 @@ void ICF::run(ArrayRef<Chunk *> vec) {
314317
}
315318

316319
// Entry point to ICF.
317-
void doICF(ArrayRef<Chunk *> chunks) { ICF().run(chunks); }
320+
void doICF(ArrayRef<Chunk *> chunks, ICFLevel icfLevel) {
321+
ICF(icfLevel).run(chunks);
322+
}
318323

319324
} // namespace coff
320325
} // namespace lld

lld/COFF/ICF.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#ifndef LLD_COFF_ICF_H
1010
#define LLD_COFF_ICF_H
1111

12+
#include "Config.h"
1213
#include "lld/Common/LLVM.h"
1314
#include "llvm/ADT/ArrayRef.h"
1415

@@ -17,7 +18,7 @@ namespace coff {
1718

1819
class Chunk;
1920

20-
void doICF(ArrayRef<Chunk *> chunks);
21+
void doICF(ArrayRef<Chunk *> chunks, ICFLevel);
2122

2223
} // namespace coff
2324
} // namespace lld

lld/test/COFF/icf-safe.s

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,28 @@
33
# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-win32 %S/Inputs/icf-safe.s -o %t2.obj
44
# RUN: lld-link /dll /noentry /out:%t.dll /verbose /opt:noref,icf %t1.obj %t2.obj 2>&1 | FileCheck %s
55
# RUN: lld-link /dll /noentry /out:%t.dll /verbose /opt:noref,icf /export:g3 /export:g4 %t1.obj %t2.obj 2>&1 | FileCheck --check-prefix=EXPORT %s
6+
# RUN: lld-link /dll /noentry /out:%t.dll /verbose /opt:noref,safeicf %t1.obj %t2.obj 2>&1 | FileCheck %s --check-prefix=SAFEICF
67

78
# CHECK-NOT: Selected
89
# CHECK: Selected g3
910
# CHECK-NEXT: Removed g4
11+
# CHECK: Selected f1
12+
# CHECK-NEXT: Removed f2
13+
# CHECK-NEXT: Removed f3
14+
# CHECK-NEXT: Removed f4
1015
# CHECK-NOT: Removed
1116
# CHECK-NOT: Selected
1217

13-
# EXPORT-NOT: Selected
18+
# EXPORT-NOT: Selected g3
19+
# EXPORT-NOT: Selected g4
20+
21+
# SAFEICF-NOT: Selected
22+
# SAFEICF: Selected g3
23+
# SAFEICF-NEXT: Removed g4
24+
# SAFEICF: Selected f3
25+
# SAFEICF-NEXT: Removed f4
26+
# SAFEICF-NOT: Removed
27+
# SAFEICF-NOT: Selected
1428

1529
.section .rdata,"dr",one_only,g1
1630
.globl g1
@@ -32,6 +46,28 @@ g3:
3246
g4:
3347
.byte 2
3448

49+
.section .text,"xr",one_only,f1
50+
.globl f1
51+
f1:
52+
nop
53+
54+
.section .text,"xr",one_only,f2
55+
.globl f2
56+
f2:
57+
nop
58+
59+
.section .text,"xr",one_only,f3
60+
.globl f3
61+
f3:
62+
nop
63+
64+
.section .text,"xr",one_only,f4
65+
.globl f4
66+
f4:
67+
nop
68+
3569
.addrsig
3670
.addrsig_sym g1
3771
.addrsig_sym g2
72+
.addrsig_sym f1
73+
.addrsig_sym f2

0 commit comments

Comments
 (0)