これは Perl 6 Advent Calendar 2017 5日目の記事です。
こんにちは id:anatofuzです。先日大学の講義でコンパイラ読み会を行ったのですが,その際にPerl6(Rakudo,MoarVM,NQP)を読みました。
その際に MoarVMがJIT処理をしているとこはどこか? という探索を行ったのですが,その時のメモです。
ちなみにPerl6の外観を知るために,rakudo-and-nqp-internals-courseを読んだりして進めていました。
本題
今回はPerl6のサブセットであるnqpを中心に呼んでいた。
理由はほぼ処理系はPerl6のコア機能を有しているので,とりあえずここ読めればいいかな…みたいな乗りでした。
さて,まずnqpからバイナリを出力します。
使用するプログラムは,上の例題にもあったフィボナッチ数列を求めるプログラム。
sub fib($n) {
$n < 2 ?? $n !! fib($n-1) + fib($n - 2);
}
my $N := 29;
my $t0 := nqp::time_n();
my $z := fib($N);
my $t1 := nqp::time_n();
nqp::say("fib($N) = " ~ fib($N));
nqp::say("time = " ~ ($t1-$t0));
これがどの用にPerl6で解釈されていくかを確認していく
nqpのMakefileなどを確認すると
$nqp/install/bin/nqp --target=mbc --output=fib.moarvm examples/fib.nqp
どうもこの様なコマンドでbinaryを吐ける。
ここでtargetにmbcを指定しているが,これはmoarVMで,JVMの場合はjvmだった。
ここで生成された fib.moarvm
はbinaryなので当然見れない。
そこで
$MoarVM/install/bin/moar --dump fib.moarvm
などとしてmoarでdumpしてあげると
MoarVM dump of binary compilation unit:
SC_0 : E5A15988716F4E5FD2FF889497136C7E4AD482E2-1512287908.66458
Callsite_0 :
num_pos: 1
arg_count: 1
Arg 1 : positional obj
Callsite_1 :
num_pos: 1
arg_count: 1
Arg 1 : positional num
Callsite_2 :
num_pos: 2
arg_count: 2
Arg 1 : positional obj
Arg 2 : positional str
Callsite_3 :
num_pos: 0
arg_count: 0
Callsite_4 :
num_pos: 1
arg_count: 1
Arg 1 : flat obj flat
Frame_0 :
cuuid : 1
name : <mainline>
Locals :
0: loc_0_obj
1: loc_1_obj
2: loc_2_obj
3: loc_3_obj
4: loc_4_obj
5: loc_5_obj
6: loc_6_obj
7: loc_7_str
8: loc_8_obj
9: loc_9_int
10: loc_10_int
11: loc_11_obj
12: loc_12_num
13: loc_13_obj
14: loc_14_str
15: loc_15_num
16: loc_16_str
Lexicals :
0: lex_Frame_0__obj
1: lex_Frame_0__obj
2: lex_Frame_0__obj
3: lex_Frame_0__obj
Instructions :
00000 getcode loc_1_obj, Frame_1
00001 takeclosure loc_1_obj, loc_1_obj
00002 checkarity 0, -1
00003 param_sp loc_0_obj, 0
00004 paramnamesused
00005 bindlex lex_Frame_0__obj, loc_1_obj
00006 const_s loc_7_str, '$*CTXSAVE'
00007 getdynlex loc_8_obj, loc_7_str
00008 set loc_6_obj, loc_8_obj
00009 isnull loc_9_int, loc_6_obj
00010 if_i loc_9_int, label_1(00020)
00011 decont loc_8_obj, loc_6_obj
00012 const_s loc_7_str, 'ctxsave'
00013 can_s loc_10_int, loc_8_obj, loc_7_str
00014 unless_i loc_10_int, label_1(00020)
00015 decont loc_11_obj, loc_6_obj
00016 findmeth loc_8_obj, loc_11_obj, 'ctxsave'
00017 prepargs Callsite_0
00018 arg_o 0, loc_6_obj
00019 invoke_o loc_8_obj, loc_8_obj
annotation: examples/fib.nqp:6
label_1:
00020 const_i64_16 loc_9_int, 29
00021 hllboxtype_i loc_11_obj
00022 box_i loc_11_obj, loc_9_int, loc_11_obj
00023 set loc_2_obj, loc_11_obj
annotation: examples/fib.nqp:8
00024 time_n loc_12_num
00025 hllboxtype_n loc_11_obj
00026 box_n loc_11_obj, loc_12_num, loc_11_obj
00027 set loc_3_obj, loc_11_obj
annotation: examples/fib.nqp:9
00028 const_s loc_7_str, '&fib'
00029 getlexstatic_o loc_11_obj, loc_7_str
00030 decont loc_13_obj, loc_11_obj
00031 prepargs Callsite_0
00032 arg_o 0, loc_2_obj
00033 invoke_o loc_13_obj, loc_13_obj
00034 set loc_4_obj, loc_13_obj
annotation: examples/fib.nqp:10
00035 time_n loc_12_num
00036 hllboxtype_n loc_13_obj
00037 box_n loc_13_obj, loc_12_num, loc_13_obj
00038 set loc_5_obj, loc_13_obj
annotation: examples/fib.nqp:12
00039 const_s loc_7_str, 'fib('
00040 decont loc_13_obj, loc_2_obj
00041 smrt_strify loc_14_str, loc_13_obj
00042 concat_s loc_14_str, loc_7_str, loc_14_str
00043 const_s loc_7_str, ') = '
00044 concat_s loc_7_str, loc_14_str, loc_7_str
00045 const_s loc_14_str, '&fib'
00046 getlexstatic_o loc_13_obj, loc_14_str
00047 decont loc_11_obj, loc_13_obj
00048 prepargs Callsite_0
00049 arg_o 0, loc_2_obj
00050 invoke_o loc_11_obj, loc_11_obj
00051 decont loc_11_obj, loc_11_obj
00052 smrt_strify loc_14_str, loc_11_obj
00053 concat_s loc_14_str, loc_7_str, loc_14_str
00054 say loc_14_str
annotation: examples/fib.nqp:13
00055 const_s loc_7_str, 'time = '
00056 decont loc_11_obj, loc_5_obj
00057 smrt_numify loc_12_num, loc_11_obj
00058 decont loc_11_obj, loc_3_obj
00059 smrt_numify loc_15_num, loc_11_obj
00060 sub_n loc_15_num, loc_12_num, loc_15_num
00061 coerce_ns loc_16_str, loc_15_num
00062 concat_s loc_16_str, loc_7_str, loc_16_str
00063 say loc_16_str
00064 null loc_11_obj
00065 return_o loc_11_obj
Frame_1 :
cuuid : 2
name : fib
outer : Frame_0
Locals :
0: loc_0_obj
1: loc_1_num
2: loc_2_obj
3: loc_3_int
4: loc_4_num
5: loc_5_str
6: loc_6_obj
7: loc_7_int
8: loc_8_num
9: loc_9_obj
Instructions :
00000 checkarity 1, 1
00001 param_rp_o loc_0_obj, 0
00002 paramnamesused
annotation: examples/fib.nqp:3
00003 decont loc_2_obj, loc_0_obj
00004 smrt_numify loc_1_num, loc_2_obj
00005 const_i64_16 loc_3_int, 2
00006 coerce_in loc_4_num, loc_3_int
00007 lt_n loc_3_int, loc_1_num, loc_4_num
00008 unless_i loc_3_int, label_1(00011)
00009 set loc_2_obj, loc_0_obj
00010 goto label_2(00041)
label_1:
00011 const_s loc_5_str, '&fib'
00012 getlexstatic_o loc_2_obj, loc_5_str
00013 decont loc_6_obj, loc_0_obj
00014 smrt_numify loc_4_num, loc_6_obj
00015 const_i64_16 loc_7_int, 1
00016 coerce_in loc_1_num, loc_7_int
00017 sub_n loc_1_num, loc_4_num, loc_1_num
00018 decont loc_6_obj, loc_2_obj
00019 prepargs Callsite_1
00020 arg_n 0, loc_1_num
00021 invoke_o loc_6_obj, loc_6_obj
00022 decont loc_6_obj, loc_6_obj
00023 smrt_numify loc_1_num, loc_6_obj
00024 const_s loc_5_str, '&fib'
00025 getlexstatic_o loc_6_obj, loc_5_str
00026 decont loc_2_obj, loc_0_obj
00027 smrt_numify loc_4_num, loc_2_obj
00028 const_i64_16 loc_7_int, 2
00029 coerce_in loc_8_num, loc_7_int
00030 sub_n loc_8_num, loc_4_num, loc_8_num
00031 decont loc_2_obj, loc_6_obj
00032 prepargs Callsite_1
00033 arg_n 0, loc_8_num
00034 invoke_o loc_2_obj, loc_2_obj
00035 decont loc_2_obj, loc_2_obj
00036 smrt_numify loc_8_num, loc_2_obj
00037 add_n loc_8_num, loc_1_num, loc_8_num
00038 hllboxtype_n loc_6_obj
00039 box_n loc_6_obj, loc_8_num, loc_6_obj
00040 set loc_2_obj, loc_6_obj
label_2:
00041 return_o loc_2_obj
Frame_2 :
cuuid : 3
name : <dependencies+deserialize>
Locals :
0: loc_0_str
1: loc_1_obj
2: loc_2_str
3: loc_3_obj
4: loc_4_obj
5: loc_5_obj
6: loc_6_str
7: loc_7_obj
8: loc_8_obj
9: loc_9_str
10: loc_10_obj
11: loc_11_int
12: loc_12_str
13: loc_13_obj
Instructions :
00000 getcode loc_10_obj, Frame_3
00001 takeclosure loc_10_obj, loc_10_obj
00002 checkarity 0, 0
00003 paramnamesused
00004 const_s loc_0_str, 'ModuleLoader.moarvm'
00005 loadbytecode loc_0_str, loc_0_str
00006 getcode loc_1_obj, Frame_0
00007 const_s loc_2_str, 'ModuleLoader'
00008 getcurhllsym loc_3_obj, loc_2_str
00009 const_s loc_2_str, 'NQPCORE'
00010 decont loc_5_obj, loc_3_obj
00011 findmeth loc_4_obj, loc_5_obj, 'load_setting'
00012 prepargs Callsite_2
00013 arg_o 0, loc_3_obj
00014 arg_s 1, loc_2_str
00015 invoke_o loc_3_obj, loc_4_obj
00016 forceouterctx loc_1_obj, loc_3_obj
00017 const_s loc_2_str, 'ModuleLoader.moarvm'
00018 loadbytecode loc_2_str, loc_2_str
00019 const_s loc_6_str, 'ModuleLoader'
00020 getcurhllsym loc_3_obj, loc_6_str
00021 const_s loc_6_str, 'NQPP6QRegex'
00022 decont loc_5_obj, loc_3_obj
00023 findmeth loc_4_obj, loc_5_obj, 'load_module'
00024 prepargs Callsite_2
00025 arg_o 0, loc_3_obj
00026 arg_s 1, loc_6_str
00027 invoke_o loc_3_obj, loc_4_obj
00028 const_s loc_6_str, 'E5A15988716F4E5FD2FF889497136C7E4AD482E2-1512287908.66458'
00029 createsc loc_4_obj, loc_6_str
00030 set loc_7_obj, loc_4_obj
00031 const_s loc_6_str, 'examples/fib.nqp'
00032 scsetdesc loc_7_obj, loc_6_str
00033 hlllist loc_4_obj
00034 create loc_4_obj, loc_4_obj
00035 set loc_8_obj, loc_4_obj
00036 null_s loc_9_str
00037 null loc_4_obj
00038 prepargs Callsite_3
00039 invoke_o loc_5_obj, loc_10_obj
00040 deserialize loc_9_str, loc_7_obj, loc_4_obj, loc_5_obj, loc_8_obj
00041 elems loc_11_int, loc_8_obj
00042 unless_i loc_11_int, label_1(00045)
00043 const_s loc_12_str, 'Repossession conflicts occurred during deserialization'
00044 die loc_5_obj, loc_12_str
label_1:
00045 null loc_4_obj
00046 return_o loc_4_obj
Frame_3 :
cuuid : 4
name :
outer : Frame_2
Locals :
0: loc_0_obj
1: loc_1_obj
Instructions :
00000 checkarity 0, 0
00001 paramnamesused
00002 bootarray loc_0_obj
00003 create loc_0_obj, loc_0_obj
00004 return_o loc_0_obj
Frame_4 :
cuuid : 5
name : <load>
Locals :
0: loc_0_obj
1: loc_1_obj
Instructions :
00000 checkarity 0, 0
00001 paramnamesused
00002 getcode loc_0_obj, Frame_0
00003 decont loc_1_obj, loc_0_obj
00004 prepargs Callsite_3
00005 invoke_o loc_1_obj, loc_1_obj
00006 return_o loc_1_obj
Frame_5 :
cuuid : 6
name : <entry>
Locals :
0: loc_0_obj
1: loc_1_obj
2: loc_2_obj
Instructions :
00000 getcode loc_0_obj, Frame_6
00001 takeclosure loc_0_obj, loc_0_obj
00002 checkarity 0, 0
00003 paramnamesused
00004 clargs loc_1_obj
00005 decont loc_2_obj, loc_0_obj
00006 prepargs Callsite_0
00007 arg_o 0, loc_1_obj
00008 invoke_o loc_1_obj, loc_2_obj
00009 return_o loc_1_obj
Frame_6 :
cuuid : 7
name : <main>
outer : Frame_5
Locals :
0: loc_0_obj
1: loc_1_obj
2: loc_2_obj
Instructions :
00000 checkarity 0, -1
00001 param_sp loc_1_obj, 0
00002 paramnamesused
00003 getcode loc_0_obj, Frame_0
00004 decont loc_2_obj, loc_0_obj
00005 prepargs Callsite_4
00006 arg_o 0, loc_1_obj
00007 invoke_o loc_2_obj, loc_2_obj
00008 return_o loc_2_obj
などと出力される。
見た感じ命令コードに _o
がついておりobjectでは?などとなっていますが,基本的なノリはいつものアセンブラの結果です。
尚MoarVMのレジスタはunionで定義されていたりとした。この辺はあとでまとめてまた書きたいと思っています。
さて今度は実行時にMoarVMはJITコンパイルしているが,本当にしているかを追いたい。
Moarには MVM_JIT_LOG
といういかにもな環境変数があるので
export MVM_JIT_LOG=fib.log
などとして
$nqp examples/fib.nqp
などとすると
Constructing JIT graph (cuuid: 139, name: 'MATCH')
append label: 0
append label: 1
Cannot handle DEOPT_ONE (ins=set)
Build tree out of: [null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, sp_getarg_o, ]
Starting dump of JIT expression tree
====================================
digraph {
n_0000 [label="LOCAL"];
n_0001 [label="ADDR(0x1c8)"];
n_0001 -> n_0000;
n_0004 [label="TC"];
n_0005 [label="ADDR(0x10)"];
n_0005 -> n_0004;
n_0008 [label="LOAD(0x8)"];
n_0008 -> n_0005;
n_0011 [label="ADDR(0x798)"];
n_0011 -> n_0008;
n_0014 [label="LOAD(0x8)"];
n_0014 -> n_0011;
n_0342 [label="STORE(0x8)"];
....
などと出てくる。呼んだ所どうもそこまで今回の例題ではJITコンパイルしていない事がわかった。
また,lldbで関数もおっていたが,かなり呼び出しまでにコストがかかっており,なかなか謎でした。
とりまとめのない作業ログでしたが,こんなところで