如何在WebAssembly中使用`indirect_call`?

7

目前还没有关于indirect_call的使用示例可以在网上找到。根据语义文档,我尝试了以下方法:

(call_indirect 
    (i32.const 0)
    (i32.const 0)
    )

数字是随机的,但与我预期的运行时错误不同,我得到了解析错误。

call_indirect 的正确语法是什么?

2个回答

6

call_indirect 的正确语法似乎是:

(call_indirect $fsig
   (i32.const 0)
)

$fsig 是在 type 部分定义的预期函数签名,参数是函数地址 (或者说它在 table 中的索引)。

以以下调用函数指针的 C 代码示例为例:

typedef void(*fp)();

void dispatch(fp x) {
  x();
}

编译

(module
  (type $FUNCSIG$v (func))
  (table 0 anyfunc)
  (memory $0 1)
  (export "memory" (memory $0))
  (export "dispatch" (func $dispatch))
  (func $dispatch (param $0 i32)
    (call_indirect $FUNCSIG$v
      (get_local $0)
    )
  )
)

下面是一个更完整的示例,我们实际上调用了一个返回值的函数test:

(module
  (type $FUNCSIG$i (func (result i32)))
  (table 1 anyfunc)
  (elem (i32.const 0) $test)
  (memory $0 1)

  (func $test (type $FUNCSIG$i) (result i32)
    (i32.const 42)
  )

  (func $main (result i32)
    (call_indirect $FUNCSIG$i
      (i32.const 0)
    )
  )

)

1
没错。除了你的回答之外(具体来说,问题说没有在线示例),规范存储库的测试套件中有示例:https://github.com/WebAssembly/spec/blob/master/test/core/call_indirect.wast - JF Bastien
@JFBastien 感谢您指出这些示例。我认为 WebAsm 的开发人员应该在文档或语义页面中提供此链接。它们非常有帮助,但可能不会想到查看测试文件。 - abhishek
@abhishek,语义与文本格式完全分离并独立存在。这是因为文本格式只是一种方便的表示WebAssembly二进制格式的方式之一。规范是事情被正式指定的地方,但它仍然是一个正在进行中的工作。完成后,它将正式指定两个语义,而不参考文本格式,并将单独指定文本格式。 - JF Bastien

3

这是文档的一部分:

表格基本上是可调整大小的引用数组,可以从WebAssembly代码中通过索引访问。为了看到为什么需要表格,我们首先需要观察调用指令需要一个静态函数索引,因此只能调用一个函数——但是如果被调用者是运行时值呢?

  • 在JavaScript中,我们经常看到这种情况:函数是一等公民。
  • 在C/C++中,我们使用函数指针。
  • 在C ++中,我们使用虚拟函数。

WebAssembly需要一种类型的调用指令来实现这一点,因此我们提供了call_indirect指令,它需要一个动态函数操作数。问题在于,目前在WebAssembly中我们可以给操作数提供的唯一类型是i32/i64/f32/f64。

WebAssembly可以添加一个名为anyfunc的类型(“any”表示该类型可以保存任何签名的函数),但不幸的是出于安全原因,无法将此anyfunc类型存储在线性内存中。线性内存以字节形式公开存储值的原始内容,这将允许WASM内容任意观察和破坏原始函数地址,这是不能在Web上允许的。

解决方案是将函数引用存储在表格中,并传递索引而非函数地址作为参数,这些索引只是i32值。因此,call_indirect的操作数可以是i32索引值。

因此,简单来说,call_indirect需要签名进行验证,并以指向相应表格中所需函数的i32值作为参数。与其他WASM指令一样,参数可以从堆栈中弹出或从嵌套指令中检索。因此,我们可以有以下内容:

call_indirect (type 123)      ;; Type as an index in the table,
                              ;; argument on the stack from previous instructions.

或者

call_indirect $funcSignature  ;; The same, but using a type name instead of index.

或者

(call_indirect $funcSignature ;; The argument is provided by the nested instructions.
      (get_local $0)
)

请参见:https://developer.mozilla.org/zh-CN/docs/WebAssembly/Understanding_the_text_format#webassembly_tables

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接