Index: lld/trunk/test/wasm/data-layout.ll =================================================================== --- lld/trunk/test/wasm/data-layout.ll +++ lld/trunk/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 Index: lld/trunk/test/wasm/data-segment-merging.ll =================================================================== --- lld/trunk/test/wasm/data-segment-merging.ll +++ lld/trunk/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 Index: lld/trunk/test/wasm/data-segments.ll =================================================================== --- lld/trunk/test/wasm/data-segments.ll +++ lld/trunk/test/wasm/data-segments.ll @@ -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 @@ @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: 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 @@ ; 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 Index: lld/trunk/test/wasm/import-memory.test =================================================================== --- lld/trunk/test/wasm/import-memory.test +++ lld/trunk/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 Index: lld/trunk/test/wasm/no-tls.test =================================================================== --- lld/trunk/test/wasm/no-tls.test +++ lld/trunk/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 Index: lld/trunk/test/wasm/relocatable.ll =================================================================== --- lld/trunk/test/wasm/relocatable.ll +++ lld/trunk/test/wasm/relocatable.ll @@ -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 Index: lld/trunk/test/wasm/shared-memory.yaml =================================================================== --- lld/trunk/test/wasm/shared-memory.yaml +++ lld/trunk/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{{$}} Index: lld/trunk/test/wasm/tls-align.ll =================================================================== --- lld/trunk/test/wasm/tls-align.ll +++ lld/trunk/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" Index: lld/trunk/test/wasm/tls.ll =================================================================== --- lld/trunk/test/wasm/tls.ll +++ lld/trunk/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" Index: lld/trunk/wasm/Config.h =================================================================== --- lld/trunk/wasm/Config.h +++ lld/trunk/wasm/Config.h @@ -35,7 +35,6 @@ bool gcSections; bool importMemory; bool sharedMemory; - bool passiveSegments; bool importTable; bool mergeDataSegments; bool pie; Index: lld/trunk/wasm/Driver.cpp =================================================================== --- lld/trunk/wasm/Driver.cpp +++ lld/trunk/wasm/Driver.cpp @@ -319,8 +319,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); @@ -473,21 +471,10 @@ 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(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. @@ -517,6 +504,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); Index: lld/trunk/wasm/MarkLive.cpp =================================================================== --- lld/trunk/wasm/MarkLive.cpp +++ lld/trunk/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 && !config->shared) + enqueue(WasmSym::initMemory); + // Follow relocations to mark all reachable chunks. while (!q.empty()) { InputChunk *c = q.pop_back_val(); Index: lld/trunk/wasm/Options.td =================================================================== --- lld/trunk/wasm/Options.td +++ lld/trunk/wasm/Options.td @@ -146,12 +146,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">; Index: lld/trunk/wasm/Symbols.h =================================================================== --- lld/trunk/wasm/Symbols.h +++ lld/trunk/wasm/Symbols.h @@ -453,14 +453,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; Index: lld/trunk/wasm/Symbols.cpp =================================================================== --- lld/trunk/wasm/Symbols.cpp +++ lld/trunk/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; Index: lld/trunk/wasm/SyntheticSections.h =================================================================== --- lld/trunk/wasm/SyntheticSections.h +++ lld/trunk/wasm/SyntheticSections.h @@ -217,6 +217,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() @@ -327,6 +339,7 @@ GlobalSection *globalSec; EventSection *eventSec; ExportSection *exportSec; + StartSection *startSec; ElemSection *elemSec; DataCountSection *dataCountSec; LinkingSection *linkingSec; Index: lld/trunk/wasm/SyntheticSections.cpp =================================================================== --- lld/trunk/wasm/SyntheticSections.cpp +++ lld/trunk/wasm/SyntheticSections.cpp @@ -314,6 +314,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; @@ -350,7 +359,7 @@ } bool DataCountSection::isNeeded() const { - return numSegments && config->passiveSegments; + return numSegments && config->sharedMemory; } static uint32_t getWasmFlags(const Symbol *sym) { Index: lld/trunk/wasm/Writer.cpp =================================================================== --- lld/trunk/wasm/Writer.cpp +++ lld/trunk/wasm/Writer.cpp @@ -254,6 +254,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); @@ -333,6 +342,7 @@ addSection(out.globalSec); addSection(out.eventSec); addSection(out.exportSec); + addSection(out.startSec); addSection(out.elemSec); addSection(out.dataCountSec); @@ -363,15 +373,15 @@ StringMap used; StringMap required; StringMap disallowed; + 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; } @@ -397,21 +407,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"); @@ -426,18 +435,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."); } @@ -655,7 +668,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); } @@ -678,32 +691,119 @@ 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 @@ 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 @@ 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 @@ out.globalSec = make(); out.eventSec = make(); out.exportSec = make(); + out.startSec = make(segments.size()); out.elemSec = make(); out.dataCountSec = make(segments.size()); out.linkingSec = make(initFunctions, segments); @@ -900,7 +993,7 @@ if (!config->relocatable) { // Create linker synthesized functions - if (config->passiveSegments) + if (config->sharedMemory) createInitMemoryFunction(); if (config->isPic) createApplyRelocationsFunction(); Index: lld/trunk/wasm/WriterUtils.h =================================================================== --- lld/trunk/wasm/WriterUtils.h +++ lld/trunk/wasm/WriterUtils.h @@ -36,6 +36,12 @@ 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); Index: lld/trunk/wasm/WriterUtils.cpp =================================================================== --- lld/trunk/wasm/WriterUtils.cpp +++ lld/trunk/wasm/WriterUtils.cpp @@ -73,6 +73,21 @@ } } +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) { Index: llvm/trunk/include/llvm/BinaryFormat/Wasm.h =================================================================== --- llvm/trunk/include/llvm/BinaryFormat/Wasm.h +++ llvm/trunk/include/llvm/BinaryFormat/Wasm.h @@ -251,9 +251,21 @@ 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 {