From 44f9f495c5d645628e5c2f2c780ebf5265d7ed26 Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Mon, 27 Jul 2026 21:58:32 -0700 Subject: [PATCH 1/9] Experimental shared Wasm GC support Add -sSHARED_WASMGC. This setting assumes the module will import a mutable shared anyref global from "env" "shared_root" and re-export it as "shared_root". On the main thread, a WebAssembly.Global containing a null value will be provided as the import. It is expected that the module's start function will allocate a shared object and assign it to the imported global when (and only when) the imported value is null. When the Emscripten pthread runtime postMessages the module and other data to new WebWorkers, it will send the along the value of the "shared_root" export from the main thread, then wrap this value in a WebAssembly.Global and import it into the instance on the WebWorker. In this way, all Workers will end up with the same shared object in their "shared_root" globals. This is enough to bootstrap arbitrary additional shared state between the Workers. Since LLVM does not know about most Wasm GC instructions or types, the best way to create a multithreaded Wasm GC program right now is to use wasm-merge to combine a runtime Wasm module written in C with a hand-written .wat file that uses shared Wasm GC. Add a test demonstrating this workflow. --- src/lib/libpthread.js | 28 ++++++++++++++- src/runtime_pthread.js | 4 +++ src/settings.js | 4 +++ test/test_other.py | 82 ++++++++++++++++++++++++++++++++++++++++++ tools/emscripten.py | 3 ++ tools/link.py | 3 ++ tools/settings.py | 2 ++ 7 files changed, 125 insertions(+), 1 deletion(-) diff --git a/src/lib/libpthread.js b/src/lib/libpthread.js index 020df4b83b1c5..cd3e77a7d7861 100644 --- a/src/lib/libpthread.js +++ b/src/lib/libpthread.js @@ -435,6 +435,9 @@ var LibraryPThread = { #if LOAD_SOURCE_MAP wasmSourceMap, #endif +#if SHARED_WASMGC + sharedRootVal: wasmExports['shared_root'].value, +#endif #if MAIN_MODULE dynamicLibraries, // Share all modules that have been loaded so far. New workers @@ -1354,7 +1357,30 @@ var LibraryPThread = { } worker.postMessage({cmd: {{{ CMD_CHECK_MAILBOX }}}}); } - } + }, + +#if SHARED_WASMGC + shared_root__deps: ['$getSharedRootGlobal'], + shared_root: null, + shared_root__postset: "if (!ENVIRONMENT_IS_PTHREAD) { _shared_root = getSharedRootGlobal(null); }", + $getSharedRootGlobal: (val) => { +#if ASSERTIONS + if (ENVIRONMENT_IS_PTHREAD) { + assert(val, "expected shared_root to be assigned on pthread"); + } else { + assert(!val, "expected shared_root to be null on main thread"); + } +#endif // ASSERTIONS + // Wasm module for acquiring a shared anyref WebAssembly.Global: + // (module (global (export "g") (mut (ref null (shared any))) (ref.null (shared any)))) + var bytes = new Uint8Array([0, 97, 115, 109, 1, 0, 0, 0, 6, 9, 1, 99, 101, 110, 1, 208, 101, 113, 11, 7, 5, 1, 1, 103, 3, 0]); + var module = new WebAssembly.Module(bytes); + var instance = new WebAssembly.Instance(module, {}); + var global = instance.exports.g; + global.value = val; + return global; + }, +#endif // SHARED_WASMGC }; autoAddDeps(LibraryPThread, '$PThread'); diff --git a/src/runtime_pthread.js b/src/runtime_pthread.js index 27996fc16499e..59b0ebaa4e069 100644 --- a/src/runtime_pthread.js +++ b/src/runtime_pthread.js @@ -112,6 +112,10 @@ if (ENVIRONMENT_IS_PTHREAD) { wasmSourceMap = resetPrototype(WasmSourceMap, msgData.wasmSourceMap); #endif +#if SHARED_WASMGC + _shared_root = getSharedRootGlobal(msgData.sharedRootVal); +#endif + #if !WASM_ESM_INTEGRATION #if MINIMAL_RUNTIME // Pass the shared Wasm module in the Module object for MINIMAL_RUNTIME. diff --git a/src/settings.js b/src/settings.js index 35c77f89e0f25..f5fb894fad677 100644 --- a/src/settings.js +++ b/src/settings.js @@ -1661,6 +1661,10 @@ var USE_SQLITE3 = false; // [compile+link] var SHARED_MEMORY = false; +// If true, enables support for experimental shared Wasm GC. +// [link] +var SHARED_WASMGC = false; + // Enables support for Wasm Workers. Wasm Workers enable applications // to create threads using a lightweight web-specific API that builds on top // of Wasm SharedArrayBuffer + Atomics API. diff --git a/test/test_other.py b/test/test_other.py index 0b20cadc8d589..bd5c1b996411b 100644 --- a/test/test_other.py +++ b/test/test_other.py @@ -13275,6 +13275,88 @@ def test_pthread_js_exception(self): self.set_setting('EXIT_RUNTIME') self.do_runf('other/test_pthread_js_exception.c', 'missing is not defined', assert_returncode=NON_ZERO, cflags=['-pthread']) + @requires_pthreads + def test_shared_wasmgc(self): + self.require_node_25() + + create_file('test_shared_wasmgc.c', r''' + #include + #include + #include + + __attribute__((import_module("wat"))) void shared_gc_main(void); + + void print_int(int val) { + emscripten_console_logf("%d", val); + } + + void* thread_main(void* arg) { + shared_gc_main(); + return NULL; + } + + int main() { + shared_gc_main(); + + pthread_t t1, t2, t3; + pthread_create(&t1, NULL, thread_main, NULL); + pthread_create(&t2, NULL, thread_main, NULL); + pthread_create(&t3, NULL, thread_main, NULL); + + pthread_join(t1, NULL); + pthread_join(t2, NULL); + pthread_join(t3, NULL); + + return 0; + } + ''') + + create_file('shared_gc.wat', r''' + (module + (type $counter (shared (struct (field (mut i32))))) + (import "env" "shared_root" (global $shared_root (mut (ref null (shared any))))) + (export "shared_root" (global $shared_root)) + (import "app" "print_int" (func $print_int (param i32))) + + (func $init + (if (ref.is_null (global.get $shared_root)) + (then + (global.set $shared_root (struct.new $counter (i32.const 0))) + ) + ) + ) + (start $init) + + (func (export "shared_gc_main") + (call $print_int (ref.is_null (global.get $shared_root))) + ) + ) + ''') + + out_js = self.in_dir('test_shared_wasmgc.js') + out_wasm = self.in_dir('test_shared_wasmgc.wasm') + + self.run_process([ + EMCC, '-pthread', '-sSHARED_WASMGC', '-sERROR_ON_UNDEFINED_SYMBOLS=0', + '-sEXIT_RUNTIME', '-sPROXY_TO_PTHREAD', + '-sEXPORTED_FUNCTIONS=_main,_print_int', 'test_shared_wasmgc.c', '-o', + out_js, + ]) + + building.run_binaryen_command( + 'wasm-merge', + None, + out_wasm, + args=['--enable-threads', '--enable-reference-types', '--enable-gc', '--enable-shared-everything', + out_wasm, 'app', 'shared_gc.wat', 'wat'], + ) + + self.node_args.append('--experimental-wasm-shared') + + # TODO: Once multithreaded casting is fixed, increment and print the counter value. + output = self.run_js(out_js) + self.assertEqual(output.splitlines(), ['0', '0', '0', '0']) + @crossplatform def test_config_closure_compiler(self): self.run_process([EMCC, test_file('hello_world.c'), '--closure=1']) diff --git a/tools/emscripten.py b/tools/emscripten.py index 82d8563214dbe..778da555362f4 100644 --- a/tools/emscripten.py +++ b/tools/emscripten.py @@ -821,6 +821,9 @@ def add_standard_wasm_imports(send_items_map): if settings.IMPORTED_MEMORY: send_items_map['memory'] = 'wasmMemory' + if settings.SHARED_WASMGC: + send_items_map['shared_root'] = '_shared_root' + if settings.AUTODEBUG: extra_sent_items += [ 'log_execution', diff --git a/tools/link.py b/tools/link.py index c287e1311c2e6..5a8c3efba032d 100644 --- a/tools/link.py +++ b/tools/link.py @@ -517,6 +517,9 @@ def setup_pthreads(): '$invokeEntryPoint', ] + if settings.SHARED_WASMGC: + settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += ['shared_root'] + if settings.MINIMAL_RUNTIME: building.user_requested_exports.add('exit') diff --git a/tools/settings.py b/tools/settings.py index 06fb37d704df7..9832dd69f0a2f 100644 --- a/tools/settings.py +++ b/tools/settings.py @@ -157,6 +157,7 @@ ('NODERAWSOCKETS', 'WASMFS', 'the node:net backend is not wired into WASMFS sockets'), ('NODERAWSOCKETS', 'PROXY_POSIX_SOCKETS', 'they are alternative socket backends'), ('NODERAWSOCKETS', 'SOCKET_WEBRTC', 'they are alternative socket backends'), + ('SHARED_WASMGC', 'NO_PTHREADS', 'SHARED_WASMGC requires threads to be enabled'), ] EXPERIMENTAL_SETTINGS = { @@ -166,6 +167,7 @@ 'CROSS_ORIGIN_STORAGE': '-sCROSS_ORIGIN_STORAGE is experimental; the underlying browser API is not yet shipped in any browser', 'SUPPORT_BIG_ENDIAN': '-sSUPPORT_BIG_ENDIAN is experimental, not all features are fully supported.', 'WASM_ESM_INTEGRATION': '-sWASM_ESM_INTEGRATION is still experimental and not yet supported in browsers', + 'SHARED_WASMGC': '-sSHARED_WASMGC is experimental and subject to change', } # For renamed settings the format is: From f9a74f66bd650dcdbadc9b969c6e180a520f4980 Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Tue, 28 Jul 2026 09:41:47 -0700 Subject: [PATCH 2/9] add experimental tag to setting --- src/settings.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/settings.js b/src/settings.js index f5fb894fad677..edabc34b4f622 100644 --- a/src/settings.js +++ b/src/settings.js @@ -1663,6 +1663,7 @@ var SHARED_MEMORY = false; // If true, enables support for experimental shared Wasm GC. // [link] +// [experimental] var SHARED_WASMGC = false; // Enables support for Wasm Workers. Wasm Workers enable applications From 6464401952b2fb5e5aa8a9f0c96309b7ff5366f0 Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Tue, 28 Jul 2026 13:20:48 -0700 Subject: [PATCH 3/9] docs --- src/settings.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/settings.js b/src/settings.js index edabc34b4f622..04de8a4b01184 100644 --- a/src/settings.js +++ b/src/settings.js @@ -1661,7 +1661,16 @@ var USE_SQLITE3 = false; // [compile+link] var SHARED_MEMORY = false; -// If true, enables support for experimental shared Wasm GC. +// If true, enables support for experimental shared Wasm GC. Expects the +// module to contain a mutable shared anyref global to be imported as "env" +// "shared_root" and exported as "shared_root". The import will be provided a +// null value on the main thread, where the user code is expected to +// initialize it with some shared object during the start function. This shared +// object will then be provided as the import when instantiating the module on +// additional Workers. This shared anyref global can be used to bootstrap +// arbitrary shared Wasm GC state. Since LLVM cannot emit Wasm GC instructions +// or shared anyref globals, users are expected to use wasm-merge to add the +// shared_root global and additional Wasm GC code post-link. // [link] // [experimental] var SHARED_WASMGC = false; From 1d08625a769ea6cc3be386ab9b98120f11ebee4f Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Tue, 28 Jul 2026 13:27:52 -0700 Subject: [PATCH 4/9] comment and shared_root => _shared_heap_root --- src/lib/libpthread.js | 14 +++++++------- src/runtime_pthread.js | 2 +- src/settings.js | 6 +++--- test/test_other.py | 10 +++++----- tools/emscripten.py | 4 +++- tools/link.py | 2 +- 6 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/lib/libpthread.js b/src/lib/libpthread.js index cd3e77a7d7861..6067d0a704067 100644 --- a/src/lib/libpthread.js +++ b/src/lib/libpthread.js @@ -436,7 +436,7 @@ var LibraryPThread = { wasmSourceMap, #endif #if SHARED_WASMGC - sharedRootVal: wasmExports['shared_root'].value, + sharedHeapRootVal: wasmExports['_shared_heap_root'].value, #endif #if MAIN_MODULE dynamicLibraries, @@ -1360,15 +1360,15 @@ var LibraryPThread = { }, #if SHARED_WASMGC - shared_root__deps: ['$getSharedRootGlobal'], - shared_root: null, - shared_root__postset: "if (!ENVIRONMENT_IS_PTHREAD) { _shared_root = getSharedRootGlobal(null); }", - $getSharedRootGlobal: (val) => { + _shared_heap_root__deps: ['$getSharedHeapRootGlobal'], + _shared_heap_root: null, + _shared_heap_root__postset: "if (!ENVIRONMENT_IS_PTHREAD) { __shared_heap_root = getSharedHeapRootGlobal(null); }", + $getSharedHeapRootGlobal: (val) => { #if ASSERTIONS if (ENVIRONMENT_IS_PTHREAD) { - assert(val, "expected shared_root to be assigned on pthread"); + assert(val, "expected _shared_heap_root to be assigned on pthread"); } else { - assert(!val, "expected shared_root to be null on main thread"); + assert(!val, "expected _shared_heap_root to be null on main thread"); } #endif // ASSERTIONS // Wasm module for acquiring a shared anyref WebAssembly.Global: diff --git a/src/runtime_pthread.js b/src/runtime_pthread.js index 59b0ebaa4e069..2b53480d4a8ca 100644 --- a/src/runtime_pthread.js +++ b/src/runtime_pthread.js @@ -113,7 +113,7 @@ if (ENVIRONMENT_IS_PTHREAD) { #endif #if SHARED_WASMGC - _shared_root = getSharedRootGlobal(msgData.sharedRootVal); + __shared_heap_root = getSharedHeapRootGlobal(msgData.sharedHeapRootVal); #endif #if !WASM_ESM_INTEGRATION diff --git a/src/settings.js b/src/settings.js index 04de8a4b01184..af2fecf89efed 100644 --- a/src/settings.js +++ b/src/settings.js @@ -1663,14 +1663,14 @@ var SHARED_MEMORY = false; // If true, enables support for experimental shared Wasm GC. Expects the // module to contain a mutable shared anyref global to be imported as "env" -// "shared_root" and exported as "shared_root". The import will be provided a -// null value on the main thread, where the user code is expected to +// "_shared_heap_root" and exported as "_shared_heap_root". The import will be +// provided a null value on the main thread, where the user code is expected to // initialize it with some shared object during the start function. This shared // object will then be provided as the import when instantiating the module on // additional Workers. This shared anyref global can be used to bootstrap // arbitrary shared Wasm GC state. Since LLVM cannot emit Wasm GC instructions // or shared anyref globals, users are expected to use wasm-merge to add the -// shared_root global and additional Wasm GC code post-link. +// _shared_heap_root global and additional Wasm GC code post-link. // [link] // [experimental] var SHARED_WASMGC = false; diff --git a/test/test_other.py b/test/test_other.py index bd5c1b996411b..26bd7c4a3ae67 100644 --- a/test/test_other.py +++ b/test/test_other.py @@ -13314,21 +13314,21 @@ def test_shared_wasmgc(self): create_file('shared_gc.wat', r''' (module (type $counter (shared (struct (field (mut i32))))) - (import "env" "shared_root" (global $shared_root (mut (ref null (shared any))))) - (export "shared_root" (global $shared_root)) + (import "env" "_shared_heap_root" (global $_shared_heap_root (mut (ref null (shared any))))) + (export "_shared_heap_root" (global $_shared_heap_root)) (import "app" "print_int" (func $print_int (param i32))) (func $init - (if (ref.is_null (global.get $shared_root)) + (if (ref.is_null (global.get $_shared_heap_root)) (then - (global.set $shared_root (struct.new $counter (i32.const 0))) + (global.set $_shared_heap_root (struct.new $counter (i32.const 0))) ) ) ) (start $init) (func (export "shared_gc_main") - (call $print_int (ref.is_null (global.get $shared_root))) + (call $print_int (ref.is_null (global.get $_shared_heap_root))) ) ) ''') diff --git a/tools/emscripten.py b/tools/emscripten.py index 778da555362f4..38dfd6cb5c43d 100644 --- a/tools/emscripten.py +++ b/tools/emscripten.py @@ -821,8 +821,10 @@ def add_standard_wasm_imports(send_items_map): if settings.IMPORTED_MEMORY: send_items_map['memory'] = 'wasmMemory' + # This import should come from user code merged into the module with + # wasm-merge post-link. if settings.SHARED_WASMGC: - send_items_map['shared_root'] = '_shared_root' + send_items_map['_shared_heap_root'] = '__shared_heap_root' if settings.AUTODEBUG: extra_sent_items += [ diff --git a/tools/link.py b/tools/link.py index 5a8c3efba032d..4ccc4939622b0 100644 --- a/tools/link.py +++ b/tools/link.py @@ -518,7 +518,7 @@ def setup_pthreads(): ] if settings.SHARED_WASMGC: - settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += ['shared_root'] + settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += ['_shared_heap_root'] if settings.MINIMAL_RUNTIME: building.user_requested_exports.add('exit') From 315221b405c723941cb2f09f9ec558c79a1abb05 Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Tue, 28 Jul 2026 13:31:08 -0700 Subject: [PATCH 5/9] refactor to avoid postset --- src/lib/libpthread.js | 15 +++------------ src/runtime_pthread.js | 2 +- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/src/lib/libpthread.js b/src/lib/libpthread.js index 6067d0a704067..03c1823098b10 100644 --- a/src/lib/libpthread.js +++ b/src/lib/libpthread.js @@ -1360,24 +1360,15 @@ var LibraryPThread = { }, #if SHARED_WASMGC - _shared_heap_root__deps: ['$getSharedHeapRootGlobal'], - _shared_heap_root: null, - _shared_heap_root__postset: "if (!ENVIRONMENT_IS_PTHREAD) { __shared_heap_root = getSharedHeapRootGlobal(null); }", - $getSharedHeapRootGlobal: (val) => { -#if ASSERTIONS - if (ENVIRONMENT_IS_PTHREAD) { - assert(val, "expected _shared_heap_root to be assigned on pthread"); - } else { - assert(!val, "expected _shared_heap_root to be null on main thread"); - } -#endif // ASSERTIONS + _shared_heap_root__deps: ['$makeSharedHeapRootGlobal'], + _shared_heap_root: "makeSharedHeapRootGlobal()", + $makeSharedHeapRootGlobal: () => { // Wasm module for acquiring a shared anyref WebAssembly.Global: // (module (global (export "g") (mut (ref null (shared any))) (ref.null (shared any)))) var bytes = new Uint8Array([0, 97, 115, 109, 1, 0, 0, 0, 6, 9, 1, 99, 101, 110, 1, 208, 101, 113, 11, 7, 5, 1, 1, 103, 3, 0]); var module = new WebAssembly.Module(bytes); var instance = new WebAssembly.Instance(module, {}); var global = instance.exports.g; - global.value = val; return global; }, #endif // SHARED_WASMGC diff --git a/src/runtime_pthread.js b/src/runtime_pthread.js index 2b53480d4a8ca..26d48b6b86103 100644 --- a/src/runtime_pthread.js +++ b/src/runtime_pthread.js @@ -113,7 +113,7 @@ if (ENVIRONMENT_IS_PTHREAD) { #endif #if SHARED_WASMGC - __shared_heap_root = getSharedHeapRootGlobal(msgData.sharedHeapRootVal); + __shared_heap_root.value = msgData.sharedHeapRootVal; #endif #if !WASM_ESM_INTEGRATION From 4039c964b7477452d8c12278d4d3217c04ae7b67 Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Tue, 28 Jul 2026 13:44:17 -0700 Subject: [PATCH 6/9] simplify test --- test/common.py | 1 + test/test_other.py | 30 ++++++++++++++++-------------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/test/common.py b/test/common.py index c608df49f0f61..fab5617495ef5 100644 --- a/test/common.py +++ b/test/common.py @@ -77,6 +77,7 @@ EMCONFIG = exe_path_from_root('em-config') EMRUN = exe_path_from_root('emrun') WASM_DIS = os.path.join(building.get_binaryen_bin(), 'wasm-dis') +WASM_MERGE = os.path.join(building.get_binaryen_bin(), 'wasm-merge') LLVM_OBJDUMP = shared.llvm_tool_path('llvm-objdump') PYTHON = sys.executable diff --git a/test/test_other.py b/test/test_other.py index 26bd7c4a3ae67..22ad2438d4b9b 100644 --- a/test/test_other.py +++ b/test/test_other.py @@ -13276,9 +13276,8 @@ def test_pthread_js_exception(self): self.do_runf('other/test_pthread_js_exception.c', 'missing is not defined', assert_returncode=NON_ZERO, cflags=['-pthread']) @requires_pthreads + @requires_node_25 def test_shared_wasmgc(self): - self.require_node_25() - create_file('test_shared_wasmgc.c', r''' #include #include @@ -13314,21 +13313,26 @@ def test_shared_wasmgc(self): create_file('shared_gc.wat', r''' (module (type $counter (shared (struct (field (mut i32))))) - (import "env" "_shared_heap_root" (global $_shared_heap_root (mut (ref null (shared any))))) - (export "_shared_heap_root" (global $_shared_heap_root)) + (import "app" "print_int" (func $print_int (param i32))) + (global $root + (export "_shared_heap_root") + (import "env" "_shared_heap_root") + (mut (ref null (shared any))) + ) + (func $init - (if (ref.is_null (global.get $_shared_heap_root)) + (if (ref.is_null (global.get $root)) (then - (global.set $_shared_heap_root (struct.new $counter (i32.const 0))) + (global.set $root (struct.new $counter (i32.const 0))) ) ) ) (start $init) (func (export "shared_gc_main") - (call $print_int (ref.is_null (global.get $_shared_heap_root))) + (call $print_int (ref.is_null (global.get $root))) ) ) ''') @@ -13343,13 +13347,11 @@ def test_shared_wasmgc(self): out_js, ]) - building.run_binaryen_command( - 'wasm-merge', - None, - out_wasm, - args=['--enable-threads', '--enable-reference-types', '--enable-gc', '--enable-shared-everything', - out_wasm, 'app', 'shared_gc.wat', 'wat'], - ) + self.run_process([ + common.WASM_MERGE, '--enable-threads', '--enable-reference-types', + '--enable-gc', '--enable-shared-everything', out_wasm, 'app', + 'shared_gc.wat', 'wat', '-o', out_wasm, + ]) self.node_args.append('--experimental-wasm-shared') From 67191ad71b94f85df9b29c4762ff3e15862a3636 Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Tue, 28 Jul 2026 13:45:10 -0700 Subject: [PATCH 7/9] regenerate docs --- .../tools_reference/settings_reference.rst | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/site/source/docs/tools_reference/settings_reference.rst b/site/source/docs/tools_reference/settings_reference.rst index de87804636bcf..f8567c530658a 100644 --- a/site/source/docs/tools_reference/settings_reference.rst +++ b/site/source/docs/tools_reference/settings_reference.rst @@ -2509,6 +2509,26 @@ If 1, target compiling a shared Wasm Memory. Default value: false +.. _shared_wasmgc: + +SHARED_WASMGC +============= + +If true, enables support for experimental shared Wasm GC. Expects the +module to contain a mutable shared anyref global to be imported as "env" +"_shared_heap_root" and exported as "_shared_heap_root". The import will be +provided a null value on the main thread, where the user code is expected to +initialize it with some shared object during the start function. This shared +object will then be provided as the import when instantiating the module on +additional Workers. This shared anyref global can be used to bootstrap +arbitrary shared Wasm GC state. Since LLVM cannot emit Wasm GC instructions +or shared anyref globals, users are expected to use wasm-merge to add the +_shared_heap_root global and additional Wasm GC code post-link. + +.. note:: This is an experimental setting + +Default value: false + .. _wasm_workers: WASM_WORKERS From a3755c9648bdfc5e3668bc2d9a7b993ae4eb4919 Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Tue, 28 Jul 2026 13:46:28 -0700 Subject: [PATCH 8/9] one more comment --- tools/link.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/link.py b/tools/link.py index 4ccc4939622b0..b009a20724ad0 100644 --- a/tools/link.py +++ b/tools/link.py @@ -517,6 +517,8 @@ def setup_pthreads(): '$invokeEntryPoint', ] + # This import should come from user code merged into the module with + # wasm-merge post-link. if settings.SHARED_WASMGC: settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += ['_shared_heap_root'] From 8a990e850353a102272c1a58d280dee181ac727e Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Tue, 28 Jul 2026 14:36:48 -0700 Subject: [PATCH 9/9] address final comments --- src/lib/libpthread.js | 3 +-- test/test_other.py | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/libpthread.js b/src/lib/libpthread.js index 03c1823098b10..e092fe4f20f39 100644 --- a/src/lib/libpthread.js +++ b/src/lib/libpthread.js @@ -1368,8 +1368,7 @@ var LibraryPThread = { var bytes = new Uint8Array([0, 97, 115, 109, 1, 0, 0, 0, 6, 9, 1, 99, 101, 110, 1, 208, 101, 113, 11, 7, 5, 1, 1, 103, 3, 0]); var module = new WebAssembly.Module(bytes); var instance = new WebAssembly.Instance(module, {}); - var global = instance.exports.g; - return global; + return instance.exports.g; }, #endif // SHARED_WASMGC }; diff --git a/test/test_other.py b/test/test_other.py index 9705254d4eff1..42f4ab821a0a0 100644 --- a/test/test_other.py +++ b/test/test_other.py @@ -43,6 +43,7 @@ NON_ZERO, PYTHON, TEST_ROOT, + WASM_MERGE, WEBIDL_BINDER, RunnerCore, check_node_version, @@ -13348,7 +13349,7 @@ def test_shared_wasmgc(self): ]) self.run_process([ - common.WASM_MERGE, '--enable-threads', '--enable-reference-types', + WASM_MERGE, '--enable-threads', '--enable-reference-types', '--enable-gc', '--enable-shared-everything', out_wasm, 'app', 'shared_gc.wat', 'wat', '-o', out_wasm, ])