MoarVMのbytecodeをのダンプとJITコンパイルのログ出力について

これは 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で関数もおっていたが,かなり呼び出しまでにコストがかかっており,なかなか謎でした。 とりまとめのない作業ログでしたが,こんなところで