Skip to content

Commit 74f9841

Browse files
committed
[lld][WebAssembly] Allow use of statically allocated TLS region.
It turns out we were already allocating static address space for TLS data along with the non-TLS static data, but this space was going unused/ignored. With this change, we include the TLS segment in `__wasm_init_memory` (which does the work of loading the passive segments into memory when a module is first loaded). We also set the `__tls_base` global to point to the start of this segment. This means that the runtime can use this static copy of the TLS data for the first/primary thread if it chooses, rather than doing a runtime allocation prior to calling `__wasm_init_tls`. Practically speaking, this will allow emscripten to avoid dynamic allocation of TLS region on the main thread. Differential Revision: https://reviews.llvm.org/D126107
1 parent 4f93d5c commit 74f9841

File tree

4 files changed

+60
-13
lines changed

4 files changed

+60
-13
lines changed

lld/test/wasm/data-segments.ll

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@
128128
; PASSIVE-PIC-NEXT: Locals:
129129
; PASSIVE32-PIC-NEXT: - Type: I32
130130
; PASSIVE64-PIC-NEXT: - Type: I64
131-
; PASSIVE-PIC-NEXT: Count: 1
131+
; PASSIVE-PIC-NEXT: Count: 2
132132
; PASSIVE-PIC-NEXT: Body: {{.*}}
133133
; PASSIVE-PIC-NEXT: - Index: 3
134134
; PASSIVE-PIC-NEXT: Locals: []
@@ -178,6 +178,19 @@
178178
; DIS-NEXT: # 2: down to label0
179179
; DIS-NEXT: end
180180

181+
; NOPIC-DIS-NEXT: [[PTR]].const 1024
182+
; NOPIC-DIS-NEXT: [[PTR]].const 1024
183+
; NOPIC-DIS-NEXT: global.set 1
184+
; PIC-DIS-NEXT: [[PTR]].const 0
185+
; PIC-DIS-NEXT: global.get 1
186+
; PIC-DIS-NEXT: [[PTR]].add
187+
; PIC-DIS-NEXT: local.tee 1
188+
; PIC-DIS-NEXT: global.set {{\d*}}
189+
; PIC-DIS-NEXT: local.get 1
190+
; DIS-NEXT: i32.const 0
191+
; DIS-NEXT: i32.const 4
192+
; DIS-NEXT: memory.init 0, 0
193+
181194
; NOPIC-DIS-NEXT: [[PTR]].const 1028
182195
; PIC-DIS-NEXT: [[PTR]].const 4
183196
; PIC-DIS-NEXT: global.get 1

lld/test/wasm/tls_init_symbols.s

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,10 @@ tls_sym:
4242
# CHECK-NEXT: - Index: 1
4343
# CHECK-NEXT: Name: __wasm_init_tls
4444
# CHECK-NEXT: - Index: 2
45-
# CHECK-NEXT: Name: __wasm_apply_global_tls_relocs
45+
# CHECK-NEXT: Name: __wasm_init_memory
4646
# CHECK-NEXT: - Index: 3
47+
# CHECK-NEXT: Name: __wasm_apply_global_tls_relocs
48+
# CHECK-NEXT: - Index: 4
4749
# CHECK-NEXT: Name: _start
4850

4951
# DIS: <__wasm_init_tls>:
@@ -53,7 +55,7 @@ tls_sym:
5355
# DIS-NEXT: i32.const 0
5456
# DIS-NEXT: i32.const 4
5557
# DIS-NEXT: memory.init 0, 0
56-
# DIS-NEXT: call 2
58+
# DIS-NEXT: call 3
5759
# DIS-NEXT: end
5860

5961
# DIS: <__wasm_apply_global_tls_relocs>:

lld/wasm/Writer.cpp

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ void Writer::writeSections() {
218218
}
219219

220220
static void setGlobalPtr(DefinedGlobal *g, uint64_t memoryPtr) {
221+
LLVM_DEBUG(dbgs() << "setGlobalPtr " << g->getName() << " -> " << memoryPtr << "\n");
221222
g->global->setPointerValue(memoryPtr);
222223
}
223224

@@ -276,13 +277,15 @@ void Writer::layoutMemory() {
276277
memoryPtr, seg->size, seg->alignment));
277278

278279
if (!config->relocatable && seg->isTLS()) {
279-
if (config->sharedMemory) {
280+
if (WasmSym::tlsSize) {
280281
auto *tlsSize = cast<DefinedGlobal>(WasmSym::tlsSize);
281282
setGlobalPtr(tlsSize, seg->size);
282-
283+
}
284+
if (WasmSym::tlsAlign) {
283285
auto *tlsAlign = cast<DefinedGlobal>(WasmSym::tlsAlign);
284286
setGlobalPtr(tlsAlign, int64_t{1} << seg->alignment);
285-
} else if (WasmSym::tlsBase) {
287+
}
288+
if (!config->sharedMemory && WasmSym::tlsBase) {
286289
auto *tlsBase = cast<DefinedGlobal>(WasmSym::tlsBase);
287290
setGlobalPtr(tlsBase, memoryPtr);
288291
}
@@ -970,9 +973,6 @@ static void createFunction(DefinedFunction *func, StringRef bodyContent) {
970973
}
971974

972975
bool Writer::needsPassiveInitialization(const OutputSegment *segment) {
973-
// TLS segments are initialized separately
974-
if (segment->isTLS())
975-
return false;
976976
// If bulk memory features is supported then we can perform bss initialization
977977
// (via memory.fill) during `__wasm_init_memory`.
978978
if (config->importMemory && !segment->requiredInBinary())
@@ -1002,6 +1002,11 @@ void Writer::createSyntheticInitFunctions() {
10021002
"__wasm_init_memory", WASM_SYMBOL_VISIBILITY_HIDDEN,
10031003
make<SyntheticFunction>(nullSignature, "__wasm_init_memory"));
10041004
WasmSym::initMemory->markLive();
1005+
if (config->sharedMemory) {
1006+
// This global is assigned during __wasm_init_memory in the shared memory
1007+
// case.
1008+
WasmSym::tlsBase->markLive();
1009+
}
10051010
}
10061011

10071012
if (config->sharedMemory && out.globalSec->needsTLSRelocations()) {
@@ -1126,7 +1131,7 @@ void Writer::createInitMemoryFunction() {
11261131
// With PIC code we cache the flag address in local 0
11271132
if (config->isPic) {
11281133
writeUleb128(os, 1, "num local decls");
1129-
writeUleb128(os, 1, "local count");
1134+
writeUleb128(os, 2, "local count");
11301135
writeU8(os, is64 ? WASM_TYPE_I64 : WASM_TYPE_I32, "address type");
11311136
writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
11321137
writeUleb128(os, WasmSym::memoryBase->getGlobalIndex(), "memory_base");
@@ -1177,10 +1182,31 @@ void Writer::createInitMemoryFunction() {
11771182
if (config->isPic) {
11781183
writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
11791184
writeUleb128(os, WasmSym::memoryBase->getGlobalIndex(),
1180-
"memory_base");
1185+
"__memory_base");
11811186
writeU8(os, is64 ? WASM_OPCODE_I64_ADD : WASM_OPCODE_I32_ADD,
11821187
"i32.add");
11831188
}
1189+
1190+
// When we initialize the TLS segment we also set the `__tls_base`
1191+
// global. This allows the runtime to use this static copy of the
1192+
// TLS data for the first/main thread.
1193+
if (config->sharedMemory && s->isTLS()) {
1194+
if (config->isPic) {
1195+
// Cache the result of the addionion in local 0
1196+
writeU8(os, WASM_OPCODE_LOCAL_TEE, "local.tee");
1197+
writeUleb128(os, 1, "local 1");
1198+
} else {
1199+
writePtrConst(os, s->startVA, is64, "destination address");
1200+
}
1201+
writeU8(os, WASM_OPCODE_GLOBAL_SET, "GLOBAL_SET");
1202+
writeUleb128(os, WasmSym::tlsBase->getGlobalIndex(),
1203+
"__tls_base");
1204+
if (config->isPic) {
1205+
writeU8(os, WASM_OPCODE_LOCAL_GET, "local.tee");
1206+
writeUleb128(os, 1, "local 1");
1207+
}
1208+
}
1209+
11841210
if (s->isBss) {
11851211
writeI32Const(os, 0, "fill value");
11861212
writeI32Const(os, s->size, "memory region size");
@@ -1243,6 +1269,10 @@ void Writer::createInitMemoryFunction() {
12431269

12441270
for (const OutputSegment *s : segments) {
12451271
if (needsPassiveInitialization(s) && !s->isBss) {
1272+
// The TLS region should not be dropped since its is needed
1273+
// during the intiailizing of each thread (__wasm_init_tls).
1274+
if (config->sharedMemory && s->isTLS())
1275+
continue;
12461276
// data.drop instruction
12471277
writeU8(os, WASM_OPCODE_MISC_PREFIX, "bulk-memory prefix");
12481278
writeUleb128(os, WASM_OPCODE_DATA_DROP, "data.drop");
@@ -1440,7 +1470,6 @@ void Writer::createInitTLSFunction() {
14401470

14411471
writeU8(os, WASM_OPCODE_GLOBAL_SET, "global.set");
14421472
writeUleb128(os, WasmSym::tlsBase->getGlobalIndex(), "global index");
1443-
WasmSym::tlsBase->markLive();
14441473

14451474
// FIXME(wvo): this local needs to be I64 in wasm64, or we need an extend op.
14461475
writeU8(os, WASM_OPCODE_LOCAL_GET, "local.get");
@@ -1623,8 +1652,10 @@ void Writer::run() {
16231652
}
16241653
}
16251654

1626-
if (WasmSym::initTLS && WasmSym::initTLS->isLive())
1655+
if (WasmSym::initTLS && WasmSym::initTLS->isLive()) {
1656+
log("-- createInitTLSFunction");
16271657
createInitTLSFunction();
1658+
}
16281659

16291660
if (errorCount())
16301661
return;

llvm/include/llvm/BinaryFormat/Wasm.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,7 @@ enum : unsigned {
283283
WASM_OPCODE_CALL = 0x10,
284284
WASM_OPCODE_LOCAL_GET = 0x20,
285285
WASM_OPCODE_LOCAL_SET = 0x21,
286+
WASM_OPCODE_LOCAL_TEE = 0x22,
286287
WASM_OPCODE_GLOBAL_GET = 0x23,
287288
WASM_OPCODE_GLOBAL_SET = 0x24,
288289
WASM_OPCODE_I32_STORE = 0x36,

0 commit comments

Comments
 (0)