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

LearnByteCode, Techc, Japanese, mruby, mruby/c

OP_GETGLOBAL

Arguments

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

How it works?

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

レジスタ[A]にシンボル[Bx]に対応するグローバル変数のオブジェクトを代入する

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

上の例では、R2に”:$val”というシンボル名に対応したグローバル変数のオブジェクトが格納されている。

Note

mruby/cの場合

シンボルをirepのテーブルから参照して、global_object_get()関数でグローバル変数オブジェクトを見つけて、レジスタに格納している。

  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);

  mrbc_release(®s[ra]);
  regs[ra] = global_object_get(sym_id);

global_object_get()の中身の実装は以下の通り。
mrb_globalobject mrbc_global[]という配列にグローバル変数が格納されている。
構造体を見ると、定数もmrbc_global[]に格納しているようだ。
現状は配列を順番に確認して、グローバル変数を探しているので、グローバル変数が多い条件での処理は早くない。
コメントを見ると、バイナリサーチも対応する予定がありそう。

mrb_value global_object_get(mrb_sym sym_id)
{
  int index = search_global_object(sym_id, MRBC_GLOBAL_OBJECT);
  if( index >= 0 ){
    mrbc_dup( &mrbc_global[index].obj );
    return mrbc_global[index].obj;
  } else {
    return mrb_nil_value();
  }
}

/* search */
/* linear search is not efficient! */
/* TODO: Use binary search */
static int search_global_object(mrb_sym sym_id, mrbc_globaltype gtype)
{
  int i;
  for( i=0 ; isym_id == sym_id && obj->gtype == gtype ) return i;
  }
  return -1;
}

typedef enum {
  MRBC_GLOBAL_OBJECT = 1,
  MRBC_CONST_OBJECT,
} mrbc_globaltype;

typedef struct GLOBAL_OBJECT {
  mrbc_globaltype gtype : 8;
  mrb_sym sym_id;
  mrb_object obj;
} mrb_globalobject;

mrubyの場合

mrb_gv_get()という関数を用いて、グローバル変数を取得して、R[a]に入れている。

      /* A Bx   R(A) := getglobal(Syms(Bx)) */
      int a = GETARG_A(i);
      int bx = GETARG_Bx(i);
      mrb_value val = mrb_gv_get(mrb, syms[bx]);
      regs[a] = val;

mrb_gv_get()の中身は、以下のとおり。

MRB_API mrb_value
mrb_gv_get(mrb_state *mrb, mrb_sym sym)
{
  mrb_value v;

  if (!mrb->globals) {
    return mrb_nil_value();
  }
  if (iv_get(mrb, mrb->globals, sym, &v))
    return v;
  return mrb_nil_value();
}

iv_get()は、MRB_USE_IV_SEGLISTの定義の有り無しで2種類存在する。

MRB_USE_IV_SEGLISTは、IV(Instance Value)の格納テーブルとして、セグメントリストを用いるか否かを表すフラグ。デフォルト無効で、通常はハッシュテーブルが利用される。
有効にすると、速度の低下と引き換えにハッシュを使わない分のメモリ使用量が減るようだ。

MRB_USE_IV_SEGLISTが定義されていない場合、ハッシュを用いて、グローバル変数を取得してる、

static mrb_bool
iv_get(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value *vp)
{
  khash_t(iv) *h = &t->h;
  khiter_t k;

  k = kh_get(iv, mrb, h, sym);
  if (k != kh_end(h)) {
    if (vp) *vp = kh_value(h, k);
    return TRUE;
  }
  return FALSE;
}

mrbconf.hでMRB_USE_IV_SEGLISTが定義されている場合。
ハッシュではなく、segmentのリンクリストを先頭から探索する。

static mrb_bool
iv_get(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value *vp)
{
  segment *seg;
  size_t i;

  seg = t->rootseg;
  while (seg) {
    for (i=0; ikey[i];

      if (!seg->next && i >= t->last_len) {
        return FALSE;
      }
      if (key == sym) {
        if (vp) *vp = seg->val[i];
        return TRUE;
      }
    }
    seg = seg->next;
  }
  return FALSE;
}

Reference


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