Skip to content

Commit 07701ce

Browse files
committed
[lld] Merge equivalent symbols found during ICF
1 parent 3c7a0e6 commit 07701ce

File tree

5 files changed

+129
-1
lines changed

5 files changed

+129
-1
lines changed

lld/ELF/ICF.cpp

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,28 @@ bool ICF<ELFT>::equalsConstant(const InputSection *a, const InputSection *b) {
333333
: constantEq(a, ra.relas, b, rb.relas);
334334
}
335335

336+
template <class RelTy>
337+
static SmallVector<Symbol *> getReloc(const InputSection *sec,
338+
Relocs<RelTy> relocs) {
339+
SmallVector<Symbol *> syms;
340+
for (auto ri = relocs.begin(), re = relocs.end(); ri != re; ++ri) {
341+
Symbol &sym = sec->file->getRelocTargetSym(*ri);
342+
syms.push_back(&sym);
343+
}
344+
return syms;
345+
}
346+
347+
template <class ELFT>
348+
static SmallVector<Symbol *> getRelocTargetSyms(const InputSection *sec) {
349+
const RelsOrRelas<ELFT> rel = sec->template relsOrRelas<ELFT>();
350+
if (rel.areRelocsCrel())
351+
return getReloc(sec, rel.crels);
352+
if (rel.areRelocsRel())
353+
return getReloc(sec, rel.rels);
354+
355+
return getReloc(sec, rel.relas);
356+
}
357+
336358
// Compare two lists of relocations. Returns true if all pairs of
337359
// relocations point to the same section in terms of ICF.
338360
template <class ELFT>
@@ -535,14 +557,35 @@ template <class ELFT> void ICF<ELFT>::run() {
535557
auto print = [&ctx = ctx]() -> ELFSyncStream {
536558
return {ctx, ctx.arg.printIcfSections ? DiagLevel::Msg : DiagLevel::None};
537559
};
560+
561+
DenseMap<Symbol *, Symbol *> symbolMap;
538562
// Merge sections by the equivalence class.
563+
// Merge symbols identified as equivalent during ICF
539564
forEachClassRange(0, sections.size(), [&](size_t begin, size_t end) {
540565
if (end - begin == 1)
541566
return;
542567
print() << "selected section " << sections[begin];
568+
SmallVector<Symbol *> syms = getRelocTargetSyms<ELFT>(sections[begin]);
543569
for (size_t i = begin + 1; i < end; ++i) {
544570
print() << " removing identical section " << sections[i];
545571
sections[begin]->replace(sections[i]);
572+
SmallVector<Symbol *> replacedSyms =
573+
getRelocTargetSyms<ELFT>(sections[i]);
574+
assert(syms.size() == replacedSyms.size() &&
575+
"Should have same number of syms!");
576+
for (size_t i = 0; i < syms.size(); i++) {
577+
if (syms[i] == replacedSyms[i] || !syms[i]->isGlobal() ||
578+
!replacedSyms[i]->isGlobal())
579+
continue;
580+
auto [it, inserted] =
581+
symbolMap.insert(std::make_pair(replacedSyms[i], syms[i]));
582+
print() << " selected symbol: " << syms[i]->getName().data()
583+
<< "; replaced symbol: " << replacedSyms[i]->getName().data();
584+
if (!inserted) {
585+
print() << " replacement already exists: "
586+
<< it->getSecond()->getName().data();
587+
}
588+
}
546589

547590
// At this point we know sections merged are fully identical and hence
548591
// we want to remove duplicate implicit dependencies such as link order
@@ -561,11 +604,17 @@ template <class ELFT> void ICF<ELFT>::run() {
561604
d->folded = true;
562605
}
563606
};
564-
for (Symbol *sym : ctx.symtab->getSymbols())
607+
for (Symbol *sym : ctx.symtab->getSymbols()) {
565608
fold(sym);
609+
if (Symbol *s = symbolMap.lookup(sym))
610+
ctx.symtab->redirect(sym, s);
611+
}
566612
parallelForEach(ctx.objectFiles, [&](ELFFileBase *file) {
567613
for (Symbol *sym : file->getLocalSymbols())
568614
fold(sym);
615+
for (Symbol *&sym : file->getMutableGlobalSymbols())
616+
if (Symbol *s = symbolMap.lookup(sym))
617+
sym = s;
569618
});
570619

571620
// InputSectionDescription::sections is populated by processSectionCommands().

lld/ELF/SymbolTable.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,13 @@ using namespace llvm::ELF;
2929
using namespace lld;
3030
using namespace lld::elf;
3131

32+
void SymbolTable::redirect(Symbol *from, Symbol *to) {
33+
int &fromIdx = symMap[CachedHashStringRef(from->getName())];
34+
const int toIdx = symMap[CachedHashStringRef(to->getName())];
35+
36+
fromIdx = toIdx;
37+
}
38+
3239
void SymbolTable::wrap(Symbol *sym, Symbol *real, Symbol *wrap) {
3340
// Redirect __real_foo to the original foo and foo to the original __wrap_foo.
3441
int &idx1 = symMap[CachedHashStringRef(sym->getName())];

lld/ELF/SymbolTable.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ class SymbolTable {
4141
SymbolTable(Ctx &ctx) : ctx(ctx) {}
4242
ArrayRef<Symbol *> getSymbols() const { return symVector; }
4343

44+
void redirect(Symbol *from, Symbol *to);
4445
void wrap(Symbol *sym, Symbol *real, Symbol *wrap);
4546

4647
Symbol *insert(StringRef name);
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// REQUIRES: aarch64
2+
3+
# RUN: llvm-mc -filetype=obj -triple=aarch64 %s -o %t
4+
# RUN: ld.lld %t -o %t2 --icf=all
5+
# RUN: llvm-objdump --section-headers %t2 | FileCheck %s --check-prefix=EXE
6+
7+
# RUN: ld.lld -shared %t -o %t3 --icf=all
8+
# RUN: llvm-objdump --section-headers %t3 | FileCheck %s --check-prefix=DSO
9+
10+
## All .rodata.* sections should merge into a single GOT entry
11+
# EXE: {{.*}}.got 00000008{{.*}}
12+
13+
## When symbols are preemptible in DSO mode, GOT entries wouldn't be merged
14+
# DSO: {{.*}}.got 00000020{{.*}}
15+
16+
.addrsig
17+
18+
callee:
19+
ret
20+
21+
.section .rodata.dummy1,"a",@progbits
22+
sym1:
23+
.long 111
24+
.long 122
25+
.byte 123
26+
27+
.section .rodata.dummy2,"a",@progbits
28+
sym2:
29+
.long 111
30+
.long 122
31+
sym3:
32+
.byte 123
33+
34+
.macro f, index
35+
36+
.section .text.f1_\index,"ax",@progbits
37+
f1_\index:
38+
adrp x0, :got:g\index
39+
mov x1, #\index
40+
b f2_\index
41+
42+
.section .text.f2_\index,"ax",@progbits
43+
f2_\index:
44+
ldr x0, [x0, :got_lo12:g\index]
45+
b callee
46+
47+
.globl g\index
48+
.section .rodata.g\index,"a",@progbits
49+
g_\index:
50+
.long 111
51+
.long 122
52+
53+
g\index:
54+
.byte 123
55+
56+
.section .text._start,"ax",@progbits
57+
bl f1_\index
58+
59+
.endm
60+
61+
.section .text._start,"ax",@progbits
62+
.globl _start
63+
_start:
64+
65+
f 0
66+
f 1
67+
f 2
68+
f 3

lld/test/ELF/icf-preemptible.s

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,15 @@
1111
# EXE-NOT: {{.}}
1212
# EXE: selected section {{.*}}:(.text.g1)
1313
# EXE-NEXT: removing identical section {{.*}}:(.text.g2)
14+
# EXE-NEXT: selected symbol: f1; replaced symbol: f2
1415
# EXE-NEXT: removing identical section {{.*}}:(.text.g3)
1516
# EXE-NEXT: selected section {{.*}}:(.text.f1)
1617
# EXE-NEXT: removing identical section {{.*}}:(.text.f2)
1718
# EXE-NEXT: selected section {{.*}}:(.text.h1)
1819
# EXE-NEXT: removing identical section {{.*}}:(.text.h2)
20+
# EXE-NEXT: selected symbol: g1; replaced symbol: g2
1921
# EXE-NEXT: removing identical section {{.*}}:(.text.h3)
22+
# EXE-NEXT: selected symbol: g1; replaced symbol: g3
2023
# EXE-NOT: {{.}}
2124

2225
## Definitions are preemptible in a DSO. Only leaf functions can be folded.

0 commit comments

Comments
 (0)