Skip to content

调用约定

理解 WebAssembly 的调用约定对于 WASM 与 JavaScript(或其他语言)之间的互操作至关重要。

基于栈的调用约定

WebAssembly 使用基于栈的调用约定:

mermaid
flowchart LR
    subgraph Caller["调用者"]
        P1[压入参数 1] --> P2[压入参数 2]
        P2 --> CALL[调用函数]
    end
    CALL --> CALLEE[被调用者弹出参数]
    CALLEE --> COMPUTE[计算结果]
    COMPUTE --> PUSH[压入结果]
    PUSH --> RET[返回]
    RET --> POP[调用者弹出结果]

参数传递

带参数的 WAT 函数

wat
;; 参数按顺序压栈,由被调用者弹出
(func $add (param $a i32) (param $b i32) (result i32)
  local.get $a    ;; 将 $a 压入栈
  local.get $b    ;; 将 $b 压入栈
  i32.add)        ;; 弹出两个值,相加,结果压栈

局部变量

wat
(func $factorial (param $n i32) (result i32)
  (local $result i32)
  (local $temp i32)

  (local.set $result (i32.const 1))

  (block $done (result i32)
    (loop $loop
      (br_if $done (i32.le_s (local.get $n) (i32.const 1)))
      (local.set $temp (local.get $result))
      (local.set $result (i32.mul (local.get $result) (local.get $n)))
      (local.set $n (i32.sub (local.get $n) (i32.const 1)))
      (br $loop))
    (local.get $result)))

分支和控制流

If-Else

wat
(func $max (param $a i32) (param $b i32) (result i32)
  (if (i32.gt_s (local.get $a) (local.get $b))
    (then (local.get $a))
    (else (local.get $b))))

循环

wat
(func $sum_to (param $n i32) (result i32)
  (local $sum i32)
  (local.set $sum (i32.const 0))
  (block $done
    (loop $start
      (br_if $done (i32.eqz (local.get $n)))
      (local.set $sum (i32.add (local.get $sum) (local.get $n)))
      (local.set $n (i32.sub (local.get $n) (i32.const 1)))
      (br $start)))
  (local.get $sum))

与 JavaScript 的互操作

传递数字

javascript
// 简单 - 直接传递数字
const result = instance.exports.add(1, 2); // 3

传递字符串

rust
// Rust 接收指针 + 长度
#[wasm_bindgen]
pub fn process_string(ptr: *const u8, len: usize) -> usize {
    let slice = unsafe { std::slice::from_raw_parts(ptr, len) };
    // 处理...
    len
}

接收数组

rust
#[wasm_bindgen]
pub fn sum_array(arr: &[i32]) -> i32 {
    arr.iter().sum()
}

性能提示

技巧说明
使用小类型在 32 位架构上 i32 比 i64 更快
最小化参数通过指针传递结构体,而非值传递
内联小函数减少调用开销
避免过度分支分支深度影响性能

继续学习导入和导出了解模块通信。

基于 MIT 协议发布