mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2024-11-24 06:10:12 +00:00
[WebAssembly] Initialize memory in start function
Summary: - `__wasm_init_memory` is now the WebAssembly start function instead of being called from `__wasm_call_ctors` or called directly by the runtime. - Adds a new synthetic data symbol `__wasm_init_memory_flag` that is atomically incremented from zero to one by the thread responsible for initializing memory. - All threads now unconditionally perform data.drop on all passive segments. - Removes --passive-segments and --active-segments flags and controls segment type based on --shared-memory instead. The deleted flags were only present to ameliorate the upgrade path in Emscripten. Reviewers: sbc100, aheejin Subscribers: dschuff, jgravelle-google, sunfish, jfb, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D65783 llvm-svn: 370965
This commit is contained in:
parent
85d6edb265
commit
09768c5d7a
@ -75,8 +75,8 @@ target triple = "wasm32-unknown-unknown"
|
||||
; CHECK-MAX-NEXT: Maximum: 0x00000002
|
||||
|
||||
; RUN: wasm-ld -no-gc-sections --allow-undefined --no-entry --shared-memory \
|
||||
; RUN: --initial-memory=131072 --max-memory=131072 -o %t_max.wasm %t.o \
|
||||
; RUN: --active-segments %t.hello.o
|
||||
; RUN: --features=atomics,bulk-memory --initial-memory=131072 \
|
||||
; RUN: --max-memory=131072 -o %t_max.wasm %t.o %t.hello.o
|
||||
; RUN: obj2yaml %t_max.wasm | FileCheck %s -check-prefix=CHECK-SHARED
|
||||
|
||||
; CHECK-SHARED: - Type: MEMORY
|
||||
|
@ -8,7 +8,8 @@ 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 -mattr=+bulk-memory -filetype=obj %s -o %t.o
|
||||
; RUN: llc -mattr=+bulk-memory,+atomics -filetype=obj %s -o %t.passive.o
|
||||
; RUN: llc -filetype=obj %s -o %t.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
|
||||
@ -67,7 +68,7 @@ target triple = "wasm32-unknown-unknown"
|
||||
; 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: wasm-ld -no-gc-sections --no-entry --shared-memory --max-memory=131072 -o %t.merged.passive.wasm %t.passive.o
|
||||
; RUN: obj2yaml %t.merged.passive.wasm | FileCheck %s --check-prefix=PASSIVE-MERGE
|
||||
|
||||
; PASSIVE-MERGE-LABEL: - Type: DATACOUNT
|
||||
@ -87,9 +88,10 @@ target triple = "wasm32-unknown-unknown"
|
||||
; PASSIVE-MERGE-NEXT: Name: __wasm_call_ctors
|
||||
; PASSIVE-MERGE-NEXT: - Index: 1
|
||||
; PASSIVE-MERGE-NEXT: Name: __wasm_init_memory
|
||||
; PASSIVE-MERGE-NOT: - Index:
|
||||
; PASSIVE-MERGE-NEXT: - Index: 2
|
||||
; PASSIVE-MERGE-NEXT: Name: __wasm_init_tls
|
||||
|
||||
; RUN: wasm-ld -no-gc-sections --no-entry --passive-segments -no-merge-data-segments -o %t.separate.passive.wasm %t.o
|
||||
; RUN: wasm-ld -no-gc-sections --no-entry --shared-memory --max-memory=131072 -no-merge-data-segments -o %t.separate.passive.wasm %t.passive.o
|
||||
; RUN: obj2yaml %t.separate.passive.wasm | FileCheck %s --check-prefix=PASSIVE-SEPARATE
|
||||
|
||||
; PASSIVE-SEPARATE-LABEL: - Type: DATACOUNT
|
||||
@ -121,4 +123,5 @@ target triple = "wasm32-unknown-unknown"
|
||||
; PASSIVE-SEPARATE-NEXT: Name: __wasm_call_ctors
|
||||
; PASSIVE-SEPARATE-NEXT: - Index: 1
|
||||
; PASSIVE-SEPARATE-NEXT: Name: __wasm_init_memory
|
||||
; PASSIVE-SEPARATE-NOT: - Index
|
||||
; PASSIVE-SEPARATE-NEXT: - Index: 2
|
||||
; PASSIVE-SEPARATE-NEXT: Name: __wasm_init_tls
|
||||
|
@ -2,39 +2,16 @@
|
||||
; 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 => error
|
||||
; atomics, shared memory => error
|
||||
; RUN: not wasm-ld -no-gc-sections --no-entry --shared-memory --max-memory=131072 %t.atomics.o -o %t.atomics.wasm 2>&1 | FileCheck %s --check-prefix ERROR
|
||||
|
||||
; 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-prefixes ACTIVE,ACTIVE-TLS
|
||||
|
||||
; 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
|
||||
; bulk memory, unshared 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 => passive segments
|
||||
; atomics, bulk memory, shared memory => passive segments
|
||||
; 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-prefixes PASSIVE,PASSIVE-TLS
|
||||
|
||||
; 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-prefixes ACTIVE,ACTIVE-TLS
|
||||
|
||||
; 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-prefixes PASSIVE,PASSIVE-TLS
|
||||
; RUN: obj2yaml %t.atomics.bulk-mem.wasm | FileCheck %s --check-prefixes PASSIVE
|
||||
|
||||
target triple = "wasm32-unknown-unknown"
|
||||
|
||||
@ -46,16 +23,13 @@ target triple = "wasm32-unknown-unknown"
|
||||
@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
|
||||
; ERROR: 'bulk-memory' feature must be used in order to use shared memory
|
||||
|
||||
; ACTIVE-LABEL: - Type: CODE
|
||||
; ACTIVE-NEXT: Functions:
|
||||
; ACTIVE-NEXT: - Index: 0
|
||||
; ACTIVE-NEXT: Locals: []
|
||||
; ACTIVE-NEXT: Body: 0B
|
||||
; ACTIVE-TLS-NEXT: - Index: 1
|
||||
; ACTIVE-TLS-NEXT: Locals: []
|
||||
; ACTIVE-TLS-NEXT: Body: 0B
|
||||
; ACTIVE-NEXT: - Type: DATA
|
||||
; ACTIVE-NEXT: Segments:
|
||||
; ACTIVE-NEXT: - SectionOffset: 7
|
||||
@ -82,20 +56,20 @@ target triple = "wasm32-unknown-unknown"
|
||||
; ACTIVE-NEXT: FunctionNames:
|
||||
; ACTIVE-NEXT: - Index: 0
|
||||
; ACTIVE-NEXT: Name: __wasm_call_ctors
|
||||
; ACTIVE-TLS-NEXT: - Index: 1
|
||||
; ACTIVE-TLS-NEXT: Name: __wasm_init_tls
|
||||
|
||||
; PASSIVE-LABEL: - Type: START
|
||||
; PASSIVE-NEXT: StartFunction: 1
|
||||
; PASSIVE-LABEL: - Type: CODE
|
||||
; PASSIVE-NEXT: Functions:
|
||||
; PASSIVE-NEXT: - Index: 0
|
||||
; PASSIVE-NEXT: Locals: []
|
||||
; PASSIVE-NEXT: Body: 10010B
|
||||
; PASSIVE-NEXT: Body: 0B
|
||||
; PASSIVE-NEXT: - Index: 1
|
||||
; PASSIVE-NEXT: Locals: []
|
||||
; PASSIVE-NEXT: Body: 41800841004114FC080000FC090041940841004190CE00FC080100FC090141A4D6004100410DFC080200FC09020B
|
||||
; PASSIVE-TLS-NEXT: - Index: 2
|
||||
; PASSIVE-TLS-NEXT: Locals: []
|
||||
; PASSIVE-TLS-NEXT: Body: 0B
|
||||
; PASSIVE-NEXT: Body: 41B4D60041004101FE480200044041B4D6004101427FFE0102001A0541800841004114FC08000041940841004190CE00FC08010041A4D6004100410DFC08020041B4D6004102FE17020041B4D600417FFE0002001A0BFC0900FC0901FC09020B
|
||||
; PASSIVE-NEXT: - Index: 2
|
||||
; PASSIVE-NEXT: Locals: []
|
||||
; PASSIVE-NEXT: Body: 0B
|
||||
; PASSIVE-NEXT: - Type: DATA
|
||||
; PASSIVE-NEXT: Segments:
|
||||
; PASSIVE-NEXT: - SectionOffset: 3
|
||||
@ -115,5 +89,5 @@ target triple = "wasm32-unknown-unknown"
|
||||
; PASSIVE-NEXT: Name: __wasm_call_ctors
|
||||
; PASSIVE-NEXT: - Index: 1
|
||||
; PASSIVE-NEXT: Name: __wasm_init_memory
|
||||
; PASSIVE-TLS-NEXT: - Index: 2
|
||||
; PASSIVE-TLS-NEXT: Name: __wasm_init_tls
|
||||
; PASSIVE-NEXT: - Index: 2
|
||||
; PASSIVE-NEXT: Name: __wasm_init_tls
|
||||
|
@ -32,7 +32,7 @@
|
||||
# CHECK-MAX-NEXT: Maximum: 0x00000005
|
||||
# CHECK-MAX-NEXT: - Type:
|
||||
|
||||
# RUN: wasm-ld --import-memory --shared-memory --active-segments \
|
||||
# RUN: wasm-ld --import-memory --shared-memory --features=atomics,bulk-memory \
|
||||
# RUN: --initial-memory=262144 --max-memory=327680 -o %t.max.wasm %t.start.o
|
||||
# RUN: obj2yaml %t.max.wasm | FileCheck -check-prefix=CHECK-SHARED %s
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
; Testing that __tls_size and __tls_align are correctly emitted when there are
|
||||
; no thread_local variables.
|
||||
|
||||
RUN: llc -mattr=+bulk-memory -filetype=obj %p/Inputs/start.ll -o %t.o
|
||||
RUN: llc -mattr=+bulk-memory,+atomics -filetype=obj %p/Inputs/start.ll -o %t.o
|
||||
|
||||
RUN: wasm-ld -no-gc-sections --shared-memory --max-memory=131072 --allow-undefined -o %t.wasm %t.o
|
||||
RUN: obj2yaml %t.wasm | FileCheck %s
|
||||
@ -14,7 +14,7 @@ CHECK-NEXT: Type: I32
|
||||
CHECK-NEXT: Mutable: true
|
||||
CHECK-NEXT: InitExpr:
|
||||
CHECK-NEXT: Opcode: I32_CONST
|
||||
CHECK-NEXT: Value: 66560
|
||||
CHECK-NEXT: Value: 66576
|
||||
|
||||
; __tls_base
|
||||
CHECK-NEXT: - Index: 1
|
||||
|
@ -3,7 +3,7 @@
|
||||
; RUN: wasm-ld -r -o %t.wasm %t.hello.o %t.o
|
||||
; RUN: obj2yaml %t.wasm | FileCheck %s --check-prefixes CHECK,NORMAL
|
||||
|
||||
; RUN: llc -filetype=obj %p/Inputs/hello.ll -o %t.hello.bm.o -mattr=+bulk-memory
|
||||
; RUN: llc -filetype=obj %p/Inputs/hello.ll -o %t.hello.bm.o -mattr=+bulk-memory,+atomics
|
||||
; RUN: llc -filetype=obj %s -o %t.bm.o -mattr=+bulk-memory
|
||||
; RUN: wasm-ld -r -o %t.mt.wasm %t.hello.bm.o %t.bm.o --shared-memory --max-memory=131072
|
||||
; RUN: obj2yaml %t.mt.wasm | FileCheck %s --check-prefixes CHECK,SHARED
|
||||
|
@ -1,19 +1,16 @@
|
||||
# RUN: yaml2obj %s -o %t1.o
|
||||
|
||||
# RUN: not wasm-ld --no-entry --shared-memory --active-segments %t1.o -o - 2>&1 | FileCheck %s --check-prefix SHARED-NO-MAX
|
||||
# RUN: not wasm-ld --no-entry --shared-memory %t1.o -o - 2>&1 | FileCheck %s --check-prefix SHARED-NO-MAX
|
||||
|
||||
# RUN: not wasm-ld --no-entry --shared-memory --active-segments --max-memory=100000 %t1.o -o - 2>&1 | FileCheck %s --check-prefix SHARED-UNALIGNED
|
||||
# RUN: not wasm-ld --no-entry --shared-memory --max-memory=100000 %t1.o -o - 2>&1 | FileCheck %s --check-prefix SHARED-UNALIGNED
|
||||
|
||||
# RUN: wasm-ld --no-entry --shared-memory --active-segments --max-memory=131072 %t1.o -o - | obj2yaml | FileCheck %s --check-prefix SHARED
|
||||
# RUN: not wasm-ld --no-entry --shared-memory --max-memory=131072 --features=bulk-memory %t1.o -o - 2>&1 | FileCheck %s --check-prefix SHARED-NO-ATOMICS
|
||||
|
||||
# RUN: not wasm-ld --no-entry --shared-memory --max-memory=131072 --features=atomics %t1.o -o - 2>&1 | FileCheck %s --check-prefix SHARED-NO-BULK-MEM
|
||||
|
||||
# RUN: not wasm-ld --no-entry --features=atomics %t1.o -o - 2>&1 | FileCheck %s --check-prefix ATOMICS-NO-SHARED
|
||||
|
||||
# RUN: not wasm-ld --no-entry --features=atomics --shared-memory --active-segments %t1.o -o - 2>&1 | FileCheck %s --check-prefix ATOMICS-NO-MAX
|
||||
|
||||
# RUN: not wasm-ld --no-entry --features=atomics --shared-memory --active-segments --max-memory=100000 %t1.o -o - 2>&1 | FileCheck %s --check-prefix ATOMICS-UNALIGNED
|
||||
|
||||
# RUN: wasm-ld --no-entry --features=atomics --shared-memory --active-segments --max-memory=131072 %t1.o -o - | obj2yaml | FileCheck %s --check-prefix SHARED
|
||||
|
||||
# RUN: wasm-ld --no-entry --shared-memory --max-memory=131072 --features=atomics,bulk-memory %t1.o -o - | obj2yaml | FileCheck %s --check-prefix SHARED
|
||||
|
||||
--- !WASM
|
||||
FileHeader:
|
||||
@ -58,18 +55,18 @@ Sections:
|
||||
Flags: [ ]
|
||||
...
|
||||
|
||||
# SHARED-NO-MAX: maximum memory too small, 66560 bytes needed{{$}}
|
||||
# SHARED-NO-MAX: maximum memory too small, 66576 bytes needed{{$}}
|
||||
|
||||
# SHARED-UNALIGNED: maximum memory must be 65536-byte aligned{{$}}
|
||||
|
||||
# SHARED-NO-ATOMICS: 'atomics' feature must be used in order to use shared memory
|
||||
|
||||
# SHARED-NO-BULK-MEM: 'bulk-memory' feature must be used in order to use shared memory
|
||||
|
||||
# ATOMICS-NO-SHARED: 'atomics' feature is used, so --shared-memory must be used{{$}}
|
||||
|
||||
# SHARED: - Type: MEMORY
|
||||
# SHARED-NEXT: Memories:
|
||||
# SHARED-NEXT: - Flags: [ HAS_MAX, IS_SHARED ]
|
||||
# SHARED-NEXT: Initial: 0x00000002
|
||||
# SHARED-NEXT: Maximum: 0x00000002
|
||||
|
||||
# ATOMICS-NO-SHARED: 'atomics' feature is used, so --shared-memory must be used{{$}}
|
||||
|
||||
# ATOMICS-NO-MAX: maximum memory too small, 66560 bytes needed{{$}}
|
||||
|
||||
# ATOMICS-UNALIGNED: maximum memory must be 65536-byte aligned{{$}}
|
||||
|
@ -1,4 +1,4 @@
|
||||
; RUN: llc -mattr=+bulk-memory -filetype=obj %s -o %t.o
|
||||
; RUN: llc -mattr=+bulk-memory,+atomics -filetype=obj %s -o %t.o
|
||||
|
||||
target triple = "wasm32-unknown-unknown"
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
; RUN: llc -mattr=+bulk-memory -filetype=obj %s -o %t.o
|
||||
; RUN: llc -mattr=+bulk-memory,+atomics -filetype=obj %s -o %t.o
|
||||
|
||||
target triple = "wasm32-unknown-unknown"
|
||||
|
||||
|
@ -35,7 +35,6 @@ struct Configuration {
|
||||
bool gcSections;
|
||||
bool importMemory;
|
||||
bool sharedMemory;
|
||||
bool passiveSegments;
|
||||
bool importTable;
|
||||
bool mergeDataSegments;
|
||||
bool pie;
|
||||
|
@ -319,8 +319,6 @@ 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);
|
||||
config->passiveSegments = args.hasFlag(
|
||||
OPT_passive_segments, OPT_active_segments, config->sharedMemory);
|
||||
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);
|
||||
@ -473,21 +471,10 @@ static void createSyntheticSymbols() {
|
||||
static llvm::wasm::WasmGlobalType globalTypeI32 = {WASM_TYPE_I32, false};
|
||||
static llvm::wasm::WasmGlobalType mutableGlobalTypeI32 = {WASM_TYPE_I32,
|
||||
true};
|
||||
|
||||
WasmSym::callCtors = symtab->addSyntheticFunction(
|
||||
"__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->isPic) {
|
||||
// For PIC code we create a synthetic function __wasm_apply_relocs which
|
||||
// is called from __wasm_call_ctors before the user-level constructors.
|
||||
@ -517,6 +504,15 @@ static void createSyntheticSymbols() {
|
||||
}
|
||||
|
||||
if (config->sharedMemory && !config->shared) {
|
||||
// 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 registered as the start function
|
||||
WasmSym::initMemory = symtab->addSyntheticFunction(
|
||||
"__wasm_init_memory", WASM_SYMBOL_VISIBILITY_HIDDEN,
|
||||
make<SyntheticFunction>(nullSignature, "__wasm_init_memory"));
|
||||
WasmSym::initMemoryFlag = symtab->addSyntheticDataSymbol(
|
||||
"__wasm_init_memory_flag", WASM_SYMBOL_VISIBILITY_HIDDEN);
|
||||
assert(WasmSym::initMemoryFlag);
|
||||
WasmSym::tlsBase = createGlobalVariable("__tls_base", true, 0);
|
||||
WasmSym::tlsSize = createGlobalVariable("__tls_size", false, 0);
|
||||
WasmSym::tlsAlign = createGlobalVariable("__tls_align", false, 1);
|
||||
|
@ -50,8 +50,6 @@ 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->isPic)
|
||||
enqueue(WasmSym::applyRelocs);
|
||||
for (const ObjFile *obj : symtab->objectFiles) {
|
||||
@ -86,6 +84,9 @@ void lld::wasm::markLive() {
|
||||
if (config->isPic)
|
||||
enqueue(WasmSym::callCtors);
|
||||
|
||||
if (config->sharedMemory && !config->shared)
|
||||
enqueue(WasmSym::initMemory);
|
||||
|
||||
// Follow relocations to mark all reachable chunks.
|
||||
while (!q.empty()) {
|
||||
InputChunk *c = q.pop_back_val();
|
||||
|
@ -146,12 +146,6 @@ 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 (default with unshared memory)">;
|
||||
|
||||
def passive_segments: F<"passive-segments">,
|
||||
HelpText<"Force segments to be passive (default with shared memory)">;
|
||||
|
||||
def import_table: F<"import-table">,
|
||||
HelpText<"Import function table from the environment">;
|
||||
|
||||
|
@ -32,6 +32,7 @@ DefinedData *WasmSym::dsoHandle;
|
||||
DefinedData *WasmSym::dataEnd;
|
||||
DefinedData *WasmSym::globalBase;
|
||||
DefinedData *WasmSym::heapBase;
|
||||
DefinedData *WasmSym::initMemoryFlag;
|
||||
GlobalSymbol *WasmSym::stackPointer;
|
||||
GlobalSymbol *WasmSym::tlsBase;
|
||||
GlobalSymbol *WasmSym::tlsSize;
|
||||
|
@ -453,14 +453,18 @@ struct WasmSym {
|
||||
// therefore be used as a backing store for brk()/malloc() implementations.
|
||||
static DefinedData *heapBase;
|
||||
|
||||
// __wasm_init_memory_flag
|
||||
// Symbol whose contents are nonzero iff memory has already been initialized.
|
||||
static DefinedData *initMemoryFlag;
|
||||
|
||||
// __wasm_init_memory
|
||||
// Function that initializes passive data segments during instantiation.
|
||||
static DefinedFunction *initMemory;
|
||||
|
||||
// __wasm_call_ctors
|
||||
// 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;
|
||||
|
@ -314,6 +314,15 @@ void ExportSection::writeBody() {
|
||||
writeExport(os, export_);
|
||||
}
|
||||
|
||||
bool StartSection::isNeeded() const {
|
||||
return !config->relocatable && numSegments && config->sharedMemory;
|
||||
}
|
||||
|
||||
void StartSection::writeBody() {
|
||||
raw_ostream &os = bodyOutputStream;
|
||||
writeUleb128(os, WasmSym::initMemory->getFunctionIndex(), "function index");
|
||||
}
|
||||
|
||||
void ElemSection::addEntry(FunctionSymbol *sym) {
|
||||
if (sym->hasTableIndex())
|
||||
return;
|
||||
@ -350,7 +359,7 @@ void DataCountSection::writeBody() {
|
||||
}
|
||||
|
||||
bool DataCountSection::isNeeded() const {
|
||||
return numSegments && config->passiveSegments;
|
||||
return numSegments && config->sharedMemory;
|
||||
}
|
||||
|
||||
static uint32_t getWasmFlags(const Symbol *sym) {
|
||||
|
@ -217,6 +217,18 @@ public:
|
||||
std::vector<llvm::wasm::WasmExport> exports;
|
||||
};
|
||||
|
||||
class StartSection : public SyntheticSection {
|
||||
public:
|
||||
StartSection(uint32_t numSegments)
|
||||
: SyntheticSection(llvm::wasm::WASM_SEC_START), numSegments(numSegments) {
|
||||
}
|
||||
bool isNeeded() const override;
|
||||
void writeBody() override;
|
||||
|
||||
protected:
|
||||
uint32_t numSegments;
|
||||
};
|
||||
|
||||
class ElemSection : public SyntheticSection {
|
||||
public:
|
||||
ElemSection()
|
||||
@ -327,6 +339,7 @@ struct OutStruct {
|
||||
GlobalSection *globalSec;
|
||||
EventSection *eventSec;
|
||||
ExportSection *exportSec;
|
||||
StartSection *startSec;
|
||||
ElemSection *elemSec;
|
||||
DataCountSection *dataCountSec;
|
||||
LinkingSection *linkingSec;
|
||||
|
@ -254,6 +254,15 @@ void Writer::layoutMemory() {
|
||||
}
|
||||
}
|
||||
|
||||
// Make space for the memory initialization flag
|
||||
if (WasmSym::initMemoryFlag) {
|
||||
memoryPtr = alignTo(memoryPtr, 4);
|
||||
WasmSym::initMemoryFlag->setVirtualAddress(memoryPtr);
|
||||
log(formatv("mem: {0,-15} offset={1,-8} size={2,-8} align={3}",
|
||||
"__wasm_init_memory_flag", memoryPtr, 4, 4));
|
||||
memoryPtr += 4;
|
||||
}
|
||||
|
||||
// TODO: Add .bss space here.
|
||||
if (WasmSym::dataEnd)
|
||||
WasmSym::dataEnd->setVirtualAddress(memoryPtr);
|
||||
@ -333,6 +342,7 @@ void Writer::addSections() {
|
||||
addSection(out.globalSec);
|
||||
addSection(out.eventSec);
|
||||
addSection(out.exportSec);
|
||||
addSection(out.startSec);
|
||||
addSection(out.elemSec);
|
||||
addSection(out.dataCountSec);
|
||||
|
||||
@ -363,15 +373,15 @@ void Writer::populateTargetFeatures() {
|
||||
StringMap<std::string> used;
|
||||
StringMap<std::string> required;
|
||||
StringMap<std::string> disallowed;
|
||||
SmallSet<std::string, 8> &allowed = out.targetFeaturesSec->features;
|
||||
bool tlsUsed = false;
|
||||
|
||||
// Only infer used features if user did not specify features
|
||||
bool inferFeatures = !config->features.hasValue();
|
||||
|
||||
if (!inferFeatures) {
|
||||
for (auto &feature : config->features.getValue())
|
||||
out.targetFeaturesSec->features.insert(feature);
|
||||
// No need to read or check features
|
||||
auto &explicitFeatures = config->features.getValue();
|
||||
allowed.insert(explicitFeatures.begin(), explicitFeatures.end());
|
||||
if (!config->checkFeatures)
|
||||
return;
|
||||
}
|
||||
@ -397,21 +407,20 @@ void Writer::populateTargetFeatures() {
|
||||
}
|
||||
}
|
||||
|
||||
for (InputSegment *segment : file->segments) {
|
||||
if (!segment->live)
|
||||
continue;
|
||||
// Find TLS data segments
|
||||
auto isTLS = [](InputSegment *segment) {
|
||||
StringRef name = segment->getName();
|
||||
if (name.startswith(".tdata") || name.startswith(".tbss"))
|
||||
tlsUsed = true;
|
||||
}
|
||||
return segment->live &&
|
||||
(name.startswith(".tdata") || name.startswith(".tbss"));
|
||||
};
|
||||
tlsUsed = tlsUsed ||
|
||||
std::any_of(file->segments.begin(), file->segments.end(), isTLS);
|
||||
}
|
||||
|
||||
if (inferFeatures)
|
||||
out.targetFeaturesSec->features.insert(used.keys().begin(),
|
||||
used.keys().end());
|
||||
allowed.insert(used.keys().begin(), used.keys().end());
|
||||
|
||||
if (out.targetFeaturesSec->features.count("atomics") &&
|
||||
!config->sharedMemory) {
|
||||
if (allowed.count("atomics") && !config->sharedMemory) {
|
||||
if (inferFeatures)
|
||||
error(Twine("'atomics' feature is used by ") + used["atomics"] +
|
||||
", so --shared-memory must be used");
|
||||
@ -426,18 +435,22 @@ void Writer::populateTargetFeatures() {
|
||||
error("'atomics' feature is disallowed by " + disallowed["atomics"] +
|
||||
", so --shared-memory must not be used");
|
||||
|
||||
if (!used.count("bulk-memory") && config->passiveSegments)
|
||||
error("'bulk-memory' feature must be used in order to emit passive "
|
||||
"segments");
|
||||
if (!allowed.count("atomics") && config->sharedMemory)
|
||||
error("'atomics' feature must be used in order to use shared "
|
||||
"memory");
|
||||
|
||||
if (!used.count("bulk-memory") && tlsUsed)
|
||||
if (!allowed.count("bulk-memory") && config->sharedMemory)
|
||||
error("'bulk-memory' feature must be used in order to use shared "
|
||||
"memory");
|
||||
|
||||
if (!allowed.count("bulk-memory") && tlsUsed)
|
||||
error("'bulk-memory' feature must be used in order to use thread-local "
|
||||
"storage");
|
||||
|
||||
// Validate that used features are allowed in output
|
||||
if (!inferFeatures) {
|
||||
for (auto &feature : used.keys()) {
|
||||
if (!out.targetFeaturesSec->features.count(feature))
|
||||
if (!allowed.count(feature))
|
||||
error(Twine("Target feature '") + feature + "' used by " +
|
||||
used[feature] + " is not allowed.");
|
||||
}
|
||||
@ -655,7 +668,7 @@ void Writer::createOutputSegments() {
|
||||
if (s == nullptr) {
|
||||
LLVM_DEBUG(dbgs() << "new segment: " << name << "\n");
|
||||
s = make<OutputSegment>(name, segments.size());
|
||||
if (config->passiveSegments || name == ".tdata")
|
||||
if (config->sharedMemory || name == ".tdata")
|
||||
s->initFlags = WASM_SEGMENT_IS_PASSIVE;
|
||||
segments.push_back(s);
|
||||
}
|
||||
@ -678,32 +691,119 @@ static void createFunction(DefinedFunction *func, StringRef bodyContent) {
|
||||
|
||||
void Writer::createInitMemoryFunction() {
|
||||
LLVM_DEBUG(dbgs() << "createInitMemoryFunction\n");
|
||||
assert(WasmSym::initMemoryFlag);
|
||||
uint32_t flagAddress = WasmSym::initMemoryFlag->getVirtualAddress();
|
||||
std::string bodyContent;
|
||||
{
|
||||
raw_string_ostream os(bodyContent);
|
||||
writeUleb128(os, 0, "num locals");
|
||||
|
||||
// initialize passive data segments
|
||||
for (const OutputSegment *s : segments) {
|
||||
if (s->initFlags & WASM_SEGMENT_IS_PASSIVE && s->name != ".tdata") {
|
||||
// destination address
|
||||
writeU8(os, WASM_OPCODE_I32_CONST, "i32.const");
|
||||
writeSleb128(os, s->startVA, "destination address");
|
||||
// source segment offset
|
||||
writeU8(os, WASM_OPCODE_I32_CONST, "i32.const");
|
||||
writeSleb128(os, 0, "segment offset");
|
||||
// memory region size
|
||||
writeU8(os, WASM_OPCODE_I32_CONST, "i32.const");
|
||||
writeSleb128(os, s->size, "memory region size");
|
||||
// memory.init instruction
|
||||
writeU8(os, WASM_OPCODE_MISC_PREFIX, "bulk-memory prefix");
|
||||
writeUleb128(os, WASM_OPCODE_MEMORY_INIT, "MEMORY.INIT");
|
||||
writeUleb128(os, s->index, "segment index immediate");
|
||||
writeU8(os, 0, "memory index immediate");
|
||||
// data.drop instruction
|
||||
writeU8(os, WASM_OPCODE_MISC_PREFIX, "bulk-memory prefix");
|
||||
writeUleb128(os, WASM_OPCODE_DATA_DROP, "DATA.DROP");
|
||||
writeUleb128(os, s->index, "segment index immediate");
|
||||
if (segments.size()) {
|
||||
// Initialize memory in a thread-safe manner. The thread that successfully
|
||||
// increments the flag from 0 to 1 is is responsible for performing the
|
||||
// memory initialization. Other threads go sleep on the flag until the
|
||||
// first thread finishing initializing memory, increments the flag to 2,
|
||||
// and wakes all the other threads. Once the flag has been set to 2,
|
||||
// subsequently started threads will skip the sleep. All threads
|
||||
// unconditionally drop their passive data segments once memory has been
|
||||
// initialized. The generated code is as follows:
|
||||
//
|
||||
// (func $__wasm_init_memory
|
||||
// (if
|
||||
// (i32.atomic.rmw.cmpxchg align=2 offset=0
|
||||
// (i32.const $__init_memory_flag)
|
||||
// (i32.const 0)
|
||||
// (i32.const 1)
|
||||
// )
|
||||
// (then
|
||||
// (drop
|
||||
// (i32.atomic.wait align=2 offset=0
|
||||
// (i32.const $__init_memory_flag)
|
||||
// (i32.const 1)
|
||||
// (i32.const -1)
|
||||
// )
|
||||
// )
|
||||
// )
|
||||
// (else
|
||||
// ( ... initialize data segments ... )
|
||||
// (i32.atomic.store align=2 offset=0
|
||||
// (i32.const $__init_memory_flag)
|
||||
// (i32.const 2)
|
||||
// )
|
||||
// (drop
|
||||
// (i32.atomic.notify align=2 offset=0
|
||||
// (i32.const $__init_memory_flag)
|
||||
// (i32.const -1u)
|
||||
// )
|
||||
// )
|
||||
// )
|
||||
// )
|
||||
// ( ... drop data segments ... )
|
||||
// )
|
||||
|
||||
// Atomically check whether this is the main thread.
|
||||
writeI32Const(os, flagAddress, "flag address");
|
||||
writeI32Const(os, 0, "expected flag value");
|
||||
writeI32Const(os, 1, "flag value");
|
||||
writeU8(os, WASM_OPCODE_ATOMICS_PREFIX, "atomics prefix");
|
||||
writeUleb128(os, WASM_OPCODE_I32_RMW_CMPXCHG, "i32.atomic.rmw.cmpxchg");
|
||||
writeMemArg(os, 2, 0);
|
||||
writeU8(os, WASM_OPCODE_IF, "IF");
|
||||
writeU8(os, WASM_TYPE_NORESULT, "blocktype");
|
||||
|
||||
// Did not increment 0, so wait for main thread to initialize memory
|
||||
writeI32Const(os, flagAddress, "flag address");
|
||||
writeI32Const(os, 1, "expected flag value");
|
||||
writeI64Const(os, -1, "timeout");
|
||||
writeU8(os, WASM_OPCODE_ATOMICS_PREFIX, "atomics prefix");
|
||||
writeUleb128(os, WASM_OPCODE_I32_ATOMIC_WAIT, "i32.atomic.wait");
|
||||
writeMemArg(os, 2, 0);
|
||||
writeU8(os, WASM_OPCODE_DROP, "drop");
|
||||
|
||||
writeU8(os, WASM_OPCODE_ELSE, "ELSE");
|
||||
|
||||
// Did increment 0, so conditionally initialize passive data segments
|
||||
for (const OutputSegment *s : segments) {
|
||||
if (s->initFlags & WASM_SEGMENT_IS_PASSIVE && s->name != ".tdata") {
|
||||
// destination address
|
||||
writeI32Const(os, s->startVA, "destination address");
|
||||
// source segment offset
|
||||
writeI32Const(os, 0, "segment offset");
|
||||
// memory region size
|
||||
writeI32Const(os, s->size, "memory region size");
|
||||
// memory.init instruction
|
||||
writeU8(os, WASM_OPCODE_MISC_PREFIX, "bulk-memory prefix");
|
||||
writeUleb128(os, WASM_OPCODE_MEMORY_INIT, "memory.init");
|
||||
writeUleb128(os, s->index, "segment index immediate");
|
||||
writeU8(os, 0, "memory index immediate");
|
||||
}
|
||||
}
|
||||
|
||||
// Set flag to 2 to mark end of initialization
|
||||
writeI32Const(os, flagAddress, "flag address");
|
||||
writeI32Const(os, 2, "flag value");
|
||||
writeU8(os, WASM_OPCODE_ATOMICS_PREFIX, "atomics prefix");
|
||||
writeUleb128(os, WASM_OPCODE_I32_ATOMIC_STORE, "i32.atomic.store");
|
||||
writeMemArg(os, 2, 0);
|
||||
|
||||
// Notify any waiters that memory initialization is complete
|
||||
writeI32Const(os, flagAddress, "flag address");
|
||||
writeI32Const(os, -1, "number of waiters");
|
||||
writeU8(os, WASM_OPCODE_ATOMICS_PREFIX, "atomics prefix");
|
||||
writeUleb128(os, WASM_OPCODE_ATOMIC_NOTIFY, "atomic.notify");
|
||||
writeMemArg(os, 2, 0);
|
||||
writeU8(os, WASM_OPCODE_DROP, "drop");
|
||||
|
||||
writeU8(os, WASM_OPCODE_END, "END");
|
||||
|
||||
// Unconditionally drop passive data segments
|
||||
for (const OutputSegment *s : segments) {
|
||||
if (s->initFlags & WASM_SEGMENT_IS_PASSIVE && s->name != ".tdata") {
|
||||
// data.drop instruction
|
||||
writeU8(os, WASM_OPCODE_MISC_PREFIX, "bulk-memory prefix");
|
||||
writeUleb128(os, WASM_OPCODE_DATA_DROP, "data.drop");
|
||||
writeUleb128(os, s->index, "segment index immediate");
|
||||
}
|
||||
}
|
||||
}
|
||||
writeU8(os, WASM_OPCODE_END, "END");
|
||||
@ -744,12 +844,6 @@ void Writer::createCallCtorsFunction() {
|
||||
raw_string_ostream os(bodyContent);
|
||||
writeUleb128(os, 0, "num locals");
|
||||
|
||||
if (config->passiveSegments) {
|
||||
writeU8(os, WASM_OPCODE_CALL, "CALL");
|
||||
writeUleb128(os, WasmSym::initMemory->getFunctionIndex(),
|
||||
"function index");
|
||||
}
|
||||
|
||||
if (config->isPic) {
|
||||
writeU8(os, WASM_OPCODE_CALL, "CALL");
|
||||
writeUleb128(os, WasmSym::applyRelocs->getFunctionIndex(),
|
||||
@ -794,11 +888,9 @@ void Writer::createInitTLSFunction() {
|
||||
writeU8(os, WASM_OPCODE_LOCAL_GET, "local.get");
|
||||
writeUleb128(os, 0, "local index");
|
||||
|
||||
writeU8(os, WASM_OPCODE_I32_CONST, "i32.const");
|
||||
writeSleb128(os, 0, "segment offset");
|
||||
writeI32Const(os, 0, "segment offset");
|
||||
|
||||
writeU8(os, WASM_OPCODE_I32_CONST, "i32.const");
|
||||
writeSleb128(os, tlsSeg->size, "memory region size");
|
||||
writeI32Const(os, tlsSeg->size, "memory region size");
|
||||
|
||||
writeU8(os, WASM_OPCODE_MISC_PREFIX, "bulk-memory prefix");
|
||||
writeUleb128(os, WASM_OPCODE_MEMORY_INIT, "MEMORY.INIT");
|
||||
@ -851,6 +943,7 @@ void Writer::createSyntheticSections() {
|
||||
out.globalSec = make<GlobalSection>();
|
||||
out.eventSec = make<EventSection>();
|
||||
out.exportSec = make<ExportSection>();
|
||||
out.startSec = make<StartSection>(segments.size());
|
||||
out.elemSec = make<ElemSection>();
|
||||
out.dataCountSec = make<DataCountSection>(segments.size());
|
||||
out.linkingSec = make<LinkingSection>(initFunctions, segments);
|
||||
@ -900,7 +993,7 @@ void Writer::run() {
|
||||
|
||||
if (!config->relocatable) {
|
||||
// Create linker synthesized functions
|
||||
if (config->passiveSegments)
|
||||
if (config->sharedMemory)
|
||||
createInitMemoryFunction();
|
||||
if (config->isPic)
|
||||
createApplyRelocationsFunction();
|
||||
|
@ -73,6 +73,21 @@ void wasm::writeSig(raw_ostream &os, const WasmSignature &sig) {
|
||||
}
|
||||
}
|
||||
|
||||
void wasm::writeI32Const(raw_ostream &os, int32_t number, const Twine &msg) {
|
||||
writeU8(os, WASM_OPCODE_I32_CONST, "i32.const");
|
||||
writeSleb128(os, number, msg);
|
||||
}
|
||||
|
||||
void wasm::writeI64Const(raw_ostream &os, int32_t number, const Twine &msg) {
|
||||
writeU8(os, WASM_OPCODE_I64_CONST, "i64.const");
|
||||
writeSleb128(os, number, msg);
|
||||
}
|
||||
|
||||
void wasm::writeMemArg(raw_ostream &os, uint32_t alignment, uint32_t offset) {
|
||||
writeUleb128(os, alignment, "alignment");
|
||||
writeUleb128(os, offset, "offset");
|
||||
}
|
||||
|
||||
void wasm::writeInitExpr(raw_ostream &os, const WasmInitExpr &initExpr) {
|
||||
writeU8(os, initExpr.Opcode, "opcode");
|
||||
switch (initExpr.Opcode) {
|
||||
|
@ -36,6 +36,12 @@ void writeValueType(raw_ostream &os, llvm::wasm::ValType type,
|
||||
|
||||
void writeSig(raw_ostream &os, const llvm::wasm::WasmSignature &sig);
|
||||
|
||||
void writeI32Const(raw_ostream &os, int32_t number, const Twine &msg);
|
||||
|
||||
void writeI64Const(raw_ostream &os, int32_t number, const Twine &msg);
|
||||
|
||||
void writeMemArg(raw_ostream &os, uint32_t alignment, uint32_t offset);
|
||||
|
||||
void writeInitExpr(raw_ostream &os, const llvm::wasm::WasmInitExpr &initExpr);
|
||||
|
||||
void writeLimits(raw_ostream &os, const llvm::wasm::WasmLimits &limits);
|
||||
|
@ -251,9 +251,21 @@ enum : unsigned {
|
||||
WASM_OPCODE_F32_CONST = 0x43,
|
||||
WASM_OPCODE_F64_CONST = 0x44,
|
||||
WASM_OPCODE_I32_ADD = 0x6a,
|
||||
};
|
||||
|
||||
// Opcodes used in synthetic functions.
|
||||
enum : unsigned {
|
||||
WASM_OPCODE_IF = 0x04,
|
||||
WASM_OPCODE_ELSE = 0x05,
|
||||
WASM_OPCODE_DROP = 0x1a,
|
||||
WASM_OPCODE_MISC_PREFIX = 0xfc,
|
||||
WASM_OPCODE_MEMORY_INIT = 0x08,
|
||||
WASM_OPCODE_DATA_DROP = 0x09,
|
||||
WASM_OPCODE_ATOMICS_PREFIX = 0xfe,
|
||||
WASM_OPCODE_ATOMIC_NOTIFY = 0x00,
|
||||
WASM_OPCODE_I32_ATOMIC_WAIT = 0x01,
|
||||
WASM_OPCODE_I32_ATOMIC_STORE = 0x17,
|
||||
WASM_OPCODE_I32_RMW_CMPXCHG = 0x48,
|
||||
};
|
||||
|
||||
enum : unsigned {
|
||||
|
Loading…
Reference in New Issue
Block a user