diff --git a/lld/test/wasm/data-layout.ll b/lld/test/wasm/data-layout.ll --- a/lld/test/wasm/data-layout.ll +++ b/lld/test/wasm/data-layout.ll @@ -75,8 +75,8 @@ ; 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 diff --git a/lld/test/wasm/data-segment-merging.ll b/lld/test/wasm/data-segment-merging.ll --- a/lld/test/wasm/data-segment-merging.ll +++ b/lld/test/wasm/data-segment-merging.ll @@ -8,7 +8,8 @@ @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 @@ ; 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 @@ ; 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 @@ ; 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 diff --git a/lld/test/wasm/data-segments.ll b/lld/test/wasm/data-segments.ll --- a/lld/test/wasm/data-segments.ll +++ b/lld/test/wasm/data-segments.ll @@ -5,36 +5,13 @@ ; atomics => 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 ; 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 ; 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 @@ @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 @@ ; 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: 41B4D6004101FE41020045044041800841004114FC08000041940841004190CE00FC08010041A4D6004100410DFC0802000BFC0900FC0901FC09020B +; 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 @@ ; 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 diff --git a/lld/test/wasm/import-memory.test b/lld/test/wasm/import-memory.test --- a/lld/test/wasm/import-memory.test +++ b/lld/test/wasm/import-memory.test @@ -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 diff --git a/lld/test/wasm/no-tls.test b/lld/test/wasm/no-tls.test --- a/lld/test/wasm/no-tls.test +++ b/lld/test/wasm/no-tls.test @@ -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: Mutable: true CHECK-NEXT: InitExpr: CHECK-NEXT: Opcode: I32_CONST -CHECK-NEXT: Value: 66560 +CHECK-NEXT: Value: 66576 ; __tls_base CHECK-NEXT: - Index: 1 diff --git a/lld/test/wasm/shared-memory.yaml b/lld/test/wasm/shared-memory.yaml --- a/lld/test/wasm/shared-memory.yaml +++ b/lld/test/wasm/shared-memory.yaml @@ -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 --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: 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: wasm-ld --no-entry --features=atomics --shared-memory --active-segments --max-memory=131072 %t1.o -o - | obj2yaml | FileCheck %s --check-prefix SHARED +# RUN: not wasm-ld --no-entry --features=atomics %t1.o -o - 2>&1 | FileCheck %s --check-prefix ATOMICS-NO-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 @@ 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{{$}} diff --git a/lld/test/wasm/tls-align.ll b/lld/test/wasm/tls-align.ll --- a/lld/test/wasm/tls-align.ll +++ b/lld/test/wasm/tls-align.ll @@ -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" diff --git a/lld/test/wasm/tls.ll b/lld/test/wasm/tls.ll --- a/lld/test/wasm/tls.ll +++ b/lld/test/wasm/tls.ll @@ -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" diff --git a/lld/wasm/Config.h b/lld/wasm/Config.h --- a/lld/wasm/Config.h +++ b/lld/wasm/Config.h @@ -34,7 +34,6 @@ bool gcSections; bool importMemory; bool sharedMemory; - bool passiveSegments; bool importTable; bool mergeDataSegments; bool pie; diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp --- a/lld/wasm/Driver.cpp +++ b/lld/wasm/Driver.cpp @@ -320,8 +320,6 @@ 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); @@ -468,29 +466,17 @@ static llvm::wasm::WasmGlobalType globalTypeI32 = {WASM_TYPE_I32, false}; static llvm::wasm::WasmGlobalType mutableGlobalTypeI32 = {WASM_TYPE_I32, true}; + assert(!config->relocatable); + WasmSym::callCtors = symtab->addSyntheticFunction( + "__wasm_call_ctors", WASM_SYMBOL_VISIBILITY_HIDDEN, + make(nullSignature, "__wasm_call_ctors")); - if (!config->relocatable) { - WasmSym::callCtors = symtab->addSyntheticFunction( - "__wasm_call_ctors", WASM_SYMBOL_VISIBILITY_HIDDEN, - make(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(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. - WasmSym::applyRelocs = symtab->addSyntheticFunction( - "__wasm_apply_relocs", WASM_SYMBOL_VISIBILITY_HIDDEN, - make(nullSignature, "__wasm_apply_relocs")); - } + 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. + WasmSym::applyRelocs = symtab->addSyntheticFunction( + "__wasm_apply_relocs", WASM_SYMBOL_VISIBILITY_HIDDEN, + make(nullSignature, "__wasm_apply_relocs")); } if (!config->shared) @@ -528,6 +514,15 @@ } 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(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); diff --git a/lld/wasm/MarkLive.cpp b/lld/wasm/MarkLive.cpp --- a/lld/wasm/MarkLive.cpp +++ b/lld/wasm/MarkLive.cpp @@ -50,8 +50,6 @@ // 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 @@ if (config->isPic) enqueue(WasmSym::callCtors); + if (config->sharedMemory) + enqueue(WasmSym::initMemory); + // Follow relocations to mark all reachable chunks. while (!q.empty()) { InputChunk *c = q.pop_back_val(); diff --git a/lld/wasm/Options.td b/lld/wasm/Options.td --- a/lld/wasm/Options.td +++ b/lld/wasm/Options.td @@ -143,12 +143,6 @@ 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">; diff --git a/lld/wasm/Symbols.h b/lld/wasm/Symbols.h --- a/lld/wasm/Symbols.h +++ b/lld/wasm/Symbols.h @@ -449,14 +449,18 @@ // therefore be used as a backing store for brk()/malloc() implementations. static DefinedData *heapBase; - // __wasm_call_ctors - // Function that directly calls all ctors in priority order. - static DefinedFunction *callCtors; + // __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 post-instantiation. + // 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_apply_relocs // Function that applies relocations to data segment post-instantiation. static DefinedFunction *applyRelocs; diff --git a/lld/wasm/Symbols.cpp b/lld/wasm/Symbols.cpp --- a/lld/wasm/Symbols.cpp +++ b/lld/wasm/Symbols.cpp @@ -32,6 +32,7 @@ DefinedData *WasmSym::dataEnd; DefinedData *WasmSym::globalBase; DefinedData *WasmSym::heapBase; +DefinedData *WasmSym::initMemoryFlag; GlobalSymbol *WasmSym::stackPointer; GlobalSymbol *WasmSym::tlsBase; GlobalSymbol *WasmSym::tlsSize; diff --git a/lld/wasm/SyntheticSections.h b/lld/wasm/SyntheticSections.h --- a/lld/wasm/SyntheticSections.h +++ b/lld/wasm/SyntheticSections.h @@ -212,6 +212,18 @@ std::vector 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(uint32_t offset) @@ -323,6 +335,7 @@ GlobalSection *globalSec; EventSection *eventSec; ExportSection *exportSec; + StartSection *startSec; ElemSection *elemSec; DataCountSection *dataCountSec; LinkingSection *linkingSec; diff --git a/lld/wasm/SyntheticSections.cpp b/lld/wasm/SyntheticSections.cpp --- a/lld/wasm/SyntheticSections.cpp +++ b/lld/wasm/SyntheticSections.cpp @@ -288,6 +288,15 @@ 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; @@ -324,7 +333,7 @@ } bool DataCountSection::isNeeded() const { - return numSegments && config->passiveSegments; + return numSegments && config->sharedMemory; } static uint32_t getWasmFlags(const Symbol *sym) { diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp --- a/lld/wasm/Writer.cpp +++ b/lld/wasm/Writer.cpp @@ -253,6 +253,15 @@ } } + // 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); @@ -332,6 +341,7 @@ addSection(out.globalSec); addSection(out.eventSec); addSection(out.exportSec); + addSection(out.startSec); addSection(out.elemSec); addSection(out.dataCountSec); @@ -362,15 +372,15 @@ StringMap used; StringMap required; StringMap disallowed; + llvm::SmallSet &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; } @@ -396,21 +406,20 @@ } } - 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"); @@ -425,18 +434,22 @@ 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 (!allowed.count("bulk-memory") && config->sharedMemory) + error("'bulk-memory' feature must be used in order to use shared " + "memory"); - if (!used.count("bulk-memory") && tlsUsed) + 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."); } @@ -652,7 +665,7 @@ if (s == nullptr) { LLVM_DEBUG(dbgs() << "new segment: " << name << "\n"); s = make(name, segments.size()); - if (config->passiveSegments || name == ".tdata") + if (config->sharedMemory || name == ".tdata") s->initFlags = WASM_SEGMENT_IS_PASSIVE; segments.push_back(s); } @@ -675,12 +688,27 @@ void Writer::createInitMemoryFunction() { LLVM_DEBUG(dbgs() << "createInitMemoryFunction\n"); + assert(WasmSym::initMemoryFlag); std::string bodyContent; { raw_string_ostream os(bodyContent); writeUleb128(os, 0, "num locals"); - // initialize passive data segments + // Atomically check whether this is the main thread + uint32_t flagAddress = WasmSym::initMemoryFlag->getVirtualAddress(); + writeU8(os, WASM_OPCODE_I32_CONST, "i32.const"); + writeSleb128(os, flagAddress, "flag address"); + writeU8(os, WASM_OPCODE_I32_CONST, "i32.const"); + writeSleb128(os, 1, "flag value"); + writeU8(os, WASM_OPCODE_ATOMICS_PREFIX, "atomics prefix"); + writeUleb128(os, WASM_OPCODE_I32_RMW_XCHG, "i32.atomic.rmw.xchg"); + writeUleb128(os, 2, "alignment"); + writeUleb128(os, 0, "offset"); + writeU8(os, WASM_OPCODE_I32_EQZ, "i32.eqz"); + writeU8(os, WASM_OPCODE_IF, "IF"); + writeU8(os, WASM_TYPE_NORESULT, "blocktype"); + + // Conditionally initialize passive data segments for (const OutputSegment *s : segments) { if (s->initFlags & WASM_SEGMENT_IS_PASSIVE && s->name != ".tdata") { // destination address @@ -697,6 +725,13 @@ writeUleb128(os, WASM_OPCODE_MEMORY_INIT, "MEMORY.INIT"); writeUleb128(os, s->index, "segment index immediate"); writeU8(os, 0, "memory index immediate"); + } + } + 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"); @@ -741,12 +776,6 @@ 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(), @@ -848,6 +877,7 @@ out.globalSec = make(); out.eventSec = make(); out.exportSec = make(); + out.startSec = make(segments.size()); out.elemSec = make(tableBase); out.dataCountSec = make(segments.size()); out.linkingSec = make(initFunctions, segments); @@ -894,7 +924,7 @@ if (!config->relocatable) { // Create linker synthesized functions - if (config->passiveSegments) + if (config->sharedMemory) createInitMemoryFunction(); if (config->isPic) createApplyRelocationsFunction(); diff --git a/llvm/include/llvm/BinaryFormat/Wasm.h b/llvm/include/llvm/BinaryFormat/Wasm.h --- a/llvm/include/llvm/BinaryFormat/Wasm.h +++ b/llvm/include/llvm/BinaryFormat/Wasm.h @@ -251,9 +251,17 @@ WASM_OPCODE_F32_CONST = 0x43, WASM_OPCODE_F64_CONST = 0x44, WASM_OPCODE_I32_ADD = 0x6a, +}; + +// Opcodes used in synthetic functions. +enum : unsigned { WASM_OPCODE_MISC_PREFIX = 0xfc, WASM_OPCODE_MEMORY_INIT = 0x08, WASM_OPCODE_DATA_DROP = 0x09, + WASM_OPCODE_ATOMICS_PREFIX = 0xfe, + WASM_OPCODE_I32_RMW_XCHG = 0x41, + WASM_OPCODE_I32_EQZ = 0x45, + WASM_OPCODE_IF = 0x04, }; enum : unsigned {