【问题标题】:How to use `indirect_call` in WebAssembly?如何在 WebAssembly 中使用`indirect_call`?
【发布时间】:2018-01-24 14:43:17
【问题描述】:

网上没有使用indirect_call 的例子。基于语义文档,我尝试了

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

数字是随机的,但不会像我预期的那样给出运行时错误。我收到解析错误。

call_indirect 的正确语法是什么?

【问题讨论】:

    标签: webassembly


    【解决方案1】:

    call_indirect 的正确语法似乎是

    (call_indirect $fsig
       (i32.const 0)
    )
    

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

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

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

    compiles

    (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)
        )
      )
    
    )
    

    【讨论】:

    • 没错。要添加到您的答案(具体来说,问题说在线没有示例),规范存储库的测试套件中有示例:github.com/WebAssembly/spec/blob/master/test/core/…
    • @JFBastien 感谢您指出示例。我认为 webAsm 人应该在文档中的某个地方或语义页面中给出这个链接。它们确实很有帮助,但有人可能不会去查看测试文件。
    • @abhishek 语义完全独立于文本格式并且独立存在。这是因为文本格式只是一种方便,它是表示 WebAssembly 二进制格式的多种可能方式之一。规范是正式指定事物的地方,但它仍在进行中。完成后,它将正式指定两种语义,不参考文本格式,并将单独指定文本格式。
    【解决方案2】:

    文档摘录:

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

    • 在 JavaScript 中我们经常看到这一点:函数是一等值。
    • 在 C/C++ 中,我们通过函数指针看到这一点。
    • 在 C++ 中,我们通过虚函数看到了这一点。

    WebAssembly 需要一种调用指令来实现这一点,所以我们 给它 call_indirect,它接受一个动态函数操作数。这 问题是我们必须在 WebAssembly 中提供操作数的唯一类型 是(当前)i32/i64/f32/f64。

    WebAssembly 可以添加一个 anyfunc 类型(“any”,因为该类型可以 持有任何签名的函数),但不幸的是这个 anyfunc 类型 出于安全原因,不能存储在线性内存中。线性 内存将存储值的原始内容公开为字节,这 将允许 wasm 内容任意观察和破坏原始内容 函数地址,这是在 网络。

    解决方案是将函数引用存储在表中并传递 而是围绕表索引,它们只是 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/en-US/docs/WebAssembly/Understanding_the_text_format#webassembly_tables

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-05-21
      • 1970-01-01
      • 2018-12-26
      • 2021-02-26
      • 2021-02-27
      • 2018-10-05
      相关资源
      最近更新 更多