独習mrubyバイトコード[GETGLOBAL]
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;
}
Recent Comments