c++ - Webassembly - 尝试编译代码缓冲区并通过 EM_JS 通过 C 执行它

标签 c++ c webassembly emcc

我正在尝试使用 webassembly,并且我制作了一个玩具模块,可以在 C 中蛮力素数

extern "C" {
bool isPrime(int n) {
    for (int i = 2; i <= n; i++) {
        if (n % i == 0) return false;
    }

    return true;
}

int getPrimes() {
    int primesFound = 0;

    for (int i = 2; i < 4206969; i++) {
        if (isPrime(i)) {
            primesFound++;
        }
    }

    return primesFound;
}

}

在 Windows 中用 emcc/O3 编译它会得到以下字节流:
    0x0, 0x61, 0x73, 0x6d, 0x1, 0x0, 0x0, 0x0, 0x1, 0x8, 0x2, 0x60, 
    0x0, 0x0, 0x60, 0x0, 0x1, 0x7f, 0x3, 0x3, 0x2, 0x0, 0x1, 0x5, 0x6, 0x1, 0x1, 0x80, 0x2,
    0x80, 0x2, 0x7, 0x1f, 0x3, 0x6, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x2, 0x0, 0x9, 0x67,
    0x65, 0x74, 0x50, 0x72, 0x69, 0x6d, 0x65, 0x73, 0x0, 0x1, 0x6, 0x5f, 0x73, 0x74, 0x61,
    0x72, 0x74, 0x0, 0x0, 0xa, 0x4e, 0x2, 0x3, 0x0, 0x1, 0xb, 0x48, 0x1, 0x4, 0x7f, 0x41,
    0x2, 0x21, 0x0, 0x3, 0x40, 0x41, 0x2, 0x21, 0x1, 0x2, 0x40, 0x3, 0x40, 0x20, 0x0, 0x20, 
    0x1, 0x70, 0x45, 0xd, 0x1, 0x20, 0x0, 0x20, 0x1, 0x46, 0x21, 0x3, 0x20, 0x1, 0x41, 0x1,
    0x6a, 0x21, 0x1, 0x20, 0x3, 0x45, 0xd, 0x0, 0xb, 0x20, 0x2, 0x41, 0x1, 0x6a, 0x21, 0x2,
    0xb, 0x20, 0x0, 0x41, 0x1, 0x6a, 0x22, 0x0, 0x41, 0xf9, 0xe2, 0x80, 0x2, 0x47, 0xd, 0x0,
    0xb, 0x20, 0x2, 0xb, 0xb, 0xa, 0x1, 0x0, 0x41, 0x80, 0xc, 0xb, 0x3, 0xa0, 0x6, 0x50

大多数人可能难以理解,但 VSC 的 WASM 到 WAT 工具表明这代表以下 WASM:
(module
 (type $t0 (func))
 (type $t1 (func (result i32)))
 (func $_start (type $t0)
   nop)
 (func $getPrimes (type $t1) (result i32)
   (local $l0 i32) (local $l1 i32) (local $l2 i32) (local $l3 i32)
   i32.const 2
   local.set $l0
   loop $L0
     i32.const 2
     local.set $l1
     block $B1
       loop $L2
         local.get $l0
         local.get $l1
         i32.rem_u
         i32.eqz
         br_if $B1
         local.get $l0
         local.get $l1
         i32.eq
         local.set $l3
         local.get $l1
         i32.const 1
         i32.add
         local.set $l1
         local.get $l3
         i32.eqz
         br_if $L2
       end
       local.get $l2
       i32.const 1
       i32.add
       local.set $l2
     end
     local.get $l0
     i32.const 1
     i32.add
     local.tee $l0
     i32.const 4206969
     i32.ne
     br_if $L0
   end
   local.get $l2)
 (memory $memory 256 256)
 (export "memory" (memory 0))
 (export "getPrimes" (func $getPrimes))
 (export "_start" (func $_start))
 (data $d0 (i32.const 1536) "\a0\06P"))

从另一个程序调用以下 EM_JS,其意图是来自 WASM,它告诉 javascript 引擎编译一些 WASM,然后执行它。
EM_JS(int, call_wasmBlock, (const uint8_t* wasmBlock, uint32_t length), {
    let wasmBuf = new Uint8Array(Module.HEAPU8.buffer, wasmBlock, length);
    WebAssembly.instantiate(wasmBuf)
        .then(
            obj => console.log(obj.instance.exports.getPrimes())
        );
});

我收到此错误:
Uncaught (in promise) CompileError: WebAssembly.instantiate(): unexpected section <Export> @+158

我不完全确定该去哪里,这主要是对 webassembly 的限制进行试验。

最佳答案

index.js

'use strict';

import fs from 'fs';

const bin = fs.readFileSync('test.wasm');

// only checks for primes up to 100
const primeAppBin = new Uint8Array([0, 97, 115, 109, 1, 0, 0, 0, 1, 10, 2, 96, 0, 1, 127, 96, 1, 127, 1, 127, 3, 3, 2, 1, 0, 5, 4, 1, 1, 1, 1, 7, 32, 3, 6, 109, 101, 109, 111, 114, 121, 2, 0, 7, 105, 115, 80, 114, 105, 109, 101, 0, 0, 9, 103, 101, 116, 80, 114, 105, 109, 101, 115, 0, 1, 10, 142, 1, 2, 61, 1, 2, 127, 65, 1, 33, 1, 32, 0, 65, 4, 78, 4, 127, 32, 0, 65, 127, 106, 33, 2, 65, 2, 33, 1, 2, 64, 3, 64, 32, 0, 32, 1, 111, 69, 13, 1, 32, 1, 65, 1, 106, 34, 1, 32, 2, 71, 13, 0, 11, 65, 1, 15, 11, 65, 0, 5, 65, 1, 11, 11, 78, 1, 4, 127, 65, 2, 33, 0, 3, 64, 2, 64, 32, 0, 65, 4, 79, 4, 64, 32, 0, 65, 127, 106, 33, 3, 65, 2, 33, 2, 3, 64, 32, 0, 32, 2, 112, 69, 13, 2, 32, 2, 65, 1, 106, 34, 2, 32, 3, 71, 13, 0, 11, 11, 32, 1, 65, 1, 106, 33, 1, 11, 32, 0, 65, 1, 106, 34, 0, 65, 228, 0, 71, 13, 0, 11, 32, 1, 11, 11, 9, 1, 0, 65, 128, 12, 11, 2, 160, 6]);

(async () => {
  let buff;
  const imports = {
    env: {
      execWasm: (byteOffset, length) => {
        WebAssembly.instantiate(new Uint8Array(buff, byteOffset, length)).then(app => console.log(app.instance.exports.getPrimes() + ' primes found'));
      }
    }
  };
  const app = await WebAssembly.instantiate(bin, imports);
  const { memory, test } = app.instance.exports;
  const view = new Uint8Array(memory.buffer, 0, primeAppBin.length);
  for (let i in view) view[i] = primeAppBin[i];
  buff = memory.buffer;
  test(view.byteOffset, view.byteLength);
})();

测试.cpp

// emcc -O3 "test.cpp" -o "test.wasm" -s STANDALONE_WASM -s EXPORTED_FUNCTIONS="['_test']" -s ERROR_ON_UNDEFINED_SYMBOLS=0 -Wl,--no-entry -msimd128 -s INITIAL_MEMORY=64kb -s ALLOW_MEMORY_GROWTH=0 -s TOTAL_STACK=0kb

#include <emscripten.h>

extern "C" {
  extern void execWasm(int byteOffset, int length);
  EMSCRIPTEN_KEEPALIVE
  void test (int byteOffset, int length) {
    execWasm(byteOffset, length);
  }
}

素数.cpp

// emcc -O3 "primes.cpp" -o "primes.wasm" -s STANDALONE_WASM -s EXPORTED_FUNCTIONS="['_getPrimes']" -s ERROR_ON_UNDEFINED_SYMBOLS=0 -Wl,--no-entry -msimd128 -s INITIAL_MEMORY=64kb -s ALLOW_MEMORY_GROWTH=0 -s TOTAL_STACK=0kb

extern "C" {
    bool isPrime(int n) {
        for (int i = 2; i < n - 1; i++) {
            if (n % i == 0)
            return false;
        }
    return true;
    }

    int getPrimes() {
        int primesFound = 0;

        for (int i = 2; i < 4'206'969; i++) {
            if (isPrime(i)) {
                primesFound++;
            }
        }
        return primesFound;
    }
}

关于c++ - Webassembly - 尝试编译代码缓冲区并通过 EM_JS 通过 C 执行它,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60748355/

相关文章:

c - 如何读取指针式 watch 的时间?

对 dtostrf 函数感到困惑

javascript - 为什么 Rust WASM 指针和 JS 指针的值不同?

webassembly - 如何dlopen大于4KB的侧模块?

c# - 将 C++(处理 Corona)函数转换为 C#

C++ vector : difference between clear() and resize()

c++ - 创建 "no-op"版本的 dll?

c++ - SDL2_gfx 屏幕在第二次循环后填充

c - 一个只调用另一个函数的函数会减慢速度吗?

c# - Blazor 始终以 UTC 格式显示日期