開発日記

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

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

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

今日はYARVバイトコードからLIRへの変換をサンプルコードを用いて説明する.

実行の流れ

まずはRubyプログラムのYARVバイトコードへのコンパイルについて概要を述べておく.

CRubyはRubyプログラムを読み込むとYARVバイトコードコンパイルし,そのバイトコードを実行する.

RuJITはRubyが実行した分岐命令に対してトレース候補の選択,コンパイルを行う.

 

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

サンプルコード

今日はCRubyのbenchmarkコードの中からbm_vm2_method.rbを実行していきます.

def m; nil; end
i = 0
while i<6_000_000
   i += 1
  m; m;
end

Rubyプログラムは以下のYARVバイトコードに変換されます.

== disasm: <RubyVM::InstructionSequence:<main>@bm_vm2_method.rb>========
== catch table
| catch type: break st: 0026 ed: 0054 sp: 0000 cont: 0054
| catch type: next st: 0026 ed: 0054 sp: 0000 cont: 0023
| catch type: redo st: 0026 ed: 0054 sp: 0000 cont: 0026
|------------------------------------------------------------------------
local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
[ 2] i
0000 trace 1 ( 1)
0002 putspecialobject 1
0004 putspecialobject 2
0006 putobject :m
0008 putiseq m
0010 opt_send_without_block <callinfo!mid:core#define_method, argc:3, ARGS_SIMPLE>
0012 pop
0013 trace 1 ( 5)
0015 putobject_OP_INT2FIX_O_0_C_
0016 setlocal_OP__WC__0 2
0018 trace 1 ( 6)
0020 jump 45
0022 putnil
0023 pop
0024 jump 45
// loop header [1]
0026 trace 1 ( 7)
0028 getlocal_OP__WC__0 2
0030 putobject_OP_INT2FIX_O_1_C_
0031 opt_plus <callinfo!mid:+, argc:1, ARGS_SIMPLE>
0033 setlocal_OP__WC__0 2
0035 trace 1 ( 8)
0037 putself
0038 opt_send_without_block <callinfo!mid:m, argc:0, FCALL|VCALL|ARGS_SIMPLE>
0040 pop
0041 getlocal_OP__WC__0 2 ( 6)
0043 putobject 6000000
0045 opt_lt <callinfo!mid:<, argc:1, ARGS_SIMPLE>
0047 branchif 26 // ← [2]
0049 putnil
0050 leave

== disasm: <RubyVM::InstructionSequence:m@bm_vm2_method.rb>=============
0000 trace 8 ( 1)
0002 putnil
0003 trace 16 ( 3)
0005 leave

[2]が複数回(RuJITのデフォルト設定では2回)実行されるとRuJITは[1]から[2]までのパスを頻繁に実行されるパスとして識別し,トレース記録を開始します.

 

トレースの記録はバイトコードの実行と並行して行われ,loop headerのバイトコードの実行するとトレース記録は終了しLIR最適化が行われる.
今回得られたLIRコードは以下のようなコードとなった.

---------------
BB0 (pc=(nil)) succ=[BB1]


0000 0 StackPop
0001 0 GuardTypeNil Exit:0x7f0de99cff38 R:0000
0006 0 LoadConstFixnum Val:3
0030 0 LoadConstNil
0057 0 LoadConstFixnum Val:b71b01
0002 0 Jump TargetBB:bb:1
---------------
BB1 (pc=0x7f0de99cfe50) pred=[BB5,BB0] succ=[BB2]

[self=v21,(0, 2)=v19]
0003 0 Trace flag:1
0004 0 EnvLoad Level:0 Index:2
0005 0 StackPush Val:0004
0007 0 StackPush Val:0006
0008 0 StackPop
0009 0 StackPop
0010 0 StackPush Val:0004
0011 0 StackPush Val:0006
0012 0 GuardMethodRedefine Exit:0x7f0de99cfe78 klass_flag:1 bop:0
0013 0 GuardTypeFixnum Exit:0x7f0de99cfe78 R:0004
0014 0 StackPop
0015 0 StackPop
0016 0 FixnumAddOverflow LHS:0004 RHS:0006
0017 0 StackPush Val:0016
0018 0 StackPop
0019 0 EnvStore Level:0 Index:2 Val:0016
0020 0 Trace flag:1
0021 0 LoadSelf
0022 0 StackPush Val:0021
0023 0 StackPop
0024 0 StackPush Val:0021
0025 0 GuardMethodCache Exit:0x7f0de99cfeb0 R:0021 ci:0x7f0de99cfd10
0026 0 StackPop
0027 0 Jump TargetBB:bb:2
---------------
BB2 (pc=0x7f0de99cfeb0) pred=[BB1] succ=[BB3]
[self=v21,(0, 2)=v19]
[self=v21,(0, 2)=v19]
0028 0 InvokeMethod PC:0x7f0de99cfeb0 ci:0x7f0de99cfd10 block:-001 argc:1 argv: 0021
0029 0 Trace flag:8
0031 0 StackPush Val:0030
0032 0 Trace flag:16
0033 0 StackPop
0034 0 FramePop
0035 0 Jump TargetBB:bb:3
---------------
BB3 (pc=0x7f0de99ce188) pred=[BB2] succ=[BB4]
[self=v21,(0, 2)=v19]
[self=v39,(0, 2)=v19]
0036 0 StackPush Val:0030
0037 0 StackPop
0038 0 Trace flag:1
0039 0 LoadSelf
0040 0 StackPush Val:0039
0041 0 StackPop
0042 0 StackPush Val:0039
0043 0 GuardMethodCache Exit:0x7f0de99cfee0 R:0039 ci:0x7f0de99d16f0
0044 0 StackPop
0045 0 Jump TargetBB:bb:4
---------------
BB4 (pc=0x7f0de99cfee0) pred=[BB3] succ=[BB5]
[self=v39,(0, 2)=v19]
[self=v39,(0, 2)=v19]
0046 0 InvokeMethod PC:0x7f0de99cfee0 ci:0x7f0de99d16f0 block:-001 argc:1 argv: 0039
0047 0 Trace flag:8
0048 0 StackPush Val:0030
0049 0 Trace flag:16
0050 0 StackPop
0051 0 FramePop
0052 0 Jump TargetBB:bb:5
---------------
BB5 (pc=0x7f0de99ce188) pred=[BB4] succ=[BB1]
[self=v39,(0, 2)=v19]
[self=v39,(0, 2)=v55]
0053 0 StackPush Val:0030
0054 0 StackPop
0055 0 EnvLoad Level:0 Index:2
0056 0 StackPush Val:0055
0058 0 StackPush Val:0057
0059 0 StackPop
0060 0 StackPop
0061 0 StackPush Val:0055
0062 0 StackPush Val:0057
0063 0 GuardMethodRedefine Exit:0x7f0de99cff18 klass_flag:1 bop:7
0064 0 GuardTypeFixnum Exit:0x7f0de99cff18 R:0055
0065 0 StackPop
0066 0 StackPop
0067 0 FixnumLt LHS:0055 RHS:0057
0068 0 StackPush Val:0067
0069 0 StackPop
0070 0 GuardTypeNil Exit:0x7f0de99cff38 R:0067
0071 0 Jump TargetBB:bb:1
---------------
```

 

 

LIR上はオペランドスタック操作,ガード命令,定数定義,そして実際のプログラムの処理内容が一緒くたになって出力される.
オペランドスタック操作,ガード命令はは次の最適化フェーズで多くのコードが除去される.

---------------
BB0 (pc=(nil)) succ=[BB1]


0000 0 StackPop
0001 0 GuardTypeNil Exit:0x7f0de99cff38 R:0000
0006 0 LoadConstFixnum Val:3
0030 0 LoadConstNil
0057 0 LoadConstFixnum Val:b71b01
0002 0 Jump TargetBB:bb:1
---------------
BB1 (pc=0x7f0de99cfe50) pred=[BB5,BB0] succ=[BB3]

[self=v21,(0, 2)=v19]
0004 0 EnvLoad Level:0 Index:2
0005 0 StackPush Val:0004
0007 0 StackPush Val:0006
0012 0 GuardMethodRedefine Exit:0x7f0de99cfe78 klass_flag:1 bop:0
0013 0 GuardTypeFixnum Exit:0x7f0de99cfe78 R:0004
0014 0 StackPop
0015 0 StackPop
0016 0 FixnumAddOverflow LHS:0004 RHS:0006
0019 0 EnvStore Level:0 Index:2 Val:0016
0021 0 LoadSelf
0022 0 StackPush Val:0021
0025 0 GuardMethodCache Exit:0x7f0de99cfeb0 R:0021 ci:0x7f0de99cfd10
0026 0 StackPop
0027 0 Jump TargetBB:bb:3
---------------
BB3 (pc=0x7f0de99ce188) pred=[BB1] succ=[BB5]
[self=v21,(0, 2)=v19]
[self=v39,(0, 2)=v19]
0039 0 LoadSelf
0040 0 StackPush Val:0039
0043 0 GuardMethodCache Exit:0x7f0de99cfee0 R:0039 ci:0x7f0de99d16f0
0044 0 StackPop
0045 0 Jump TargetBB:bb:5
---------------
BB5 (pc=0x7f0de99ce188) pred=[BB3] succ=[BB1]
[self=v39,(0, 2)=v19]
[self=v39,(0, 2)=v55]
0055 0 EnvLoad Level:0 Index:2
0056 0 StackPush Val:0055
0058 0 StackPush Val:0057
0063 0 GuardMethodRedefine Exit:0x7f0de99cff18 klass_flag:1 bop:7
0064 0 GuardTypeFixnum Exit:0x7f0de99cff18 R:0055
0065 0 StackPop
0066 0 StackPop
0067 0 FixnumLt LHS:0055 RHS:0057
0070 0 GuardTypeNil Exit:0x7f0de99cff38 R:0067
0071 0 Jump TargetBB:bb:1
---------------

 

最適化によって得られたLIRは上記のとおりとなった.
この後RuJITはバックエンドにLIRを渡し機械語を生成する.