2017-01-26 232 views
5

我想測試WebAssembly以進行一些複雜的數組計算。將JavaScript數組作爲參數傳遞給WebAssembly函數

因此,我已經寫了一個簡單的C++函數加入含有每3個元件中的兩個int數組:

// hello.cpp 
extern "C" { 

void array_add(int * summed, int* a, int* b) { 
    for (int i=0; i < 3; i++) { 
    summed[i] = a[i] + b[i]; 
    } 
} 

} 

並與編譯此:

emcc hello.cpp -s WASM=1 -s "MODULARIZE=1" -s "EXPORT_NAME='HELLO'" -s "BINARYEN_METHOD='native-wasm'" -s "EXPORTED_FUNCTIONS=['_array_add']" -o build/hello.js

等等產生,一個js和一個wasm文件。我加載這些與下面的HTML頁面:

<!doctype html> 
<html lang="en-us"> 
    <head> 
    <meta charset="utf-8"> 
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> 
    <script type="text/javascript" src="build/hello.js"></script> 
    <script type="text/javascript"> 
     function reqListener() { 
     // Loading wasm module 
     var arrayBuffer = oReq.response 
     HELLO['wasmBinary'] = arrayBuffer 
     hello = HELLO({ wasmBinary: HELLO.wasmBinary }) 

     // Calling function 
     var result = new Int32Array(3) 
     var a = new Int32Array([1, 2, 3]) 
     var b = new Int32Array([4, 5, 2]) 
     hello._array_add(result, a, b) 
     console.log('result', result) 
     } 

     var oReq = new XMLHttpRequest(); 
     oReq.responseType = "arraybuffer"; 
     oReq.addEventListener("load", reqListener); 
     oReq.open("GET", "build/hello.wasm"); 
     oReq.send(); 
    </script> 
    </head> 
    <body> 

    </body> 
</html> 

但不知何故,result陣列總是[0, 0, 0]

我已經嘗試了各種各樣的東西,包括調用ccall()(見emscripten docs)函數,似乎我無法獲得作爲我的wasm編譯函數參數傳遞的數組。

例如,用下面的C++函數:在JavaScript調用時

extern "C" { 

int first(int * arr) { 
    return arr[0]; 
} 

} 

結果是隨機ISH整數,而不是從本人送出的參數陣列的預期值。

我錯過了什麼?

NB:我知道C++幾乎什麼都沒有,所以一切道歉,如果這是與我的C++的無知初學者的問題...

回答

7

你提的問題是非常相似的this one:WebAssembly只支持i32/i64/f32/f64value types以及i8/i16用於存儲。

這意味着你不能傳入指針。當你從C++的角度來看時(無需爲無知而道歉),你所做的完全是理智的,但這不僅僅是WebAssembly的邊界如何工作。這對C++專家來說也是令人驚訝的。

如串問題,你需要:

  • 複製陣列中的一個在同一時間通過調用一次出口每個條目(如set(size_t index, int value))。
  • 將WebAssembly實例的堆作爲ArrayBuffer公開爲JavaScript,並直接寫入ArrayBuffer所需的值。

你可以做後者與我在其他答案提出了相同的代碼:

const bin = ...; // WebAssembly binary, I assume below that it imports a memory from module "imports", field "memory". 
const module = new WebAssembly.Module(bin); 
const memory = new WebAssembly.Memory({ initial: 2 }); // Size is in pages. 
const instance = new WebAssembly.Instance(module, { imports: { memory: memory } }); 
const arrayBuffer = memory.buffer; 
const buffer = new Uint8Array(arrayBuffer); 

由C即將++你可能想知道:「但你如何指針的工作?」。上面我解釋說WebAssembly↔JavaScript你不能傳遞指針!WebAssembly內部的指針表示爲簡單的i32值。 Empscripten依賴於LLVM來做到這一點,並且由於WebAssembly以4GiB最大堆大小顯示自己爲ILP32,因此它工作正常。

它確實對間接函數調用和函數指針有意義!我會留下另一個問題;-)

然而,這並不意味着JavaScript可以「談論」指向WebAssembly的指針:i32i32。如果您知道某個值位於堆中某處,那麼您可以將該i32傳遞給JavaScript,並且JavaScript可以對其進行修改並將其傳遞迴WebAssembly。如果JavaScript有權訪問堆的ArrayBuffer,那麼擁有i32可讓您知道堆中的內容,並像修改C++一樣修改堆。

雖然WebAssembly堆與大多數C++堆不同:它無法訪問可執行頁面,也無法訪問調用堆棧(或者更確切地說,大部分調用堆棧:編譯器(如LLVM)可能「溢出「一些地址取值到堆,而不是使用WebAssembly的本地化)。這基本上是哈佛架構所做的(與馮諾依曼相反)。


那麼你的hello._array_add(result, a, b)在做什麼?使用ToInteger從陣列強制執行ab。這成爲0,它在WebAssembly中是一個有效的堆位置!你正在訪問堆中非常意想不到的部分!

3

所以感謝其他類似的問題:

Pass array to C function with emscripten

How to handle passing/returning array pointers to emscripten compiled code?

和API文檔:

https://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html#getValue

我已經想通了。要examplify如何通過一個數組WASM功能/得到一個數組回來,我已經實現了在C++一個簡單的數組副本:

#include <stdint.h> 

extern "C" { 

int* copy_array(int* in_array, int length) { 
    int out_array[length]; 
    for (int i=0; i<length; i++) { 
    out_array[i] = in_array[i]; 
    } 
    return out_array; 
} 

} 

,你可以這樣進行編譯:

emcc wasm_dsp.cpp -s WASM=1 -s "MODULARIZE=1" -s "EXPORT_NAME='WasmDsp'" -s "BINARYEN_METHOD='native-wasm'" -s "EXPORTED_FUNCTIONS=['_copy_array']" -o build/wasm_dsp.js

而且這樣在瀏覽器中運行:

<!doctype html> 
<html lang="en-us"> 
    <head> 
    <meta charset="utf-8"> 
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> 
    <script type="text/javascript" src="build/wasm_dsp.js"></script> 
    <script type="text/javascript"> 
     function reqListener() { 
     // Loading wasm module 
     var arrayBuffer = oReq.response 
     WasmDsp['wasmBinary'] = arrayBuffer 
     wasmDsp = WasmDsp({ wasmBinary: WasmDsp.wasmBinary }) 

     var inArray = new Int32Array([22, 44, 66, 999]) 
     var nByte = 4 
     copyArray = wasmDsp.cwrap('copy_array', null, ['number', 'number']); 

     // Takes an Int32Array, copies it to the heap and returns a pointer 
     function arrayToPtr(array) { 
      var ptr = wasmDsp._malloc(array.length * nByte) 
      wasmDsp.HEAP32.set(array, ptr/nByte) 
      return ptr 
     } 

     // Takes a pointer and array length, and returns a Int32Array from the heap 
     function ptrToArray(ptr, length) { 
      var array = new Int32Array(length) 
      var pos = ptr/nByte 
      array.set(wasmDsp.HEAP32.subarray(pos, pos + length)) 
      return array 
     } 

     var copiedArray = ptrToArray(
      copyArray(arrayToPtr(inArray), inArray.length) 
     , inArray.length) 

     console.log(copiedArray) 
     } 

     var oReq = new XMLHttpRequest(); 
     oReq.responseType = "arraybuffer"; 
     oReq.addEventListener("load", reqListener); 
     oReq.open("GET", "build/wasm_dsp.wasm"); 
     oReq.send(); 
    </script> 
    </head> 
    <body> 

    </body> 
</html> 

注意這裏的arrayToPtrptrToArray功能...他們是那些做傳遞/返回數組的工作。

相關問題