[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:
Thomas Lively 2019-09-04 19:50:39 +00:00
parent 85d6edb265
commit 09768c5d7a
21 changed files with 264 additions and 147 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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{{$}}

View File

@ -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"

View File

@ -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"

View File

@ -35,7 +35,6 @@ struct Configuration {
bool gcSections;
bool importMemory;
bool sharedMemory;
bool passiveSegments;
bool importTable;
bool mergeDataSegments;
bool pie;

View File

@ -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);

View File

@ -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();

View File

@ -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">;

View File

@ -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;

View File

@ -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;

View File

@ -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) {

View File

@ -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;

View File

@ -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();

View File

@ -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) {

View File

@ -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);

View File

@ -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 {