|
| 1 | +diff --git a/lld/test/wasm/cooperative-threading-gc.s b/lld/test/wasm/cooperative-threading-gc.s |
| 2 | +new file mode 100644 |
| 3 | +index 0000000000000..2af2460939aad |
| 4 | +--- /dev/null |
| 5 | ++++ b/lld/test/wasm/cooperative-threading-gc.s |
| 6 | +@@ -0,0 +1,57 @@ |
| 7 | ++# Verify that --gc-sections preserves functions reachable only through the |
| 8 | ++# thread-context accessors __wasm_{get,set}_tls_base. These accessors are |
| 9 | ++# invoked by synthetic init functions (e.g. __wasm_init_tls) via `call` |
| 10 | ++# instructions that carry no relocations, so the linker marks the accessors |
| 11 | ++# live explicitly. Their own relocations must still be followed during GC, |
| 12 | ++# otherwise functions they call (here modeling the cooperative-threading |
| 13 | ++# context.set builtin) are incorrectly collected and the call is mis-resolved. |
| 14 | ++ |
| 15 | ++# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s |
| 16 | ++# RUN: wasm-ld --cooperative-threading --gc-sections -o %t.wasm %t.o |
| 17 | ++# RUN: obj2yaml %t.wasm | FileCheck %s |
| 18 | ++ |
| 19 | ++ .functype set_helper (i32) -> () |
| 20 | ++ .import_module set_helper, "env" |
| 21 | ++ .import_name set_helper, "set_helper" |
| 22 | ++ |
| 23 | ++.globl __wasm_get_tls_base |
| 24 | ++__wasm_get_tls_base: |
| 25 | ++ .functype __wasm_get_tls_base () -> (i32) |
| 26 | ++ i32.const 0 |
| 27 | ++ end_function |
| 28 | ++ |
| 29 | ++# Reachable only via the synthetic __wasm_init_tls. Its call to `set_helper` |
| 30 | ++# must keep `set_helper` live. |
| 31 | ++.globl __wasm_set_tls_base |
| 32 | ++__wasm_set_tls_base: |
| 33 | ++ .functype __wasm_set_tls_base (i32) -> () |
| 34 | ++ local.get 0 |
| 35 | ++ call set_helper |
| 36 | ++ end_function |
| 37 | ++ |
| 38 | ++.globl _start |
| 39 | ++_start: |
| 40 | ++ .functype _start () -> (i32) |
| 41 | ++ call __wasm_get_tls_base |
| 42 | ++ i32.const tls1@TLSREL |
| 43 | ++ i32.add |
| 44 | ++ i32.load 0 |
| 45 | ++ end_function |
| 46 | ++ |
| 47 | ++.section .tdata.tls1,"",@ |
| 48 | ++.globl tls1 |
| 49 | ++tls1: |
| 50 | ++ .int32 1 |
| 51 | ++ .size tls1, 4 |
| 52 | ++ |
| 53 | ++.section .custom_section.target_features,"",@ |
| 54 | ++ .int8 2 |
| 55 | ++ .int8 43 |
| 56 | ++ .int8 11 |
| 57 | ++ .ascii "bulk-memory" |
| 58 | ++ .int8 43 |
| 59 | ++ .int8 7 |
| 60 | ++ .ascii "atomics" |
| 61 | ++ |
| 62 | ++# The imported helper called by __wasm_set_tls_base must survive GC. |
| 63 | ++# CHECK: Field: set_helper |
| 64 | +diff --git a/lld/wasm/MarkLive.cpp b/lld/wasm/MarkLive.cpp |
| 65 | +index 2b2cf19f14b30..3364be006ca24 100644 |
| 66 | +--- a/lld/wasm/MarkLive.cpp |
| 67 | ++++ b/lld/wasm/MarkLive.cpp |
| 68 | +@@ -126,6 +126,20 @@ void MarkLive::run() { |
| 69 | + enqueueRetainedSegments(obj); |
| 70 | + } |
| 71 | + |
| 72 | ++ // `__wasm_{get,set}_tls_base` are called from synthetic init functions (e.g. |
| 73 | ++ // `__wasm_init_tls`, `__wasm_init_memory`) via raw `call` instructions that |
| 74 | ++ // carry no relocations, so the mark phase below cannot discover the functions |
| 75 | ++ // they in turn call (e.g. the cooperative-threading |
| 76 | ++ // `context.get`/`context.set` builtins). They are already marked live, but |
| 77 | ++ // their defining chunks were never enqueued; enqueue them here so their |
| 78 | ++ // relocations are followed. This mirrors the handling of ctor functions |
| 79 | ++ // reached via `__wasm_call_ctors`. |
| 80 | ++ for (Symbol *sym : {static_cast<Symbol *>(ctx.sym.getTLSBase), |
| 81 | ++ static_cast<Symbol *>(ctx.sym.setTLSBase)}) |
| 82 | ++ if (sym) |
| 83 | ++ if (InputChunk *c = sym->getChunk()) |
| 84 | ++ enqueue(c); |
| 85 | ++ |
| 86 | + mark(); |
| 87 | + |
| 88 | + // If we have any non-discarded init functions, mark `__wasm_call_ctors` as |
0 commit comments