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

LearnByteCode, TechJapanese, mruby

OP_LOADSYM

Arguments

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

How it works?

R(A) := Syms(Bx)

レジスタ[A]に実行中のmrb_irepに格納されているBx番目のシンボルオブジェクトを代入する

When used?

シンボルオブジェクトを扱うときに使われる。例えば、

def test_method
  puts "Hello"
end

次のようなバイトコードに変換される。

irep 0x60001a7c0 nregs=3 nlocals=1 pools=0 syms=1 reps=1
file: test.rb
    1 000 OP_TCLASS     R1
    1 001 OP_LAMBDA     R2      I(+1)   method
    1 002 OP_METHOD     R1      :test_method
    1 003 OP_LOADSYM    R1      :test_method
    1 004 OP_STOP

irep 0x600087510 nregs=5 nlocals=2 pools=1 syms=1 reps=0
file: test.rb
    1 000 OP_ENTER      0:0:0:0:0:0:0
    2 001 OP_LOADSELF   R2
    2 002 OP_STRING     R3      L(0)    ; "Hello"
    2 003 OP_SEND       R2      :puts   1
    2 004 OP_RETURN     R2      normal

:test_method は実際は整数で、irep->ptr_to_symsのn番目のシンボルを示している。
この例では、:test_methodは、def test_method の戻り値として設定するために、R[1]にセットされている。
(defメソッドの戻り値がシンボルということを初めて知った…)

Note

ptr_to_symsのデータ構造

ptr_to_symは、uint8_t* 型であり、mrb_irep構造体に含まれている。mrb_irep構造体は下記のように構築されている。(mruby/cの場合)

*/
typedef struct IREP {
  uint16_t nlocals;     //!< # of local variables
  uint16_t nregs;       //!< # of register variables
  uint16_t rlen;        //!< # of child IREP blocks
  uint16_t ilen;        //!< # of irep
  uint16_t plen;        //!< # of pool

  uint8_t     *code;        //!< ISEQ (code) BLOCK
  mrb_object  **pools;          //!< array of POOL objects pointer.
  uint8_t     *ptr_to_sym;
  struct IREP **reps;       //!< array of child IREP's pointer.

} mrb_irep;

ptr_to_symが指している先は、シンボル文字列の文字数と文字列本体が順番に並んでいる。なので、N番目のシンボル文字列を参照するには、N回ポインタをずらしながら探しに行かないといけない。
バイトコードファイルの中では、下記のような配置になっている。(mruby/cもバイトコードはmrubyのmrbcを使用するので、フォーマットはmruby一緒)

irep in bytecode file
irep in bytecode file(mruby/c)

mruby/cの場合

見つかった文字列を、str_to_symid()でシンボルIDに変換する。ある文字列のシンボルIDが実際に何番になるのかは、バイトコード実行中のシンボルをIDに初めて変換したタイミングに依存するので、事前に把握することができない。そのためirep上ではptr_to_symの頭から数えてN番目、という指定の仕方になる。
毎回ptr_to_symの先頭から数えるので、その分処理時間が掛かるが、バイトコードの情報をそのまま参照しているので、メモリの節約になっている。

mrubyの場合

シンボルは、irepのが持っているシンボルはすでにバイトコードを読み込んだ時点で、IDに変換済みの配列(irep->syms[])になっているので、irep->syms[bx]でIDを直接参照できる。

Reference

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