これは Perl 6 Advent Calendar 2017 5日目の記事です。
こんにちは id:anatofuzです。先日大学の講義でコンパイラ読み会を行ったのですが,その際にPerl6(Rakudo,MoarVM,NQP)を読みました。
その際に MoarVMがJIT処理をしているとこはどこか? という探索を行ったのですが,その時のメモです。
ちなみにPerl6の外観を知るために,rakudo-and-nqp-internals-courseを読んだりして進めていました。
本題
今回はPerl6のサブセットであるnqpを中心に呼んでいた。
理由はほぼ処理系はPerl6のコア機能を有しているので,とりあえずここ読めればいいかな…みたいな乗りでした。
さて,まずnqpからバイナリを出力します。 使用するプログラムは,上の例題にもあったフィボナッチ数列を求めるプログラム。
#! 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で定義されていたりとした。この辺はあとでまとめてまた書きたいと思っています。
JITを追う
さて今度は実行時に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で関数もおっていたが,かなり呼び出しまでにコストがかかっており,なかなか謎でした。 とりまとめのない作業ログでしたが,こんなところで