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

LearnByteCode, Techc, Japanese, mruby, mruby/c

OP_LOADF

Arguments

  • A:レジスタ番号

How it works?

R(A) := true

レジスタ[A]にfalseオブジェクトを代入する

When used?

falseを扱うときに用いられる。

a = false

出力されるバイトコードは、こんな感じ。

00001 NODE_SCOPE:
00001   local variables:
00001     a
00001   NODE_BEGIN:
00001     NODE_ASGN:
00001       lhs:
00001         NODE_LVAR a
00001       rhs:
00001         NODE_FALSE
irep 0x60001a7f0 nregs=3 nlocals=2 pools=0 syms=0 reps=0
file: a.rb
    1 000 OP_LOADF      R2
    1 001 OP_MOVE       R1      R2              ; R1:a
    1 002 OP_STOP

falseオブジェクトをR2にセットして、R1(変数a)に代入している。

Note

mruby/cの場合

  int ra = GETARG_A(code);

  mrbc_release(®s[ra]);
  regs[ra].tt = MRB_TT_FALSE;

mrubyの場合

      /* A      R(A) := false */
      int a = GETARG_A(i);
      SET_FALSE_VALUE(regs[a]);

SET_TRUE_VALUEの定義は、boxingなしの場合は以下の通り。

#define BOXNIX_SET_VALUE(o, ttt, attr, v) do {\
  (o).tt = ttt;\
  (o).attr = v;\
} while (0)

#define SET_FALSE_VALUE(r) BOXNIX_SET_VALUE(r, MRB_TT_FALSE, value.i, 1)

value.iにも1がセットされている。これによって、nilと区別しているっぽい。
用例としては、class.c mrb_singleton_class()をあげてみる。

MRB_API mrb_value
mrb_singleton_class(mrb_state *mrb, mrb_value v)
{
  struct RBasic *obj;

  switch (mrb_type(v)) {
  case MRB_TT_FALSE:
    if (mrb_nil_p(v))
      return mrb_obj_value(mrb->nil_class);
    return mrb_obj_value(mrb->false_class);
  case MRB_TT_TRUE:
    return mrb_obj_value(mrb->true_class);
...snip...
  }
  obj = mrb_basic_ptr(v);
  prepare_singleton_class(mrb, obj);
  return mrb_obj_value(obj->c);
}

mrb_nil_pで、value.iを参照して、falseオブジェクトからnilオブジェクトを分離していると思われる。

#define mrb_nil_p(o)  (mrb_type(o) == MRB_TT_FALSE && !mrb_fixnum(o))

メモリは節約できるけど、処理は遅くなっているんで、トレードオフ。mrubyもmruby/cほどでないにしても、基本的にはメモリ節約する方針と理解しているので、こういうものなのかな。

Reference