web-dev-qa-db-ja.com

どういうわけか、emscriptenの「接着剤」を使わずにWebAssemblyコードをビルドできますか?

説明されているようにそれ自体で機能するwasmファイルを何らかの方法で作成できますか ここではMDNで (オブジェクトをインスタンス化し、それらの関数を呼び出すことによって)?

私が見つけることができるすべてのガイド( MDNのこれなど )は、emscriptenを使用することをお勧めします。ただし、これには〜70kBの「グルーコード」(オプションのファイルシステムエミュレーション〜50 kB)、追加のロジック(検出ノード/ブラウザー環境、自動フェッチなど)、およびおそらく他のエミュレーションも含まれます。

その「グルーコード」が不要で、WASMを直接(おそらくCコードから、おそらく他の何かから)作成したい場合はどうなりますか?今それは可能ですか?

21
Karel Bílek

Emscriptenを使用して、最小限のコード出力を生成できます。

次の簡単なファイルadder.cを考えてみます。

int adder (int a, int b) {
    return a + b;
}

次のようにコンパイルします(かなり最近のemscriptenが必要です):

emcc -O2 -s WASM=1 -s SIDE_MODULE=1 -o adder.wasm

何が生成されたかを確認するには、binaryenのwasm-disを使用して、それを無駄なテキスト形式に分解します(wabtからwasm2wastを使用することもできます)。

wasm-dis adder.wasm -o adder.wast

逆アセンブルされたソースは次のようになります。

(module
 (type $0 (func (param i32 i32) (result i32)))
 (type $1 (func))
 (import "env" "memoryBase" (global $import$0 i32))
 (import "env" "memory" (memory $0 256))
 (import "env" "table" (table 0 anyfunc))
 (import "env" "tableBase" (global $import$3 i32))
 (global $global$0 (mut i32) (i32.const 0))
 (global $global$1 (mut i32) (i32.const 0))
 (export "__post_instantiate" (func $2))
 (export "runPostSets" (func $1))
 (export "_adder" (func $0))
 (func $0 (type $0) (param $var$0 i32) (param $var$1 i32) (result i32)
  (i32.add
   (get_local $var$1)
   (get_local $var$0)
  )
 )
 (func $1 (type $1)
  (nop)
 )
 (func $2 (type $1)
  (block $label$0
   (set_global $global$0
    (get_global $import$0)
   )
   (set_global $global$1
    (i32.add
     (get_global $global$0)
     (i32.const 5242880)
    )
   )
   (call $1)
  )
 )
 ;; custom section "dylink", size 5
)

次に、これをノード(v8.X以降)で次のように実行できます。

const WA = WebAssembly,
      env = {memoryBase: 0,
             tableBase: 0,
             memory: new WA.Memory({initial: 256}),
             table: new WA.Table({initial: 0, element: 'anyfunc'})},
      code = new Uint8Array(require('fs').readFileSync('adder.wasm'))
WA.compile(code).then(m => {
    return new WA.Instance(m, {env: env})
}).then(i => {
    console.log(i.exports._adder(7, 8))
})

スタックやヒープメモリを使用するコードをサポートする場合は、状況がさらに複雑になることに注意してください。つまり他のエクスポートを呼び出す前に、少なくともmemoryBaseを設定し、ホスト環境から__post_instantiateを呼び出す必要があります。

JavaScript環境なしでWebAssemblyコードを解釈したい場合は、 wac/wace を使用して実行できます(完全な開示:このプロジェクトを作成しました)。 waceは、「_ main」または「main」関数が定義されていることを前提としています。

12
kanaka

ONLY_MY_CODEフラグを使用できます。これにより、glue.jsのないwasmモジュールのみが生成されます。

emcc -O1 ./src/foo.cpp -o release/foo.wasm -s WASM=1 -s ONLY_MY_CODE=1

Settings.jsから https://github.com/kripken/emscripten/blob/master/src/settings.js#L58

var ONLY_MY_CODE = 0; // This disables linking and other causes of adding extra code
                      // automatically, and as a result, your output compiled code
                      // (in the .asm.js file, if you emit with --separate-asm) will
                      //  contain only the functions you provide.
4
Lisa Schuh

できますが、時間の経過とともに簡単になっています。

C++を完全に回避したい場合、たとえば spec tests または WebKit test suite で行われるように、WebAssemblyモジュールを作成することが可能です。

C++でも、Emscriptenがなくても可能です。 wasm-stat.us たとえば、 GCC拷問テスト。ビルド出力を確認するか、 ソースを確認してください

たとえば、コンパイル/リンク/アセンブルでは次のようになります。

# Get a .o file:
src/work/wasm-install/bin/clang src/work/gcc/gcc/testsuite/gcc.c-torture/execute/20020227-1.c -o src/work/torture-o/20020227-1.c.o --std=gnu89 -DSTACK_SIZE=1044480 -w -Wno-implicit-function-declaration --target=wasm32-unknown-unknown-wasm -c -O2 --sysroot=src/work/wasm-install/sysroot
# Link with libc:
src/work/wasm-install/bin/lld -flavor wasm -entry=main --allow-undefined-file=src/work/wasm-install/sysroot/lib/wasm.syms -o src/work/torture-lld-musl/20020510-1.c.o.wasm src/work/torture-o/20020510-1.c.o src/work/wasm-install/sysroot/lib/libc.a
# Or without a libc (you need to provide one somehow):
src/work/wasm-install/bin/lld -flavor wasm -entry=main --allow-undefined-file=src/work/wasm-install/sysroot/lib/wasm.syms -o src/work/torture-lld/20020510-1.c.o.wasm src/work/torture-o/20020510-1.c.o

# Or, if you want an Assembly file instead:
src/work/wasm-install/bin/clang src/work/gcc/gcc/testsuite/gcc.c-torture/execute/20020227-1.c -o src/work/torture-s/20020227-1.c.s --std=gnu89 -DSTACK_SIZE=1044480 -w -Wno-implicit-function-declaration --target=wasm32-unknown-unknown -S -O2 --sysroot=src/work/wasm-install/sysroot
# And get the binary file:
src/work/wasm-install/bin/wast2wasm src/work/torture-s2wasm/loop-6.c.s.wast -o src/work/torture-wast2wasm/loop-6.c.s.wast.wasm

完全なツールチェーンを含む、ウォーターフォールのすべてのビルドアーティファクトをダウンロードすることもできます。緑のボックスをクリックして、探しているダウンロードを見つけてください。

大胆に感じている場合は、Cで記述された既存の実装をリンクする代わりに、 JavaScriptでlibcを記述 することもできます。

「単独で」と言うとき、WebAssemblyは現在、その埋め込み(つまりJavaScript)にリンクしないと何もできないことに注意してください。 Emscriptenが従うモデル(他の人にも同じことを期待する)は、JavaScriptがマイクロカーネルであり、syscallを提供するというものです。

4
JF Bastien

LLVMは、WASIを使用してwasmへのCの直接コンパイルをサポートするようになりました。 Emscriptenはもう必要ありません。

Libcが不要な場合は、すぐにLLVMを使用できます。たとえば、ファイルfoo.cは次のようにコンパイルできます:

clang --target=wasm32 --no-standard-libraries -Wl,--export-all -Wl,--no-entry -o foo.wasm foo.c

それ以外の場合、WASI-libcプロジェクトには、使用可能なスタンドアロンlibcがあります。

LLVMを使用してCをWebAssemblyにコンパイルし、ブラウザーで実行するための完全な手順は この投稿で利用可能 です。

1
Rich Apodaca