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

LearnByteCode, Techc, Japanese, mruby, mruby/c

OP_SETGLOBAL

Arguments

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

How it works?

setglobal(Syms(Bx), R(A))

シンボル[Bx]に対応するグローバル変数として、レジスタ[A]のオブジェクトをセットする

When used?

グローバル変数を定義するときに用いられる。

$val = "test"
puts $val

出力されるバイトコードは、下記の通り。

00001 NODE_SCOPE:
00001   NODE_BEGIN:
00001     NODE_ASGN:
00001       lhs:
00001         NODE_GVAR $val
00001       rhs:
00001         NODE_STR "test" len 4
00002     NODE_FCALL:
00002       NODE_SELF
00002       method='puts' (228)
00002       args:
00002         NODE_GVAR $val
irep 0x60001a840 nregs=4 nlocals=1 pools=1 syms=2 reps=0
file: a.rb
    1 000 OP_STRING     R1      L(0)    ; "test"
    1 001 OP_SETGLOBAL  :$val   R1
    2 002 OP_LOADSELF   R1
    2 003 OP_GETGLOBAL  R2      :$val
    2 004 OP_SEND       R1      :puts   1
    2 005 OP_STOP

上の例では、”:$val”というシンボルに対応したグローバル変数として、オブジェクトR1をセットしている。

Note

mruby/cの場合

シンボルをirepのテーブルから参照して、global_object_add()関数で新しいグローバル変数オブジェクトregs[ra]を追加している。

  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);
  global_object_add(sym_id, regs[ra]);

global_object_add()の中身の実装は以下の通り。
mrb_globalobject mrbc_global[]という配列にグローバル変数を追加している。OP_GETGLOBALの説明も参照。

/* TODO: Check reference count */
void global_object_add(mrb_sym sym_id, mrb_value v)
{
  int index = search_global_object(sym_id, MRBC_GLOBAL_OBJECT);
  if( index == -1 ) {
    index = global_end++;
    assert( index < MAX_GLOBAL_OBJECT_SIZE );   // maybe raise ex
  } else {
    mrbc_release( &(mrbc_global[index].obj) );
  }

  mrbc_global[index].gtype = MRBC_GLOBAL_OBJECT;
  mrbc_global[index].sym_id = sym_id;
  mrbc_global[index].obj = v;
  mrbc_dup( &v );
}

mrubyの場合

mrb_gv_set()という関数を用いて、グローバル変数をセットしている。

      /* A Bx   setglobal(Syms(Bx), R(A)) */
      int a = GETARG_A(i);
      int bx = GETARG_Bx(i);
      mrb_gv_set(mrb, syms[bx], regs[a]);

mrb_gv_set()の中身は、以下のとおり。
mrb->globalsにグローバル変数が格納されているのが分かる。
mrb->globalsが空ならば、iv_new()の中でハッシュテーブルを定義している1

もしMRB_USE_IV_SEGLISTが定義されているならば、ハッシュではなくて、セグメントリストが確保される。

iv_put()で、実際の新しいグローバル変数をセットしている。
通常は、新しいハッシュテーブル要素としてR[A]として渡されたオブジェクトが、追加される。
MRB_USE_IV_SEGLISTの場合は、R[A]をsegment構造体でラップして、リストに追加される。

MRB_API void
mrb_gv_set(mrb_state *mrb, mrb_sym sym, mrb_value v)
{
  iv_tbl *t;

  if (!mrb->globals) {
    t = mrb->globals = iv_new(mrb);
  }
  else {
    t = mrb->globals;
  }
  iv_put(mrb, t, sym, v);
}

mruby/cとmrubyの実装を比較すると、mruby/cはグローバル変数と定数をまとめて一つのあらかじめ確保した配列で管理している点が特徴的。

Reference


http://d.hatena.ne.jp/kyab/20130203


  1. iv_new()では、マクロを利用してVMが必要とするハッシュテーブルごとに処理関数群を定義している。同じような処理関数群のコピーが複数定義されてROMの使用量的には不利だが、テーブルにIDを振ってswitchで振り分けるよりも、処理は高速になる。(知らないマクロの文法だったので、勉強になる。 https://en.wikibooks.org/wiki/C_Programming/Preprocessor_directives_and_macros )