4

嘿@all 我在玩WebAssembly Studio并创建了一个空的 Rust 项目。

在我的 Rust 代码中,我返回了“Hello World”字符串的指针和长度。编译工作正常,但我期待生成的 WASM 有一个返回两个 i32 的函数。但我发现一个函数接受一个 i32 并且什么都不返回。

  1. 为什么函数没有签名 fn () -> (i32,i32) ?

  2. 我应该如何从 WASM 模块中提取这两个值?(使用Rust-wasmtime

您可以在下面找到我正在谈论的代码片段。提前致谢!

#[no_mangle]
pub extern "C" fn say() -> (*const u8,i32)  {
    let pointcut = "Hello World";

    (pointcut.as_ptr(),11)
}
(module
  (type $t0 (func (param i32)))
  (func $say (export "say") (type $t0) (param $p0 i32)
    get_local $p0
    i32.const 11
    i32.store offset=4
    get_local $p0
    i32.const 1024
    i32.store)
  (table $T0 1 1 anyfunc)
  (memory $memory (export "memory") 17)
  (data (i32.const 1024) "Hello World"))
4

1 回答 1

5

WebAssembly最近才能够返回多个值。所使用的编译器似乎还不支持此功能,或者出于兼容性原因不使用它,即用于尚不知道此功能的运行时。

因此,编译器将代码改写如下:

#[no_mangle]
pub extern "C" fn say(output: &mut (*const u8, i32)) {
    let pointcut = "Hello World";

    output.1 = 11;
    output.0 = pointcut.as_ptr();
}

至少这会导致与您的代码相同的 WebAssembly 代码。该参数output对应$p0于 WebAssembly 代码中的参数。

WebAssembly 代码现在执行以下操作:

首先,数字 11 被写入元组中的第二项,因此在内存地址output + 4. 偏移量是 4 个字节,因为i32元组的第一个有 4 个字节。

其次,将字符串的内存地址pointcut写入元组的第一个值。正如您在生成的 WebAssembly 代码的数据部分中看到的那样,字符串被放入内存地址 1024 的线性内存中。注意:pointcut是字符串文字,因此是静态的,因此它不存在于堆栈中,这将是非法的 (正如Coder-256已经评论的那样)并且会导致编译器错误。编辑:由于原始指针,没有编译错误。仅针对参考执行终身检查。

因此,要获取这两个值,您必须在线性内存中分配 8 字节内存(用于两个i32),使用指向该内存区域的指针调用函数,然后读取这两个值。将函数拆分为两个单独的函数可能更方便,其中一个返回指针,另一个返回长度。至少,这会使提取值更容易,因为这两个函数都会返回一个i32.

编辑:我找到了来自 Mozilla 的一篇文章,它也解释了我在“wasm-bindgen”部分中的想法。

于 2022-01-09T18:14:53.267 回答