独習mrubyバイトコード[SETGLOBAL]
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
- iv_new()では、マクロを利用してVMが必要とするハッシュテーブルごとに処理関数群を定義している。同じような処理関数群のコピーが複数定義されてROMの使用量的には不利だが、テーブルにIDを振ってswitchで振り分けるよりも、処理は高速になる。(知らないマクロの文法だったので、勉強になる。 https://en.wikibooks.org/wiki/C_Programming/Preprocessor_directives_and_macros ) ↩
Recent Comments