Skip to content

Commit 7d7b961

Browse files
jcranmer-intelSquallATF
authored andcommitted
[LLD][COFF] Deduplicate common chunks when linking COFF files. (llvm#162553)
This fixes [issue 162148](llvm#162148). Common symbols are intended to have only a single version of the data present in the final executable. The MSVC linker is able to successfully deduplicate these chunks. If you have an application with a large number of translation units with a large block of common data (this is possible, for example, with Fortran code), then failing to deduplicate these chunks can make the data size so large that the resulting executable fails to load. The logic in this patch doesn't catch all of the potential cases for deduplication, but it should catch the most common ones.
1 parent b0176ed commit 7d7b961

File tree

5 files changed

+48
-1
lines changed

5 files changed

+48
-1
lines changed

lld/COFF/Chunks.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -783,7 +783,7 @@ uint32_t SectionChunk::getSectionNumber() const {
783783
return s.getIndex() + 1;
784784
}
785785

786-
CommonChunk::CommonChunk(const COFFSymbolRef s) : sym(s) {
786+
CommonChunk::CommonChunk(const COFFSymbolRef s) : live(false), sym(s) {
787787
// The value of a common symbol is its size. Align all common symbols smaller
788788
// than 32 bytes naturally, i.e. round the size up to the next power of two.
789789
// This is what MSVC link.exe does.

lld/COFF/Chunks.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,8 @@ class CommonChunk : public NonSectionChunk {
520520
uint32_t getOutputCharacteristics() const override;
521521
StringRef getSectionName() const override { return ".bss"; }
522522

523+
bool live;
524+
523525
private:
524526
const COFFSymbolRef sym;
525527
};

lld/COFF/Symbols.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,8 @@ class DefinedCommon : public DefinedCOFF {
232232
CommonChunk *c = nullptr)
233233
: DefinedCOFF(DefinedCommonKind, f, n, s), data(c), size(size) {
234234
this->isExternal = true;
235+
if (c)
236+
c->live = true;
235237
}
236238

237239
static bool classof(const Symbol *s) {

lld/COFF/Writer.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1101,6 +1101,10 @@ void Writer::createSections() {
11011101
sc->printDiscardedMessage();
11021102
continue;
11031103
}
1104+
if (auto *cc = dyn_cast<CommonChunk>(c)) {
1105+
if (!cc->live)
1106+
continue;
1107+
}
11041108
StringRef name = c->getSectionName();
11051109
if (shouldStripSectionSuffix(sc, name, ctx.config.mingw))
11061110
name = name.split('$').first;

lld/test/COFF/common-dedup.ll

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
; REQUIRES: x86
2+
; RUN: rm -rf %t.dir
3+
; RUN: split-file %s %t.dir
4+
; RUN: llc %t.dir/t1.ll -o %t.dir/t1.obj --filetype=obj
5+
; RUN: llc %t.dir/t2.ll -o %t.dir/t2.obj --filetype=obj
6+
; RUN: lld-link %t.dir/t1.obj %t.dir/t2.obj -entry:main -out:%t.dir/out.exe
7+
; RUN: llvm-readobj --section-headers %t.dir/out.exe | FileCheck %s
8+
9+
; Make sure that the data section contains just one copy of @a, not two.
10+
; CHECK: Name: .data
11+
; CHECK-NEXT: VirtualSize: 0x1000
12+
13+
;--- t1.ll
14+
target triple = "x86_64-pc-windows-msvc"
15+
@a = common global [4096 x i8] zeroinitializer
16+
17+
define i32 @usea() {
18+
%ref_common = load i32, ptr @a
19+
ret i32 %ref_common
20+
}
21+
22+
;--- t2.ll
23+
target triple = "x86_64-pc-windows-msvc"
24+
@a = common global [4096 x i8] zeroinitializer
25+
26+
define i32 @useb() {
27+
%ref_common = load i32, ptr @a
28+
ret i32 %ref_common
29+
}
30+
31+
declare i32 @usea()
32+
33+
define dso_local i32 @main() local_unnamed_addr {
34+
entry:
35+
%a = tail call i32 @usea()
36+
%b = tail call i32 @useb()
37+
%add = add nsw i32 %a, %b
38+
ret i32 %add
39+
}

0 commit comments

Comments
 (0)