独習mrubyバイトコード[OP_LOADSYM]
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一緒)
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を直接参照できる。
Recent Comments