FacebookのHermes Javascript Engineについて
最近、JSエンジンが何故かいくつか出て来たのでいっちょ見て見ることに
最初はFacebookが実装したjavascriptエンジンHermes(エルメス)の実装を見てみた
面倒くさいのでコードとかは引用しない
概要
どうやらReactNativeの高速化のために実装したエンジンのようだ
ReactNative側ですでに利用できるっぽい
売りとしてはバイトコードを出力・読み込みができるのでスタートアップタイムを高速化できるということらしい
commonjs
の静的解析機能もついており今風な感じ
仕様
サポートされる仕様はhttps://github.com/facebook/hermes/blob/master/doc/Features.mdにある
サポートしている言語仕様はES5 + α
let/constやclass、ES Moduleといった機能はサポートされていない
とりあえずbabelとかts使うから動くでしょ といったところか
またReflection
やwith
、Symbol.species
といったものは今後もサポートしないらしい
Function.prototype.toString
もソースコードを返さないなど、割と必要ないものはバッサリ切った感じ
React Navtive
のためと言っているのでいいのかな
ビルド
成果物が結構あるのに解説があんまないので困る
以下が成果物
- bin/hermes
- ソースコードから実行する場合はこのバイナリ
- bin/hermesc
- バイトコードを生成する場合はこれ
- bin/hdb
- デバッガ
- bin/hermes-repl
- repl
- bin/hbcdump
- バイトコードのダンパー
- bin/hbc-deltaprep
- バイトコードのフォーマットを差分形式に変換する
- bin/hbc-diff
- バイトコードのdiff
- bin/hbc-attribute
中身
とりあえず概要をみてみた
以下が基本的なパスとなりそう
ソースコード => AST => IR => (最適化) => バイトコード => 実行
またバイトコードを読み込むことで
バイトコード => 実行
のパスもある
AST
Node
があってBNF定義に近いASTがある
要は普通のAST実装
ただdecoratorで実装しているっぽいのでちょっと読みづらい
Visitorパターンでトラバーサルする
IR
ASTが生成されたらIRを生成する
hermesのIRはCFGも兼ねるグラフとなっている
このIRは結構低レベルでInstructionレベルまで表現している
バイトコード
IRから変換して生成される
OPコードは1byteでOperandは可変長
VM
バイトコードを実行するVMはレジスタベースの仮想マシン
教科書通りGCC拡張のアドレスへのgotoとLabelアドレスの取得機能でループ無しで実装されている
ただしサポートされないコンパイラ向けにループとSwitch構文での実行機能も持っている
レジスタ
VMで利用されるレジスタは一応無限の仮想レジスタとなっているが、単純なリニア生存区間解析をCFG上で行っている
一応無制限と書いたのは、Bytecodeのレジスタインデックスが1byteしか受けつけないので、実質256までしかレジスタ割付ができないからである
https://github.com/facebook/hermes/blob/master/doc/Design.mdに書いてあるが、Facebook調べでは256以上のレジスタを使う関数は
見当たらなかったらしい(ので大丈夫ということか)
ABI
Facebook HermesはNaN-Boxingでオブジェクトを表現している
NaN-Boxingとは64bitフロートのNaN定義を利用したポインタと数値の表現方法
NaNは上位17bitが1であれば下位のBitがどんな値でも問題ないので、それを利用してポインタやタグ情報を埋め込んでいる
ただし64bitシステムだとポインタは64bit利用するので埋め込めないように見えるのだが、現状64bitシステムであっても全ての領域は使い切っていないので問題なく収まる
(Linux x86-64で48bitまで利用と書いてあった)
ただし、そこまで考えなくてもVMのヒープが47bitで収めきればいいだけなので、そこまで問題は起きないだろう
Hidden Class
今のご時世、ShapeやらHidden Classは高速化に必須だろう
HermesもHidden Classを実装している
V8と同じようにオブジェクトレイアウトをclassとして認識して、プロパティの追加・削除等のレイアウト変更を行うと新たなクラスが生成されるようになっている
新たにHidden Classを生成した場合はtransitionをした結果を保存して検索するような仕組みになっている
IC
複雑なインラインキャッシングは実装されていないように見える
一応プロパティのキャッシュとhidden classの保持はしているのでプロパティキャッシュ自体はしているが
正規表現
正規表現は独自バイトコードを使ったVM型のエンジンを実装している
既存のエンジンのJITに比べるとちょっと見劣りするかもしれない
最適化
主にIRに対しての最適化が実行されている
内容はIRのloweringとかinline化など
面倒だったのであんまりちゃんと読んでない
JIT
実はJITエンジンももっている がまだリリースされていなのでOFFになっている
中を見た感じx86-64から提供する様だ
ARMは...?
まとめ
React Nativeのために作ったというだけあって、色々省いてあったりバイトコードローディングなど工夫があった
後発のエンジンなだけにcommonjs対応してたり、今風な感もあって面白い
ただ、言語仕様がEcmascriptのエディションと変わってきてしまっている(withがなかったり)のはちょっと気になっている
最近感じていることだが、Ecmascriptも仕様が大きくなってきていてエンジンの実装も大変になってきているし、
使用目的がはっきりしているエンジンにとっては多分サポートする意味の無い機能(withとかnot strictなモード、多言語対応等)は省いたEcmascriptのサブセットがあってもいいのかもしれない
というか個人的にはほしい