[WebAssembly] Elide data segments for .bss sections

Summary:
WebAssembly memories are zero-initialized, so when module does not
import its memory initializing .bss sections is guaranteed to be a
no-op. To reduce binary size and initialization time, .bss sections
are simply not emitted into the final binary unless the memory is
imported.

Reviewers: sbc100

Subscribers: dschuff, jgravelle-google, aheejin, sunfish, llvm-commits

Tags: #llvm

Differential Revision: https://reviews.llvm.org/D68965

llvm-svn: 374940
This commit is contained in:
Thomas Lively 2019-10-15 19:05:11 +00:00
parent 0330fba6e1
commit 190dacc3cc
13 changed files with 75 additions and 49 deletions

View File

@ -1,11 +1,11 @@
target triple = "wasm32-unknown-unknown"
; Will collide: local (internal linkage) with global (external) linkage
@colliding_global1 = internal default global i32 0, align 4
@colliding_global1 = internal default global i32 1, align 4
; Will collide: global with local
@colliding_global2 = default global i32 0, align 4
@colliding_global2 = default global i32 1, align 4
; Will collide: local with local
@colliding_global3 = internal default global i32 0, align 4
@colliding_global3 = internal default global i32 1, align 4
; Will collide: local with global
define internal i32 @colliding_func1() {

View File

@ -1,11 +1,11 @@
target triple = "wasm32-unknown-unknown"
; Will collide: local (internal linkage) with global (external) linkage
@colliding_global1 = default global i32 0, align 4
@colliding_global1 = default global i32 1, align 4
; Will collide: global with local
@colliding_global2 = internal default global i32 0, align 4
@colliding_global2 = internal default global i32 1, align 4
; Will collide: local with local
@colliding_global3 = internal default global i32 0, align 4
@colliding_global3 = internal default global i32 1, align 4
; Will collide: local with global
define i32 @colliding_func1() {

14
lld/test/wasm/bss-only.ll Normal file
View File

@ -0,0 +1,14 @@
; RUN: llc -filetype=obj %s -o %t.o
; RUN: wasm-ld -no-gc-sections --no-entry %t.o -o %t.wasm
; RUN: obj2yaml %t.wasm | FileCheck %s
; Test that the data section is skipped entirely when there are only
; bss segments
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
target triple = "wasm32-unknown-unknown"
@a = global [1000 x i8] zeroinitializer, align 1
@b = global i32 0
; CHECK-NOT: - Type: DATA

View File

@ -1,6 +1,8 @@
; RUN: llc -filetype=obj %s -o %t.o
; RUN: wasm-ld -no-gc-sections --no-entry -o %t.wasm %t.o
; RUN: obj2yaml %t.wasm | FileCheck %s
; RUN: obj2yaml %t.wasm | FileCheck %s --check-prefixes=CHECK,NO-BSS
; RUN: wasm-ld -no-gc-sections --no-entry --import-memory -o %t.bss.wasm %t.o
; RUN: obj2yaml %t.bss.wasm | FileCheck %s --check-prefixes=CHECK,BSS
; RUN: wasm-ld -no-gc-sections --no-entry -o %t_reloc.o %t.o --relocatable
; RUN: obj2yaml %t_reloc.o | FileCheck -check-prefix RELOC %s
@ -32,12 +34,13 @@ target triple = "wasm32-unknown-unknown"
; CHECK-NEXT: Opcode: I32_CONST
; CHECK-NEXT: Value: 1032
; CHECK-NEXT: Content: '07000000'
; CHECK-NEXT: - SectionOffset: 37
; CHECK-NEXT: InitFlags: 0
; CHECK-NEXT: Offset:
; CHECK-NEXT: Opcode: I32_CONST
; CHECK-NEXT: Value: 1036
; CHECK-NEXT: Content: '00000000'
; BSS-NEXT: - SectionOffset: 37
; BSS-NEXT: InitFlags: 0
; BSS-NEXT: Offset:
; BSS-NEXT: Opcode: I32_CONST
; BSS-NEXT: Value: 1036
; BSS-NEXT: Content: '00000000'
; NO-BSS-NOT: - SectionOffset:
; RELOC-LABEL: SegmentInfo:
; RELOC-NEXT: - Index: 0

View File

@ -54,12 +54,6 @@ target triple = "wasm32-unknown-unknown"
; CHECK-NEXT: Opcode: I32_CONST
; CHECK-NEXT: Value: 1040
; CHECK-NEXT: Content: '0100000000000000000000000000000003000000000000000004000034040000'
; CHECK-NEXT: - SectionOffset: 58
; CHECK-NEXT: InitFlags: 0
; CHECK-NEXT: Offset:
; CHECK-NEXT: Opcode: I32_CONST
; CHECK-NEXT: Value: 1072
; CHECK-NEXT: Content: '0000000000000000'
; CHECK-NEXT: - Type: CUSTOM

View File

@ -44,13 +44,6 @@ target triple = "wasm32-unknown-unknown"
; ACTIVE-NEXT: Opcode: I32_CONST
; ACTIVE-NEXT: Value: 1040
; ACTIVE-NEXT: Content: 68656C6C6F00676F6F646279650000002A000000
; ACTIVE-NEXT: - SectionOffset: 53
; ACTIVE-NEXT: InitFlags: 0
; ACTIVE-NEXT: Offset:
; ACTIVE-NEXT: Opcode: I32_CONST
; ACTIVE-NEXT: Value: 1060
; ACTIVE-NEXT: Content: '0000000000
; ACTIVE-SAME: 0000000000'
; ACTIVE-NEXT: - Type: CUSTOM
; ACTIVE-NEXT: Name: name
; ACTIVE-NEXT: FunctionNames:
@ -78,10 +71,6 @@ target triple = "wasm32-unknown-unknown"
; PASSIVE-NEXT: - SectionOffset: 18
; PASSIVE-NEXT: InitFlags: 1
; PASSIVE-NEXT: Content: 68656C6C6F00676F6F646279650000002A000000
; PASSIVE-NEXT: - SectionOffset: 41
; PASSIVE-NEXT: InitFlags: 1
; PASSIVE-NEXT: Content: '0000000000
; PASSIVE-SAME: 0000000000'
; PASSIVE-NEXT: - Type: CUSTOM
; PASSIVE-NEXT: Name: name
; PASSIVE-NEXT: FunctionNames:

View File

@ -167,7 +167,7 @@
; CHECK-NEXT: Offset:
; CHECK-NEXT: Opcode: I32_CONST
; CHECK-NEXT: Value: 1024
; CHECK-NEXT: Content: '000000000000000000000000000000000000000000000000'
; CHECK-NEXT: Content: '010000000100000001000000010000000100000001000000'
; CHECK-NEXT: - Type: CUSTOM
; CHECK-NEXT: Name: name
; CHECK-NEXT: FunctionNames:
@ -341,19 +341,19 @@
; RELOC-NEXT: Offset:
; RELOC-NEXT: Opcode: I32_CONST
; RELOC-NEXT: Value: 0
; RELOC-NEXT: Content: '0000000000000000'
; RELOC-NEXT: Content: '0100000001000000'
; RELOC-NEXT: - SectionOffset: 19
; RELOC-NEXT: InitFlags: 0
; RELOC-NEXT: Offset:
; RELOC-NEXT: Opcode: I32_CONST
; RELOC-NEXT: Value: 8
; RELOC-NEXT: Content: '0000000000000000'
; RELOC-NEXT: Content: '0100000001000000'
; RELOC-NEXT: - SectionOffset: 32
; RELOC-NEXT: InitFlags: 0
; RELOC-NEXT: Offset:
; RELOC-NEXT: Opcode: I32_CONST
; RELOC-NEXT: Value: 16
; RELOC-NEXT: Content: '0000000000000000'
; RELOC-NEXT: Content: '0100000001000000'
; RELOC-NEXT: - Type: CUSTOM
; RELOC-NEXT: Name: linking
; RELOC-NEXT: Version: 2
@ -489,15 +489,15 @@
; RELOC-NEXT: Size: 4
; RELOC-NEXT: SegmentInfo:
; RELOC-NEXT: - Index: 0
; RELOC-NEXT: Name: .bss.colliding_global1
; RELOC-NEXT: Name: .data.colliding_global1
; RELOC-NEXT: Alignment: 2
; RELOC-NEXT: Flags: [ ]
; RELOC-NEXT: - Index: 1
; RELOC-NEXT: Name: .bss.colliding_global2
; RELOC-NEXT: Name: .data.colliding_global2
; RELOC-NEXT: Alignment: 2
; RELOC-NEXT: Flags: [ ]
; RELOC-NEXT: - Index: 2
; RELOC-NEXT: Name: .bss.colliding_global3
; RELOC-NEXT: Name: .data.colliding_global3
; RELOC-NEXT: Alignment: 2
; RELOC-NEXT: Flags: [ ]
; RELOC-NEXT: - Type: CUSTOM

View File

@ -128,8 +128,11 @@ void CodeSection::writeRelocations(raw_ostream &os) const {
void DataSection::finalizeContents() {
raw_string_ostream os(dataSectionHeader);
unsigned segmentCount =
std::count_if(segments.begin(), segments.end(),
[](OutputSegment *segment) { return !segment->isBss; });
writeUleb128(os, segments.size(), "data segment count");
writeUleb128(os, segmentCount, "data segment count");
os.flush();
bodySize = dataSectionHeader.size();
@ -137,6 +140,8 @@ void DataSection::finalizeContents() {
"Currenly only a single data segment is supported in PIC mode");
for (OutputSegment *segment : segments) {
if (segment->isBss)
continue;
raw_string_ostream os(segment->header);
writeUleb128(os, segment->initFlags, "init flags");
if (segment->initFlags & WASM_SEGMENT_HAS_MEMINDEX)
@ -181,6 +186,8 @@ void DataSection::writeTo(uint8_t *buf) {
memcpy(buf, dataSectionHeader.data(), dataSectionHeader.size());
for (const OutputSegment *segment : segments) {
if (segment->isBss)
continue;
// Write data segment header
uint8_t *segStart = buf + segment->sectionOffset;
memcpy(segStart, segment->header.data(), segment->header.size());
@ -205,6 +212,13 @@ void DataSection::writeRelocations(raw_ostream &os) const {
c->writeRelocations(os);
}
bool DataSection::isNeeded() const {
for (const OutputSegment *seg : segments)
if (!seg->isBss)
return true;
return false;
}
void CustomSection::finalizeContents() {
raw_string_ostream os(nameData);
encodeULEB128(name.size(), os);

View File

@ -82,7 +82,7 @@ public:
void writeTo(uint8_t *buf) override;
uint32_t getNumRelocations() const override;
void writeRelocations(raw_ostream &os) const override;
bool isNeeded() const override { return segments.size() > 0; }
bool isNeeded() const override;
void finalizeContents() override;
protected:

View File

@ -32,6 +32,7 @@ public:
}
StringRef name;
bool isBss = false;
uint32_t index = 0;
uint32_t initFlags = 0;
uint32_t sectionOffset = 0;

View File

@ -365,6 +365,12 @@ void ElemSection::writeBody() {
}
}
DataCountSection::DataCountSection(ArrayRef<OutputSegment *> segments)
: SyntheticSection(llvm::wasm::WASM_SEC_DATACOUNT),
numSegments(std::count_if(
segments.begin(), segments.end(),
[](OutputSegment *const segment) { return !segment->isBss; })) {}
void DataCountSection::writeBody() {
writeUleb128(bodyOutputStream, numSegments, "data count");
}

View File

@ -250,9 +250,7 @@ protected:
class DataCountSection : public SyntheticSection {
public:
DataCountSection(uint32_t numSegments)
: SyntheticSection(llvm::wasm::WASM_SEC_DATACOUNT),
numSegments(numSegments) {}
DataCountSection(ArrayRef<OutputSegment *> segments);
bool isNeeded() const override;
void writeBody() override;

View File

@ -669,6 +669,13 @@ void Writer::createOutputSegments() {
s = make<OutputSegment>(name);
if (config->sharedMemory || name == ".tdata")
s->initFlags = WASM_SEGMENT_IS_PASSIVE;
// Exported memories are guaranteed to be zero-initialized, so no need
// to emit data segments for bss sections.
// TODO: consider initializing bss sections with memory.fill
// instructions when memory is imported and bulk-memory is available.
if (!config->importMemory && !config->relocatable &&
name.startswith(".bss"))
s->isBss = true;
segments.push_back(s);
}
s->addInputSegment(segment);
@ -961,7 +968,7 @@ void Writer::createSyntheticSections() {
out.exportSec = make<ExportSection>();
out.startSec = make<StartSection>(segments.size());
out.elemSec = make<ElemSection>();
out.dataCountSec = make<DataCountSection>(segments.size());
out.dataCountSec = make<DataCountSection>(segments);
out.linkingSec = make<LinkingSection>(initFunctions, segments);
out.nameSec = make<NameSection>();
out.producersSec = make<ProducersSection>();