
Binaryenは、C ++で記述されたWebAssembly用のコンパイラおよびツールチェーンインフラストラクチャライブラリです。 WebAssemblyを簡単に、高速で、効果的にコンパイルすることを目指しています。
簡単:Binaryenには単一のヘッダーに単純なC APIがあり、JavaScriptからも使用できます。 WebAssemblyのような形式での入力を受け入れますが、それを好むコンパイラの一般的な制御フローグラフも受け入れます。
FAST :Binaryenの内部IRは、コンパクトなデータ構造を使用し、利用可能なすべてのCPUコアを使用して、完全に並列コードゲンと最適化のために設計されています。また、BinaryenのIRは、本質的にWebAssemblyのサブセットであるため、非常に簡単かつ迅速にWebAssemblyにコンパイルします。
有効:Binaryenのオプティマイザーには、コードのサイズと速度を改善できる多くのパス(後で概要を参照)があります。これらの最適化は、バイナリエンをコンパイラバックエンドとして単独で使用するのに十分な強力にすることを目的としています。焦点の1つの領域は、Webセンブリ固有の最適化(汎用コンパイラが実行できない可能性がある)にあります。これは、JavaScript、CSSなどの模倣と同様のWASMの最小化と考えることができます。これらはすべて言語固有です。
Binaryenをコンポーネントとして使用するツールチェーン(通常はwasm-optを実行している)が含まれます。
Emscripten (c/c ++)wasm-pack (錆)J2CL (Java; J2Wasm )Kotlin (Kotlin/Wasm)Dart (フラッター)wasm_of_ocaml (ocaml)これらの作品のいくつかについては、V8 WASMGCポーティングBlogPostのツールチェーンアーキテクチャ部分を参照してください。
ライブラリとしてBinaryenを使用するコンパイラは次のとおりです。
AssemblyScriptwasm2jsAsteriusGrainは、WebAssemblyにコンパイルしますBinaryenはまた、できる一連のツールチェーンユーティリティを提供します
参加に興味がある場合は、貢献指示を参照してください。
Binaryenの内部IRは設計されています
Binaryen IRとWebAssembly言語にはいくつかの違いがあります。
catchブロックでのみサポートされています)は、 popサブ発現として表されます。--generate-stack-ir --print-stack-ir実行できます。これは、WASMパーサーに対して有効であることが保証されています。)ref.funcがアドレスを撮影する関数をテーブル内にあるか、 (elem declare func $..)で宣言する必要がある必要があります。 Binaryenは必要に応じてそのデータを放出しますが、IRでそれを表しません。つまり、IRは、関数の参照の宣言について考える必要なく、取り組むことができます。local.getが検証するためにlocal.setによって構造的に支配されなければなりません(nullのデフォルト値を読み取らないことを保証します)。 WASM仕様に沿っているにもかかわらず、あなたが気付くかもしれないいくつかの小さな詳細があります:Blockは、検証を妨げません。無名のブロックがバイナリ形式に放出されることはありません(コンテンツを放出するだけです)ので、非脆弱な地元の人々の目的でそれらを無視します。その結果、Binaryenによって放出されたWASMテキストを読むと、仕様ごとに検証されてはならないコードと思われるものが表示されます(WASMテキストパーサーでは検証できない場合があります)が、バイナリ形式ではその違いは存在しません(Binaryenによって放出されるバイナリは、もちろん、バグのために常に機能します)。pass.hおよびLocalStructuralDominanceクラスのrequiresNonNullableLocalFixups()フックを参照してください。ref.funcのIRタイプは常に特定の関数タイプであり、単純なfuncrefではありません。それはまた否定できません。try_tableブランチに送信するタイプにも使用されます(分岐した場合、nullは送信されません)。どちらの場合も、GCが有効になっていない場合、バイナリであまり洗練されていないタイプを発します。バイナリを読むと、IRを構築する際に、より洗練されたタイプが適用されます。br_if出力タイプは、バイナリのIRでより洗練されています。値のタイプがあります。値が流れると、WASMスペックでは、洗練されていないブランチターゲットのタイプです。ここでより洗練されたタイプを使用すると、すべてのタイプ情報を使用して、可能な限り最良の方法で最適化することが保証されますが、一部の往復操作は少し違うように見えることを意味します。特に、タイプがバイナリエンIRでより洗練されているbr_ifを発すると、その直後にキャストを発し、出力がWASM仕様に適切なタイプを持つようにします。まれな場合には、数バイトの余分なサイズが発生する可能性があります( br_if値が使用されていない一般的なケースでは、このオーバーヘッドを回避します)。stringview_wtf16など)をref.castを使用してキャストできます。これにより、 ref.castすべての場所で常に使用できるようになります(そして、可能な限りOptimizerでref.as_non_nullに下げられます)。 StringRef Specはこれを許可していないようであり、バイナリライターがref.as_non_nullに非微細なタイプに文字列ビューをキャストするref.castを置き換えることを修正するために修正します。 No-opである文字列ビューのref.cast 、完全にスキップされます。その結果、一部のコーナーケースでは、往復変換(wasm => binaryen ir => wasm)が少し変更されることに気付くかもしれません。
src/wasm-stack.hを参照)。 Stack IRは、WebAssemblyのバイナリ形式のスタックマシン形式に合わせて調整された多数の最適化を可能にします(ただし、スタックIRは、主要なバイナリIRよりも一般的な最適化の効率が低くなります)。特に最適化されたWASMファイルがある場合、単純な往復変換(最適化なしで読み取りと書き込みだけです)は、BinaryenがBinaryen IRのより構造化された形式に適合するため、より顕著な違いを引き起こす可能性があります。また、往復変換中に最適化すると、Stack IR Optsが実行され、最終的なWASMが最適化されます。ノートbinaryen irで作業する場合:
バイナリエン固有関数は、インポートへの呼び出しのように見えます。
( import " binaryen-intrinsics " " foo " ( func $foo ))それらをそのように実装することで、他のツールによって読み書きができます。また、カスタムバイナリ形式の拡張機能がある場合にこれらのツールで発生する可能性のあるバイナリ形式のエラーでの混乱エラーが回避されます。
本質的な方法は、オプティマイザーによって最適化される場合があります。そうでない場合は、WASMを出荷する前に下げる必要があります。そうしないと、存在しないインポートへの呼び出しのように見えます(そして、VMはそのインポートに適切な値を持たないというエラーが表示されます)。その最終低下は自動的に行われません。ユーザーが複数の最適化ステップのパイプラインを持っている可能性があるため、または局所的な実験を行う可能性があるため、ユーザーが最適化を終了することをツールが知らないため、ツールは最適化/削減などを行うことができるため、ツールが最適化を終了することをツールが明示的に実行する必要があります。ユーザーのみが「最終的な」前に最終的な最適化が発生し、準備ができていることを知っているため、ツールは最適化を完了することをツールが知らないため、明示的にパスを実行する必要があります。一般的に、最終低下後にいくつかの追加の最適化が可能になる可能性があるため、通常、本質的に一度最適化してから、それらを下げてから最適化してから、その後最適化することであることに注意してください。
wasm-opt input.wasm -o output.wasm -O --intrinsic-lowering -Oそれぞれの内因性は、オプティマイザーがそれを許可していることと、最終低下がそれを変えるものを含むセマンティクスを定義します。詳細な定義については、Intrinsics.hを参照してください。ここに簡単な要約が表示されます:
call.without.effects : call_refと同様に、パラメーターを受信し、コールする関数への参照と、それらのパラメーターでその機能を呼び出します。 このリポジトリにはbin/に次のツールを構築するコードが含まれています(建物の指示を参照):
wasm-opt :WebAssemblyをロードし、Binaryen IRを実行します。wasm-as :WebAssemblyをテキスト形式で組み立て(現在はS-Expressionフォーマット)、バイナリ形式(Binaryen IRを介して)に組み立てます。wasm-dis :バイナリ形式でWebAssemblyをテキスト形式に組み立てる(Binaryen IRを使用)。wasm2js :WebAssembly-to-JSコンパイラ。これは、emscriptenがWebAssemblyの代替としてJavaScriptを生成するために使用されます。wasm-reduce :WebAssemblyファイル用のテストケースリデューサー。何らかの理由で興味深いWASMファイル(特定のVMをクラッシュさせるなど)を考えると、WASM-Reduceは同じプロパティを持つ小さなWASMファイルを見つけることができます。詳細については、ドキュメントを参照してください。wasm-shell :WebAssemblyコードをロードおよび解釈できるシェル。また、仕様テストスイートを実行できます。wasm-emscripten-finalize :LLVM+LLDによって生成されたWASMバイナリを取得し、それを上回るEmscripten固有のパスを実行します。wasm-ctor-eval :コンパイル時に関数(または関数の一部)を実行できるツール。wasm-merge :複数のWASMファイルを単一のファイルにマージし、対応するインポートをエクスポートに接続します。 JSのバンドラーのように、しかしWASMのための。wasm-metadce :Moduleの使用方法に依存する柔軟な方法でWASMファイルの一部を削除するツール。binaryen.js :WASMモジュールを作成および最適化するためのBiniryenメソッドを公開するスタンドアロンJavaScriptライブラリ。ビルドについては、npmのbinaryen.jsを参照してください(またはGithubまたはunpkgから直接ダウンロードしてください)。最小要件:node.js V15.8またはChrome V75またはFirefox V78。すべてのバイナリツールは決定論的です。つまり、同じ入力が与えられると、常に同じ出力を取得する必要があります。 (そうでなければ動作するケースが表示されている場合は、問題を提出してください。)
それぞれの使用手順は以下にあります。
Binaryenには、WebAssemblyをより小さく速くするために、多くの最適化パスが含まれています。 wasm-optを使用してバイナリオプティマイザーを実行できますが、 wasm2jsやwasm-metadceなどの他のツールを使用しながら実行することもできます。
addDefaultFunctionOptimizationPassesなどの関数によって設定されます。wasm-opt --help参照してください。それが何をするかの詳細については、各最適化パスを参照してくださいが、ここに関連するもののいくつかの簡単な概要があります。
if 2つの場合)。block外側のブロックにマージして、数を減らします。local.setを削除します。 (Coalescelocalsと重複しています。これにより、他のすべての作業が行われずに言及された特定の操作が達成されます。したがって、最適化パイプラインの他の場所で役立ちます。)brまたはbr_tableを取り除く可能性のあるさまざまな変換を含むキー「マイナーコントロールフロー最適化」パス(可能な場合if中央にbrを使用してblockを回すなど)。local.get/set/tee " optimizationパス。セットを交換して、セットの値をGet(およびTeeを作成する)に移動することなどを行います。また、値を渡すためにローカルを使用する代わりにblock/if/loop戻り値を作成します。if 、副作用のない一定の値のドロップ、一人の子供を持つblockなどを削除するなどを行います。上記の「LTO」は、最適化が複数の関数にわたって機能するという点でリンク時間最適化のようなものであることを意味しますが、ある意味では、最終リンクされたWASMで通常実行されるため、Binaryenは常に「LTO」です。
バイナリエンオプティマイザーの高度な最適化手法には、SSAIFICITION、FLAT IR、およびSTACK/POPPY IRが含まれます。
オプティマイザーを効果的に使用する方法の詳細については、Optimizer Cookbook Wikiページを参照してください。
Binaryenには、JavaScript、Asyncifyなどの合法化など、最適化以外のことを行うさまざまなパスも含まれています。
BinaryenはGitサブモジュールを使用します(GTESTのためだけに書く時点で)。したがって、ビルドする前にサブモジュールを初期化する必要があります。
git submodule init
git submodule updateその後、cmakeで構築できます。
cmake . && make C ++ 17コンパイラが必要です。 MacOSでは、たとえばbrew install cmake cmakeを介してCmakeをインストールする必要があります。 ninja発電機として使用することもできることに注意してください: cmake -G Ninja . && ninja 。
GTESTの依存関係を回避するために、 -DBUILD_TESTS=OFFをcmakeに渡すことができます。
binaryen.jsは、sdkを介してインストールできるemscriptenを使用して構築できます。
emcmake cmake . && emmake make binaryen_jsemcmake cmake -DBUILD_FOR_BROWSER=ON . && emmake makeMicrosoft Visual Studioインストーラーを使用して、「CMake用のVisual C ++ツール」コンポーネントをインストールします。
プロジェクトを生成します:
mkdir build
cd build
" %VISUAL_STUDIO_ROOT%Common7IDECommonExtensionsMicrosoftCMakeCMakebincmake.exe " ..Visual StudioインストールへのパスでVisual_Studio_Rootを置き換えます。 Visual Studioビルドツールを使用している場合、パスは「C:プログラムファイル(x86) Microsoft Visual Studio 2017 BuildTools」になります。
開発者コマンドプロンプトから、目的のプロジェクトを構築します。
msbuild binaryen.vcxprojCmakeは、すべてのプロジェクトを便利に構築するために、「all_build.vcxproj」という名前のプロジェクトを生成します。
ビルドは、emscripten、 wasm-packなどのBinaryenを使用するさまざまなツールチェーンによって配布されます。Githubには公式リリースもあります。
https://github.com/webassembly/binaryen/releases
現在、次のプラットフォームのビルドが含まれています。
Linux-x86_64Linux-arm64MacOS-x86_64MacOS-arm64Windows-x86_64Node.js (実験):javascript+webAssemblyへのwasm-optのポート。 node.jsが実行されている任意のプラットフォームでwasm-optのネイティブビルドのドロップイン置換としてnode wasm-opt.jsを実行します。 node.js 18+が必要です(WASM EHおよびWASMスレッドの場合)。 (このビルドは、Deno、Bun、またはその他のJavaScript+WebAssembly環境でも実行される場合がありますが、Node.jsでのみテストされています) 走る
bin/wasm-opt [.wasm or .wat file] [options] [passes, see --help] [--help]WASM Optimizerは、入力としてWebAssemblyを受信し、変換を実行することができます。たとえば、試してみてください
bin/wasm-opt test/lit/passes/name-types.wast -all -S -o -これにより、テストスイートにテストケースの1つが出力されます。変換パスを実行するには、試してみてください
bin/wasm-opt test/lit/passes/name-types.wast --name-types -all -S -o - name-types Passは、各タイプに名前があり、非常に長いタイプ名を変更します。 2つのコマンドの出力を比較することにより、変換が引き起こす変更を確認できます。
独自の変換パスをシェルに追加し、 .cppファイルをsrc/passesに追加してシェルを再構築するのは簡単です。たとえば、コードを使用して、 name-typesパスをご覧ください。
いくつかのメモ:
bin/wasm-opt --helpオプションとパスの完全なリストについては、ヘルプ。--debugを渡すと、いくつかのデバッグ情報が発生します。個々のデバッグチャネル( #define DEBUG_TYPE xxxを介してソースコードで定義)は、コンマ分離された文字列のリストとして渡すことで有効にできます。例: bin/wasm-opt --debug=binary 。これらのデバッグチャネルは、 BINARYEN_DEBUG環境変数を介して有効にすることもできます。走る
bin/wasm2js [input.wasm file]これにより、JavaScriptがコンソールに印刷されます。
たとえば、試してみてください
bin/wasm2js test/hello_world.watその出力には含まれています
function add ( x , y ) {
x = x | 0 ;
y = y | 0 ;
return x + y | 0 | 0 ;
}の翻訳として
( func $add (; 0 ;) ( type $0 ) ( param $x i32 ) ( param $y i32 ) ( result i32 )
( i32.add
( local.get $x )
( local.get $y )
)
)WASM2JSの出力はES6モジュール形式です - 基本的に、WASMモジュールをES6モジュールに変換します(古いブラウザーとnode.jsバージョンで実行するには、babelなどを使用してES5に変換できます)。 Hello World Watを呼んでいる例を見てみましょう。まず、メインJSファイルを作成します。
// main.mjs
import { add } from "./hello_world.mjs" ;
console . log ( 'the sum of 1 and 2 is:' , add ( 1 , 2 ) ) ;これを実行します(ES6モジュールサポートを備えた新しいNode.jsが必要であることに注意してください):
$ bin/wasm2js test/hello_world.wat -o hello_world.mjs
$ node --experimental-modules main.mjs
the sum of 1 and 2 is: 3wasm2jsの出力では、物事は留意してください。
-Oまたは別の最適化レベルを使用して、リリースビルドの最適化でwasm2jsを実行する必要があります。パイプライン全体(WASMおよびJS)全体に沿って最適化されます。しかし、JSミニファーがWhitespaceをMinifyするようにすべてを実行するわけではないため、その後は通常のJSミニファーを実行する必要があります。 wasm-ctor-evalコンパイル時に関数、またはそれらの一部を実行します。その後、ランタイム状態をWASMにシリアル化します。これは「スナップショット」を取るようなものです。 WASMが後でロードされてVMで実行されると、すでに実行されている作業を再入力することなく、その時点から実行を継続します。
たとえば、この小さなプログラムを考えてみましょう。
( module
;; A global variable that begins at 0.
( global $global ( mut i32 ) ( i32.const 0 ))
( import " import " " import " ( func $import ))
( func " main "
;; Set the global to 1.
( global.set $global
( i32.const 1 ))
;; Call the imported function. This *cannot* be executed at
;; compile time.
( call $import )
;; We will never get to this point, since we stop at the
;; import.
( global.set $global
( i32.const 2 ))
)
)このようなコンパイル時間でその一部を評価できます。
wasm-ctor-eval input.wat --ctors=main -S -o -これは、実行したい単一の関数があることを示しています(「ctor」は「グローバルコンストラクター」、プログラムのエントリポイントの前に実行されるコードから来る名前)、そしてそれをテキストとしてstdoutに印刷することです。その結果、これは次のとおりです。
trying to eval main
...partial evalling successful, but stopping since could not eval: call import: import.import
...stopping
(module
(type $none_ = > _none (func))
(import " import " " import " (func $import ))
(global $global (mut i32) (i32.const 1))
(export " main " (func $0 _0))
(func $0 _0
(call $import )
(global.set $global
(i32.const 2)
)
)
)ロギングは、すべてのmain()の一部を評価していることを示していますが、予想通りではありません。最初のglobal.getを評価できますが、インポートされた関数への呼び出しで停止します(WASMが実際にVMで実行されるときにその関数が何であるかわからないため)。出力がWASMでは、グローバルの値が0から1に更新され、最初のglobal.getが削除されたことに注意してください。WASMは、VMで実行すると、 wasm-ctor-evalに実行され続けるという状態にあります。
この小さな例では、少量の作業を保存しました。どれだけの作業を節約できるかは、プログラムによって異なります。 (それは純粋な計算を前もって行い、できるだけ遅く輸入への呼び出しを残すのに役立ちます。)
前述のように、 wasm-ctor-evalの名前はグローバルコンストラクター関数に関連していることに注意してくださいが、ここで実行できるものには制限はありません。その内容が適切な場合、WASMからのエクスポートは実行できます。たとえば、emscriptenでは、可能な場合はmain()でwasm-ctor-evalが実行されます。
wasm-merge WASMファイルを組み合わせます。たとえば、複数のツールチェーンのWASMファイルを使用するプロジェクトがあると想像してください。 1つのWASMファイルでは、モジュール間の呼び出しがモジュール内の通常の呼び出しになるため、速度とサイズを改善する可能性があるため、モジュール間の呼び出しがモジュール内の通常の呼び出しになるためです。
wasm-merge通常のWASMファイルで動作します。 wasm-ldはWASMオブジェクトファイルで動作するため、その点でwasm-ld LDとは異なります。 wasm-merge少なくとも1つのツールチェーンがWASMオブジェクトファイルを使用しないマルチツールチェーンの状況で役立ちます。
たとえば、これら2つのWASMファイルがあると想像してください。
;; a.wasm
( module
( import " second " " bar " ( func $second.bar ))
( export " main " ( func $func ))
( func $func
( call $second.bar )
)
) ;; b.wasm
( module
( import " outside " " log " ( func $log ( param i32 )))
( export " bar " ( func $func ))
( func $func
( call $log
( i32.const 42 )
)
)
)ローカルドライブのファイル名はa.wasmとb.wasmですが、マージ /バンドリングの目的では、1つ目は"first"として知られており、2番目は"second"として知られています。つまり、最初のモジュールの"second.bar"のインポートが、2番目のモジュールの関数$funcを呼び出すことを望んでいます。これがそのためのwasm-mergeコマンドです:
wasm-merge a.wasm first b.wasm second -o output.wasm最初のWASMファイル、次にその名前、次に2番目のWASMファイル、次にその名前を付けます。マージされた出力はこれです:
( module
( import " outside " " log " ( func $log ( param i32 )))
( export " main " ( func $func ))
( export " bar " ( func $func_2 ))
( func $func
( call $func_2 )
)
( func $func_2
( call $log
( i32.const 42 )
)
)
) wasm-merge 2つのファイルを1つに組み合わせて、機能、インポートなどをすべてマージし、名前の競合を修正し、対応するインポートをエクスポートに接続しました。特に、 $func $func_2呼び出す方法に注意してください。これはまさに私たちが望んでいたものです: $func_2 2番目のモジュールの関数です(名前の衝突を避けるために変更されました)。
この例のWASM出力は、追加の最適化の恩恵を受ける可能性があることに注意してください。まず、 $func_2への呼び出しを簡単にインラインすることができるため、 wasm-opt -O3を実行してそれを行うことができます。また、すべての輸入品と輸出を必要としない場合があります。そのため、WASM-Metadceを実行できます。優れたワークフローは、 wasm-mergeを実行してから、 wasm-metadce実行してから、 wasm-optで終了することです。
wasm-merge 、「JSバンドラー」という意味で、WASMの意味で、WASMファイルのバンドラーのようなものです。つまり、上記のWASMファイルを使用して、実行時にこのJSコードをインスタンス化して接続するためのJSコードがあると想像してください。
// Compile the first module.
var first = await fetch ( "a.wasm" ) ;
first = new WebAssembly . Module ( first ) ;
// Compile the first module.
var second = await fetch ( "b.wasm" ) ;
second = new WebAssembly . Module ( second ) ;
// Instantiate the second, with a JS import.
second = new WebAssembly . Instance ( second , {
outside : {
log : ( value ) => {
console . log ( 'value:' , value ) ;
}
}
} ) ;
// Instantiate the first, importing from the second.
first = new WebAssembly . Instance ( first , {
second : second . exports
} ) ;
// Call the main function.
first . exports . main ( ) ; wasm-mergeが行うことは、基本的にJSが行うことです。エクスポートにインポートを接続し、提供したモジュール名を使用して名前を解決します。つまり、 wasm-merge実行することにより、ランタイムからコンパイルされるモジュールを接続する作業を動かしています。その結果、 wasm-mergeを実行した後、同じ結果を得るにはJSがはるかに少ない必要があります。
// Compile the single module.
var merged = await fetch ( "merged.wasm" ) ;
merged = new WebAssembly . Module ( merged ) ;
// Instantiate it with a JS import.
merged = new WebAssembly . Instance ( merged , {
outside : {
log : ( value ) => {
console . log ( 'value:' , value ) ;
}
}
} ) ;
// Call the main function.
merged . exports . main ( ) ;マージされたWASMを取得してコンパイルし、JSインポートを提供する必要がありますが、2つのWASMモジュールを接続する作業はもう必要ありません。
デフォルトでは、エクスポート名が重複している場合はwasm-mergeエラー。つまり、 wasm-merge 、外部から表示されていないため(コードはまだ同じように動作します)、輸出と改名された場合、外部を変更するために変更するために変更する必要があるため、wasm-mergeは重複する関数名などを自動的に処理します。
エクスポートの名前を変更したい場合は、 --rename-export-conflictsでwasm-merge実行します。後のエクスポートには、以前のエクスポートと重複しないように、サフィックスが追加されます。接尾辞は決定論的であるため、それらが何であるかを確認したら、外側から呼び出すことができます。
別のオプションは、競合する名前がある後のエクスポートを単純にスキップするだけで--skip-export-conflictsを使用することです。たとえば、これは、最初のモジュールが外部と対話する唯一のモジュールであり、後のモジュールが最初のモジュールと対話する場合に役立ちます。
wasm-mergeマルチメモリとマルチテーブル機能を使用しています。つまり、複数の入力モジュールにそれぞれメモリがある場合、出力WASMにはいくつかの記憶があり、マルチメモリ機能に依存します。つまり、古いWASM VMがWASMを実行できない可能性があります。 (このような古いVMの回避策としてwasm-opt --multi-memory-loweringを実行することができます。
./check.py (またはpython check.py )は、 wasm-shell 、 wasm-optなどをtest/のテストケースで実行し、出力を検証します。
check.pyスクリプトはいくつかのオプションをサポートしています。
./check.py [--interpreter = /path/to/interpreter] [TEST1] [TEST2].../check.py --list-suites 。emccまたはnodejs必要です。ツールが見つからない場合は実行されません。警告が表示されます。tests/specの上流からのテスト、Gitサブモジュールがあります。 running ./check.pyはそれらを更新する必要があります。 Legacy WASM-OPTテストを徐々に移植して、 litとfilecheck変更するときに使用しようとしていることに注意してください。出力が廃棄さpasses passesテストの場合、これはscripts/port_passes_tests_to_lit.pyで自動的に実行できます。
LITテストの場合、テストの期待(チェックライン)は、バイナリの変更が行われると、多くの場合自動的に更新できます。 scripts/update_lit_checks.pyを参照してください。
ほとんどの場合、非明かりのテストも自動的に更新できます。 scripts/auto_update_tests.pyを参照してください。
./third_party/setup.py [mozjs | v8 | wabt | all] (またはpython third_party/setup.py )Spidermonkey JSシェル、V8 JSシェル、WABTのような必要な依存関係をthird_party/にインストールします。他のスクリプトは、インストール時にこれらを自動的にピックアップします。
pip3 install -r requirements-dev.txtを実行して、 litテストの要件を取得します。 $PATH (linux、 ~/.local/bin )にロケーションpipをインストールする必要があることに注意してください。
./scripts/fuzz_opt.py [--binaryen-bin = build/bin] (またはpython scripts/fuzz_opt.py )は、バグが可能になるまで、ランダムなパスでランダム入力でさまざまなファジングモードを実行します。すべての詳細については、Wikiページを参照してください。
Binaryen can read and write source maps (see the -ism and -osm flags to wasm-opt ). It can also read and read source map annotations in the text format, that is,
;; @ src.cpp:100:33
( i32.const 42 ) That 42 constant is annotated as appearing in a file called src.cpp at line 100 and column 33 . Source maps and text format annotations are interchangeable, that is, they both lead to the same IR representation, so you can start with an annotated wat and have Binaryen write that to a binary + a source map file, or read a binary + source map file and print text which will contain those annotations.
The IR representation of source map info is simple: in each function we have a map of expressions to their locations. Optimization passes should update the map as relevant. Often this "just works" because the optimizer tries to reuse nodes when possible, so they keep the same debug info.
The text format annotations support a shorthand in which repeated annotations are not necessary. For example, children are tagged with the debug info of the parent, if they have no annotation of their own:
;; @ src.cpp:100:33
( i32.add
( i32.const 41 ) ;; This receives an annotation of src.cpp:100:33
;; @ src.cpp:111:44
( i32.const 1 )
)The first const will have debug info identical to the parent, because it has none specified, and generally such nesting indicates a "bundle" of instructions that all implement the same source code.
Note that text printing will not emit such repeated annotations, which can be confusing. To print out all the annotations, set BINARYEN_PRINT_FULL=1 in the environment. That will print this for the above add :
[i32] ;; @ src.cpp:100:33
( i32.add
[i32] ;; @ src.cpp:100:33
( i32.const 41 )
[i32] ;; @ src.cpp:111:44
( i32.const 1 )
) (full print mode also adds a [type] for each expression, right before the debug location).
The debug information is also propagated from an expression to its next sibling:
;; @ src.cpp:100:33
( local.set $x
( i32.const 0 )
)
( local.set $y ;; This receives an annotation of src.cpp:100:33
( i32.const 0 )
) You can prevent the propagation of debug info by explicitly mentioning that an expression has not debug info using the annotation ;;@ with nothing else:
;; @ src.cpp:100:33
( local.set $x
;; @
( i32.const 0 ) ;; This does not receive any annotation
)
;; @
( local.set $y ;; This does not receive any annotation
( i32.const 7 )
) This stops the propagatation to children and siblings as well. So, expression (i32.const 7) does not have any debug info either.
There is no shorthand in the binary format. That is, roundtripping (writing and reading) through a binary + source map should not change which expressions have debug info on them or the contents of that info.
The source maps format defines a mapping using segments , that is, if a segment starts at binary offset 10 then it applies to all instructions at that offset and until another segment begins (or the end of the input is reached). Binaryen's IR represents a mapping from expressions to locations, as mentioned, so we need to map to and from the segment-based format when writing and reading source maps.
That is mostly straightforward, but one thing we need to do is to handle the lack of debug info in between things that have it. If we have ABC where B lacks debug info, then just emitting a segment for A and C would lead A 's segment to also cover B , since in source maps segments do not have a size - rather they end when a new segment begins. To avoid B getting smeared in this manner, we emit a source maps entry to B of size 1, which just marks the binary offset it has, and without the later 3 fields of the source file, line number, and column. (This appears to be the intent of the source maps spec, and works in browsers and tools.)
Binaryen also has optional support for DWARF. This primarily just tracks the locations of expressions and rewrites the DWARF's locations accordingly; it does not handle things like re-indexing of locals, and so passes that might break DWARF are disabled by default. As a result, this mode is not suitable for a fully optimized release build, but it can be useful for local debugging.
Binaryen's name was inspired by Emscripten 's: Emscripten's name suggests it converts something into a script - specifically JavaScript - and Binaryen's suggests it converts something into a binary - specifically WebAssembly . Binaryen began as Emscripten's WebAssembly generation and optimization tool, so the name fit as it moved Emscripten from something that emitted the text-based format JavaScript (as it did from its early days) to the binary format WebAssembly (which it has done since WebAssembly launched).
"Binaryen" is pronounced in the same manner as "Targaryen".
はい、そうです。 Here's a step-by-step tutorial on how to compile it under Windows 10 x64 with with CMake and Visual Studio 2015 . However, Visual Studio 2017 may now be required. Help would be appreciated on Windows and OS X as most of the core devs are on Linux.