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 @@ -128,7 +128,7 @@ ; PASSIVE-PIC-NEXT: Locals: ; PASSIVE32-PIC-NEXT: - Type: I32 ; PASSIVE64-PIC-NEXT: - Type: I64 -; PASSIVE-PIC-NEXT: Count: 1 +; PASSIVE-PIC-NEXT: Count: 2 ; PASSIVE-PIC-NEXT: Body: {{.*}} ; PASSIVE-PIC-NEXT: - Index: 3 ; PASSIVE-PIC-NEXT: Locals: [] @@ -178,6 +178,19 @@ ; DIS-NEXT: # 2: down to label0 ; DIS-NEXT: end +; NOPIC-DIS-NEXT: [[PTR]].const 1024 +; NOPIC-DIS-NEXT: [[PTR]].const 1024 +; NOPIC-DIS-NEXT: global.set 1 +; PIC-DIS-NEXT: [[PTR]].const 0 +; PIC-DIS-NEXT: global.get 1 +; PIC-DIS-NEXT: [[PTR]].add +; PIC-DIS-NEXT: local.tee 1 +; PIC-DIS-NEXT: global.set {{\d*}} +; PIC-DIS-NEXT: local.get 1 +; DIS-NEXT: i32.const 0 +; DIS-NEXT: i32.const 4 +; DIS-NEXT: memory.init 0, 0 + ; NOPIC-DIS-NEXT: [[PTR]].const 1028 ; PIC-DIS-NEXT: [[PTR]].const 4 ; PIC-DIS-NEXT: global.get 1 diff --git a/lld/test/wasm/tls_init_symbols.s b/lld/test/wasm/tls_init_symbols.s --- a/lld/test/wasm/tls_init_symbols.s +++ b/lld/test/wasm/tls_init_symbols.s @@ -42,8 +42,10 @@ # CHECK-NEXT: - Index: 1 # CHECK-NEXT: Name: __wasm_init_tls # CHECK-NEXT: - Index: 2 -# CHECK-NEXT: Name: __wasm_apply_global_tls_relocs +# CHECK-NEXT: Name: __wasm_init_memory # CHECK-NEXT: - Index: 3 +# CHECK-NEXT: Name: __wasm_apply_global_tls_relocs +# CHECK-NEXT: - Index: 4 # CHECK-NEXT: Name: _start # DIS: <__wasm_init_tls>: @@ -53,7 +55,7 @@ # DIS-NEXT: i32.const 0 # DIS-NEXT: i32.const 4 # DIS-NEXT: memory.init 0, 0 -# DIS-NEXT: call 2 +# DIS-NEXT: call 3 # DIS-NEXT: end # DIS: <__wasm_apply_global_tls_relocs>: diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp --- a/lld/wasm/Writer.cpp +++ b/lld/wasm/Writer.cpp @@ -218,6 +218,7 @@ } static void setGlobalPtr(DefinedGlobal *g, uint64_t memoryPtr) { + LLVM_DEBUG(dbgs() << "setGlobalPtr " << g->getName() << " -> " << memoryPtr << "\n"); g->global->setPointerValue(memoryPtr); } @@ -276,13 +277,15 @@ memoryPtr, seg->size, seg->alignment)); if (!config->relocatable && seg->isTLS()) { - if (config->sharedMemory) { + if (WasmSym::tlsSize) { auto *tlsSize = cast(WasmSym::tlsSize); setGlobalPtr(tlsSize, seg->size); - + } + if (WasmSym::tlsAlign) { auto *tlsAlign = cast(WasmSym::tlsAlign); setGlobalPtr(tlsAlign, int64_t{1} << seg->alignment); - } else if (WasmSym::tlsBase) { + } + if (!config->sharedMemory && WasmSym::tlsBase) { auto *tlsBase = cast(WasmSym::tlsBase); setGlobalPtr(tlsBase, memoryPtr); } @@ -970,9 +973,6 @@ } bool Writer::needsPassiveInitialization(const OutputSegment *segment) { - // TLS segments are initialized separately - if (segment->isTLS()) - return false; // If bulk memory features is supported then we can perform bss initialization // (via memory.fill) during `__wasm_init_memory`. if (config->importMemory && !segment->requiredInBinary()) @@ -1002,6 +1002,11 @@ "__wasm_init_memory", WASM_SYMBOL_VISIBILITY_HIDDEN, make(nullSignature, "__wasm_init_memory")); WasmSym::initMemory->markLive(); + if (config->sharedMemory) { + // This global is assigned during __wasm_init_memory in the shared memory + // case. + WasmSym::tlsBase->markLive(); + } } if (config->sharedMemory && out.globalSec->needsTLSRelocations()) { @@ -1126,7 +1131,7 @@ // With PIC code we cache the flag address in local 0 if (config->isPic) { writeUleb128(os, 1, "num local decls"); - writeUleb128(os, 1, "local count"); + writeUleb128(os, 2, "local count"); writeU8(os, is64 ? WASM_TYPE_I64 : WASM_TYPE_I32, "address type"); writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET"); writeUleb128(os, WasmSym::memoryBase->getGlobalIndex(), "memory_base"); @@ -1177,10 +1182,31 @@ if (config->isPic) { writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET"); writeUleb128(os, WasmSym::memoryBase->getGlobalIndex(), - "memory_base"); + "__memory_base"); writeU8(os, is64 ? WASM_OPCODE_I64_ADD : WASM_OPCODE_I32_ADD, "i32.add"); } + + // When we initialize the TLS segment we also set the `__tls_base` + // global. This allows the runtime to use this static copy of the + // TLS data for the first/main thread. + if (config->sharedMemory && s->isTLS()) { + if (config->isPic) { + // Cache the result of the addionion in local 0 + writeU8(os, WASM_OPCODE_LOCAL_TEE, "local.tee"); + writeUleb128(os, 1, "local 1"); + } else { + writePtrConst(os, s->startVA, is64, "destination address"); + } + writeU8(os, WASM_OPCODE_GLOBAL_SET, "GLOBAL_SET"); + writeUleb128(os, WasmSym::tlsBase->getGlobalIndex(), + "__tls_base"); + if (config->isPic) { + writeU8(os, WASM_OPCODE_LOCAL_GET, "local.tee"); + writeUleb128(os, 1, "local 1"); + } + } + if (s->isBss) { writeI32Const(os, 0, "fill value"); writeI32Const(os, s->size, "memory region size"); @@ -1243,6 +1269,10 @@ for (const OutputSegment *s : segments) { if (needsPassiveInitialization(s) && !s->isBss) { + // The TLS region should not be dropped since its is needed + // during the intiailizing of each thread (__wasm_init_tls). + if (config->sharedMemory && s->isTLS()) + continue; // data.drop instruction writeU8(os, WASM_OPCODE_MISC_PREFIX, "bulk-memory prefix"); writeUleb128(os, WASM_OPCODE_DATA_DROP, "data.drop"); @@ -1440,7 +1470,6 @@ writeU8(os, WASM_OPCODE_GLOBAL_SET, "global.set"); writeUleb128(os, WasmSym::tlsBase->getGlobalIndex(), "global index"); - WasmSym::tlsBase->markLive(); // FIXME(wvo): this local needs to be I64 in wasm64, or we need an extend op. writeU8(os, WASM_OPCODE_LOCAL_GET, "local.get"); @@ -1623,8 +1652,10 @@ } } - if (WasmSym::initTLS && WasmSym::initTLS->isLive()) + if (WasmSym::initTLS && WasmSym::initTLS->isLive()) { + log("-- createInitTLSFunction"); createInitTLSFunction(); + } if (errorCount()) return; 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 @@ -283,6 +283,7 @@ WASM_OPCODE_CALL = 0x10, WASM_OPCODE_LOCAL_GET = 0x20, WASM_OPCODE_LOCAL_SET = 0x21, + WASM_OPCODE_LOCAL_TEE = 0x22, WASM_OPCODE_GLOBAL_GET = 0x23, WASM_OPCODE_GLOBAL_SET = 0x24, WASM_OPCODE_I32_STORE = 0x36,