WebAssembly 中 call_indirect 的错误结果
Wrong result of call_indirect in WebAssembly
不明白如何正确使用call_indirect。
在网上搜索,我发现在 web assembly 中让函数指针工作并不容易。函数地址存储在 Table 中,基本上是 i32 标识符。
好吧..让我们编码吧!
定义中间表示中的语义(调用-indirect.ll文件):
define i32 @call_indirect_method(i32 (i32, i32)* %callee, i32 %arg, i32 %arg2) {
%t = call i32 %callee(i32 %arg, i32 %arg2)
ret i32 %t
}
编写程序(program.c文件):
#include <emscripten.h>
int EMSCRIPTEN_KEEPALIVE call_indirect_method();
int EMSCRIPTEN_KEEPALIVE sum(int a, int b)
{
return (a + b);
}
int EMSCRIPTEN_KEEPALIVE calc(int a, int b)
{
return call_indirect_method();
}
一堆 js (index.js):
const fs = require("fs");
const raw_source = new Uint8Array(fs.readFileSync(`module.wasm`));
const wasm_module = new WebAssembly.Module(raw_source);
var table = new WebAssembly.Table({initial: 1, element: "anyfunc"});
var memory = new WebAssembly.Memory({initial: 1});
const _exp = new WebAssembly.Instance(wasm_module, {
"env": {
__memory_base: 0,
__table_base: 0,
memoryBase: 0,
tableBase: 0,
memory: memory,
table: table,
nullFunc_X: () => console.info("nullFunc_X()"),
jsCall_X: () => console.info("jsCall_X()"),
abort: err => {
throw new Error('abort ' + err);
},
abortOnCannotGrowMemory: err => {
throw new Error('abortOnCannotGrowMemory ' + err);
},
abortWhosebug: err => {
throw new Error('abortWhosebug ' + err); // Wellcome here if compile with -O0 level.
}
}
}).exports;
table.set(0, _exp._sum);
console.log(_exp._calc(1, 2)); // expect to be 3, got 0 instead
我构建 .wasm 的方式:
emcc -O1 -s WASM=1 -s SIDE_MODULE=1 -s ONLY_MY_CODE=1 -s FILESYSTEM=0 -s TOTAL_MEMORY=65536 -s TOTAL_STACK=1024 -s ENVIRONMENT='web' -s ALLOW_MEMORY_GROWTH=0 -s NO_EXIT_RUNTIME=1 -s STACK_OVERFLOW_CHECK=0 call-indirect.ll program.c -o module.wasm
最后,致电:
node index.js
当一切为 "assembled" - 我在控制台中得到 0。但期待 3.
另外(一件小事,但是..)如果您尝试构建没有优化的代码 -O0 它将由于堆栈溢出错误而中止。不知道为什么。任何的想法? (是的,我已经在尝试增加内存了)。
所以,也许有人知道我的情况出了什么问题?
嗯,看来你应该为你的函数创建一个额外的 table。
几点注意事项:
- 看起来这个table会被放在堆内存的开头,但这不是100%的信息;
- 而且,当你有不止一种函数指针类型时,我不会检查大小写;
- 此外,您可能会在 JS 中使用 table 的初始大小。
#include <emscripten.h>
typedef int (*FPTR_TYPE)(int,int);
FPTR_TYPE fMathOp;
int EMSCRIPTEN_KEEPALIVE add(int a, int b)
{
return (a + b);
}
int EMSCRIPTEN_KEEPALIVE sub(int a, int b)
{
return (a - b);
}
FPTR_TYPE my_table[] = {
add,
sub
};
int EMSCRIPTEN_KEEPALIVE calc(int a, int b)
{
return fMathOp(a, b);
}
所以"call-indirect.ll"可能会掉线。
不明白如何正确使用call_indirect。
在网上搜索,我发现在 web assembly 中让函数指针工作并不容易。函数地址存储在 Table 中,基本上是 i32 标识符。 好吧..让我们编码吧!
定义中间表示中的语义(调用-indirect.ll文件):
define i32 @call_indirect_method(i32 (i32, i32)* %callee, i32 %arg, i32 %arg2) {
%t = call i32 %callee(i32 %arg, i32 %arg2)
ret i32 %t
}
编写程序(program.c文件):
#include <emscripten.h>
int EMSCRIPTEN_KEEPALIVE call_indirect_method();
int EMSCRIPTEN_KEEPALIVE sum(int a, int b)
{
return (a + b);
}
int EMSCRIPTEN_KEEPALIVE calc(int a, int b)
{
return call_indirect_method();
}
一堆 js (index.js):
const fs = require("fs");
const raw_source = new Uint8Array(fs.readFileSync(`module.wasm`));
const wasm_module = new WebAssembly.Module(raw_source);
var table = new WebAssembly.Table({initial: 1, element: "anyfunc"});
var memory = new WebAssembly.Memory({initial: 1});
const _exp = new WebAssembly.Instance(wasm_module, {
"env": {
__memory_base: 0,
__table_base: 0,
memoryBase: 0,
tableBase: 0,
memory: memory,
table: table,
nullFunc_X: () => console.info("nullFunc_X()"),
jsCall_X: () => console.info("jsCall_X()"),
abort: err => {
throw new Error('abort ' + err);
},
abortOnCannotGrowMemory: err => {
throw new Error('abortOnCannotGrowMemory ' + err);
},
abortWhosebug: err => {
throw new Error('abortWhosebug ' + err); // Wellcome here if compile with -O0 level.
}
}
}).exports;
table.set(0, _exp._sum);
console.log(_exp._calc(1, 2)); // expect to be 3, got 0 instead
我构建 .wasm 的方式:
emcc -O1 -s WASM=1 -s SIDE_MODULE=1 -s ONLY_MY_CODE=1 -s FILESYSTEM=0 -s TOTAL_MEMORY=65536 -s TOTAL_STACK=1024 -s ENVIRONMENT='web' -s ALLOW_MEMORY_GROWTH=0 -s NO_EXIT_RUNTIME=1 -s STACK_OVERFLOW_CHECK=0 call-indirect.ll program.c -o module.wasm
最后,致电:
node index.js
当一切为 "assembled" - 我在控制台中得到 0。但期待 3.
另外(一件小事,但是..)如果您尝试构建没有优化的代码 -O0 它将由于堆栈溢出错误而中止。不知道为什么。任何的想法? (是的,我已经在尝试增加内存了)。
所以,也许有人知道我的情况出了什么问题?
嗯,看来你应该为你的函数创建一个额外的 table。
几点注意事项:
- 看起来这个table会被放在堆内存的开头,但这不是100%的信息;
- 而且,当你有不止一种函数指针类型时,我不会检查大小写;
- 此外,您可能会在 JS 中使用 table 的初始大小。
#include <emscripten.h>
typedef int (*FPTR_TYPE)(int,int);
FPTR_TYPE fMathOp;
int EMSCRIPTEN_KEEPALIVE add(int a, int b)
{
return (a + b);
}
int EMSCRIPTEN_KEEPALIVE sub(int a, int b)
{
return (a - b);
}
FPTR_TYPE my_table[] = {
add,
sub
};
int EMSCRIPTEN_KEEPALIVE calc(int a, int b)
{
return fMathOp(a, b);
}
所以"call-indirect.ll"可能会掉线。