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

LearnByteCode, TechJapanese, mruby, mruby/c

OP_GETIV

Arguments

  • A: レジスタ番号
  • Bx: シンボル番号

How it works?

R(A) := ivget(Syms(Bx))

シンボル[Bx]に対応するインスタンスオブジェクトを、レジスタ[A]にセットする

When used?

インスタンスオブジェクトを利用するときに用いられる。

class Test
        def set_val(v)
                @instance_val = v
        end
        def show_val
                puts @instance_val
        end
end

t = Test.new
t.set_val("value")
t.show_val

例えば上記のようなコードをバイトコンパイルすると、下記のようなバイトコードが出力される。ちょっと長い。

00001 NODE_SCOPE:
00010   local variables:
00010     t
00001   NODE_BEGIN:
00001     NODE_CLASS:
00008       :Test
00008       body:
00001         NODE_BEGIN:
00002           NODE_DEF:
00004             set_val
00002             local variables:
00002               v
00002             mandatory args:
00002               NODE_ARG v
00003             NODE_BEGIN:
00003               NODE_ASGN:
00003                 lhs:
00003                   NODE_IVAR @instance_val
00003                 rhs:
00003                   NODE_LVAR v
00005           NODE_DEF:
00007             show_val
00006             NODE_BEGIN:
00006               NODE_FCALL:
00006                 NODE_SELF
00006                 method='puts' (232)
00006                 args:
00006                   NODE_IVAR @instance_val
00010     NODE_ASGN:
00010       lhs:
00010         NODE_LVAR t
00010       rhs:
00010         NODE_CALL(.):
00010           NODE_CONST Test
00010           method='new' (15)
00011     NODE_CALL(.):
00011       NODE_LVAR t
00011       method='set_val' (228)
00011       args:
00011         NODE_STR "value" len 5
00012     NODE_CALL(.):
00012       NODE_LVAR t
00012       method='show_val' (231)
irep 0x600081450 nregs=5 nlocals=2 pools=1 syms=4 reps=1
file: d.rb
    1 000 OP_LOADNIL    R2
    1 001 OP_LOADNIL    R3
    1 002 OP_CLASS      R2      :Test
    1 003 OP_EXEC       R2      I(+1)
   10 004 OP_GETCONST   R2      :Test
   10 005 OP_SEND       R2      :new    0
   10 006 OP_MOVE       R1      R2              ; R1:t
   11 007 OP_STRING     R3      L(0)    ; "value"
   11 008 OP_SEND       R2      :set_val        1
   12 009 OP_MOVE       R2      R1              ; R1:t
   12 010 OP_SEND       R2      :show_val       0
   12 011 OP_STOP

irep 0x600087670 nregs=3 nlocals=1 pools=0 syms=2 reps=2
file: d.rb
    2 000 OP_TCLASS     R1
    2 001 OP_LAMBDA     R2      I(+1)   method
    2 002 OP_METHOD     R1      :set_val
    5 003 OP_TCLASS     R1
    5 004 OP_LAMBDA     R2      I(+2)   method
    5 005 OP_METHOD     R1      :show_val
    5 006 OP_LOADSYM    R1      :show_val
    5 007 OP_RETURN     R0      normal

irep 0x60008d870 nregs=4 nlocals=3 pools=0 syms=1 reps=0
file: d.rb
    2 000 OP_ENTER      1:0:0:0:0:0:0
    3 001 OP_SETIV      @instance_val   R1      ; R1:v
    3 002 OP_RETURN     R1      normal          ; R1:v

irep 0x60008f410 nregs=5 nlocals=2 pools=0 syms=2 reps=0
file: d.rb
    5 000 OP_ENTER      0:0:0:0:0:0:0
    6 001 OP_LOADSELF   R2
    6 002 OP_GETIV      R3      @instance_val
    6 003 OP_SEND       R2      :puts   1
    6 004 OP_RETURN     R2      normal

クラスの定義などの説明を割愛すると、show_valメソッドに対応する”irep 0x60008f410”のirepの中で、OP_GETIVが呼ばれている。
set_valメソッドに対応する”irep 0x60008d870”の中で、OP_SETIVでセットしたインスタンス変数@instance_valをOP_GETIVによって読み出してR3にセットしている。

Note

mruby/cの場合

mruby/cの場合、下記の流れで処理が行われる。

  1. irepのシンボル領域から、インスタンス変数名の文字列を引く
  2. その文字列をシンボルに変換する
  3. mbc_instance_getiv()を呼び、シンボルに対応したインスタンス変数を見つけ、valに代入
  4. valをR[A]へ代入

mbc_instance_getiv()の中では、対象オブジェクトのR[0]が持つインスタンス変数格納用のハッシュテーブル”instance->ivar”から入力されたシンボルをキーにして、対応するオブジェクトを探している。

  int ra = GETARG_A(code);
  int rb = GETARG_Bx(code);

  const char *sym_name = mrbc_get_irep_symbol(vm->pc_irep->ptr_to_sym, rb);
  mrb_sym sym_id = str_to_symid(sym_name+1);    // skip '@'

  mrb_value val = mrbc_instance_getiv(®s[0], sym_id);

  mrbc_release(®s[ra]);
  regs[ra] = val;

  return 0;

参考:インスタンス構造体の定義

プリミティブなオブジェクト以外全般を表す構造体mrb_instanceは、下記のようなポインタ情報を持っている。

  • MRBC_OBJECT_HEADER: ヘッダ領域(参照カウンタ、デバッグ用情報等が入る)
  • cls: そのインスタンスに対応するクラスオブジェクト
  • ivar: インスタンス変数格納用のテーブル
  • data: 配列、文字列格納用(その他にも用途あるか?)
typedef struct RInstance {
  MRBC_OBJECT_HEADER;

  struct RClass *cls;
  struct RKeyValueHandle *ivar;
  uint8_t data[];
} mrb_instance;

mrubyの場合

mrubyの場合、syms[]に予め文字列をシンボルに変換したテーブルが用意されているので、mrb_vm_iv_getを呼ぶだけの処理になっている。
内部では最終的に、iv_get()が呼ばれている。iv_get()については、OP_GETGLOBALの説明を参照。

    CASE(OP_GETIV) {
      /* A Bx   R(A) := ivget(Bx) */
      int a = GETARG_A(i);
      int bx = GETARG_Bx(i);
      mrb_value val = mrb_vm_iv_get(mrb, syms[bx]);
      regs[a] = val;
      NEXT;
    }

attr_accessorを利用した場合

例えば、attr_accessorを用いた場合はどうなるか見てみる。

class Test
        attr_accessor :instance_val
end

t = Test.new
t.instance_val="value"
puts t.instance_val

このようなコードをバイトコンパイルすると下記のようになる。

00001 NODE_SCOPE:
00005   local variables:
00005     t
00001   NODE_BEGIN:
00001     NODE_CLASS:
00003       :Test
00003       body:
00001         NODE_BEGIN:
00002           NODE_FCALL:
00002             NODE_SELF
00002             method='attr_accessor' (228)
00002             args:
00002               NODE_SYM :instance_val (229)
00005     NODE_ASGN:
00005       lhs:
00005         NODE_LVAR t
00005       rhs:
00005         NODE_CALL(.):
00005           NODE_CONST Test
00005           method='new' (15)
00006     NODE_ASGN:
00006       lhs:
00006         NODE_CALL(.):
00006           NODE_LVAR t
00006           method='instance_val' (229)
00006       rhs:
00006         NODE_STR "value" len 5
00007     NODE_FCALL:
00007       NODE_SELF
00007       method='puts' (231)
00007       args:
00007         NODE_CALL(.):
00007           NODE_LVAR t
00007           method='instance_val' (229)
irep 0x600081430 nregs=6 nlocals=2 pools=1 syms=5 reps=1
file: e.rb
    1 000 OP_LOADNIL    R2
    1 001 OP_LOADNIL    R3
    1 002 OP_CLASS      R2      :Test
    1 003 OP_EXEC       R2      I(+1)
    5 004 OP_GETCONST   R2      :Test
    5 005 OP_SEND       R2      :new    0
    5 006 OP_MOVE       R1      R2              ; R1:t
    6 007 OP_STRING     R2      L(0)    ; "value"
    6 008 OP_MOVE       R3      R1              ; R1:t
    6 009 OP_MOVE       R4      R2
    6 010 OP_SEND       R3      :instance_val=  1
    7 011 OP_LOADSELF   R2
    7 012 OP_MOVE       R3      R1              ; R1:t
    7 013 OP_SEND       R3      :instance_val   0
    7 014 OP_SEND       R2      :puts   1
    7 015 OP_STOP

irep 0x600087630 nregs=4 nlocals=1 pools=0 syms=2 reps=0
file: e.rb
    2 000 OP_LOADSELF   R1
    2 001 OP_LOADSYM    R2      :instance_val
    2 002 OP_SEND       R1      :attr_accessor  1
    2 003 OP_RETURN     R0      normal

クラスの初期化のコードに対応する、”irep 0x600087630”の中で、シンボル:instance_valを引き数にとって、Testクラス(R[1])のattr_accessorメソッドが呼ばれている。
attr_accessorメソッドの中で、入力されたシンボルを元に2つのアクセサメソッドの定義が行われる。それぞれ、OP_GETIV、OP_SETIVの内部処理で呼ばれる関数をラップしたメソッドになる。

  • instance_val=
  • instance_val

Reference