Skip to content

Commit

Permalink
[WebAssembly] Add option to emit passive segments
Browse files Browse the repository at this point in the history
Summary:
Adds `--passive-segments` and `--active-segments` flags to control
what kind of segments are emitted. For now the default is always
to emit active segments so this is not a breaking change, but in
the future the default will be changed to passive segments when
shared memory is requested and active segments otherwise. When
passive segments are emitted, corresponding memory.init and
data.drop instructions are emitted in a `__wasm_init_memory`
function that is automatically called at the beginning of
`__wasm_call_ctors`.

Reviewers: sbc100, aheejin, dschuff

Subscribers: azakai, dschuff, jgravelle-google, sunfish, jfb, llvm-commits

Tags: #llvm

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

llvm-svn: 365088
  • Loading branch information
tlively committed Jul 3, 2019
1 parent c96c174 commit 6004d9a
Show file tree
Hide file tree
Showing 13 changed files with 329 additions and 70 deletions.
137 changes: 107 additions & 30 deletions lld/test/wasm/data-segment-merging.ll
Expand Up @@ -8,40 +8,117 @@ target triple = "wasm32-unknown-unknown"
@e = private constant [9 x i8] c"constant\00", align 1
@f = private constant i8 43, align 4

; RUN: llc -filetype=obj %s -o %t.data-segment-merging.o
; RUN: llc -mattr=+bulk-memory -filetype=obj %s -o %t.o

; RUN: wasm-ld -no-gc-sections --no-entry -o %t.merged.wasm %t.data-segment-merging.o
; RUN: wasm-ld -no-gc-sections --no-entry -o %t.merged.wasm %t.o
; RUN: obj2yaml %t.merged.wasm | FileCheck %s --check-prefix=MERGE

; MERGE-NOT: DATACOUNT
; MERGE: - Type: DATA
; MERGE: Segments:
; MERGE: Content: 68656C6C6F00676F6F6462796500776861746576657200002A000000
; MERGE: Content: 636F6E7374616E74000000002B
; MERGE-NOT: Content:
; MERGE-LABEL: - Type: DATA
; MERGE-NEXT: Segments:
; MERGE-NEXT: - SectionOffset: 7
; MERGE-NEXT: InitFlags: 0
; MERGE-NEXT: Offset:
; MERGE: Content: 68656C6C6F00676F6F6462796500776861746576657200002A000000
; MERGE-NEXT: - SectionOffset: 41
; MERGE-NEXT: InitFlags: 0
; MERGE-NEXT: Offset:
; MERGE: Content: 636F6E7374616E74000000002B
; MERGE-NEXT: - Type: CUSTOM
; MERGE-NEXT: Name: name
; MERGE-NEXT: FunctionNames:
; MERGE-NEXT: - Index: 0
; MERGE-NEXT: Name: __wasm_call_ctors
; MERGE-NOT: - Index:

; RUN: wasm-ld -no-gc-sections --no-entry --no-merge-data-segments -o %t.separate.wasm %t.data-segment-merging.o
; RUN: wasm-ld -no-gc-sections --no-entry --no-merge-data-segments -o %t.separate.wasm %t.o
; RUN: obj2yaml %t.separate.wasm | FileCheck %s --check-prefix=SEPARATE

; SEPARATE-NOT: DATACOUNT
; SEPARATE: - Type: DATA
; SEPARATE: Segments:
; SEPARATE: Content: 68656C6C6F00
; SEPARATE: Content: 676F6F6462796500
; SEPARATE: Content: '776861746576657200'
; SEPARATE: Content: 2A000000
; SEPARATE: Content: 636F6E7374616E7400
; SEPARATE: Content: 2B
; SEPARATE-NOT: Content:

; RUN: llc -filetype=obj %s -mattr=+bulk-memory -o %t.data-segment-merging.bulk-memory.o
; RUN: wasm-ld -no-gc-sections --no-entry -o %t.merged.bulk-memory.wasm %t.data-segment-merging.bulk-memory.o
; RUN: obj2yaml %t.merged.bulk-memory.wasm | FileCheck %s --check-prefix=BULK-MEMORY

; BULK-MEMORY: - Type: DATACOUNT
; BULK-MEMORY: Count: 2
; BULK-MEMORY: - Type: DATA
; BULK-MEMORY: Segments:
; BULK-MEMORY: Content: 68656C6C6F00676F6F6462796500776861746576657200002A000000
; BULK-MEMORY: Content: 636F6E7374616E74000000002B
; BULK-MEMORY-NOT: Content:
; SEPARATE-LABEL: - Type: DATA
; SEPARATE-NEXT: Segments:
; SEPARATE-NEXT: - SectionOffset: 7
; SEPARATE-NEXT: InitFlags: 0
; SEPARATE-NEXT: Offset:
; SEPARATE: Content: 68656C6C6F00
; SEPARATE-NEXT: - SectionOffset: 19
; SEPARATE-NEXT: InitFlags: 0
; SEPARATE-NEXT: Offset:
; SEPARATE: Content: 676F6F6462796500
; SEPARATE-NEXT: - SectionOffset: 33
; SEPARATE-NEXT: InitFlags: 0
; SEPARATE-NEXT: Offset:
; SEPARATE: Content: '776861746576657200'
; SEPARATE-NEXT: - SectionOffset: 48
; SEPARATE-NEXT: InitFlags: 0
; SEPARATE-NEXT: Offset:
; SEPARATE: Content: 2A000000
; SEPARATE-NEXT: - SectionOffset: 58
; SEPARATE-NEXT: InitFlags: 0
; SEPARATE-NEXT: Offset:
; SEPARATE: Content: 636F6E7374616E7400
; SEPARATE-NEXT: - SectionOffset: 73
; SEPARATE-NEXT: InitFlags: 0
; SEPARATE-NEXT: Offset:
; SEPARATE: Content: 2B
; SEPARATE-NEXT: - Type: CUSTOM
; SEPARATE-NEXT: Name: name
; SEPARATE-NEXT: FunctionNames:
; SEPARATE-NEXT: - Index: 0
; SEPARATE-NEXT: Name: __wasm_call_ctors
; SEPARATE-NOT: - Index:

; RUN: wasm-ld -no-gc-sections --no-entry --passive-segments -o %t.merged.passive.wasm %t.o
; RUN: obj2yaml %t.merged.passive.wasm | FileCheck %s --check-prefix=PASSIVE-MERGE

; PASSIVE-MERGE-LABEL: - Type: DATACOUNT
; PASSIVE-MERGE-NEXT: Count: 2
; PASSIVE-MERGE-LABEL: - Type: DATA
; PASSIVE-MERGE-NEXT: Segments:
; PASSIVE-MERGE-NEXT: - SectionOffset: 3
; PASSIVE-MERGE-NEXT: InitFlags: 1
; PASSIVE-MERGE-NEXT: Content: 68656C6C6F00676F6F6462796500776861746576657200002A000000
; PASSIVE-MERGE-NEXT: - SectionOffset: 33
; PASSIVE-MERGE-NEXT: InitFlags: 1
; PASSIVE-MERGE-NEXT: Content: 636F6E7374616E74000000002B
; PASSIVE-MERGE-NEXT: - Type: CUSTOM
; PASSIVE-MERGE-NEXT: Name: name
; PASSIVE-MERGE-NEXT: FunctionNames:
; PASSIVE-MERGE-NEXT: - Index: 0
; PASSIVE-MERGE-NEXT: Name: __wasm_call_ctors
; PASSIVE-MERGE-NEXT: - Index: 1
; PASSIVE-MERGE-NEXT: Name: __wasm_init_memory
; PASSIVE-MERGE-NOT: - Index:

; RUN: wasm-ld -no-gc-sections --no-entry --passive-segments -no-merge-data-segments -o %t.separate.passive.wasm %t.o
; RUN: obj2yaml %t.separate.passive.wasm | FileCheck %s --check-prefix=PASSIVE-SEPARATE

; PASSIVE-SEPARATE-LABEL: - Type: DATACOUNT
; PASSIVE-SEPARATE-NEXT: Count: 6
; PASSIVE-SEPARATE-LABEL: - Type: DATA
; PASSIVE-SEPARATE-NEXT: Segments:
; PASSIVE-SEPARATE-NEXT: - SectionOffset: 3
; PASSIVE-SEPARATE-NEXT: InitFlags: 1
; PASSIVE-SEPARATE-NEXT: Content: 68656C6C6F00
; PASSIVE-SEPARATE-NEXT: - SectionOffset: 11
; PASSIVE-SEPARATE-NEXT: InitFlags: 1
; PASSIVE-SEPARATE-NEXT: Content: 676F6F6462796500
; PASSIVE-SEPARATE-NEXT: - SectionOffset: 21
; PASSIVE-SEPARATE-NEXT: InitFlags: 1
; PASSIVE-SEPARATE-NEXT: Content: '776861746576657200'
; PASSIVE-SEPARATE-NEXT: - SectionOffset: 32
; PASSIVE-SEPARATE-NEXT: InitFlags: 1
; PASSIVE-SEPARATE-NEXT: Content: 2A000000
; PASSIVE-SEPARATE-NEXT: - SectionOffset: 38
; PASSIVE-SEPARATE-NEXT: InitFlags: 1
; PASSIVE-SEPARATE-NEXT: Content: 636F6E7374616E7400
; PASSIVE-SEPARATE-NEXT: - SectionOffset: 49
; PASSIVE-SEPARATE-NEXT: InitFlags: 1
; PASSIVE-SEPARATE-NEXT: Content: 2B
; PASSIVE-SEPARATE-NEXT: - Type: CUSTOM
; PASSIVE-SEPARATE-NEXT: Name: name
; PASSIVE-SEPARATE-NEXT: FunctionNames:
; PASSIVE-SEPARATE-NEXT: - Index: 0
; PASSIVE-SEPARATE-NEXT: Name: __wasm_call_ctors
; PASSIVE-SEPARATE-NEXT: - Index: 1
; PASSIVE-SEPARATE-NEXT: Name: __wasm_init_memory
; PASSIVE-SEPARATE-NOT: - Index
99 changes: 99 additions & 0 deletions lld/test/wasm/data-segments.ll
@@ -0,0 +1,99 @@
; RUN: llc -filetype=obj %s -o %t.atomics.o -mattr=+atomics
; RUN: llc -filetype=obj %s -o %t.bulk-mem.o -mattr=+bulk-memory
; RUN: llc -filetype=obj %s -o %t.atomics.bulk-mem.o -mattr=+atomics,+bulk-memory

; atomics => active segments (TODO: error)
; RUN: wasm-ld -no-gc-sections --no-entry --shared-memory --max-memory=131072 %t.atomics.o -o %t.atomics.wasm
; RUN: obj2yaml %t.atomics.wasm | FileCheck %s --check-prefix ACTIVE

; atomics, active segments => active segments
; RUN: wasm-ld -no-gc-sections --no-entry --shared-memory --max-memory=131072 --active-segments %t.atomics.o -o %t.atomics.active.wasm
; RUN: obj2yaml %t.atomics.active.wasm | FileCheck %s --check-prefix ACTIVE

; atomics, passive segments => error
; RUN: not wasm-ld -no-gc-sections --no-entry --shared-memory --max-memory=131072 --passive-segments %t.atomics.o -o %t.atomics.passive.wasm 2>&1 | FileCheck %s --check-prefix ERROR

; bulk memory => active segments
; RUN: wasm-ld -no-gc-sections --no-entry %t.bulk-mem.o -o %t.bulk-mem.wasm
; RUN: obj2yaml %t.bulk-mem.wasm | FileCheck %s --check-prefix ACTIVE

; bulk-memory, active segments => active segments
; RUN: wasm-ld -no-gc-sections --no-entry --active-segments %t.bulk-mem.o -o %t.bulk-mem.active.wasm
; RUN: obj2yaml %t.bulk-mem.active.wasm | FileCheck %s --check-prefix ACTIVE

; bulk memory, passive segments => passive segments
; RUN: wasm-ld -no-gc-sections --no-entry --passive-segments %t.bulk-mem.o -o %t.bulk-mem.passive.wasm
; RUN: obj2yaml %t.bulk-mem.passive.wasm | FileCheck %s --check-prefix PASSIVE

; atomics, bulk memory => active segments (TODO: passive)
; RUN: wasm-ld -no-gc-sections --no-entry --shared-memory --max-memory=131072 %t.atomics.bulk-mem.o -o %t.atomics.bulk-mem.wasm
; RUN: obj2yaml %t.atomics.bulk-mem.wasm | FileCheck %s --check-prefix ACTIVE

; atomics, bulk memory, active segments => active segments
; RUN: wasm-ld -no-gc-sections --no-entry --shared-memory --max-memory=131072 --active-segments %t.atomics.bulk-mem.o -o %t.atomics.bulk-mem.active.wasm
; RUN: obj2yaml %t.atomics.bulk-mem.active.wasm | FileCheck %s --check-prefix ACTIVE

; atomics, bulk memory, passive segments => passive segments
; RUN: wasm-ld -no-gc-sections --no-entry --shared-memory --max-memory=131072 --passive-segments %t.atomics.bulk-mem.o -o %t.atomics.bulk-mem.passive.wasm
; RUN: obj2yaml %t.atomics.bulk-mem.passive.wasm | FileCheck %s --check-prefix PASSIVE

target triple = "wasm32-unknown-unknown"

@a = hidden global [6 x i8] c"hello\00", align 1
@b = hidden global [8 x i8] c"goodbye\00", align 1
@c = hidden global [9 x i8] c"whatever\00", align 1
@d = hidden global i32 42, align 4

@e = private constant [9 x i8] c"constant\00", align 1
@f = private constant i8 43, align 4

; ERROR: 'bulk-memory' feature must be used in order to emit passive segments

; ACTIVE-LABEL: - Type: CODE
; ACTIVE-NEXT: Functions:
; ACTIVE-NEXT: - Index: 0
; ACTIVE-NEXT: Locals: []
; ACTIVE-NEXT: Body: 0B
; ACTIVE-NEXT: - Type: DATA
; ACTIVE-NEXT: Segments:
; ACTIVE-NEXT: - SectionOffset: 7
; ACTIVE-NEXT: InitFlags: 0
; ACTIVE-NEXT: Offset:
; ACTIVE-NEXT: Opcode: I32_CONST
; ACTIVE-NEXT: Value: 1024
; ACTIVE-NEXT: Content: 68656C6C6F00676F6F6462796500776861746576657200002A000000
; ACTIVE-NEXT: - SectionOffset: 41
; ACTIVE-NEXT: InitFlags: 0
; ACTIVE-NEXT: Offset:
; ACTIVE-NEXT: Opcode: I32_CONST
; ACTIVE-NEXT: Value: 1052
; ACTIVE-NEXT: Content: 636F6E7374616E74000000002B
; ACTIVE-NEXT: - Type: CUSTOM
; ACTIVE-NEXT: Name: name
; ACTIVE-NEXT: FunctionNames:
; ACTIVE-NEXT: - Index: 0
; ACTIVE-NEXT: Name: __wasm_call_ctors

; PASSIVE-LABEL: - Type: CODE
; PASSIVE-NEXT: Functions:
; PASSIVE-NEXT: - Index: 0
; PASSIVE-NEXT: Locals: []
; PASSIVE-NEXT: Body: 10010B
; PASSIVE-NEXT: - Index: 1
; PASSIVE-NEXT: Locals: []
; PASSIVE-NEXT: Body: 4180084100411CFC080000FC0900419C084100410DFC080100FC09010B
; PASSIVE-NEXT: - Type: DATA
; PASSIVE-NEXT: Segments:
; PASSIVE-NEXT: - SectionOffset: 3
; PASSIVE-NEXT: InitFlags: 1
; PASSIVE-NEXT: Content: 68656C6C6F00676F6F6462796500776861746576657200002A000000
; PASSIVE-NEXT: - SectionOffset: 33
; PASSIVE-NEXT: InitFlags: 1
; PASSIVE-NEXT: Content: 636F6E7374616E74000000002B
; PASSIVE-NEXT: - Type: CUSTOM
; PASSIVE-NEXT: Name: name
; PASSIVE-NEXT: FunctionNames:
; PASSIVE-NEXT: - Index: 0
; PASSIVE-NEXT: Name: __wasm_call_ctors
; PASSIVE-NEXT: - Index: 1
; PASSIVE-NEXT: Name: __wasm_init_memory
1 change: 1 addition & 0 deletions lld/wasm/Config.h
Expand Up @@ -34,6 +34,7 @@ struct Configuration {
bool GcSections;
bool ImportMemory;
bool SharedMemory;
bool PassiveSegments;
bool ImportTable;
bool MergeDataSegments;
bool Pie;
Expand Down
18 changes: 15 additions & 3 deletions lld/wasm/Driver.cpp
Expand Up @@ -320,6 +320,9 @@ static void readConfigs(opt::InputArgList &Args) {
Args.hasFlag(OPT_fatal_warnings, OPT_no_fatal_warnings, false);
Config->ImportMemory = Args.hasArg(OPT_import_memory);
Config->SharedMemory = Args.hasArg(OPT_shared_memory);
// TODO: Make passive segments the default with shared memory
Config->PassiveSegments =
Args.hasFlag(OPT_passive_segments, OPT_active_segments, false);
Config->ImportTable = Args.hasArg(OPT_import_table);
Config->LTOO = args::getInteger(Args, OPT_lto_O, 2);
Config->LTOPartitions = args::getInteger(Args, OPT_lto_partitions, 1);
Expand Down Expand Up @@ -460,10 +463,19 @@ static void createSyntheticSymbols() {
"__wasm_call_ctors", WASM_SYMBOL_VISIBILITY_HIDDEN,
make<SyntheticFunction>(NullSignature, "__wasm_call_ctors"));

if (Config->PassiveSegments) {
// Passive segments are used to avoid memory being reinitialized on each
// thread's instantiation. These passive segments are initialized and
// dropped in __wasm_init_memory, which is the first function called from
// __wasm_call_ctors.
WasmSym::InitMemory = Symtab->addSyntheticFunction(
"__wasm_init_memory", WASM_SYMBOL_VISIBILITY_HIDDEN,
make<SyntheticFunction>(NullSignature, "__wasm_init_memory"));
}

if (Config->Pic) {
// For PIC code we create a synthetic function call __wasm_apply_relocs
// and add this as the first call in __wasm_call_ctors.
// We also unconditionally export
// For PIC code we create a synthetic function __wasm_apply_relocs which
// is called from __wasm_call_ctors before the user-level constructors.
WasmSym::ApplyRelocs = Symtab->addSyntheticFunction(
"__wasm_apply_relocs", WASM_SYMBOL_VISIBILITY_HIDDEN,
make<SyntheticFunction>(NullSignature, "__wasm_apply_relocs"));
Expand Down
8 changes: 5 additions & 3 deletions lld/wasm/MarkLive.cpp
Expand Up @@ -50,6 +50,10 @@ void lld::wasm::markLive() {
// function. However, this function does not contain relocations so we
// have to manually mark the ctors as live if CallCtors itself is live.
if (Sym == WasmSym::CallCtors) {
if (Config->PassiveSegments)
Enqueue(WasmSym::InitMemory);
if (Config->Pic)
Enqueue(WasmSym::ApplyRelocs);
for (const ObjFile *Obj : Symtab->ObjectFiles) {
const WasmLinkingData &L = Obj->getWasmObj()->linkingData();
for (const WasmInitFunc &F : L.InitFunctions) {
Expand Down Expand Up @@ -79,10 +83,8 @@ void lld::wasm::markLive() {
}
}

if (Config->Pic) {
if (Config->Pic)
Enqueue(WasmSym::CallCtors);
Enqueue(WasmSym::ApplyRelocs);
}

// Follow relocations to mark all reachable chunks.
while (!Q.empty()) {
Expand Down
6 changes: 6 additions & 0 deletions lld/wasm/Options.td
Expand Up @@ -143,6 +143,12 @@ def import_memory: F<"import-memory">,
def shared_memory: F<"shared-memory">,
HelpText<"Use shared linear memory">;

def active_segments: F<"active-segments">,
HelpText<"Force segments to be active">;

def passive_segments: F<"passive-segments">,
HelpText<"Force segments to be passive">;

def import_table: F<"import-table">,
HelpText<"Import function table from the environment">;

Expand Down
28 changes: 17 additions & 11 deletions lld/wasm/OutputSections.cpp
Expand Up @@ -132,20 +132,26 @@ void DataSection::finalizeContents() {
OS.flush();
BodySize = DataSectionHeader.size();

assert(!Config->Pic ||
Segments.size() <= 1 &&
"Currenly only a single data segment is supported in PIC mode");

for (OutputSegment *Segment : Segments) {
raw_string_ostream OS(Segment->Header);
writeUleb128(OS, 0, "memory index");
WasmInitExpr InitExpr;
if (Config->Pic) {
assert(Segments.size() <= 1 &&
"Currenly only a single data segment is supported in PIC mode");
InitExpr.Opcode = WASM_OPCODE_GLOBAL_GET;
InitExpr.Value.Global = WasmSym::MemoryBase->getGlobalIndex();
} else {
InitExpr.Opcode = WASM_OPCODE_I32_CONST;
InitExpr.Value.Int32 = Segment->StartVA;
writeUleb128(OS, Segment->InitFlags, "init flags");
if (Segment->InitFlags & WASM_SEGMENT_HAS_MEMINDEX)
writeUleb128(OS, 0, "memory index");
if ((Segment->InitFlags & WASM_SEGMENT_IS_PASSIVE) == 0) {
WasmInitExpr InitExpr;
if (Config->Pic) {
InitExpr.Opcode = WASM_OPCODE_GLOBAL_GET;
InitExpr.Value.Global = WasmSym::MemoryBase->getGlobalIndex();
} else {
InitExpr.Opcode = WASM_OPCODE_I32_CONST;
InitExpr.Value.Int32 = Segment->StartVA;
}
writeInitExpr(OS, InitExpr);
}
writeInitExpr(OS, InitExpr);
writeUleb128(OS, Segment->Size, "segment size");
OS.flush();

Expand Down
1 change: 1 addition & 0 deletions lld/wasm/OutputSegment.h
Expand Up @@ -33,6 +33,7 @@ class OutputSegment {

StringRef Name;
const uint32_t Index;
uint32_t InitFlags = 0;
uint32_t SectionOffset = 0;
uint32_t Alignment = 0;
uint32_t StartVA = 0;
Expand Down
1 change: 1 addition & 0 deletions lld/wasm/Symbols.cpp
Expand Up @@ -25,6 +25,7 @@ using namespace lld;
using namespace lld::wasm;

DefinedFunction *WasmSym::CallCtors;
DefinedFunction *WasmSym::InitMemory;
DefinedFunction *WasmSym::ApplyRelocs;
DefinedData *WasmSym::DsoHandle;
DefinedData *WasmSym::DataEnd;
Expand Down
4 changes: 4 additions & 0 deletions lld/wasm/Symbols.h
Expand Up @@ -438,6 +438,10 @@ struct WasmSym {
// Function that directly calls all ctors in priority order.
static DefinedFunction *CallCtors;

// __wasm_init_memory
// Function that initializes passive data segments post-instantiation.
static DefinedFunction *InitMemory;

// __wasm_apply_relocs
// Function that applies relocations to data segment post-instantiation.
static DefinedFunction *ApplyRelocs;
Expand Down
2 changes: 1 addition & 1 deletion lld/wasm/SyntheticSections.cpp
Expand Up @@ -323,7 +323,7 @@ void DataCountSection::writeBody() {
}

bool DataCountSection::isNeeded() const {
return NumSegments && Out.TargetFeaturesSec->Features.count("bulk-memory");
return NumSegments && Config->PassiveSegments;
}

static uint32_t getWasmFlags(const Symbol *Sym) {
Expand Down

0 comments on commit 6004d9a

Please sign in to comment.