Jos*_*ski 5 c rust webassembly
将多个值从 WebAssembly 传递到 Javascript 可能比实际需要的更困难。通常,我发现我必须:
多值是 Wasm 的一项功能,旨在使这一过程变得更容易,其中多个值可以直接从 Wasm 传递到 JavaScript,从而无需处理指针。步骤变为:
例如:
(module
(func $multResult (export "multResult")
(result f64 f64)
f64.const 1
f64.const 2
)
)
Run Code Online (Sandbox Code Playgroud)
我们直接输出1和2。
我可以使用Rick Battagline 的有用函数从 WAT 编译 Wasm(稍作修复以正确支持多值标志):
node ./bin/watwasm bugrepro.wat -o newoutput.wasm -O3 --multi-value
将生成的 Wasm 编译转回 WAT,我们得到:
(module
(type $none_=>_f64_f64 (func (result f64 f64)))
(export "multResult" (func $0))
(func $0 (result f64 f64)
(tuple.make
(f64.const 1)
(f64.const 2)
)
)
)
Run Code Online (Sandbox Code Playgroud)
该tuple.make命令是使函数可以直接在 JavaScript 中使用的秘密武器。如果我写这个 JavaScript:
const fs = require('fs');
const wasmBytes = fs.readFileSync('./newoutput.wasm');
WebAssembly.instantiate(wasmBytes)
.then(obj => obj.instance.exports)
.then(exported => exported.multResult())
.then(res => console.log(res));
Run Code Online (Sandbox Code Playgroud)
我可以看到它[1,2]是由 Wasm 函数返回的。了不起。
我希望能够使用比 WAT 更高级的语言来做到这一点。是否有任何高级语言可以产生多值 Wasm?
有两种:Rust 和 C。Rust 似乎生成.wasm迄今为止最小的文件。
TinyGo 没有。AssemblyScript 没有。谷物没有。SwiftWASM 没有。
对于生锈
下面的示例假设您打算在浏览器中使用 WebAssembly。WASI 编译(在操作系统中使用 WebAssembly)略有不同;看到这个评论。WebAssembly 的主要目的是在浏览器中提供轻量、快速的代码,其轻量/速度取决于 WebAssembly 目标是否与源语言的优先级一致。下面的示例假设您希望 WebAssembly 尽可能轻。(有关如何在更复杂的情况下减小大小的有用信息,请参阅此处.wasm。)
npm install -g binaryenwasm32-unknown-unknown使用添加目标rustup target add wasm32-unknown-unknown。(WASI 使用不同的目标。)cargo new,并将此代码添加到Cargo.toml:[profile.release]
lto = true
opt-level = "z"
strip = "debuginfo"
[lib]
crate-type = ["cdylib"]
Run Code Online (Sandbox Code Playgroud)
lib.rs并添加以下内容:#[no_mangle]
fn flip(a: u32, b: u32) -> (u32, u32) {
(b, a)
}
Run Code Online (Sandbox Code Playgroud)
无论您打算在何处运行命令,都应用以下环境变量cargo build:RUSTFLAGS="-Ctarget-feature=+multivalue -Clink-args=-zstack-size=64000"。(实现细节取决于您的操作系统和命令行程序。)标志概述:
-Ctarget-feature=+multivalue:告诉 Rust 启用 WASM 多值功能
-Clink-args=-zstack-size=64000:告诉 Rust 使用一页 WASM 线性内存。(Rust 默认为 16 页内存。对于像示例这样的非常简单的用例,这是浪费的,因此重置内存以使用最小设置。无论如何,浏览器为每个 WASM 线程分配 1 页。)
您将需要发布版本,而不是调试版本。在上面的步骤 5 中,strip = "debuginfo"正在剥离调试信息。此外,程序的重点是在浏览器上下文中运行,因此任何调试/测试都应该通过将代码嵌入 Javascript 中来完成。使用此命令编译.wasm文件:cargo build --release --target=wasm32-unknown-unknown
使用wasm-optBinaryen 的工具进一步压缩代码:wasm-opt -Oz -o [output file name].wasm [input file name].wasm
使用此页面将 转换.wasm为.wat,这是整个程序:
(module
(type $t0 (func (param i32 i32) (result i32 i32)))
(func $flip (export "flip") (type $t0) (param $p0 i32) (param $p1 i32) (result i32 i32)
(local.get $p1)
(local.get $p0))
(memory $memory (export "memory") 1)
(global $__data_end (export "__data_end") i32 (i32.const 64000))
(global $__heap_base (export "__heap_base") i32 (i32.const 64000)))
Run Code Online (Sandbox Code Playgroud)
很干净。并不完美,但也差不多。该文件只有 239 字节,.wasm比用 C 生成的文件要小。
对于C
您必须以特殊方式使用 Emscripten 编译器。(这显然只是在此 Twitter 条目中的其他地方提到的。)
首先,用 C 语言构建函数。例如:
typedef struct _nums
{
int x;
int y;
} nums;
nums echo(int x, int y)
{
nums result = {x,y};
return result;
}
Run Code Online (Sandbox Code Playgroud)
这定义了一个struct包含两个ints 的 C,然后定义了一个函数,该函数回显发送给它的任何内容。
可以使用 Emscripten 进行编译,如下所示:
emcc -mmultivalue -Xclang -target-abi -Xclang experimental-mv -Oz -s STANDALONE_WASM -s EXPORTED_FUNCTIONS="['_echo']" -Wl,--no-entry hello.world.c -o bob.wasm
Run Code Online (Sandbox Code Playgroud)
标志的快速分解:
-mmultivalue:告诉 Clang 启用多值支持-Xclang -target-abi:告诉 Clang 以应用程序二进制接口为目标-Xclang experimental-mv:启用更多多值内容?-Oz:告诉 Clang 生成针对大小进行积极优化的代码-s STANDALONE_WASM: 告诉 Emscripten 不要生成它通常生成的 Javascript 粘合代码-s EXPORTED_FUNCTIONS="['_echo']":告诉 Emscripten 如果没有找到对“echo”函数的引用,就不要优化它。这允许您将代码从 WebAssembly 导出到 Javascript。-Wl,--no-entry:告诉 Emscripten 不要尝试在 WebAssembly 中创建默认条目hello.world.c:输入C文件-o bob.wasm:输出一个名为“bob.wasm”的文件这会产生以下代码:
(module
(type $none_=>_i32 (func (result i32)))
(type $i32_=>_none (func (param i32)))
(type $i32_=>_i32 (func (param i32) (result i32)))
(type $i32_i32_=>_i32_i32 (func (param i32 i32) (result i32 i32)))
(memory $0 256 256)
(table $0 1 1 funcref)
(global $global$0 (mut i32) (i32.const 5243920))
(export "memory" (memory $0))
(export "echo" (func $0))
(export "__indirect_function_table" (table $0))
(export "__errno_location" (func $4))
(export "stackSave" (func $1))
(export "stackRestore" (func $2))
(export "stackAlloc" (func $3))
(func $0 (param $0 i32) (param $1 i32) (result i32 i32)
(tuple.make
(local.get $0)
(local.get $1)
)
)
(func $1 (result i32)
(global.get $global$0)
)
(func $2 (param $0 i32)
(global.set $global$0
(local.get $0)
)
)
(func $3 (param $0 i32) (result i32)
(global.set $global$0
(local.tee $0
(i32.and
(i32.sub
(global.get $global$0)
(local.get $0)
)
(i32.const -16)
)
)
)
(local.get $0)
)
(func $4 (result i32)
(i32.const 1024)
)
)
Run Code Online (Sandbox Code Playgroud)
这会产生大量的样板文件。重要的部分:
...
(export "echo" (func $0))
...
(func $0 (param $0 i32) (param $1 i32) (result i32 i32)
(tuple.make
(local.get $0)
(local.get $1)
)
)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1220 次 |
| 最近记录: |