V8は、GoogleのオープンソースJavaScriptエンジンです。 Chrome、Node.js、および他の多くのアプリケーションで使用されます。 Googleの従業員Francisco Hinkelmannが作成したこの資料では、V8バイトコード形式について説明しています。 いくつかの基本的なことを理解すれば、バイトコードは非常に読みやすくなります。

V8コンパイルパイプライン
点火! スタート! Ignitionインタープリターは、その名前を「点火」と翻訳することができ、2016年からV8コンパイルパイプラインの一部となっています。V8がJavaScriptコードをコンパイルすると、パーサーは抽象構文ツリーを生成します。 構文ツリーは、JSコードの構文構造のツリービューです。 Ignitionインタープリターは、このデータ構造からバイトコードを生成します。 最適化TurboFanコンパイラは、最終的にバイトコードから最適化されたマシンコードを生成します。
V8コンパイルパイプラインV8に2つの実行モードがある理由を知りたい場合は、JSConfEUでの私の
パフォーマンスを見てください。
V8バイトコードの基本
バイトコードはマシンコードの抽象化です 。 バイトコードが物理プロセッサで使用されているのと同じ計算モデルを使用して設計されている場合、バイトコードをマシンコードにコンパイルするのは簡単です。 そのため、通訳者はしばしばマシンを登録またはスタックします。
Ignitionインタープリターは、累積レジスターを持つレジスターマシンです。
左のコードは人々にとって便利です。 右側のコード-車用V8バイトコードは、JavaScript機能を実装できる
小さなビルディングブロックと考えることができます。 V8には数百のバイトコードがあります。
Addや
TypeOfなどのステートメント、または
LdaNamedPropertyなどのプロパティをロードするためのコードがあります。 V8には、
CreateObjectLiteralや
SuspendGeneratorなど、かなり具体的なバイトコードもあります。
bytecodes.hヘッダーファイルには、V8バイトコードの完全なリストがあります。
各バイトコードは、入力および出力データをレジスタオペランドとして定義します。 Ignitionは、レジスター
r0, r1, r2, ... 、および累積レジスターを使用します。 ほとんどすべてのバイトコードはストレージレジスタを使用します。 通常の場合と似ていますが、バイトコードで明示的に示されていない点が異なります。 たとえば、
Add r1コマンドは、レジスタ
r1値を累積レジスタに格納されている値に追加します。 これにより、バイトコードが短くなり、メモリが節約されます。
多くのバイトコード名は
Ldaまたは
Sta始まります。
Ldaおよび
Staの文字
aは、累積器(累積レジスタ)の略語です。
たとえば、
LdaSmi [42]コマンド
LdaSmi [42]は、小さな整数(Small Integer、Smi)
42を累積レジスタにロードします。
Star r0コマンドは、累算レジスタにある値を
r0レジスタに書き込みます。
機能バイトコード分析
ここで、基本的な概念を検討した後、実際の関数のバイトコードを見てみましょう。
function incrementX(obj) { return 1 + obj.x; } incrementX({x: 42});
JavaScriptコードのバイトコードを表示したい場合は、
--print-bytecodeフラグを指定し
てD8またはNode.js
デバッガー (バージョン8.3以降)を呼び出し
て表示できます。 Chromeの場合、コマンドラインからキー
--js-flags="--print-bytecode"ます。 これがChromiumのキーコール
です 。
$ node --print-bytecode incrementX.js ... [generating bytecode for function: incrementX] Parameter count 2 Frame size 8 12 E> 0x2ddf8802cf6e @ StackCheck 19 S> 0x2ddf8802cf6f @ LdaSmi [1] 0x2ddf8802cf71 @ Star r0 34 E> 0x2ddf8802cf73 @ LdaNamedProperty a0, [0], [4] 28 E> 0x2ddf8802cf77 @ Add r0, [6] 36 S> 0x2ddf8802cf7a @ Return Constant pool (size = 1) 0x2ddf8802cf21: [FixedArray] in OldSpace - map = 0x2ddfb2d02309 <Map(HOLEY_ELEMENTS)> - length: 1 0: 0x2ddf8db91611 <String[1]: x> Handler Table (size = 16)
バイトコードに焦点を当てて、このデータのかなりの部分を無視できます。 ここに表示される内容の説明を示します。
LdaSmi [1]LdaSmi [1]コマンドは、定数
1を累積レジスタにロードします。
スターr0Star r0コマンドは、累積レジスタの値、つまり
1を
r0レジスタに書き込みます。
LdaNamedProperty a0、[0]、[4]LdaNamedPropertyコマンドは、名前付きプロパティ
a0を累積レジスターにロードします。
aiコンストラクトは、
incrementX()関数のi番目の引数を参照し
incrementX() 。 この例では、
a0の名前付きプロパティ、つまり
incrementX()最初の引数にアクセスします。 名前は定数
0によって決定され
0 。
LdaNamedPropertyは
0を使用して、別のテーブルで名前を検索します。
- length: 1 0: 0x2ddf8db91611 <String[1]: x>
ここで
0 xマッピングされます。 その結果、このバイトコードが
obj.xロードすることが
obj.x4オペランドは何に使用されますか? これは、
increment(x)いわゆるフィードバックベクトルのインデックスです。 フィードバックベクトルには、パフォーマンスを最適化するために使用されるランタイム情報が含まれています。
現在、レジスタの内容は次のとおりです。
r0を追加、[6]最後の命令は
r0の内容を累積レジスタに追加し、最終的な値は
43ます。 番号
6 — 、フィードバックベクトルの別のインデックスです。
戻るReturnコマンドは、累積レジスタの内容を返します。 これは、
incrementX()関数の完了です。
incrementX()原因は、累積レジスタの番号
43から始まり、この値でいくつかのアクションを実行し続けることができます。
この素材が専用のバイトコードは、V8バージョン6.2、Chrome 62、およびまだリリースされていないノード9で使用されていることに注意してください。 V8の他のバージョンでは、ここで説明したものとバイトコードにいくつかの違いがある場合があります。
まとめ
一見すると、V8バイトコードは、特に大量の追加情報が表示されている場合は、かなりわかりにくいように見えます。 ただし、Ignitionが累積レジスタを持つレジスタマシンであることがわかると、ほとんどのバイトコードの目的を理解できます。
親愛なる読者! JSプログラムのバイトコードを分析する予定ですか?