独習mrubyバイトコード[OP_LOADSELF]

LearnByteCode, Techc, Japanese, mruby, mruby/c

OP_LOADSELF

Arguments

  • A:レジスタ番号

How it works?

R(A) := self

レジスタ[A]にselfオブジェクトを代入する

When used?

selfに対して、何か処理をするときにあちこちで使われる。
例えば、

puts "test"

出力されるバイトコードは、こんな感じ。

00001 NODE_SCOPE:
00001   NODE_BEGIN:
00001     NODE_FCALL:
00001       NODE_SELF
00001       method='puts' (227)
00001       args:
00001         NODE_STR "test" len 4
irep 0x60001a790 nregs=4 nlocals=1 pools=1 syms=1 reps=0
file: a.rb
    1 000 OP_LOADSELF   R1
    1 001 OP_STRING     R2      L(0)    ; "test"
    1 002 OP_SEND       R1      :puts   1
    1 003 OP_STOP

スクリプト実行時のトップレベルのself、すなわちmainオブジェクトをR1にセットして、R1に対して、putsメソッドをsendして処理を実行している。

Note

レジスタの0番にはselfが格納されている。

mruby/cの場合

  int ra = GETARG_A(code);

  mrbc_release(®s[ra]);
  mrbc_dup(®s[0]);       // TODO: Need?
  regs[ra] = regs[0];

0番レジスタの値をA番のレジスタにコピーしている。

mrubyの場合

      int a = GETARG_A(i);
      regs[a] = regs[0];

参照カウントが無いぶん、よりシンプル。

@miura1729さんのコメントに、

MOVE Rm, R0じゃ駄目なのか?と思わないでもないが、何か理由があるのだろう。私は知らないが。あ、mrubyのVMでは常にR0にselfが入っているんだよ。Rubyレベルでは何の役にも立たない知識でした。
LOADSELFはMOVEより少し早い可能性があるって言う利点はありそう。早いと言ってもほんの少しだけど、LOADSELFはいっぱい出てくるので効果が出てくるかもしれない。

とあるので、MOVEと比較してみる。

      /* A B    R(A) := R(B) */
      int a = GETARG_A(i);
      int b = GETARG_B(i);
      regs[a] = regs[b];

確かに、コピー元のレジスタが固定されていぶん、アドレスの参照回数が少なくて済む、というメリットはありそう。

Reference

https://qiita.com/miura1729/items/256d205bc2a464bfb3c6