Experimental shared Wasm GC support - #27427
Conversation
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.
| send_items_map['memory'] = 'wasmMemory' | ||
|
|
||
| if settings.SHARED_WASMGC: | ||
| send_items_map['shared_root'] = '_shared_root' |
There was a problem hiding this comment.
Lets add a comment here. Something like: "This import gets injected by xxx, post-link"
| ] | ||
|
|
||
| if settings.SHARED_WASMGC: | ||
| settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += ['shared_root'] |
There was a problem hiding this comment.
Lets give this a more usnique name, how about _wasmgc_root_node, or _wasmgc_shared_root?
Maybe good to also add a leading underscore (which then means 2 underscores in the JS code :) Sorry.
| // [compile+link] | ||
| var SHARED_MEMORY = false; | ||
|
|
||
| // If true, enables support for experimental shared Wasm GC. |
There was a problem hiding this comment.
Maybe elaborate a little here.. mention the that it enabled a shared too object?
| #endif | ||
|
|
||
| #if SHARED_WASMGC | ||
| _shared_root = getSharedRootGlobal(msgData.sharedRootVal); |
There was a problem hiding this comment.
Maybe this should be called makeSharedRootGlobal.
Could we just do shared_root: 'makeSharedRootGlobal()' (i.e. remove the postset and remove the assertion in makeSharedRootGlobal).
Then here in runtime_pthread.js we could just do:
assert(msgData.sharedRootVal);
_shared_root.value = msgData.sharedRootVal;
That way all thread including the main thread create the global on startup and its just the value of the global that gets updated when the postMessage arrives?
There was a problem hiding this comment.
Good idea, will investigate.
|
|
||
| @requires_pthreads | ||
| def test_shared_wasmgc(self): | ||
| self.require_node_25() |
There was a problem hiding this comment.
You can use the requires_node_25 decorator instead of this (unless it somehow conflicts with requires_pthreads)?
| out_js, | ||
| ]) | ||
|
|
||
| building.run_binaryen_command( |
There was a problem hiding this comment.
Can you just do self.run_process([WASM_MERGE (see elsewhere where we run WASM_DIS list this).
The building.run_binaryen_command is more geared to running commands inside the compiler itself.
|
|
||
| # 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']) |
There was a problem hiding this comment.
Would it make sense to log some non-zero dummy value?
There was a problem hiding this comment.
Yes, but doing anything more interesting than just sticking an i32.eqz in there is blocked on V8 fixing casts across threads.
sbc100
left a comment
There was a problem hiding this comment.
Weren't you going to bring back node canary testing for this?
Canary isn't necessary to run the current test case. Once V8 fixes the casting bug, I will make the test case more interesting and bring back node canary to test it. |
|
@sbc100, this should be ready for another look. |
|
Can you update the PR description? |
| 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; |
There was a problem hiding this comment.
Maybe just return instance.exports.g?
| ]) | ||
|
|
||
| self.run_process([ | ||
| common.WASM_MERGE, '--enable-threads', '--enable-reference-types', |
There was a problem hiding this comment.
We tend to drop the common. prefix for command names and add them to import xxx from common instead
Add -sSHARED_WASMGC. This setting assumes the module will import a mutable shared anyref global from "env" "_shared_heap_root" and re-export it as "_shared_heap_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 Workers, it will send along the value of the "_shared_heap_root" export from the main thread, then wrap this value in a WebAssembly.Global and import it into the instance on the Worker. In this way, all Workers will end up with the same shared object in their "_shared_heap_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.