Consider the following snippet:
constmyfunc=@extern(*constfn(...)callconv(.{.wasm_mvp=.{}})void,.{.name="myfunc",.library_name="js"});exportfnmain()void{myfunc(@as(i32,1));myfunc(@as(i32,2),@as(i32,3));}The compiled code of this (with ReleaseSmall) converts the values a stack value and passes the pointer to javascript.
This works but requires glue code on the javascript side to decode the passed values from the pointer.
(module
(func $js.myfunc (;0;) (import "js" "myfunc") (param i32))
(table $table0 1 1 funcref)
(memory $memory (;0;) (export "memory") 16)
(global $global0 (mut i32) (i32.const 1048576))
(func $main (;1;) (export "main")
(local $var0 i32)
global.get $global0
i32.const 32
i32.sub
local.tee $var0
global.set $global0
local.get $var0
i32.const 1
i32.store offset=16
local.get $var0
i32.const 16
i32.add
call $js.myfunc
local.get $var0
i64.const 12884901890
i64.store
local.get $var0
call $js.myfunc
local.get $var0
i32.const 32
i32.add
global.set $global0
)
)
What can be done instead is to import the function twice with different names (wasm does allow you to do that) and call the function normally instead, this results in much simpler and faster codegen while at the same time requiring no extra js glue code introspecting wasm memory to function.
In this version I can map myfunc directly to console.log for example and it works as expected.
(module
(func $js.myfunc (;0;) (import "js" "myfunc") (param i32))
(func $js.myfunc2 (;1;) (import "js" "myfunc") (param i32 i32))
(table $table0 1 1 funcref)
(memory $memory (;0;) (export "memory") 16)
(global $__stack_pointer (;0;) (mut i32) (i32.const 1048576))
(func $main.main (;2;) (export "main")
i32.const 1
call $js.myfunc
i32.const 2
i32.const 6
call $js.myfunc2
)
)