Skip to content

Use wasm-gc based call adaptor if available#5310

Merged
hoodmane merged 11 commits intopyodide:mainfrom
hoodmane:wasm-gc-trampoline
Jan 13, 2025
Merged

Use wasm-gc based call adaptor if available#5310
hoodmane merged 11 commits intopyodide:mainfrom
hoodmane:wasm-gc-trampoline

Conversation

@hoodmane
Copy link
Member

@hoodmane hoodmane commented Jan 8, 2025

I figured out a way to remove the super inefficient calculate_wasm_func_nargs_fallback and replace it with wasm-gc code. This should perform much better. The cpython patch message says:

Part of the ongoing quest to support JSPI. The JSPI spec removed its dependence on JS type reflection, and now the plan is for runtimes to ship JSPI while keeping type reflection in stage 3. So we need an alternative way to count the number of parameters of a function. It is possible to count them by repeatedly trying to instantiate a webassembly module with the function as an import of a different type signature. But this is pretty inefficient.

Since WebAssembly gc is now stage 4, there is a new option. WebAssembly gc added the ref.test instruction which can ask if a funcref has a given type. It's a bit difficult to apply because even our usual assembler the wasm binary toolkit doesn't support this instruction yet. But all JS engines that support JSPI support it. We just have to do some manual work to produce the binary.

This code also has to be written carefully to interact properly with memory snapshots. Importantly, no JS initialization code can be called from the C initialization code. For this reason, we make a C function pointer to fill from JS and fill it in a preRun function.

Upstream PR:
python/cpython#128628

I figured out a way to remove the super inefficient `calculate_wasm_func_nargs_fallback` and
replace it with wasm-gc code. This should perform much better. The cpython patch message says:

Part of the ongoing quest to support JSPI. The JSPI spec removed its dependence on
JS type reflection, and now the plan is for runtimes to ship JSPI while keeping
type reflection in stage 3. So we need an alternative way to count the number of
parameters of a function. It is possible to count them by repeatedly trying to
instantiate a webassembly module with the function as an import of a different type
signature. But this is pretty inefficient.

Since WebAssembly gc is now stage 4, there is a new option. WebAssembly gc added the
`ref.test` instruction which can ask if a funcref has a given type. It's a bit difficult
to apply because even our usual assembler the wasm binary toolkit doesn't support this
instruction yet. But all JS engines that support JSPI support it. We just have to do some
manual work to produce the binary.

This code also has to be written carefully to interact properly with memory snapshots.
Importantly, no JS initialization code can be called from the C initialization code.
For this reason, we make a C function pointer to fill from JS and fill it in a preRun
function.
Copy link
Member

@ryanking13 ryanking13 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on this. I don't fully understand the patch but it generally looks okay to me. Let's see what CPython devs say about the patch.

Comment on lines 185 to 190
+ const code = new Uint8Array([
+ 0x00, 0x61, 0x73, 0x6d, // \0asm magic number
+ 0x01, 0x00, 0x00, 0x00, // version 1
+ 0x01, 0x23, // Type section, body is 0x23 bytes
+ 0x06, // 6 entries
+ 0x60, 0x00, 0x01, 0x7f, // (type $type0 (func (param) (result i32)))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This part is quite scary 😅

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well you can see the wat above. The problem is that the wasm binary toolkit doesn't know how to handle ref.test $type4. Probably I should make a wasm binary toolkit PR to add support for this, but we'll see if I get around to it.

@hoodmane hoodmane added this to the 0.27.1 milestone Jan 13, 2025
@hoodmane
Copy link
Member Author

Upstream PR was merged so I'll go ahead and merge this too.

@hoodmane hoodmane merged commit f51f4b1 into pyodide:main Jan 13, 2025
23 of 26 checks passed
@hoodmane hoodmane deleted the wasm-gc-trampoline branch January 13, 2025 13:14
hoodmane added a commit to hoodmane/pyodide that referenced this pull request Jan 13, 2025
I figured out a way to remove the super inefficient `calculate_wasm_func_nargs_fallback` and
replace it with wasm-gc code. This should perform much better. The cpython patch message says:

Part of the ongoing quest to support JSPI. The JSPI spec removed its dependence on
JS type reflection, and now the plan is for runtimes to ship JSPI while keeping
type reflection in stage 3. So we need an alternative way to count the number of
parameters of a function. It is possible to count them by repeatedly trying to
instantiate a webassembly module with the function as an import of a different type
signature. But this is pretty inefficient.

Since WebAssembly gc is now stage 4, there is a new option. WebAssembly gc added the
`ref.test` instruction which can ask if a funcref has a given type. It's a bit difficult
to apply because even our usual assembler the wasm binary toolkit doesn't support this
instruction yet. But all JS engines that support JSPI support it. We just have to do some
manual work to produce the binary.

This code also has to be written carefully to interact properly with memory snapshots.
Importantly, no JS initialization code can be called from the C initialization code.
For this reason, we make a C function pointer to fill from JS and fill it in a preRun
function.

Upstream PR:
python/cpython#128628
@ryanking13
Copy link
Member

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants