開発日記

JITコンパイラの開発日記だった

RuJITの中間表現 LIRへのコンパイラ

Ruby向けJITコンパイラ RuJIT Advent Calendar 5日目です.

 

今日はYARVバイトコードからLIRへのコンパイラについて書きます.

YARVバイトコードからLIRへのコンパイラはsendやinvokeblockなど
複雑なバイトコード命令を除きほとんどが自動生成されるコードによって
変換されます.

 

変換ルールはyarv2lir.rbに用意されているDSLによって記述されています.
ここではローカル変数を取得するYARVバイトコードgetlocal,そして加算を扱うopt_plusのコードを紹介します.

getlocal命令はEnvLoadのLIR命令に1体1対応しているので特に難しいことはない.ここで,local.idx, local.lev, local.vはLIR命令への変換時に利用している一時変数でコードが生成された時にはC言語のローカル変数として振る舞う.つまりlocal.idx, local.leveはint型の変数,そしてlocal.vはLIRのレジスタ型の変数となっています.

245 match(:getlocal) {
246 local.idx = operand :int, 1
247 local.lev = operand :int, 2
248 local.v = emit :EnvLoad, local.lev, local.idx
249 push local.v
250 }

opt_plus命令からLIR命令の変換は実行されるパスによって生成される命令が異なる.ここではguard式によって型チェック,メソッドの再定義の検査をして対応するLIR命令の生成を行っています.

660 match(:opt_plus) {
661 local.snapshot = take_snapshot
662 local.ci = operand :CALL_INFO, 1
663 local.obj = pop
664 local.recv = pop
665 local.vobj = topn(0)
666 local.vrecv = topn(1)
667 guard(:Fixnum, local.recv, local.vrecv, local.snapshot) {
668 guard(:Fixnum, local.obj, local.vobj, local.snapshot) {
669 guard('Fixnum.+') {
670 local.v = emit :FixnumAddOverflow, local.recv, local.obj
671 push local.v
672 }
673 }
674 }
675 guard(:Float, local.recv, local.vrecv, local.snapshot) {
676 guard(:Float, local.obj, local.vobj, local.snapshot) {
677 guard('Float.+') {
678 local.v = emit :FloatAdd, local.recv, local.obj
679 push local.v
680 }
681 }
682 guard(:Fixnum, local.obj, local.vobj, local.snapshot) {
683 guard('Float.+') {
684 local.t = emit :FixnumToFloat, local.obj
685 local.v = emit :FloatAdd, local.recv, local.t
686 push local.v
687 }
688 }
689 }

ところで,RubyDSLを記述するのは初めてだったのでRubyプログラム上でローカル変数の代入操作にフックする方法がわかっていない.
このDSLは,あまり綺麗にかけたとは思っていないので,もし上手い書き方があるのなら書き直したいところである.

 

明日はサンプルコードを用いながらRubyプログラムがどのように機械語コンパイルされるかを紹介したいと思います.