From fcbf63e62c627deae76c1b8cb8c0876c536ed811 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Mon, 16 Mar 2020 18:49:26 +0900 Subject: Fresh start --- jni/ruby/insns.def | 2235 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2235 insertions(+) create mode 100644 jni/ruby/insns.def (limited to 'jni/ruby/insns.def') diff --git a/jni/ruby/insns.def b/jni/ruby/insns.def new file mode 100644 index 0000000..eae0b8e --- /dev/null +++ b/jni/ruby/insns.def @@ -0,0 +1,2235 @@ +/** ##skip -*- mode:c; style:ruby; coding: utf-8 -*- + insns.def - YARV instruction definitions + + $Author: $ + created at: 04/01/01 01:17:55 JST + + Copyright (C) 2004-2007 Koichi Sasada +*/ + +/** ##skip + instruction comment + @c: category + @e: english description + @j: japanese description + + instruction form: + DEFINE_INSN + instruction_name + (instruction_operands, ..) + (pop_values, ..) + (return value) + { + .. // insn body + } + + */ + + +/** + @c nop + @e nop + @j nop + */ +DEFINE_INSN +nop +() +() +() +{ + /* none */ +} + +/**********************************************************/ +/* deal with variables */ +/**********************************************************/ + +/** + @c variable + @e Get local variable (pointed by `idx' and `level'). + 'level' indicates the nesting depth from the current block. + @j level, idx で指定されたローカル変数の値をスタックに置く。 + level はブロックのネストレベルで、何段上かを示す。 + */ +DEFINE_INSN +getlocal +(lindex_t idx, rb_num_t level) +() +(VALUE val) +{ + int i, lev = (int)level; + VALUE *ep = GET_EP(); + + /* optimized insns generated for level == (0|1) in defs/opt_operand.def */ + for (i = 0; i < lev; i++) { + ep = GET_PREV_EP(ep); + } + val = *(ep - idx); +} + +/** + @c variable + @e Set a local variable (pointed to by 'idx') as val. + 'level' indicates the nesting depth from the current block. + @j level, idx で指定されたローカル変数の値を val にする。 + level はブロックのネストレベルで、何段上かを示す。 + */ +DEFINE_INSN +setlocal +(lindex_t idx, rb_num_t level) +(VALUE val) +() +{ + int i, lev = (int)level; + VALUE *ep = GET_EP(); + + /* optimized insns generated for level == (0|1) in defs/opt_operand.def */ + for (i = 0; i < lev; i++) { + ep = GET_PREV_EP(ep); + } + *(ep - idx) = val; +} + +/** + @c variable + @e Get value of special local variable ($~, $_, ..). + @j 特殊なローカル変数($~, $_, ...)の値を得る。 + */ +DEFINE_INSN +getspecial +(rb_num_t key, rb_num_t type) +() +(VALUE val) +{ + val = vm_getspecial(th, GET_LEP(), key, type); +} + +/** + @c variable + @e Set value of special local variable ($~, $_, ...) to obj. + @j 特別なローカル変数($~, $_, ...)の値を設定する。 + */ +DEFINE_INSN +setspecial +(rb_num_t key) +(VALUE obj) +() +{ + lep_svar_set(th, GET_LEP(), key, obj); +} + +/** + @c variable + @e Get value of instance variable id of self. + If is_local is not 0, get value of class local variable. + @j self のインスタンス変数 id の値を得る。 + */ +DEFINE_INSN +getinstancevariable +(ID id, IC ic) +() +(VALUE val) +{ + val = vm_getinstancevariable(GET_SELF(), id, ic); +} + +/** + @c variable + @e Set value of instance variable id of self to val. + If is_local is not 0, set value of class local variable. + @j self のインスタンス変数 id を val にする。 + */ +DEFINE_INSN +setinstancevariable +(ID id, IC ic) +(VALUE val) +() +{ + vm_setinstancevariable(GET_SELF(), id, val, ic); +} + +/** + @c variable + @e Get value of class variable id of klass as val. + @j 現在のスコープのクラス変数 id の値を得る。 + */ +DEFINE_INSN +getclassvariable +(ID id) +() +(VALUE val) +{ + NODE *cref = rb_vm_get_cref(GET_ISEQ(), GET_EP()); + val = rb_cvar_get(vm_get_cvar_base(cref, GET_CFP()), id); +} + +/** + @c variable + @e Set value of class variable id of klass as val. + @j klass のクラス変数 id を val にする。 + */ +DEFINE_INSN +setclassvariable +(ID id) +(VALUE val) +() +{ + NODE *cref = rb_vm_get_cref(GET_ISEQ(), GET_EP()); + rb_cvar_set(vm_get_cvar_base(cref, GET_CFP()), id, val); +} + +/** + @c variable + @e + Get constant variable id. If klass is Qnil, constants + are searched in the current scope. If klass is Qfalse, constants + are searched as top level constants. Otherwise, get constant under klass + class or module. + @j 定数 id の値を得る。 + klass が Qnil なら、そのスコープで得られる定数の値を得る。 + Qfalse なら、トップレベルスコープを得る。 + それ以外なら、klass クラスの下の定数を得る。 + */ +DEFINE_INSN +getconstant +(ID id) +(VALUE klass) +(VALUE val) +{ + val = vm_get_ev_const(th, GET_ISEQ(), klass, id, 0); +} + +/** + @c variable + @e + Set constant variable id. If klass is Qfalse, constant + is able to access in this scope. if klass is Qnil, set + top level constant. otherwise, set constant under klass + class or module. + + @j 定数 id の値を val にする。 + klass が Qfalse なら、そのスコープで得られる定数 id の値を設定する。 + Qnil なら、トップレベルスコープの値を設定する。 + それ以外なら、klass クラスの下の定数を設定する。 + */ +DEFINE_INSN +setconstant +(ID id) +(VALUE val, VALUE cbase) +() +{ + vm_check_if_namespace(cbase); + rb_const_set(cbase, id, val); +} + +/** + @c variable + @e get global variable id. + @j グローバル変数 id の値を得る。 + */ +DEFINE_INSN +getglobal +(GENTRY entry) +() +(VALUE val) +{ + val = GET_GLOBAL((VALUE)entry); +} + +/** + @c variable + @e set global variable id as val. + @j グローバル変数 id の値を設定する。 + */ +DEFINE_INSN +setglobal +(GENTRY entry) +(VALUE val) +() +{ + SET_GLOBAL((VALUE)entry, val); +} + + +/**********************************************************/ +/* deal with values */ +/**********************************************************/ + +/** + @c put + @e put nil to stack. + @j スタックに nil をプッシュする。 + */ +DEFINE_INSN +putnil +() +() +(VALUE val) +{ + val = Qnil; +} + +/** + @c put + @e put self. + @j スタックに self をプッシュする。 + */ +DEFINE_INSN +putself +() +() +(VALUE val) +{ + val = GET_SELF(); +} + +/** + @c put + @e put some object. + i.e. Fixnum, true, false, nil, and so on. + @j オブジェクト val をスタックにプッシュする。 + i.e. Fixnum, true, false, nil, and so on. + */ +DEFINE_INSN +putobject +(VALUE val) +() +(VALUE val) +{ + /* */ +} + +/** + @c put + @e put special object. "value_type" is for expansion. + @j 特別なオブジェクト val をスタックにプッシュする。 + オブジェクトの種類は value_type による. + */ +DEFINE_INSN +putspecialobject +(rb_num_t value_type) +() +(VALUE val) +{ + enum vm_special_object_type type = (enum vm_special_object_type)value_type; + + switch (type) { + case VM_SPECIAL_OBJECT_VMCORE: + val = rb_mRubyVMFrozenCore; + break; + case VM_SPECIAL_OBJECT_CBASE: + val = vm_get_cbase(GET_ISEQ(), GET_EP()); + break; + case VM_SPECIAL_OBJECT_CONST_BASE: + val = vm_get_const_base(GET_ISEQ(), GET_EP()); + break; + default: + rb_bug("putspecialobject insn: unknown value_type"); + } +} + +/** + @c put + @e put iseq value. + @j put iseq value. + */ +DEFINE_INSN +putiseq +(ISEQ iseq) +() +(VALUE ret) +{ + ret = iseq->self; +} + +/** + @c put + @e put string val. string will be copied. + @j 文字列をコピーしてスタックにプッシュする。 + */ +DEFINE_INSN +putstring +(VALUE str) +() +(VALUE val) +{ + val = rb_str_resurrect(str); +} + +/** + @c put + @e put concatenate strings + @j スタックトップの文字列を n 個連結し,結果をスタックにプッシュする。 + */ +DEFINE_INSN +concatstrings +(rb_num_t num) +(...) +(VALUE val) // inc += 1 - num; +{ + rb_num_t i = num - 1; + + val = rb_str_resurrect(TOPN(i)); + while (i-- > 0) { + const VALUE v = TOPN(i); + rb_str_append(val, v); + } + POPN(num); +} + +/** + @c put + @e to_str + @j to_str の結果をスタックにプッシュする。 + */ +DEFINE_INSN +tostring +() +(VALUE val) +(VALUE val) +{ + val = rb_obj_as_string(val); +} + +/** + @c put + @e to Regexp + @j 文字列 str を正規表現にコンパイルしてスタックにプッシュする。 + コンパイル時,opt を正規表現のオプションとする。 + */ +DEFINE_INSN +toregexp +(rb_num_t opt, rb_num_t cnt) +(...) +(VALUE val) // inc += 1 - cnt; +{ + VALUE rb_reg_new_ary(VALUE ary, int options); + rb_num_t i; + const VALUE ary = rb_ary_tmp_new(cnt); + for (i = 0; i < cnt; i++) { + rb_ary_store(ary, cnt-i-1, TOPN(i)); + } + POPN(cnt); + val = rb_reg_new_ary(ary, (int)opt); + rb_ary_clear(ary); +} + +/** + @c put + @e put new array. + @j 新しい配列をスタック上の num 個の値で初期化して生成しプッシュする。 + */ +DEFINE_INSN +newarray +(rb_num_t num) +(...) +(VALUE val) // inc += 1 - num; +{ + val = rb_ary_new4((long)num, STACK_ADDR_FROM_TOP(num)); + POPN(num); +} + +/** + @c put + @e dup array + @j 配列 ary を dup してスタックにプッシュする。 + */ +DEFINE_INSN +duparray +(VALUE ary) +() +(VALUE val) +{ + val = rb_ary_resurrect(ary); +} + +/** + @c put + @e expand array to num objects. + @j スタックトップのオブジェクトが配列であれば、それを展開する。 + 配列オブジェクトの要素数が num以下ならば、代わりに nil を積む。num以上なら、 + num以上の要素は切り捨てる。 + 配列オブジェクトでなければ、num - 1 個の nil を積む。 + もし flag が真なら、残り要素の配列を積む + flag: 0x01 - 最後を配列に + flag: 0x02 - postarg 用 + flag: 0x04 - reverse? + */ +DEFINE_INSN +expandarray +(rb_num_t num, rb_num_t flag) +(..., VALUE ary) +(...) // inc += num - 1 + (flag & 1 ? 1 : 0); +{ + vm_expandarray(GET_CFP(), ary, num, (int)flag); +} + +/** + @c put + @e concat two arrays + @j 二つの配列 ary1, ary2 を連結しスタックへプッシュする。 + */ +DEFINE_INSN +concatarray +() +(VALUE ary1, VALUE ary2st) +(VALUE ary) +{ + const VALUE ary2 = ary2st; + VALUE tmp1 = rb_check_convert_type(ary1, T_ARRAY, "Array", "to_a"); + VALUE tmp2 = rb_check_convert_type(ary2, T_ARRAY, "Array", "to_a"); + + if (NIL_P(tmp1)) { + tmp1 = rb_ary_new3(1, ary1); + } + + if (NIL_P(tmp2)) { + tmp2 = rb_ary_new3(1, ary2); + } + + if (tmp1 == ary1) { + tmp1 = rb_ary_dup(ary1); + } + ary = rb_ary_concat(tmp1, tmp2); +} + +/** + @c put + @e splat array + @j 配列 ary に対して to_a を呼び出す。 + */ +DEFINE_INSN +splatarray +(VALUE flag) +(VALUE ary) +(VALUE obj) +{ + VALUE tmp = rb_check_convert_type(ary, T_ARRAY, "Array", "to_a"); + if (NIL_P(tmp)) { + tmp = rb_ary_new3(1, ary); + } + else if (RTEST(flag)) { + tmp = rb_ary_dup(tmp); + } + obj = tmp; +} + +/** + @c put + @e put new Hash. + @j 新しいハッシュをスタックトップの n 個を初期値として生成する。 + n はキーと値のペアなので 2 の倍数でなければならない。 + */ +DEFINE_INSN +newhash +(rb_num_t num) +(...) +(VALUE val) // inc += 1 - num; +{ + rb_num_t i; + + if(RUBY_DTRACE_HASH_CREATE_ENABLED()) { + RUBY_DTRACE_HASH_CREATE(num, rb_sourcefile(), rb_sourceline()); + } + + val = rb_hash_new(); + + for (i = num; i > 0; i -= 2) { + const VALUE v = TOPN(i - 2); + const VALUE k = TOPN(i - 1); + rb_hash_aset(val, k, v); + } + POPN(num); +} + +/** + @c put + @e put new Range object.(Range.new(low, high, flag)) + @j Range.new(low, high, flag) のようなオブジェクトを生成しスタックにプッシュする。 + */ +DEFINE_INSN +newrange +(rb_num_t flag) +(VALUE low, VALUE high) +(VALUE val) +{ + val = rb_range_new(low, high, (int)flag); +} + +/**********************************************************/ +/* deal with stack operation */ +/**********************************************************/ + +/** + @c stack + @e pop from stack. + @j スタックから一つポップする。 + */ +DEFINE_INSN +pop +() +(VALUE val) +() +{ + (void)val; + /* none */ +} + +/** + @c stack + @e duplicate stack top. + @j スタックトップをコピーしてスタックにプッシュする。 + */ +DEFINE_INSN +dup +() +(VALUE val) +(VALUE val1, VALUE val2) +{ + val1 = val2 = val; +} + +/** + @c stack + @e duplicate stack top n elements + @j スタックトップの n 個をコピーしてスタックにプッシュする。 + */ +DEFINE_INSN +dupn +(rb_num_t n) +(...) +(...) // inc += n; +{ + rb_num_t i; + VALUE *sp = STACK_ADDR_FROM_TOP(n); + for (i = 0; i < n; i++) { + GET_SP()[i] = sp[i]; + } + INC_SP(n); +} + + +/** + @c stack + @e swap top 2 vals + @j スタックトップの 2 つの値を交換する。 + */ +DEFINE_INSN +swap +() +(VALUE val, VALUE obj) +(VALUE obj, VALUE val) +{ + /* none */ +} + +/** + @c stack + @e for stack caching. + @j スタックキャッシングの状態を調整するために必要な命令。 + */ +DEFINE_INSN +reput +() +(..., VALUE val) +(VALUE val) // inc += 0; +{ + /* none */ +} + +/** + @c stack + @e get nth stack value from stack top + @j スタックトップから n 個目をスタックにプッシュする。 + */ +DEFINE_INSN +topn +(rb_num_t n) +(...) +(VALUE val) // inc += 1; +{ + val = TOPN(n); +} + +/** + @c stack + @e set Nth stack entry to stack top + @j スタックトップの値を n 個目のスタックにコピー + */ +DEFINE_INSN +setn +(rb_num_t n) +(..., VALUE val) +(VALUE val) // inc += 0 +{ + TOPN(n-1) = val; +} + +/** + @c stack + @e empt current stack + @j current stack を空にする。 + */ +DEFINE_INSN +adjuststack +(rb_num_t n) +(...) +(...) // inc -= n +{ + DEC_SP(n); +} + + +/**********************************************************/ +/* deal with setting */ +/**********************************************************/ + +/** + @c setting + @e defined? + @j defined? を行う。 + */ +DEFINE_INSN +defined +(rb_num_t op_type, VALUE obj, VALUE needstr) +(VALUE v) +(VALUE val) +{ + VALUE klass; + enum defined_type expr_type = 0; + enum defined_type type = (enum defined_type)op_type; + + val = Qnil; + + switch (type) { + case DEFINED_IVAR: + if (rb_ivar_defined(GET_SELF(), SYM2ID(obj))) { + expr_type = DEFINED_IVAR; + } + break; + case DEFINED_IVAR2: + klass = vm_get_cbase(GET_ISEQ(), GET_EP()); + break; + case DEFINED_GVAR: + if (rb_gvar_defined(rb_global_entry(SYM2ID(obj)))) { + expr_type = DEFINED_GVAR; + } + break; + case DEFINED_CVAR: { + NODE *cref = rb_vm_get_cref(GET_ISEQ(), GET_EP()); + klass = vm_get_cvar_base(cref, GET_CFP()); + if (rb_cvar_defined(klass, SYM2ID(obj))) { + expr_type = DEFINED_CVAR; + } + break; + } + case DEFINED_CONST: + klass = v; + if (vm_get_ev_const(th, GET_ISEQ(), klass, SYM2ID(obj), 1)) { + expr_type = DEFINED_CONST; + } + break; + case DEFINED_FUNC: + klass = CLASS_OF(v); + if (rb_method_boundp(klass, SYM2ID(obj), 0)) { + expr_type = DEFINED_METHOD; + } + break; + case DEFINED_METHOD:{ + VALUE klass = CLASS_OF(v); + const rb_method_entry_t *me = rb_method_entry(klass, SYM2ID(obj), 0); + + if (me) { + if (!(me->flag & NOEX_PRIVATE)) { + if (!((me->flag & NOEX_PROTECTED) && + !rb_obj_is_kind_of(GET_SELF(), + rb_class_real(klass)))) { + expr_type = DEFINED_METHOD; + } + } + } + { + VALUE args[2]; + VALUE r; + + args[0] = obj; args[1] = Qfalse; + r = rb_check_funcall(v, idRespond_to_missing, 2, args); + if (r != Qundef && RTEST(r)) + expr_type = DEFINED_METHOD; + } + break; + } + case DEFINED_YIELD: + if (GET_BLOCK_PTR()) { + expr_type = DEFINED_YIELD; + } + break; + case DEFINED_ZSUPER:{ + rb_call_info_t cit; + if (vm_search_superclass(GET_CFP(), GET_ISEQ(), Qnil, &cit) == 0) { + VALUE klass = cit.klass; + ID id = cit.mid; + if (rb_method_boundp(klass, id, 0)) { + expr_type = DEFINED_ZSUPER; + } + } + break; + } + case DEFINED_REF:{ + val = vm_getspecial(th, GET_LEP(), Qfalse, FIX2INT(obj)); + if (val != Qnil) { + expr_type = DEFINED_GVAR; + } + break; + } + default: + rb_bug("unimplemented defined? type (VM)"); + break; + } + if (expr_type != 0) { + if (needstr != Qfalse) { + val = rb_iseq_defined_string(expr_type); + } + else { + val = Qtrue; + } + } +} + +/** + @c setting + @e check `target' matches `pattern'. + `flag & VM_CHECKMATCH_TYPE_MASK' describe how to check pattern. + VM_CHECKMATCH_TYPE_WHEN: ignore target and check pattern is truthy. + VM_CHECKMATCH_TYPE_CASE: check `patten === target'. + VM_CHECKMATCH_TYPE_RESCUE: check `pattern.kind_op?(Module) && pattern == target'. + if `flag & VM_CHECKMATCH_ARRAY' is not 0, then `patten' is array of patterns. + @j see above comments. + */ +DEFINE_INSN +checkmatch +(rb_num_t flag) +(VALUE target, VALUE pattern) +(VALUE result) +{ + enum vm_check_match_type checkmatch_type = + (enum vm_check_match_type)(flag & VM_CHECKMATCH_TYPE_MASK); + result = Qfalse; + + if (flag & VM_CHECKMATCH_ARRAY) { + int i; + for (i = 0; i < RARRAY_LEN(pattern); i++) { + if (RTEST(check_match(RARRAY_AREF(pattern, i), target, checkmatch_type))) { + result = Qtrue; + break; + } + } + } + else { + if (RTEST(check_match(pattern, target, checkmatch_type))) { + result = Qtrue; + } + } +} + +/** + @c setting + @e check keywords are specified or not. + @j キーワードが指定されているかどうかチェックする + */ +DEFINE_INSN +checkkeyword +(lindex_t kw_bits_index, rb_num_t keyword_index) +() +(VALUE ret) +{ + const VALUE *ep = GET_EP(); + const VALUE kw_bits = *(ep - kw_bits_index); + + if (FIXNUM_P(kw_bits)) { + int bits = FIX2INT(kw_bits); + ret = (bits & (0x01 << keyword_index)) ? Qfalse : Qtrue; + } + else { + assert(RB_TYPE_P(kw_bits, T_HASH)); + ret = rb_hash_has_key(kw_bits, INT2FIX(keyword_index)) ? Qfalse : Qtrue; + } +} + +/** + @c setting + @e trace + @j trace 用の命令。 + */ +DEFINE_INSN +trace +(rb_num_t nf) +() +() +{ + rb_event_flag_t flag = (rb_event_flag_t)nf; + + if (RUBY_DTRACE_METHOD_ENTRY_ENABLED() || + RUBY_DTRACE_METHOD_RETURN_ENABLED() || + RUBY_DTRACE_CMETHOD_ENTRY_ENABLED() || + RUBY_DTRACE_CMETHOD_RETURN_ENABLED()) { + + switch(flag) { + case RUBY_EVENT_CALL: + RUBY_DTRACE_METHOD_ENTRY_HOOK(th, 0, 0); + break; + case RUBY_EVENT_C_CALL: + RUBY_DTRACE_CMETHOD_ENTRY_HOOK(th, 0, 0); + break; + case RUBY_EVENT_RETURN: + RUBY_DTRACE_METHOD_RETURN_HOOK(th, 0, 0); + break; + case RUBY_EVENT_C_RETURN: + RUBY_DTRACE_CMETHOD_RETURN_HOOK(th, 0, 0); + break; + } + } + + EXEC_EVENT_HOOK(th, flag, GET_SELF(), 0, 0 /* id and klass are resolved at callee */, + (flag & (RUBY_EVENT_RETURN | RUBY_EVENT_B_RETURN)) ? TOPN(0) : Qundef); +} + +/**********************************************************/ +/* deal with control flow 1: class/module */ +/**********************************************************/ + +/** + @c class/module + @e + enter class definition scope. if super is Qfalse, and class + "klass" is defined, it's redefine. otherwise, define "klass" class. + @j クラス定義スコープへ移行する。 + もし super が Qfalse で klassクラスが定義されていれば再定義である。 + そうでなければ、klass クラスを定義する。 + */ +DEFINE_INSN +defineclass +(ID id, ISEQ class_iseq, rb_num_t flags) +(VALUE cbase, VALUE super) +(VALUE val) +{ + VALUE klass; + rb_vm_defineclass_type_t type = VM_DEFINECLASS_TYPE(flags); + + switch (type) { + case VM_DEFINECLASS_TYPE_CLASS: + /* val is dummy. classdef returns class scope value */ + + if (VM_DEFINECLASS_HAS_SUPERCLASS_P(flags) && + !RB_TYPE_P(super, T_CLASS)) { + rb_raise(rb_eTypeError, "superclass must be a Class (%s given)", + rb_obj_classname(super)); + } + + if (super == Qnil) { + super = rb_cObject; + } + + vm_check_if_namespace(cbase); + + /* find klass */ + rb_autoload_load(cbase, id); + if ((klass = vm_search_const_defined_class(cbase, id)) != 0) { + /* already exist */ + klass = VM_DEFINECLASS_SCOPED_P(flags) ? + rb_public_const_get_at(klass, id) : rb_const_get_at(klass, id); + if (!RB_TYPE_P(klass, T_CLASS)) { + rb_raise(rb_eTypeError, "%s is not a class", rb_id2name(id)); + } + + if (super != rb_cObject) { + VALUE tmp; + tmp = rb_class_real(RCLASS_SUPER(klass)); + + if (tmp != super) { + rb_raise(rb_eTypeError, "superclass mismatch for class %s", + rb_id2name(id)); + } + } + } + else { + /* new class declaration */ + klass = rb_define_class_id(id, super); + rb_set_class_path_string(klass, cbase, rb_id2str(id)); + rb_const_set(cbase, id, klass); + rb_class_inherited(super, klass); + } + break; + case VM_DEFINECLASS_TYPE_SINGLETON_CLASS: + /* val is dummy. classdef returns class scope value */ + /* super is dummy */ + klass = rb_singleton_class(cbase); + break; + case VM_DEFINECLASS_TYPE_MODULE: + /* val is dummy. classdef returns class scope value */ + /* super is dummy */ + + vm_check_if_namespace(cbase); + + /* find klass */ + if ((klass = vm_search_const_defined_class(cbase, id)) != 0) { + klass = VM_DEFINECLASS_SCOPED_P(flags) ? + rb_public_const_get_at(klass, id) : rb_const_get_at(klass, id); + /* already exist */ + if (!RB_TYPE_P(klass, T_MODULE)) { + rb_raise(rb_eTypeError, "%s is not a module", rb_id2name(id)); + } + } + else { + /* new module declaration */ + klass = rb_define_module_id(id); + rb_set_class_path_string(klass, cbase, rb_id2str(id)); + rb_const_set(cbase, id, klass); + } + break; + default: + rb_bug("unknown defineclass type: %d", (int)type); + } + + COPY_CREF(class_iseq->cref_stack, vm_cref_push(th, klass, NOEX_PUBLIC, NULL)); + + /* enter scope */ + vm_push_frame(th, class_iseq, VM_FRAME_MAGIC_CLASS, + klass, 0, VM_ENVVAL_BLOCK_PTR(GET_BLOCK_PTR()), + class_iseq->iseq_encoded, GET_SP(), + class_iseq->local_size, 0, class_iseq->stack_max); + RESTORE_REGS(); + NEXT_INSN(); +} + + +/**********************************************************/ +/* deal with control flow 2: method/iterator */ +/**********************************************************/ + +/** + @c method/iterator + @e invoke method. + @j メソッド呼び出しを行う。ci に必要な情報が格納されている。 + */ +DEFINE_INSN +send +(CALL_INFO ci) +(...) +(VALUE val) // inc += - (int)(ci->orig_argc + ((ci->flag & VM_CALL_ARGS_BLOCKARG) ? 1 : 0)); +{ + ci->argc = ci->orig_argc; + vm_caller_setup_arg_block(th, reg_cfp, ci, FALSE); + vm_search_method(ci, ci->recv = TOPN(ci->argc)); + CALL_METHOD(ci); +} + +DEFINE_INSN +opt_str_freeze +(VALUE str) +() +(VALUE val) +{ + if (BASIC_OP_UNREDEFINED_P(BOP_FREEZE, STRING_REDEFINED_OP_FLAG)) { + val = str; + } + else { + val = rb_funcall(rb_str_resurrect(str), idFreeze, 0); + } +} + +/** + @c optimize + @e Invoke method without block + @j Invoke method without block + */ +DEFINE_INSN +opt_send_without_block +(CALL_INFO ci) +(...) +(VALUE val) // inc += -ci->orig_argc; +{ + ci->argc = ci->orig_argc; + vm_search_method(ci, ci->recv = TOPN(ci->argc)); + CALL_METHOD(ci); +} + +/** + @c method/iterator + @e super(args) # args.size => num + @j super を実行する。ci に必要な情報が格納されている。 + */ +DEFINE_INSN +invokesuper +(CALL_INFO ci) +(...) +(VALUE val) // inc += - (int)(ci->orig_argc + ((ci->flag & VM_CALL_ARGS_BLOCKARG) ? 1 : 0)); +{ + ci->argc = ci->orig_argc; + vm_caller_setup_arg_block(th, reg_cfp, ci, TRUE); + ci->recv = GET_SELF(); + vm_search_super_method(th, GET_CFP(), ci); + CALL_METHOD(ci); +} + +/** + @c method/iterator + @e yield(args) + @j yield を実行する。 + */ +DEFINE_INSN +invokeblock +(CALL_INFO ci) +(...) +(VALUE val) // inc += 1 - ci->orig_argc; +{ + ci->argc = ci->orig_argc; + ci->blockptr = 0; + ci->recv = GET_SELF(); + val = vm_invoke_block(th, GET_CFP(), ci); + if (val == Qundef) { + RESTORE_REGS(); + NEXT_INSN(); + } +} + +/** + @c method/iterator + @e return from this scope. + @j このスコープから抜ける。 + */ +DEFINE_INSN +leave +() +(VALUE val) +(VALUE val) +{ + if (OPT_CHECKED_RUN) { + if (reg_cfp->sp != vm_base_ptr(reg_cfp)) { + rb_bug("Stack consistency error (sp: %"PRIdPTRDIFF", bp: %"PRIdPTRDIFF")", + VM_SP_CNT(th, reg_cfp->sp), VM_SP_CNT(th, vm_base_ptr(reg_cfp))); + } + } + + RUBY_VM_CHECK_INTS(th); + + if (UNLIKELY(VM_FRAME_TYPE_FINISH_P(GET_CFP()))) { + vm_pop_frame(th); + +#if OPT_CALL_THREADED_CODE + th->retval = val; + return 0; +#else + return val; +#endif + } + else { + vm_pop_frame(th); + RESTORE_REGS(); + } +} + +/**********************************************************/ +/* deal with control flow 3: exception */ +/**********************************************************/ + +/** + @c exception + @e longjump + @j 大域ジャンプを行う。 + */ +DEFINE_INSN +throw +(rb_num_t throw_state) +(VALUE throwobj) +(VALUE val) +{ + RUBY_VM_CHECK_INTS(th); + val = vm_throw(th, GET_CFP(), throw_state, throwobj); + THROW_EXCEPTION(val); + /* unreachable */ +} + +/**********************************************************/ +/* deal with control flow 4: local jump */ +/**********************************************************/ + +/** + @c jump + @e set PC to (PC + dst). + @j PC を (PC + dst) にする。 + */ +DEFINE_INSN +jump +(OFFSET dst) +() +() +{ + RUBY_VM_CHECK_INTS(th); + JUMP(dst); +} + +/** + @c jump + @e if val is not false or nil, set PC to (PC + dst). + @j もし val が false か nil でなければ、PC を (PC + dst) にする。 + */ +DEFINE_INSN +branchif +(OFFSET dst) +(VALUE val) +() +{ + if (RTEST(val)) { + RUBY_VM_CHECK_INTS(th); + JUMP(dst); + } +} + +/** + @c jump + @e if val is false or nil, set PC to (PC + dst). + @j もし val が false か nil ならば、PC を (PC + dst) にする。 + */ +DEFINE_INSN +branchunless +(OFFSET dst) +(VALUE val) +() +{ + if (!RTEST(val)) { + RUBY_VM_CHECK_INTS(th); + JUMP(dst); + } +} + + +/**********************************************************/ +/* for optimize */ +/**********************************************************/ + +/** + @c optimize + @e push inline-cached value and go to dst if it is valid + @j インラインキャッシュが有効なら、値をスタックにプッシュして dst へジャンプする。 + */ +DEFINE_INSN +getinlinecache +(OFFSET dst, IC ic) +() +(VALUE val) +{ + if (ic->ic_serial == GET_GLOBAL_CONSTANT_STATE()) { + val = ic->ic_value.value; + JUMP(dst); + } + else { + /* none */ + val = Qnil; + } +} + +/** + @c optimize + @e set inline cache + @j インラインキャッシュの値を設定する。 + */ +DEFINE_INSN +setinlinecache +(IC ic) +(VALUE val) +(VALUE val) +{ + if (ic->ic_value.value == Qundef) { + rb_iseq_add_mark_object(GET_ISEQ(), val); + } + ic->ic_value.value = val; + ic->ic_serial = GET_GLOBAL_CONSTANT_STATE() - ruby_vm_const_missing_count; + ruby_vm_const_missing_count = 0; +} + +/** + @c optimize + @e run iseq only once + @j once を実現する。 + */ +DEFINE_INSN +once +(ISEQ iseq, IC ic) +() +(VALUE val) +{ + union iseq_inline_storage_entry *is = (union iseq_inline_storage_entry *)ic; + +#define RUNNING_THREAD_ONCE_DONE ((rb_thread_t *)(0x1)) + retry: + if (is->once.running_thread == RUNNING_THREAD_ONCE_DONE) { + val = is->once.value; + } + else if (is->once.running_thread == NULL) { + is->once.running_thread = th; + val = is->once.value = rb_ensure(vm_once_exec, (VALUE)iseq, vm_once_clear, (VALUE)is); + /* is->once.running_thread is cleared by vm_once_clear() */ + is->once.running_thread = RUNNING_THREAD_ONCE_DONE; /* success */ + rb_iseq_add_mark_object(GET_ISEQ(), val); + } + else if (is->once.running_thread == th) { + /* recursive once */ + val = vm_once_exec((VALUE)iseq); + } + else { + /* waiting for finish */ + RUBY_VM_CHECK_INTS(th); + rb_thread_schedule(); + goto retry; + } +} + +/** + @c optimize + @e case dispatcher, jump by table if possible + @j case 文で、可能なら表引きでジャンプする。 + */ +DEFINE_INSN +opt_case_dispatch +(CDHASH hash, OFFSET else_offset) +(..., VALUE key) +() // inc += -1; +{ + switch(TYPE(key)) { + case T_FLOAT: { + double ival; + if (modf(RFLOAT_VALUE(key), &ival) == 0.0) { + key = FIXABLE(ival) ? LONG2FIX((long)ival) : rb_dbl2big(ival); + } + } + case T_SYMBOL: /* fall through */ + case T_FIXNUM: + case T_BIGNUM: + case T_STRING: + if (BASIC_OP_UNREDEFINED_P(BOP_EQQ, + SYMBOL_REDEFINED_OP_FLAG | + FIXNUM_REDEFINED_OP_FLAG | + BIGNUM_REDEFINED_OP_FLAG | + STRING_REDEFINED_OP_FLAG)) { + st_data_t val; + if (st_lookup(RHASH_TBL_RAW(hash), key, &val)) { + JUMP(FIX2INT((VALUE)val)); + } + else { + JUMP(else_offset); + } + break; + } + default: + break; + } +} + +/** simple functions */ + +/** + @c optimize + @e optimized X+Y. + @j 最適化された X+Y。 + */ +DEFINE_INSN +opt_plus +(CALL_INFO ci) +(VALUE recv, VALUE obj) +(VALUE val) +{ + if (FIXNUM_2_P(recv, obj) && + BASIC_OP_UNREDEFINED_P(BOP_PLUS,FIXNUM_REDEFINED_OP_FLAG)) { + /* fixnum + fixnum */ +#ifndef LONG_LONG_VALUE + val = (recv + (obj & (~1))); + if ((~(recv ^ obj) & (recv ^ val)) & + ((VALUE)0x01 << ((sizeof(VALUE) * CHAR_BIT) - 1))) { + val = rb_big_plus(rb_int2big(FIX2LONG(recv)), + rb_int2big(FIX2LONG(obj))); + } +#else + long a, b, c; + a = FIX2LONG(recv); + b = FIX2LONG(obj); + c = a + b; + if (FIXABLE(c)) { + val = LONG2FIX(c); + } + else { + val = rb_big_plus(rb_int2big(a), rb_int2big(b)); + } +#endif + } + else if (FLONUM_2_P(recv, obj) && + BASIC_OP_UNREDEFINED_P(BOP_PLUS, FLOAT_REDEFINED_OP_FLAG)) { + val = DBL2NUM(RFLOAT_VALUE(recv) + RFLOAT_VALUE(obj)); + } + else if (!SPECIAL_CONST_P(recv) && !SPECIAL_CONST_P(obj)) { + if (RBASIC_CLASS(recv) == rb_cFloat && RBASIC_CLASS(obj) == rb_cFloat && + BASIC_OP_UNREDEFINED_P(BOP_PLUS, FLOAT_REDEFINED_OP_FLAG)) { + val = DBL2NUM(RFLOAT_VALUE(recv) + RFLOAT_VALUE(obj)); + } + else if (RBASIC_CLASS(recv) == rb_cString && RBASIC_CLASS(obj) == rb_cString && + BASIC_OP_UNREDEFINED_P(BOP_PLUS, STRING_REDEFINED_OP_FLAG)) { + val = rb_str_plus(recv, obj); + } + else if (RBASIC_CLASS(recv) == rb_cArray && + BASIC_OP_UNREDEFINED_P(BOP_PLUS, ARRAY_REDEFINED_OP_FLAG)) { + val = rb_ary_plus(recv, obj); + } + else { + goto INSN_LABEL(normal_dispatch); + } + } + else { + INSN_LABEL(normal_dispatch): + PUSH(recv); + PUSH(obj); + CALL_SIMPLE_METHOD(recv); + } +} + +/** + @c optimize + @e optimized X-Y. + @j 最適化された X-Y。 + */ +DEFINE_INSN +opt_minus +(CALL_INFO ci) +(VALUE recv, VALUE obj) +(VALUE val) +{ + if (FIXNUM_2_P(recv, obj) && + BASIC_OP_UNREDEFINED_P(BOP_MINUS, FIXNUM_REDEFINED_OP_FLAG)) { + long a, b, c; + + a = FIX2LONG(recv); + b = FIX2LONG(obj); + c = a - b; + + if (FIXABLE(c)) { + val = LONG2FIX(c); + } + else { + val = rb_big_minus(rb_int2big(a), rb_int2big(b)); + } + } + else if (FLONUM_2_P(recv, obj) && + BASIC_OP_UNREDEFINED_P(BOP_MINUS, FLOAT_REDEFINED_OP_FLAG)) { + val = DBL2NUM(RFLOAT_VALUE(recv) - RFLOAT_VALUE(obj)); + } + else if (!SPECIAL_CONST_P(recv) && !SPECIAL_CONST_P(obj)) { + if (RBASIC_CLASS(recv) == rb_cFloat && RBASIC_CLASS(obj) == rb_cFloat && + BASIC_OP_UNREDEFINED_P(BOP_MINUS, FLOAT_REDEFINED_OP_FLAG)) { + val = DBL2NUM(RFLOAT_VALUE(recv) - RFLOAT_VALUE(obj)); + } + else { + goto INSN_LABEL(normal_dispatch); + } + } + else { + /* other */ + INSN_LABEL(normal_dispatch): + PUSH(recv); + PUSH(obj); + CALL_SIMPLE_METHOD(recv); + } +} + +/** + @c optimize + @e optimized X*Y. + @j 最適化された X*Y。 + */ +DEFINE_INSN +opt_mult +(CALL_INFO ci) +(VALUE recv, VALUE obj) +(VALUE val) +{ + if (FIXNUM_2_P(recv, obj) && + BASIC_OP_UNREDEFINED_P(BOP_MULT, FIXNUM_REDEFINED_OP_FLAG)) { + long a, b; + + a = FIX2LONG(recv); + if (a == 0) { + val = recv; + } + else { + b = FIX2LONG(obj); + if (MUL_OVERFLOW_FIXNUM_P(a, b)) { + val = rb_big_mul(rb_int2big(a), rb_int2big(b)); + } + else { + val = LONG2FIX(a * b); + } + } + } + else if (FLONUM_2_P(recv, obj) && + BASIC_OP_UNREDEFINED_P(BOP_MULT, FLOAT_REDEFINED_OP_FLAG)) { + val = DBL2NUM(RFLOAT_VALUE(recv) * RFLOAT_VALUE(obj)); + } + else if (!SPECIAL_CONST_P(recv) && !SPECIAL_CONST_P(obj)) { + if (RBASIC_CLASS(recv) == rb_cFloat && RBASIC_CLASS(obj) == rb_cFloat && + BASIC_OP_UNREDEFINED_P(BOP_MULT, FLOAT_REDEFINED_OP_FLAG)) { + val = DBL2NUM(RFLOAT_VALUE(recv) * RFLOAT_VALUE(obj)); + } + else { + goto INSN_LABEL(normal_dispatch); + } + } + else { + INSN_LABEL(normal_dispatch): + PUSH(recv); + PUSH(obj); + CALL_SIMPLE_METHOD(recv); + } +} + +/** + @c optimize + @e optimized X/Y. + @j 最適化された X/Y。 + */ +DEFINE_INSN +opt_div +(CALL_INFO ci) +(VALUE recv, VALUE obj) +(VALUE val) +{ + if (FIXNUM_2_P(recv, obj) && + BASIC_OP_UNREDEFINED_P(BOP_DIV, FIXNUM_REDEFINED_OP_FLAG)) { + long x, y, div; + + x = FIX2LONG(recv); + y = FIX2LONG(obj); + { + /* copied from numeric.c#fixdivmod */ + long mod; + if (y == 0) + goto INSN_LABEL(normal_dispatch); + if (y < 0) { + if (x < 0) + div = -x / -y; + else + div = -(x / -y); + } + else { + if (x < 0) + div = -(-x / y); + else + div = x / y; + } + mod = x - div * y; + if ((mod < 0 && y > 0) || (mod > 0 && y < 0)) { + mod += y; + div -= 1; + } + } + val = LONG2NUM(div); + } + else if (FLONUM_2_P(recv, obj) && + BASIC_OP_UNREDEFINED_P(BOP_DIV, FLOAT_REDEFINED_OP_FLAG)) { + val = DBL2NUM(RFLOAT_VALUE(recv) / RFLOAT_VALUE(obj)); + } + else if (!SPECIAL_CONST_P(recv) && !SPECIAL_CONST_P(obj)) { + if (RBASIC_CLASS(recv) == rb_cFloat && RBASIC_CLASS(obj) == rb_cFloat && + BASIC_OP_UNREDEFINED_P(BOP_DIV, FLOAT_REDEFINED_OP_FLAG)) { + val = DBL2NUM(RFLOAT_VALUE(recv) / RFLOAT_VALUE(obj)); + } + else { + goto INSN_LABEL(normal_dispatch); + } + } + else { + INSN_LABEL(normal_dispatch): + PUSH(recv); + PUSH(obj); + CALL_SIMPLE_METHOD(recv); + } +} + +/** + @c optimize + @e optimized X%Y. + @j 最適化された X%Y。 + */ +DEFINE_INSN +opt_mod +(CALL_INFO ci) +(VALUE recv, VALUE obj) +(VALUE val) +{ + if (FIXNUM_2_P(recv, obj) && + BASIC_OP_UNREDEFINED_P(BOP_MOD, FIXNUM_REDEFINED_OP_FLAG )) { + long x, y; + + x = FIX2LONG(recv); + y = FIX2LONG(obj); + if (x > 0 && y > 0) { + val = LONG2FIX(x % y); + } + else { + /* copied from numeric.c#fixdivmod */ + long div, mod; + + if (y == 0) + rb_num_zerodiv(); + if (y < 0) { + if (x < 0) + div = -x / -y; + else + div = -(x / -y); + } + else { + if (x < 0) + div = -(-x / y); + else + div = x / y; + } + mod = x - div * y; + if ((mod < 0 && y > 0) || (mod > 0 && y < 0)) { + mod += y; + div -= 1; + } + val = LONG2FIX(mod); + } + } + else if (FLONUM_2_P(recv, obj) && + BASIC_OP_UNREDEFINED_P(BOP_MOD, FLOAT_REDEFINED_OP_FLAG)) { + val = DBL2NUM(ruby_float_mod(RFLOAT_VALUE(recv), RFLOAT_VALUE(obj))); + } + else if (!SPECIAL_CONST_P(recv) && !SPECIAL_CONST_P(obj)) { + if (RBASIC_CLASS(recv) == rb_cFloat && RBASIC_CLASS(obj) == rb_cFloat && + BASIC_OP_UNREDEFINED_P(BOP_MOD, FLOAT_REDEFINED_OP_FLAG)) { + val = DBL2NUM(ruby_float_mod(RFLOAT_VALUE(recv), RFLOAT_VALUE(obj))); + } + else { + goto INSN_LABEL(normal_dispatch); + } + } + else { + INSN_LABEL(normal_dispatch): + PUSH(recv); + PUSH(obj); + CALL_SIMPLE_METHOD(recv); + } +} + +/** + @c optimize + @e optimized X==Y. + @j 最適化された X==Y。 + */ +DEFINE_INSN +opt_eq +(CALL_INFO ci) +(VALUE recv, VALUE obj) +(VALUE val) +{ + val = opt_eq_func(recv, obj, ci); + + if (val == Qundef) { + /* other */ + PUSH(recv); + PUSH(obj); + CALL_SIMPLE_METHOD(recv); + } +} + +/** + @c optimize + @e optimized X!=Y. + @j 最適化された X!=Y。 + */ +DEFINE_INSN +opt_neq +(CALL_INFO ci, CALL_INFO ci_eq) +(VALUE recv, VALUE obj) +(VALUE val) +{ + extern VALUE rb_obj_not_equal(VALUE obj1, VALUE obj2); + vm_search_method(ci, recv); + val = Qundef; + + if (check_cfunc(ci->me, rb_obj_not_equal)) { + val = opt_eq_func(recv, obj, ci_eq); + + if (val != Qundef) { + val = RTEST(val) ? Qfalse : Qtrue; + } + } + + if (val == Qundef) { + /* other */ + PUSH(recv); + PUSH(obj); + CALL_SIMPLE_METHOD(recv); + } +} + +/** + @c optimize + @e optimized XY. + @j 最適化された X>Y。 + */ +DEFINE_INSN +opt_gt +(CALL_INFO ci) +(VALUE recv, VALUE obj) +(VALUE val) +{ + if (FIXNUM_2_P(recv, obj) && + BASIC_OP_UNREDEFINED_P(BOP_GT, FIXNUM_REDEFINED_OP_FLAG)) { + SIGNED_VALUE a = recv, b = obj; + + if (a > b) { + val = Qtrue; + } + else { + val = Qfalse; + } + } + else if (FLONUM_2_P(recv, obj) && + BASIC_OP_UNREDEFINED_P(BOP_GT, FLOAT_REDEFINED_OP_FLAG)) { + /* flonum is not NaN */ + val = RFLOAT_VALUE(recv) > RFLOAT_VALUE(obj) ? Qtrue : Qfalse; + } + else if (!SPECIAL_CONST_P(recv) && !SPECIAL_CONST_P(obj)) { + if (RBASIC_CLASS(recv) == rb_cFloat && RBASIC_CLASS(obj) == rb_cFloat && + BASIC_OP_UNREDEFINED_P(BOP_GT, FLOAT_REDEFINED_OP_FLAG)) { + val = double_cmp_gt(RFLOAT_VALUE(recv), RFLOAT_VALUE(obj)); + } + else { + goto INSN_LABEL(normal_dispatch); + } + } + else { + INSN_LABEL(normal_dispatch): + PUSH(recv); + PUSH(obj); + CALL_SIMPLE_METHOD(recv); + } +} + +/** + @c optimize + @e optimized X>=Y. + @j 最適化された X>=Y。 + */ +DEFINE_INSN +opt_ge +(CALL_INFO ci) +(VALUE recv, VALUE obj) +(VALUE val) +{ + if (FIXNUM_2_P(recv, obj) && + BASIC_OP_UNREDEFINED_P(BOP_GE, FIXNUM_REDEFINED_OP_FLAG)) { + SIGNED_VALUE a = recv, b = obj; + + if (a >= b) { + val = Qtrue; + } + else { + val = Qfalse; + } + } + else if (FLONUM_2_P(recv, obj) && + BASIC_OP_UNREDEFINED_P(BOP_GE, FLOAT_REDEFINED_OP_FLAG)) { + /* flonum is not NaN */ + val = RFLOAT_VALUE(recv) >= RFLOAT_VALUE(obj) ? Qtrue : Qfalse; + } + else { + PUSH(recv); + PUSH(obj); + CALL_SIMPLE_METHOD(recv); + } +} + +/** + @c optimize + @e << + @j 最適化された X<me, rb_obj_not)) { + val = RTEST(recv) ? Qfalse : Qtrue; + } + else { + PUSH(recv); + CALL_SIMPLE_METHOD(recv); + } +} + + +/** + @c optimize + @e optimized regexp match + @j 最適化された正規表現マッチ。 + */ +DEFINE_INSN +opt_regexpmatch1 +(VALUE r) +(VALUE obj) +(VALUE val) +{ + if (BASIC_OP_UNREDEFINED_P(BOP_MATCH, REGEXP_REDEFINED_OP_FLAG)) { + val = rb_reg_match(r, obj); + } + else { + val = rb_funcall(r, idEqTilde, 1, obj); + } +} + +/** + @c optimize + @e optimized regexp match 2 + @j 最適化された正規表現マッチ 2 + */ +DEFINE_INSN +opt_regexpmatch2 +(CALL_INFO ci) +(VALUE obj2, VALUE obj1) +(VALUE val) +{ + if (CLASS_OF(obj2) == rb_cString && + BASIC_OP_UNREDEFINED_P(BOP_MATCH, STRING_REDEFINED_OP_FLAG)) { + val = rb_reg_match(obj1, obj2); + } + else { + PUSH(obj2); + PUSH(obj1); + CALL_SIMPLE_METHOD(obj2); + } +} + +/** + @c optimize + @e call native compiled method + @j ネイティブコンパイルしたメソッドを起動。 + */ +DEFINE_INSN +opt_call_c_function +(rb_insn_func_t funcptr) +() +() +{ + reg_cfp = (funcptr)(th, reg_cfp); + + if (reg_cfp == 0) { + VALUE err = th->errinfo; + th->errinfo = Qnil; + THROW_EXCEPTION(err); + } + + RESTORE_REGS(); + NEXT_INSN(); +} + +/** + @c joke + @e BLT + @j BLT + */ +DEFINE_INSN +bitblt +() +() +(VALUE ret) +{ + ret = rb_str_new2("a bit of bacon, lettuce and tomato"); +} + +/** + @c joke + @e The Answer to Life, the Universe, and Everything + @j 人生、宇宙、すべての答え。 + */ +DEFINE_INSN +answer +() +() +(VALUE ret) +{ + ret = INT2FIX(42); +} + -- cgit v1.2.3