Inside yarv2llvm(その1)
yarv2llvmがとりあえず1段落しました。今後しばらくは、大きな新機能は追加せず、こまごました機能追加とBug fixを行おうと思います。安定してきたら、多相メソッド起動を作りたいと思います。なんか思いついたら、新機能を入れるかもしれないですが。
yarv2llvmの内部構造のドキュメントが無いので、内部構造の説明を書いていこうと思います。今日はyarv2llvmの大まかな概要です。
yarv2llvmは、Ruby1.9のVM(かってYARVと呼ばれていました。以下、Ruby1.9のVMのことをYARVと(おそらく間違っていますが便利なので)呼びます)のバイトコードをllvmのbitコード列に変換するソフトウエアです。YARVとllvmはどちらも仮想計算機と呼ばれる種類のソフトウエアですが、大きな違いがあります。
- スタックベース
- 動的型付け
- 命令が高機能(ほぼRubyの1命令に対応)
このようなギャップを乗り越えるため、yarv2llvmでは次のようにしています。
- スタックベース → レジスタベース については、コンパイル時にスタック(@expstack)を用意しています。@expstackはYARVのスタックにPUSHするタイミングでPUSHし、POPするタイミングでPOPします。@expstackにPUSHする内容は、YARVのスタックでPUSHするデータに相当するデータ(PUSHデータ)を生成するllvmのbitコード列を生成するProcオブジェクトとPUSHデータの型情報です。Procオブジェクトと型情報については、訳が分からないと思いますがyarv2llvmの肝なので後で詳しく説明します。
- 動的型付け→静的型付け については、型推論によって型情報を強引に作っています。頻度は少ない(多分、ほとんどの人が考えている以上に)のですが、型が決まらないこともあってこのときは動的に型変換を行うようなコードを出します。llvmはCと同じ程度の型の制限を破る機能も持っているのでユーザの責任で動的型付けも実現できます。もちろん、常に型変換を行うコードを出してもいいのですが、そうすると実行速度はRuby1.9より大幅に上回ることは不可能じゃないかと思います。Rubyが遅い(とよく言われているような気がします、私はそうは思わないのですが)のは、ネイティブコードを生成しないからじゃなく、色々なチェックや型変換等のオーバヘッドが掛かっているからだと思います。いずれにしてもRuby 1.9は細かいところをみれば見るほど良くできています。
- 命令が低機能か高機能かはそれほど問題になりません。低機能のllvmを複数組み合わせるだけです。ただし、例外とcallccは別です。今もどうしようか考えています。
次回は、yarv2llvmの全体の流れについて説明します。yarv2llvmは2パスなのですが、1パス目が出力する中間コードが珍しい(と思うのですが良くあるのかな?)です。
続く