Неверный результат call_indirect в WebAssembly - PullRequest
0 голосов
/ 20 октября 2019

Не понимаю, как правильно использовать call_indirect.

При поиске в Интернете я обнаружил, что заставить указатели функций работать в веб-сборке нелегко. Адреса функций хранятся в таблице и в основном являются идентификаторами i32. Хорошо ... давайте кодировать!

Определить семантику в промежуточном представлении (файл call -irect.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);
        },
        abortStackOverflow: err => {
            throw new Error('abortStackOverflow ' + 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

Когда все "собрано" - у меня 0 в консоли. Но ожидая 3.

Также (незначительная вещь, но ..), если вы пытаетесь собрать код без оптимизации -O0, он будет прерван из-за ошибки переполнения стека. Не знаю почему. Любая идея? (Да, я уже пытаюсь увеличить память).

Так, может, кто-то знает, что не так с моей ситуацией?

1 Ответ

0 голосов
/ 03 ноября 2019

Похоже, вам просто нужно создать дополнительную таблицу для своих функций.

Несколько замечаний:

  • И похоже, эта таблица будет помещена в начало кучипамяти, но это не 100% информации;
  • И еще, я не проверяю случай, когда у вас есть более одного типа указателя на функцию;
  • А также, вы, вероятно, можете играть сначальный размер таблицы в JS).
#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 -irect.ll" может быть удален.

...