Передача строк в модуль .wasm - PullRequest
0 голосов
/ 18 ноября 2018

Я застрял на этом некоторое время и не могу найти хорошие ресурсы для своей проблемы. Я пришел из "только C", поэтому большинство вещей для веб-разработчиков для меня совершенно новые.

Я написал функцию C float editDistance(char *str1, char *str2), которая возвращает расстояние редактирования двух массивов символов. Сейчас цель состоит в том, чтобы успешно вызвать эту функцию из среды JS.

Убедившись, что код работает с рекомендованным методом Emscipten ccall, я решил двигаться дальше. Сейчас Я использую Emscripten для компиляции кода C с флагами -O3, -s WASM=1, -s EXPORTED_FUNCTIONS="['_editDistance']" и -s SIDE_MODULE=1 -s для Wasm. Код JS, который я пытаюсь обернуть вокруг своей WebAssembly:

// Allocate memory for the wasm module to run in. (65536*256 bit)
let wasmMemory = new WebAssembly.Memory({
    initial: 256
});

let info = {
    env: {
        abort: function() {},
        memoryBase: 0,
        tableBase: 0,
        memory: wasmMemory,
        table: new WebAssembly.Table({initial: 2, element: 'anyfunc'}),
    }
}

// Define the strings
let str1 = "abcd";
let str2 = "abcd";

// Allocate memory on the wasm partition for the HEAPU8
let HEAPU8 = new Uint8Array(wasmMemory.buffer);

// Create the char arrays on the heap from the strings
let stackPtr = 0;
let str1Ptr = stackPtr;
stackPtr = stringToASCIIArray(str1, HEAPU8, stackPtr);

let str2Ptr = stackPtr;
stackPtr = stringToASCIIArray(str2, HEAPU8, stackPtr);

// Read the wasm file and instantiate it with the above environment setup. Then
// call the exported function with the string pointers.
let wasmBinaryFile = 'bin/edit_distanceW.wasm';
fetch(wasmBinaryFile, {credentials:"same-origin"})
    .then((response) => response.arrayBuffer())
    .then((binary) => WebAssembly.instantiate(binary,info))
    .then((wa) => alert(wa.instance.exports._editDistance(str1Ptr, str2Ptr)));

// Converts a string to an ASCII byte array on the specified memory
function stringToASCIIArray(str, outU8Array, idx){
    let length = str.length + 1;

    let i;
    for(i=0; i<length; i++){
        outU8Array[idx+i] = str.charCodeAt(i);
    }
    outU8Array[idx+i]=0;

    return (idx + length);
}

Сгенерированный файл wasm при преобразовании в wat требует следующих импортов:

  (import "env" "abort" (func (;0;) (type 0)))
  (import "env" "memoryBase" (global (;0;) i32))
  (import "env" "tableBase" (global (;1;) i32))
  (import "env" "memory" (memory (;0;) 256))
  (import "env" "table" (table (;0;) 2 anyfunc))

.. и экспортирует эти:

  (export "__post_instantiate" (func 7))
  (export "_editDistance" (func 9))
  (export "runPostSets" (func 6))
  (elem (;0;) (get_global 1) 8 1))

Теперь, когда я тестирую код, строки без проблем передаются в модуль C. Несколько вызовов функций даже сделаны на них (strLen), прежде чем дела пойдут на юг. В функции C есть этот неприятный вложенный цикл, который выполняет основные вычисления, итерируя по двумерному массиву, читая символы из строк (код C только что был перенесен с бумаги с уродливым псевдокодом, так что извините за имена переменных) :

do{
    for(p=0; p<editDistance; p++){
        // Do stuff
    }
    // Do more stuff
    editDistance++;
} while(fkp[len2*2-len1][editDistance] != len1);

До того, как функция входит в цикл for(), в модуле по-прежнему хранятся строки в памяти str1Ptr=0x00 и str2Ptr=0x05 с правильной длиной и содержанием. Напротив, сразу после входа в цикл for() память перезаписывается мусором (в основном 0 с), что приводит к повреждению конечного результата. Я подозреваю, что некоторые проблемы с сохранением и восстановлением стека при изменении области видимости, так как тот же код, скомпилированный на моем ПК с использованием gcc, работает как чудо.

Есть идеи, какие настройки мне не хватает, что мешает правильному завершению функции C?

1 Ответ

0 голосов
/ 18 ноября 2018

Если вы только начинаете, вы, вероятно, захотите использовать сгенерированный emscripten клей JS.То есть не используйте SIDE_MODULE = 1 и вместо этого выводите в файл с именем .js.Затем компилятор emscripten сгенерирует как файл .js, так и файл .wasm.Затем вы можете включить файл .js в свой проект, и он будет обрабатывать всю загрузку и настройку за вас.

Если вы попытаетесь загрузить файл wasm самостоятельно, вам придется проделать большую работу для репликации.среда emscripten, которая потребует много внутренних деталей emscripten.Кроме того, эти внутренние данные могут быть изменены при обновлении до новой версии emscripten, поэтому вы создаете больше работы для себя.

...