diff options
Diffstat (limited to 'insns.def')
| -rw-r--r-- | insns.def | 2470 |
1 files changed, 1046 insertions, 1424 deletions
@@ -1,36 +1,62 @@ -/** ##skip -*- mode:c; style:ruby; coding: utf-8 -*- +/* -*- C -*- 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) - { + Massive rewrite by @shyouhei in 2017. + */ + +/* Some comments about this file's contents: + + - The new format aims to be editable by C editor of your choice; + your mileage might vary of course. + + - Each instructions are in following format: + + DEFINE_INSN + instruction_name + (type operand, type operand, ..) + (pop_values, ..) + (return values ..) + // attr type name contents.. + { .. // insn body - } + } - */ + - Unlike the old format which was line-oriented, you can now place + newlines and comments at liberal positions. + + - `DEFINE_INSN` is a keyword. + + - An instruction name must be a valid C identifier. + - Operands, pop values, return values are series of either variable + declarations, keyword `void`, or keyword `...`. They are much + like C function declarations. -/** - @c nop - @e nop - @j nop + - Attribute pragmas are optional, and can include arbitrary C + expressions. You can write anything there but as of writing, + supported attributes are: + + * sp_inc: Used to dynamically calculate sp increase in + `insn_stack_increase`. + + * handles_sp: If it is true, VM deals with sp in the insn. + Default is if the instruction takes ISEQ operand or not. + + * leaf: indicates that the instruction is "leaf" i.e. it does + not introduce new stack frame on top of it. + If an instruction handles sp, that can never be a leaf. + + - Attributes can access operands, but not stack (push/pop) variables. + + - An instruction's body is a pure C block, copied verbatimly into + the generated C source code. */ + +/* nop */ DEFINE_INSN nop () @@ -44,12 +70,8 @@ nop /* deal with variables */ /**********************************************************/ -/** - @c variable - @e Get local variable (pointed by `idx' and `level'). +/* Get local variable (pointed by `idx' and `level'). 'level' indicates the nesting depth from the current block. - @j level, idx で指定されたローカル変数の値をスタックに置く。 - level はブロックのネストレベルで、何段上かを示す。 */ DEFINE_INSN getlocal @@ -57,22 +79,13 @@ getlocal () (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); + val = *(vm_get_ep(GET_EP(), level) - idx); + RB_DEBUG_COUNTER_INC(lvar_get); + (void)RB_DEBUG_COUNTER_INC_IF(lvar_get_dynamic, level > 0); } -/** - @c variable - @e Set a local variable (pointed to by 'idx') as val. +/* 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 @@ -80,184 +93,239 @@ setlocal (VALUE val) () { - int i, lev = (int)level; - VALUE *ep = GET_EP(); + vm_env_write(vm_get_ep(GET_EP(), level), -(int)idx, val); + RB_DEBUG_COUNTER_INC(lvar_set); + (void)RB_DEBUG_COUNTER_INC_IF(lvar_set_dynamic, level > 0); +} + +/* Get a block parameter. */ +DEFINE_INSN +getblockparam +(lindex_t idx, rb_num_t level) +() +(VALUE val) +{ + const VALUE *ep = vm_get_ep(GET_EP(), level); + VM_ASSERT(VM_ENV_LOCAL_P(ep)); - /* optimized insns generated for level == (0|1) in defs/opt_operand.def */ - for (i = 0; i < lev; i++) { - ep = GET_PREV_EP(ep); + if (!VM_ENV_FLAGS(ep, VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM)) { + val = rb_vm_bh_to_procval(ec, VM_ENV_BLOCK_HANDLER(ep)); + vm_env_write(ep, -(int)idx, val); + VM_ENV_FLAGS_SET(ep, VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM); + } + else { + val = *(ep - idx); + RB_DEBUG_COUNTER_INC(lvar_get); + (void)RB_DEBUG_COUNTER_INC_IF(lvar_get_dynamic, level > 0); } - *(ep - idx) = val; } -/** - @c variable - @e Get value of special local variable ($~, $_, ..). - @j 特殊なローカル変数($~, $_, ...)の値を得る。 +/* Set block parameter. */ +DEFINE_INSN +setblockparam +(lindex_t idx, rb_num_t level) +(VALUE val) +() +{ + const VALUE *ep = vm_get_ep(GET_EP(), level); + VM_ASSERT(VM_ENV_LOCAL_P(ep)); + + vm_env_write(ep, -(int)idx, val); + RB_DEBUG_COUNTER_INC(lvar_set); + (void)RB_DEBUG_COUNTER_INC_IF(lvar_set_dynamic, level > 0); + + VM_ENV_FLAGS_SET(ep, VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM); +} + +/* Get special proxy object which only responds to `call` method if the block parameter + represents a iseq/ifunc block. Otherwise, same as `getblockparam`. */ DEFINE_INSN +getblockparamproxy +(lindex_t idx, rb_num_t level) +() +(VALUE val) +// attr bool zjit_profile = true; +{ + const VALUE *ep = vm_get_ep(GET_EP(), level); + VM_ASSERT(VM_ENV_LOCAL_P(ep)); + + if (!VM_ENV_FLAGS(ep, VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM)) { + VALUE block_handler = VM_ENV_BLOCK_HANDLER(ep); + + if (block_handler) { + switch (vm_block_handler_type(block_handler)) { + case block_handler_type_iseq: + case block_handler_type_ifunc: + val = rb_block_param_proxy; + break; + case block_handler_type_symbol: + val = rb_sym_to_proc(VM_BH_TO_SYMBOL(block_handler)); + goto INSN_LABEL(set); + case block_handler_type_proc: + val = VM_BH_TO_PROC(block_handler); + goto INSN_LABEL(set); + default: + VM_UNREACHABLE(getblockparamproxy); + } + } + else { + val = Qnil; + INSN_LABEL(set): + vm_env_write(ep, -(int)idx, val); + VM_ENV_FLAGS_SET(ep, VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM); + } + } + else { + val = *(ep - idx); + RB_DEBUG_COUNTER_INC(lvar_get); + (void)RB_DEBUG_COUNTER_INC_IF(lvar_get_dynamic, level > 0); + } +} + +/* Get value of special local variable ($~, $_, ..). */ +DEFINE_INSN getspecial (rb_num_t key, rb_num_t type) () (VALUE val) +/* `$~ = MatchData.allocate; $&` can raise. */ +// attr bool leaf = (type == 0) ? true : false; { - val = vm_getspecial(th, GET_LEP(), key, type); + val = vm_getspecial(ec, GET_LEP(), key, type); } -/** - @c variable - @e Set value of special local variable ($~, $_, ...) to obj. - @j 特別なローカル変数($~, $_, ...)の値を設定する。 - */ +/* Set value of special local variable ($~, $_, ...) to obj. */ DEFINE_INSN setspecial (rb_num_t key) (VALUE obj) () { - lep_svar_set(th, GET_LEP(), key, obj); + lep_svar_set(ec, 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 の値を得る。 - */ +/* Get value of instance variable id of self. */ DEFINE_INSN getinstancevariable -(ID id, IC ic) +(ID id, IVC ic) () (VALUE val) +/* Ractor crashes when it accesses class/module-level instances variables. */ +// attr bool leaf = false; /* has IVAR_ACCESSOR_SHOULD_BE_MAIN_RACTOR() */ +// attr bool zjit_profile = true; { - val = vm_getinstancevariable(GET_SELF(), id, ic); + val = vm_getinstancevariable(GET_ISEQ(), 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 にする。 - */ +/* Set value of instance variable id of self to val. */ DEFINE_INSN setinstancevariable -(ID id, IC ic) +(ID id, IVC ic) (VALUE val) () +// attr bool leaf = false; /* has rb_check_frozen() */ +// attr bool zjit_profile = true; { - vm_setinstancevariable(GET_SELF(), id, val, ic); + vm_setinstancevariable(GET_ISEQ(), GET_SELF(), id, val, ic); } -/** - @c variable - @e Get value of class variable id of klass as val. - @j 現在のスコープのクラス変数 id の値を得る。 - */ +/* Get value of class variable id of klass as val. */ DEFINE_INSN getclassvariable -(ID id) +(ID id, ICVARC ic) () (VALUE val) +/* "class variable access from toplevel" warning can be hooked. */ +// attr bool leaf = false; /* has rb_warning() */ { - val = rb_cvar_get(vm_get_cvar_base(rb_vm_get_cref(GET_EP()), GET_CFP()), id); + rb_control_frame_t *cfp = GET_CFP(); + val = vm_getclassvariable(GET_ISEQ(), cfp, id, ic); } -/** - @c variable - @e Set value of class variable id of klass as val. - @j klass のクラス変数 id を val にする。 - */ +/* Set value of class variable id of klass as val. */ DEFINE_INSN setclassvariable -(ID id) +(ID id, ICVARC ic) (VALUE val) () +/* "class variable access from toplevel" warning can be hooked. */ +// attr bool leaf = false; /* has rb_warning() */ +{ + vm_ensure_not_refinement_module(GET_SELF()); + vm_setclassvariable(GET_ISEQ(), GET_CFP(), id, val, ic); +} + +DEFINE_INSN +opt_getconstant_path +(IC ic) +() +(VALUE val) +// attr bool leaf = false; /* may autoload or raise */ { - rb_cvar_set(vm_get_cvar_base(rb_vm_get_cref(GET_EP()), GET_CFP()), id, val); + val = rb_vm_opt_getconstant_path(ec, GET_CFP(), ic); } -/** - @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 +/* Get constant variable id. If klass is Qnil and allow_nil is Qtrue, constants + are searched in the current scope. Otherwise, get constant under klass class or module. - @j 定数 id の値を得る。 - klass が Qnil なら、そのスコープで得られる定数の値を得る。 - Qfalse なら、トップレベルスコープを得る。 - それ以外なら、klass クラスの下の定数を得る。 */ DEFINE_INSN getconstant (ID id) -(VALUE klass) +(VALUE klass, VALUE allow_nil) (VALUE val) +/* getconstant can kick autoload */ +// attr bool leaf = false; /* has rb_autoload_load() */ { - val = vm_get_ev_const(th, klass, id, 0); + val = vm_get_ev_const(ec, klass, id, allow_nil == Qtrue, 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 クラスの下の定数を設定する。 +/* Set constant variable id under cbase class or module. */ DEFINE_INSN setconstant (ID id) (VALUE val, VALUE cbase) () +/* Assigning an object to a constant is basically a leaf operation. + * The problem is, assigning a Module instance to a constant _names_ + * that module. Naming involves string manipulations, which are + * method calls. */ +// attr bool leaf = false; /* has StringValue() */ { vm_check_if_namespace(cbase); + vm_ensure_not_refinement_module(GET_SELF()); rb_const_set(cbase, id, val); } -/** - @c variable - @e get global variable id. - @j グローバル変数 id の値を得る。 - */ +/* get global variable id. */ DEFINE_INSN getglobal -(GENTRY entry) +(ID gid) () (VALUE val) +// attr bool leaf = false; { - val = GET_GLOBAL((VALUE)entry); + val = rb_gvar_get(gid); } -/** - @c variable - @e set global variable id as val. - @j グローバル変数 id の値を設定する。 - */ +/* set global variable id as val. */ DEFINE_INSN setglobal -(GENTRY entry) +(ID gid) (VALUE val) () +// attr bool leaf = false; { - SET_GLOBAL((VALUE)entry, val); + rb_gvar_set(gid, val); } - /**********************************************************/ /* deal with values */ /**********************************************************/ -/** - @c put - @e put nil to stack. - @j スタックに nil をプッシュする。 - */ +/* put nil to stack. */ DEFINE_INSN putnil () @@ -267,11 +335,7 @@ putnil val = Qnil; } -/** - @c put - @e put self. - @j スタックに self をプッシュする。 - */ +/* put self. */ DEFINE_INSN putself () @@ -281,11 +345,7 @@ putself val = GET_SELF(); } -/** - @c put - @e put some object. - i.e. Fixnum, true, false, nil, and so on. - @j オブジェクト val をスタックにプッシュする。 +/* put some object. i.e. Fixnum, true, false, nil, and so on. */ DEFINE_INSN @@ -297,259 +357,253 @@ putobject /* */ } -/** - @c put - @e put special object. "value_type" is for expansion. - @j 特別なオブジェクト val をスタックにプッシュする。 - オブジェクトの種類は value_type による. - */ +/* put special object. "value_type" is for expansion. */ DEFINE_INSN putspecialobject (rb_num_t value_type) () (VALUE val) +// attr bool leaf = (value_type == VM_SPECIAL_OBJECT_VMCORE); /* others may raise when allocating singleton */ { - enum vm_special_object_type type = (enum vm_special_object_type)value_type; + enum vm_special_object_type type; - switch (type) { - case VM_SPECIAL_OBJECT_VMCORE: - val = rb_mRubyVMFrozenCore; - break; - case VM_SPECIAL_OBJECT_CBASE: - val = vm_get_cbase(GET_EP()); - break; - case VM_SPECIAL_OBJECT_CONST_BASE: - val = vm_get_const_base(GET_EP()); - break; - default: - rb_bug("putspecialobject insn: unknown value_type"); - } + type = (enum vm_special_object_type)value_type; + val = vm_get_special_object(GET_EP(), type); } -/** - @c put - @e put iseq value. - @j put iseq value. - */ +/* put string val. string will be copied. */ DEFINE_INSN -putiseq -(ISEQ iseq) +dupstring +(VALUE str) () -(VALUE ret) +(VALUE val) { - ret = (VALUE)iseq; + val = rb_ec_str_resurrect(ec, str, false); } -/** - @c put - @e put string val. string will be copied. - @j 文字列をコピーしてスタックにプッシュする。 - */ +/* put chilled string val. string will be copied but frozen in the future. */ DEFINE_INSN -putstring +dupchilledstring (VALUE str) () (VALUE val) { - val = rb_str_resurrect(str); + val = rb_ec_str_resurrect(ec, str, true); } -/** - @c put - @e put concatenate strings - @j スタックトップの文字列を n 個連結し,結果をスタックにプッシュする。 - */ +/* put concatenate strings */ DEFINE_INSN concatstrings (rb_num_t num) (...) -(VALUE val) // inc += 1 - num; +(VALUE val) +/* This instruction can concat UTF-8 and binary strings, resulting in + * Encoding::CompatibilityError. */ +// attr bool leaf = false; /* has rb_enc_cr_str_buf_cat() */ +// attr rb_snum_t sp_inc = 1 - (rb_snum_t)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); + val = rb_str_concat_literals(num, STACK_ADDR_FROM_TOP(num)); } -/** - @c put - @e to_str - @j to_str の結果をスタックにプッシュする。 - */ +/* Convert the result to string if not already a string. + This is used as a backup if to_s does not return a string. */ DEFINE_INSN -tostring +anytostring () -(VALUE val) +(VALUE val, VALUE str) (VALUE val) { - val = rb_obj_as_string(val); + val = rb_obj_as_string_result(str, val); } -/** - @c put - @e to Regexp - @j 文字列 str を正規表現にコンパイルしてスタックにプッシュする。 - コンパイル時,opt を正規表現のオプションとする。 +/* compile str to Regexp and push it. + opt is the option for the Regexp. */ DEFINE_INSN toregexp (rb_num_t opt, rb_num_t cnt) (...) -(VALUE val) // inc += 1 - cnt; +(VALUE val) +/* This instruction can raise RegexpError, thus can call + * RegexpError#initialize */ +// attr bool leaf = false; +// attr rb_snum_t sp_inc = 1 - (rb_snum_t)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); + val = rb_reg_new_from_values(cnt, STACK_ADDR_FROM_TOP(cnt), (int)opt); } -/** - @c put - @e put new array. - @j 新しい配列をスタック上の num 個の値で初期化して生成しプッシュする。 - */ +/* intern str to Symbol and push it. */ +DEFINE_INSN +intern +() +(VALUE str) +(VALUE sym) +{ + sym = rb_str_intern(str); +} + +/* put new array initialized with num values on the stack. */ DEFINE_INSN newarray (rb_num_t num) (...) -(VALUE val) // inc += 1 - num; +(VALUE val) +// attr rb_snum_t sp_inc = 1 - (rb_snum_t)num; { - val = rb_ary_new4((long)num, STACK_ADDR_FROM_TOP(num)); - POPN(num); + val = rb_ec_ary_new_from_values(ec, num, STACK_ADDR_FROM_TOP(num)); } -/** - @c put - @e dup array - @j 配列 ary を dup してスタックにプッシュする。 +/* push hash onto array unless the hash is empty (as empty keyword + splats should be ignored). */ DEFINE_INSN +pushtoarraykwsplat +() +(VALUE ary, VALUE hash) +(VALUE ary) +{ + if (!RHASH_EMPTY_P(hash)) { + rb_ary_push(ary, hash); + } +} + +/* dup array */ +DEFINE_INSN duparray (VALUE ary) () (VALUE val) { + RUBY_DTRACE_CREATE_HOOK(ARRAY, RARRAY_LEN(ary)); 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 用 +/* dup hash */ +DEFINE_INSN +duphash +(VALUE hash) +() +(VALUE val) +{ + RUBY_DTRACE_CREATE_HOOK(HASH, RHASH_SIZE(hash) << 1); + val = rb_hash_resurrect(hash); +} + +/* if TOS is an array expand, expand it to num objects. + if the number of the array is less than num, push nils to fill. + if it is greater than num, exceeding elements are dropped. + unless TOS is an array, push num - 1 nils. + if flags is non-zero, push the array of the rest elements. + flag: 0x01 - rest args array + flag: 0x02 - for postarg flag: 0x04 - reverse? */ DEFINE_INSN expandarray (rb_num_t num, rb_num_t flag) (..., VALUE ary) -(...) // inc += num - 1 + (flag & 1 ? 1 : 0); +(...) +// attr bool handles_sp = true; +// attr bool leaf = false; /* has rb_check_array_type() */ +// attr rb_snum_t sp_inc = (rb_snum_t)num - 1 + (flag & 1 ? 1 : 0); { vm_expandarray(GET_CFP(), ary, num, (int)flag); } -/** - @c put - @e concat two arrays - @j 二つの配列 ary1, ary2 を連結しスタックへプッシュする。 +/* concat two arrays, without modifying first array. + * attempts to convert both objects to arrays using to_a. */ DEFINE_INSN concatarray () -(VALUE ary1, VALUE ary2st) +(VALUE ary1, VALUE ary2) (VALUE ary) +// attr bool leaf = false; /* has rb_check_array_type() */ { - 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); - } + ary = vm_concat_array(ary1, ary2); +} - if (NIL_P(tmp2)) { - tmp2 = rb_ary_new3(1, ary2); - } +/* concat second array to first array. + * first argument must already be an array. + * attempts to convert second object to array using to_a. + */ +DEFINE_INSN +concattoarray +() +(VALUE ary1, VALUE ary2) +(VALUE ary) +// attr bool leaf = false; /* has rb_check_array_type() */ +{ + ary = vm_concat_to_array(ary1, ary2); +} - if (tmp1 == ary1) { - tmp1 = rb_ary_dup(ary1); - } - ary = rb_ary_concat(tmp1, tmp2); +/* push given number of objects to array directly before. */ +DEFINE_INSN +pushtoarray +(rb_num_t num) +(...) +(VALUE val) +// attr rb_snum_t sp_inc = -(rb_snum_t)num; +{ + const VALUE *objp = STACK_ADDR_FROM_TOP(num); + val = rb_ary_cat(*(objp-1), objp, num); } -/** - @c put - @e splat array - @j 配列 ary に対して to_a を呼び出す。 - */ +/* call to_a on array ary to splat */ DEFINE_INSN splatarray (VALUE flag) (VALUE ary) (VALUE obj) +// attr bool leaf = false; /* has rb_check_array_type() */ +{ + obj = vm_splat_array(flag, ary); +} + +/* call to_hash on hash to keyword splat before converting block */ +DEFINE_INSN +splatkw +() +(VALUE hash, VALUE block) +(VALUE obj, VALUE block) +// attr bool leaf = false; /* has rb_to_hash_type() */ +// attr bool zjit_profile = true; { - VALUE tmp = rb_check_convert_type(ary, T_ARRAY, "Array", "to_a"); - if (NIL_P(tmp)) { - tmp = rb_ary_new3(1, ary); + if (NIL_P(hash)) { + obj = Qnil; } - else if (RTEST(flag)) { - tmp = rb_ary_dup(tmp); + else { + obj = rb_to_hash_type(hash); } - obj = tmp; } -/** - @c put - @e put new Hash. - @j 新しいハッシュをスタックトップの n 個を初期値として生成する。 - n はキーと値のペアなので 2 の倍数でなければならない。 - */ +/* put new Hash from n elements. n must be an even number. */ DEFINE_INSN newhash (rb_num_t num) (...) -(VALUE val) // inc += 1 - num; +(VALUE val) +// attr bool leaf = false; /* has rb_hash_key_str() */ +// attr rb_snum_t sp_inc = 1 - (rb_snum_t)num; { - rb_num_t i; + RUBY_DTRACE_CREATE_HOOK(HASH, num); - if(RUBY_DTRACE_HASH_CREATE_ENABLED()) { - RUBY_DTRACE_HASH_CREATE(num, rb_sourcefile(), rb_sourceline()); + if (num) { + val = rb_hash_new_with_bulk_insert(num, STACK_ADDR_FROM_TOP(num)); } - - 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); + else { + val = rb_hash_new(); } - POPN(num); } -/** - @c put - @e put new Range object.(Range.new(low, high, flag)) - @j Range.new(low, high, flag) のようなオブジェクトを生成しスタックにプッシュする。 - */ +/* put new Range object.(Range.new(low, high, flag)) */ DEFINE_INSN newrange (rb_num_t flag) (VALUE low, VALUE high) (VALUE val) +/* rb_range_new() exercises "bad value for range" check. */ +// attr bool leaf = false; /* see also: range.c:range_init() */ { val = rb_range_new(low, high, (int)flag); } @@ -558,11 +612,7 @@ newrange /* deal with stack operation */ /**********************************************************/ -/** - @c stack - @e pop from stack. - @j スタックから一つポップする。 - */ +/* pop from stack. */ DEFINE_INSN pop () @@ -573,11 +623,7 @@ pop /* none */ } -/** - @c stack - @e duplicate stack top. - @j スタックトップをコピーしてスタックにプッシュする。 - */ +/* duplicate stack top. */ DEFINE_INSN dup () @@ -587,31 +633,21 @@ dup val1 = val2 = val; } -/** - @c stack - @e duplicate stack top n elements - @j スタックトップの n 個をコピーしてスタックにプッシュする。 - */ +/* duplicate stack top n elements */ DEFINE_INSN dupn (rb_num_t n) (...) -(...) // inc += n; +(...) +// attr rb_snum_t sp_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); -} + void *dst = GET_SP(); + void *src = STACK_ADDR_FROM_TOP(n); + MEMCPY(dst, src, VALUE, n); +} -/** - @c stack - @e swap top 2 vals - @j スタックトップの 2 つの値を交換する。 - */ +/* swap top 2 vals */ DEFINE_INSN swap () @@ -621,16 +657,13 @@ swap /* none */ } -/** - @c stack - @e reverse stack top N order. - @j スタックトップの n 個の値を逆転する。 - */ +/* reverse stack top N order. */ DEFINE_INSN -reverse +opt_reverse (rb_num_t n) (...) -(...) // inc += 0; +(...) +// attr rb_snum_t sp_inc = 0; { rb_num_t i; VALUE *sp = STACK_ADDR_FROM_TOP(n); @@ -643,191 +676,126 @@ reverse } } -/** - @c stack - @e for stack caching. - @j スタックキャッシングの状態を調整するために必要な命令。 - */ -DEFINE_INSN +/* for stack caching. */ +DEFINE_INSN_IF(STACK_CACHING) reput () (..., VALUE val) -(VALUE val) // inc += 0; +(VALUE val) +// attr rb_snum_t sp_inc = 0; { /* none */ } -/** - @c stack - @e get nth stack value from stack top - @j スタックトップから n 個目をスタックにプッシュする。 - */ +/* get nth stack value from stack top */ DEFINE_INSN topn (rb_num_t n) (...) -(VALUE val) // inc += 1; +(VALUE val) +// attr rb_snum_t sp_inc = 1; { val = TOPN(n); } -/** - @c stack - @e set Nth stack entry to stack top - @j スタックトップの値を n 個目のスタックにコピー - */ +/* set Nth stack entry to stack top */ DEFINE_INSN setn (rb_num_t n) (..., VALUE val) -(VALUE val) // inc += 0 +(VALUE val) +// attr rb_snum_t sp_inc = 0; { - TOPN(n-1) = val; + TOPN(n) = val; } -/** - @c stack - @e empty current stack - @j current stack を空にする。 - */ +/* empty current stack */ DEFINE_INSN adjuststack (rb_num_t n) (...) -(...) // inc -= n +(...) +// attr rb_snum_t sp_inc = -(rb_snum_t)n; { - DEC_SP(n); + /* none */ } - /**********************************************************/ /* deal with setting */ /**********************************************************/ -/** - @c setting - @e defined? - @j defined? を行う。 - */ +/* defined? */ DEFINE_INSN defined -(rb_num_t op_type, VALUE obj, VALUE needstr) +(rb_num_t op_type, VALUE obj, VALUE pushval) (VALUE v) (VALUE val) +// attr bool leaf = leafness_of_defined(op_type); { - val = vm_defined(th, GET_CFP(), op_type, obj, needstr, v); + val = Qnil; + if (vm_defined(ec, GET_CFP(), op_type, obj, v)) { + val = pushval; + } +} + +/* defined?(@foo) */ +DEFINE_INSN +definedivar +(ID id, IVC ic, VALUE pushval) +() +(VALUE val) +// attr bool leaf = false; +// attr bool zjit_profile = true; +{ + val = Qnil; + if (!UNDEF_P(vm_getivar(GET_SELF(), id, GET_ISEQ(), ic, NULL, FALSE, Qundef))) { + val = pushval; + } } -/** - @c setting - @e check `target' matches `pattern'. +/* 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'. + VM_CHECKMATCH_TYPE_RESCUE: check `pattern.kind_of?(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) +// attr bool leaf = leafness_of_checkmatch(flag); { - 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; - } - } + result = vm_check_match(ec, target, pattern, flag); } -/** - @c setting - @e check keywords are specified or not. - @j キーワードが指定されているかどうかチェックする - */ +/* check keywords are specified or not. */ DEFINE_INSN checkkeyword -(lindex_t kw_bits_index, rb_num_t keyword_index) +(lindex_t kw_bits_index, lindex_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; - } + ret = vm_check_keyword(kw_bits_index, keyword_index, GET_EP()); } -/** - @c setting - @e trace - @j trace 用の命令。 - */ +/* check if val is type. */ DEFINE_INSN -trace -(rb_num_t nf) -() -() +checktype +(rb_num_t type) +(VALUE val) +(VALUE ret) { - 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); + ret = RBOOL(TYPE(val) == (int)type); } /**********************************************************/ /* 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 クラスを定義する。 +/* enter class definition scope. if super is Qfalse, and class + "klass" is defined, it's redefined. Otherwise, define "klass" class. */ DEFINE_INSN defineclass @@ -835,220 +803,404 @@ defineclass (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 (%"PRIsVALUE" given)", - rb_obj_class(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, "%"PRIsVALUE" is not a class", rb_id2str(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 %"PRIsVALUE"", - rb_id2str(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, "%"PRIsVALUE" is not a module", rb_id2str(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); + VALUE klass = vm_find_or_create_class_by_id(id, flags, cbase, super); + const rb_box_t *box = rb_current_box(); + + rb_iseq_check(class_iseq); + + rb_cref_t *cref = vm_cref_push(ec, klass, NULL, FALSE, FALSE); + + if (VM_DEFINECLASS_DYNAMIC_CREF_P(flags)) { + CREF_DYNAMIC_SET(cref); } /* enter scope */ - vm_push_frame(th, class_iseq, VM_FRAME_MAGIC_CLASS, klass, - VM_ENVVAL_BLOCK_PTR(GET_BLOCK_PTR()), - (VALUE)vm_cref_push(th, klass, NULL), - class_iseq->body->iseq_encoded, GET_SP(), - class_iseq->body->local_size, class_iseq->body->stack_max); - + vm_push_frame(ec, class_iseq, VM_FRAME_MAGIC_CLASS | VM_ENV_FLAG_LOCAL, klass, + GC_GUARDED_PTR(box), + (VALUE)cref, + ISEQ_BODY(class_iseq)->iseq_encoded, GET_SP(), + ISEQ_BODY(class_iseq)->local_table_size, + ISEQ_BODY(class_iseq)->stack_max); RESTORE_REGS(); NEXT_INSN(); } +DEFINE_INSN +definemethod +(ID id, ISEQ iseq) +() +() +{ + vm_define_method(ec, Qnil, id, (VALUE)iseq, FALSE); +} + +DEFINE_INSN +definesmethod +(ID id, ISEQ iseq) +(VALUE obj) +() +{ + vm_define_method(ec, obj, id, (VALUE)iseq, TRUE); +} /**********************************************************/ /* deal with control flow 2: method/iterator */ /**********************************************************/ -/** - @c method/iterator - @e invoke method. - @j メソッド呼び出しを行う。ci に必要な情報が格納されている。 - */ +/* invoke method. */ DEFINE_INSN send -(CALL_INFO ci) +(CALL_DATA cd, ISEQ blockiseq) (...) -(VALUE val) // inc += - (int)(ci->orig_argc + ((ci->flag & VM_CALL_ARGS_BLOCKARG) ? 1 : 0)); +(VALUE val) +// attr bool zjit_profile = true; +// attr rb_snum_t sp_inc = sp_inc_of_sendish(cd->ci); +// attr rb_snum_t comptime_sp_inc = sp_inc_of_sendish(ci); { - 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); + VALUE bh = vm_caller_setup_arg_block(ec, GET_CFP(), cd->ci, blockiseq, false); + val = vm_sendish(ec, GET_CFP(), cd, bh, mexp_search_method); + JIT_EXEC(ec, val); + + if (UNDEF_P(val)) { + RESTORE_REGS(); + NEXT_INSN(); + } } +/* invoke forward method. */ DEFINE_INSN -opt_str_freeze -(VALUE str) -() +sendforward +(CALL_DATA cd, ISEQ blockiseq) +(...) (VALUE val) +// attr rb_snum_t sp_inc = sp_inc_of_sendish(cd->ci); +// attr rb_snum_t comptime_sp_inc = sp_inc_of_sendish(ci); { - if (BASIC_OP_UNREDEFINED_P(BOP_FREEZE, STRING_REDEFINED_OP_FLAG)) { - val = str; + struct rb_forwarding_call_data adjusted_cd; + struct rb_callinfo adjusted_ci; + + VALUE bh = vm_caller_setup_fwd_args(ec, GET_CFP(), cd, blockiseq, 0, &adjusted_cd, &adjusted_ci); + + val = vm_sendish(ec, GET_CFP(), &adjusted_cd.cd, bh, mexp_search_method); + JIT_EXEC(ec, val); + + if (cd->cc != adjusted_cd.cd.cc && vm_cc_markable(adjusted_cd.cd.cc)) { + RB_OBJ_WRITE(GET_ISEQ(), &cd->cc, adjusted_cd.cd.cc); } - else { - val = rb_funcall(rb_str_resurrect(str), idFreeze, 0); + + if (UNDEF_P(val)) { + RESTORE_REGS(); + NEXT_INSN(); } } -/** - @c optimize - @e Invoke method without block - @j Invoke method without block - */ +/* Invoke method without block */ DEFINE_INSN opt_send_without_block -(CALL_INFO ci) +(CALL_DATA cd) (...) -(VALUE val) // inc += -ci->orig_argc; +(VALUE val) +// attr bool zjit_profile = true; +// attr bool handles_sp = true; +// attr rb_snum_t sp_inc = sp_inc_of_sendish(cd->ci); +// attr rb_snum_t comptime_sp_inc = sp_inc_of_sendish(ci); { - ci->argc = ci->orig_argc; - vm_search_method(ci, ci->recv = TOPN(ci->argc)); - CALL_METHOD(ci); + VALUE bh = VM_BLOCK_HANDLER_NONE; + val = vm_sendish(ec, GET_CFP(), cd, bh, mexp_search_method); + JIT_EXEC(ec, val); + + if (UNDEF_P(val)) { + RESTORE_REGS(); + NEXT_INSN(); + } +} + +/* Jump if "new" method has been defined by user */ +DEFINE_INSN +opt_new +(CALL_DATA cd, OFFSET dst) +() +() +// attr bool leaf = false; +{ + VALUE argc = vm_ci_argc(cd->ci); + VALUE val = TOPN(argc); + + // The bookkeeping slot should be empty. + RUBY_ASSERT(TOPN(argc + 1) == Qnil); + + if (vm_method_cfunc_is(GET_CFP(), cd, val, rb_class_new_instance_pass_kw)) { + RB_DEBUG_COUNTER_INC(opt_new_hit); + val = rb_obj_alloc(val); + TOPN(argc) = val; + TOPN(argc + 1) = val; + } + else { + RB_DEBUG_COUNTER_INC(opt_new_miss); + JUMP(dst); + } +} + +/* Convert object to string using to_s or equivalent. */ +DEFINE_INSN +objtostring +(CALL_DATA cd) +(VALUE recv) +(VALUE val) +// attr bool leaf = false; +// attr bool zjit_profile = true; +{ + val = vm_objtostring(GET_CFP(), recv, cd); + + if (UNDEF_P(val)) { + CALL_SIMPLE_METHOD(); + } +} + +DEFINE_INSN +opt_ary_freeze +(VALUE ary, CALL_DATA cd) +() +(VALUE val) +{ + val = vm_opt_ary_freeze(ary, BOP_FREEZE, idFreeze); + + if (UNDEF_P(val)) { + RUBY_DTRACE_CREATE_HOOK(ARRAY, RARRAY_LEN(ary)); + PUSH(rb_ary_resurrect(ary)); + CALL_SIMPLE_METHOD(); + } } -/** - @c method/iterator - @e super(args) # args.size => num - @j super を実行する。ci に必要な情報が格納されている。 - */ +DEFINE_INSN +opt_hash_freeze +(VALUE hash, CALL_DATA cd) +() +(VALUE val) +{ + val = vm_opt_hash_freeze(hash, BOP_FREEZE, idFreeze); + + if (UNDEF_P(val)) { + RUBY_DTRACE_CREATE_HOOK(HASH, RHASH_SIZE(hash) << 1); + PUSH(rb_hash_resurrect(hash)); + CALL_SIMPLE_METHOD(); + } +} + +DEFINE_INSN +opt_str_freeze +(VALUE str, CALL_DATA cd) +() +(VALUE val) +{ + val = vm_opt_str_freeze(str, BOP_FREEZE, idFreeze); + + if (UNDEF_P(val)) { + PUSH(rb_str_resurrect(str)); + CALL_SIMPLE_METHOD(); + } +} + +/* optimized nil? */ +DEFINE_INSN +opt_nil_p +(CALL_DATA cd) +(VALUE recv) +(VALUE val) +// attr bool zjit_profile = true; +{ + val = vm_opt_nil_p(GET_CFP(), cd, recv); + + if (UNDEF_P(val)) { + CALL_SIMPLE_METHOD(); + } +} + +DEFINE_INSN +opt_str_uminus +(VALUE str, CALL_DATA cd) +() +(VALUE val) +{ + val = vm_opt_str_freeze(str, BOP_UMINUS, idUMinus); + + if (UNDEF_P(val)) { + PUSH(rb_str_resurrect(str)); + CALL_SIMPLE_METHOD(); + } +} + +DEFINE_INSN +opt_duparray_send +(VALUE ary, ID method, rb_num_t argc) +(...) +(VALUE val) +/* This instruction typically has no funcalls. But it may compare array + * contents to each other which may call methods when necessary. + * No way to detect such method calls beforehand. + * We must mark it as not leaf. */ +// attr bool leaf = false; /* has rb_funcall() */ +// attr rb_snum_t sp_inc = 1 - (rb_snum_t)argc; +// attr rb_snum_t comptime_sp_inc = 1 - (rb_snum_t)argc; +{ + switch (method) { + case idIncludeP: + val = vm_opt_duparray_include_p(ec, ary, TOPN(0)); + break; + default: + rb_bug("unreachable"); + } +} + +DEFINE_INSN +opt_newarray_send +(rb_num_t num, rb_num_t method) +(...) +(VALUE val) +/* This instruction typically has no funcalls. But it compares array + * contents each other by nature. That part could call methods when + * necessary. No way to detect such method calls beforehand. We + * cannot but mark it being not leaf. */ +// attr bool leaf = false; /* has rb_funcall() */ +// attr rb_snum_t sp_inc = 1 - (rb_snum_t)num; +// attr rb_snum_t comptime_sp_inc = 1 - (rb_snum_t)num; +{ + switch (method) { + case VM_OPT_NEWARRAY_SEND_HASH: + val = vm_opt_newarray_hash(ec, num, STACK_ADDR_FROM_TOP(num)); + break; + case VM_OPT_NEWARRAY_SEND_MIN: + val = vm_opt_newarray_min(ec, num, STACK_ADDR_FROM_TOP(num)); + break; + case VM_OPT_NEWARRAY_SEND_MAX: + val = vm_opt_newarray_max(ec, num, STACK_ADDR_FROM_TOP(num)); + break; + case VM_OPT_NEWARRAY_SEND_INCLUDE_P: + val = vm_opt_newarray_include_p(ec, (long)num-1, STACK_ADDR_FROM_TOP(num), TOPN(0)); + break; + case VM_OPT_NEWARRAY_SEND_PACK: + val = vm_opt_newarray_pack_buffer(ec, (long)num-1, STACK_ADDR_FROM_TOP(num), TOPN(0), Qundef); + break; + case VM_OPT_NEWARRAY_SEND_PACK_BUFFER: + val = vm_opt_newarray_pack_buffer(ec, (long)num-2, STACK_ADDR_FROM_TOP(num), TOPN(1), TOPN(0)); + break; + default: + rb_bug("unreachable"); + } +} + +/* super(args) # args.size => num */ DEFINE_INSN invokesuper -(CALL_INFO ci) +(CALL_DATA cd, ISEQ blockiseq) (...) -(VALUE val) // inc += - (int)(ci->orig_argc + ((ci->flag & VM_CALL_ARGS_BLOCKARG) ? 1 : 0)); +(VALUE val) +// attr rb_snum_t sp_inc = sp_inc_of_sendish(cd->ci); +// attr rb_snum_t comptime_sp_inc = sp_inc_of_sendish(ci); +// attr bool zjit_profile = true; { - 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); + struct rb_callinfo adjusted_ci = VM_CI_ON_STACK(vm_ci_mid(cd->ci), + vm_ci_flag(cd->ci), + vm_ci_argc(cd->ci), + vm_ci_kwarg(cd->ci)); + const struct rb_callcache *original_cc = rbimpl_atomic_ptr_load((void **)&cd->cc, RBIMPL_ATOMIC_ACQUIRE); + struct rb_call_data adjusted_cd = { + .ci = &adjusted_ci, + .cc = original_cc, + }; + + VALUE bh = vm_caller_setup_arg_block(ec, GET_CFP(), adjusted_cd.ci, blockiseq, true); + val = vm_sendish(ec, GET_CFP(), &adjusted_cd, bh, mexp_search_super); + JIT_EXEC(ec, val); + + if (original_cc != adjusted_cd.cc && vm_cc_markable(adjusted_cd.cc)) { + rbimpl_atomic_ptr_store((volatile void **)&cd->cc, (void *)adjusted_cd.cc, RBIMPL_ATOMIC_RELEASE); + RB_OBJ_WRITTEN(GET_ISEQ(), Qundef, adjusted_cd.cc); + } + + if (UNDEF_P(val)) { + RESTORE_REGS(); + NEXT_INSN(); + } } -/** - @c method/iterator - @e yield(args) - @j yield を実行する。 - */ +/* super(args) # args.size => num */ +DEFINE_INSN +invokesuperforward +(CALL_DATA cd, ISEQ blockiseq) +(...) +(VALUE val) +// attr rb_snum_t sp_inc = sp_inc_of_sendish(cd->ci); +// attr rb_snum_t comptime_sp_inc = sp_inc_of_sendish(ci); +{ + struct rb_forwarding_call_data adjusted_cd; + struct rb_callinfo adjusted_ci; + + VALUE bh = vm_caller_setup_fwd_args(ec, GET_CFP(), cd, blockiseq, 1, &adjusted_cd, &adjusted_ci); + + val = vm_sendish(ec, GET_CFP(), &adjusted_cd.cd, bh, mexp_search_super); + JIT_EXEC(ec, val); + + if (cd->cc != adjusted_cd.cd.cc && vm_cc_markable(adjusted_cd.cd.cc)) { + RB_OBJ_WRITE(GET_ISEQ(), &cd->cc, adjusted_cd.cd.cc); + } + + if (UNDEF_P(val)) { + RESTORE_REGS(); + NEXT_INSN(); + } +} + +/* yield(args) */ DEFINE_INSN invokeblock -(CALL_INFO ci) +(CALL_DATA cd) (...) -(VALUE val) // inc += 1 - ci->orig_argc; +(VALUE val) +// attr bool handles_sp = true; +// attr rb_snum_t sp_inc = sp_inc_of_invokeblock(cd->ci); +// attr rb_snum_t comptime_sp_inc = sp_inc_of_invokeblock(ci); +// attr bool zjit_profile = true; { - 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(); + VALUE bh = VM_BLOCK_HANDLER_NONE; + val = vm_sendish(ec, GET_CFP(), cd, bh, mexp_search_invokeblock); + JIT_EXEC(ec, val); + + if (UNDEF_P(val)) { + RESTORE_REGS(); + NEXT_INSN(); } } -/** - @c method/iterator - @e return from this scope. - @j このスコープから抜ける。 - */ +/* return from this scope. */ DEFINE_INSN leave () (VALUE val) (VALUE val) +/* This is super surprising but when leaving from a frame, we check + * for interrupts. If any, that should be executed on top of the + * current execution context. This is a method call. */ +// attr bool leaf = false; /* has rb_threadptr_execute_interrupts() */ +// attr bool handles_sp = true; { if (OPT_CHECKED_RUN) { - const VALUE *const bp = vm_base_ptr(reg_cfp); - if (reg_cfp->sp != bp) { - rb_bug("Stack consistency error (sp: %"PRIdPTRDIFF", bp: %"PRIdPTRDIFF")", - VM_SP_CNT(th, reg_cfp->sp), VM_SP_CNT(th, bp)); - } + const VALUE *const bp = vm_base_ptr(GET_CFP()); + if (GET_SP() != bp) { + vm_stack_consistency_error(ec, GET_CFP(), bp); + } } - RUBY_VM_CHECK_INTS(th); - - if (UNLIKELY(VM_FRAME_TYPE_FINISH_P(GET_CFP()))) { - vm_pop_frame(th); - + if (vm_pop_frame(ec, GET_CFP(), GET_EP())) { #if OPT_CALL_THREADED_CODE - th->retval = val; - return 0; + rb_ec_thread_ptr(ec)->retval = val; + return 0; #else - return val; + return val; #endif } else { - vm_pop_frame(th); - RESTORE_REGS(); + RESTORE_REGS(); } } @@ -1056,19 +1208,16 @@ leave /* deal with control flow 3: exception */ /**********************************************************/ -/** - @c exception - @e longjump - @j 大域ジャンプを行う。 - */ +/* longjump */ DEFINE_INSN throw (rb_num_t throw_state) (VALUE throwobj) (VALUE val) +/* Same discussion as leave. */ +// attr bool leaf = false; /* has rb_threadptr_execute_interrupts() */ { - RUBY_VM_CHECK_INTS(th); - val = vm_throw(th, GET_CFP(), throw_state, throwobj); + val = vm_throw(ec, GET_CFP(), throw_state, throwobj); THROW_EXCEPTION(val); /* unreachable */ } @@ -1077,1065 +1226,543 @@ throw /* deal with control flow 4: local jump */ /**********************************************************/ -/** - @c jump - @e set PC to (PC + dst). - @j PC を (PC + dst) にする。 - */ +/* set PC to (PC + dst). */ DEFINE_INSN jump (OFFSET dst) () () +/* Same discussion as leave. */ +// attr bool leaf = false; /* has rb_threadptr_execute_interrupts() */ { - RUBY_VM_CHECK_INTS(th); + RUBY_VM_CHECK_INTS(ec); JUMP(dst); } -/** - @c jump - @e if val is not false or nil, set PC to (PC + dst). - @j もし val が false か nil でなければ、PC を (PC + dst) にする。 - */ +/* if val is not false or nil, set PC to (PC + dst). */ DEFINE_INSN branchif (OFFSET dst) (VALUE val) () +/* Same discussion as jump. */ +// attr bool leaf = false; /* has rb_threadptr_execute_interrupts() */ { if (RTEST(val)) { - RUBY_VM_CHECK_INTS(th); - JUMP(dst); + RUBY_VM_CHECK_INTS(ec); + JUMP(dst); } } -/** - @c jump - @e if val is false or nil, set PC to (PC + dst). - @j もし val が false か nil ならば、PC を (PC + dst) にする。 - */ +/* if val is false or nil, set PC to (PC + dst). */ DEFINE_INSN branchunless (OFFSET dst) (VALUE val) () +/* Same discussion as jump. */ +// attr bool leaf = false; /* has rb_threadptr_execute_interrupts() */ { if (!RTEST(val)) { - RUBY_VM_CHECK_INTS(th); - JUMP(dst); + RUBY_VM_CHECK_INTS(ec); + JUMP(dst); } } +/* if val is nil, set PC to (PC + dst). */ +DEFINE_INSN +branchnil +(OFFSET dst) +(VALUE val) +() +/* Same discussion as jump. */ +// attr bool leaf = false; /* has rb_threadptr_execute_interrupts() */ +{ + if (NIL_P(val)) { + RUBY_VM_CHECK_INTS(ec); + JUMP(dst); + } +} -/**********************************************************/ -/* for optimize */ -/**********************************************************/ - -/** - @c optimize - @e push inline-cached value and go to dst if it is valid - @j インラインキャッシュが有効なら、値をスタックにプッシュして dst へジャンプする。 - */ +/* same as jump, but without interrupt check */ DEFINE_INSN -getinlinecache -(OFFSET dst, IC ic) +jump_without_ints +(OFFSET dst) () +() +// attr bool leaf = true; +{ + JUMP(dst); +} + +/* same as branchif, but without interrupt check */ +DEFINE_INSN +branchif_without_ints +(OFFSET dst) (VALUE val) +() +// attr bool leaf = true; { - if (ic->ic_serial == GET_GLOBAL_CONSTANT_STATE() && - ic->ic_cref == rb_vm_get_cref(GET_EP())) { - val = ic->ic_value.value; - JUMP(dst); - } - else { - /* none */ - val = Qnil; + if (RTEST(val)) { + JUMP(dst); } } -/** - @c optimize - @e set inline cache - @j インラインキャッシュの値を設定する。 - */ +/* same as branchunless, but without interrupt check */ DEFINE_INSN -setinlinecache -(IC ic) +branchunless_without_ints +(OFFSET dst) (VALUE val) +() +// attr bool leaf = true; +{ + if (!RTEST(val)) { + JUMP(dst); + } +} + +/* same as branchnil, but without interrupt check */ +DEFINE_INSN +branchnil_without_ints +(OFFSET dst) (VALUE val) +() +// attr bool leaf = true; { - if (ic->ic_value.value == Qundef) { - rb_iseq_add_mark_object(GET_ISEQ(), val); + if (NIL_P(val)) { + JUMP(dst); } - ic->ic_value.value = val; - ic->ic_serial = GET_GLOBAL_CONSTANT_STATE() - ruby_vm_const_missing_count; - ic->ic_cref = rb_vm_get_cref(GET_EP()); - ruby_vm_const_missing_count = 0; } -/** - @c optimize - @e run iseq only once - @j once を実現する。 - */ +/**********************************************************/ +/* for optimize */ +/**********************************************************/ + +/* run iseq only once */ DEFINE_INSN once -(ISEQ iseq, IC ic) +(ISEQ iseq, ISE ise) () (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; - } + val = vm_once_dispatch(ec, iseq, ise); } -/** - @c optimize - @e case dispatcher, jump by table if possible - @j case 文で、可能なら表引きでジャンプする。 - */ +/* case dispatcher, jump by table if possible */ 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; +() +// attr rb_snum_t sp_inc = -1; +{ + OFFSET dst = vm_case_dispatch(hash, else_offset, key); + + if (dst) { + JUMP(dst); } } /** simple functions */ -/** - @c optimize - @e optimized X+Y. - @j 最適化された X+Y。 - */ +/* optimized X+Y. */ DEFINE_INSN opt_plus -(CALL_INFO ci) +(CALL_DATA cd) (VALUE recv, VALUE obj) (VALUE val) +// attr bool zjit_profile = true; { - 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); + val = vm_opt_plus(recv, obj); + + if (UNDEF_P(val)) { + CALL_SIMPLE_METHOD(); } } -/** - @c optimize - @e optimized X-Y. - @j 最適化された X-Y。 - */ +/* optimized X-Y. */ DEFINE_INSN opt_minus -(CALL_INFO ci) +(CALL_DATA cd) (VALUE recv, VALUE obj) (VALUE val) +// attr bool zjit_profile = true; { - 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; + val = vm_opt_minus(recv, obj); - 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); + if (UNDEF_P(val)) { + CALL_SIMPLE_METHOD(); } } -/** - @c optimize - @e optimized X*Y. - @j 最適化された X*Y。 - */ +/* optimized X*Y. */ DEFINE_INSN opt_mult -(CALL_INFO ci) +(CALL_DATA cd) (VALUE recv, VALUE obj) (VALUE val) +// attr bool zjit_profile = true; { - if (FIXNUM_2_P(recv, obj) && - BASIC_OP_UNREDEFINED_P(BOP_MULT, FIXNUM_REDEFINED_OP_FLAG)) { - long a, b; + val = vm_opt_mult(recv, obj); - 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); + if (UNDEF_P(val)) { + CALL_SIMPLE_METHOD(); } } -/** - @c optimize - @e optimized X/Y. - @j 最適化された X/Y。 - */ +/* optimized X/Y. */ DEFINE_INSN opt_div -(CALL_INFO ci) +(CALL_DATA cd) (VALUE recv, VALUE obj) (VALUE val) +/* In case of division by zero, it raises. Thus + * ZeroDivisionError#initialize is called. */ +// attr bool leaf = false; +// attr bool zjit_profile = true; { - 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); + val = vm_opt_div(recv, obj); + + if (UNDEF_P(val)) { + CALL_SIMPLE_METHOD(); } } -/** - @c optimize - @e optimized X%Y. - @j 最適化された X%Y。 - */ +/* optimized X%Y. */ DEFINE_INSN opt_mod -(CALL_INFO ci) +(CALL_DATA cd) (VALUE recv, VALUE obj) (VALUE val) +/* Same discussion as opt_div. */ +// attr bool leaf = false; +// attr bool zjit_profile = true; { - 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); + val = vm_opt_mod(recv, obj); + + if (UNDEF_P(val)) { + CALL_SIMPLE_METHOD(); } } -/** - @c optimize - @e optimized X==Y. - @j 最適化された X==Y。 - */ +/* optimized X==Y. */ DEFINE_INSN opt_eq -(CALL_INFO ci) +(CALL_DATA cd) (VALUE recv, VALUE obj) (VALUE val) +// attr bool zjit_profile = true; { - val = opt_eq_func(recv, obj, ci); + val = opt_equality(GET_CFP(), recv, obj, cd); - if (val == Qundef) { - /* other */ - PUSH(recv); - PUSH(obj); - CALL_SIMPLE_METHOD(recv); + if (UNDEF_P(val)) { + CALL_SIMPLE_METHOD(); } } -/** - @c optimize - @e optimized X!=Y. - @j 最適化された X!=Y。 - */ +/* optimized X!=Y. */ DEFINE_INSN opt_neq -(CALL_INFO ci, CALL_INFO ci_eq) +(CALL_DATA cd_eq, CALL_DATA cd) (VALUE recv, VALUE obj) (VALUE val) +// attr bool zjit_profile = true; { - 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); + val = vm_opt_neq(GET_CFP(), cd, cd_eq, recv, obj); - if (val != Qundef) { - val = RTEST(val) ? Qfalse : Qtrue; - } - } - - if (val == Qundef) { - /* other */ - PUSH(recv); - PUSH(obj); - CALL_SIMPLE_METHOD(recv); + if (UNDEF_P(val)) { + CALL_SIMPLE_METHOD(); } } -/** - @c optimize - @e optimized X<Y. - @j 最適化された X<Y。 - */ +/* optimized X<Y. */ DEFINE_INSN opt_lt -(CALL_INFO ci) +(CALL_DATA cd) (VALUE recv, VALUE obj) (VALUE val) +// attr bool zjit_profile = true; { - if (FIXNUM_2_P(recv, obj) && - BASIC_OP_UNREDEFINED_P(BOP_LT, FIXNUM_REDEFINED_OP_FLAG)) { - SIGNED_VALUE a = recv, b = obj; + val = vm_opt_lt(recv, obj); - if (a < b) { - val = Qtrue; - } - else { - val = Qfalse; - } - } - else if (FLONUM_2_P(recv, obj) && - BASIC_OP_UNREDEFINED_P(BOP_LT, 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_LT, FLOAT_REDEFINED_OP_FLAG)) { - val = double_cmp_lt(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); + if (UNDEF_P(val)) { + CALL_SIMPLE_METHOD(); } } -/** - @c optimize - @e optimized X<=Y. - @j 最適化された X<=Y。 - */ +/* optimized X<=Y. */ DEFINE_INSN opt_le -(CALL_INFO ci) +(CALL_DATA cd) (VALUE recv, VALUE obj) (VALUE val) +// attr bool zjit_profile = true; { - if (FIXNUM_2_P(recv, obj) && - BASIC_OP_UNREDEFINED_P(BOP_LE, FIXNUM_REDEFINED_OP_FLAG)) { - SIGNED_VALUE a = recv, b = obj; + val = vm_opt_le(recv, obj); - if (a <= b) { - val = Qtrue; - } - else { - val = Qfalse; - } - } - else if (FLONUM_2_P(recv, obj) && - BASIC_OP_UNREDEFINED_P(BOP_LE, FLOAT_REDEFINED_OP_FLAG)) { - /* flonum is not NaN */ - val = RFLOAT_VALUE(recv) <= RFLOAT_VALUE(obj) ? Qtrue : Qfalse; - } - else { - /* other */ - PUSH(recv); - PUSH(obj); - CALL_SIMPLE_METHOD(recv); + if (UNDEF_P(val)) { + CALL_SIMPLE_METHOD(); } } -/** - @c optimize - @e optimized X>Y. - @j 最適化された X>Y。 - */ +/* optimized X>Y. */ DEFINE_INSN opt_gt -(CALL_INFO ci) +(CALL_DATA cd) (VALUE recv, VALUE obj) (VALUE val) +// attr bool zjit_profile = true; { - if (FIXNUM_2_P(recv, obj) && - BASIC_OP_UNREDEFINED_P(BOP_GT, FIXNUM_REDEFINED_OP_FLAG)) { - SIGNED_VALUE a = recv, b = obj; + val = vm_opt_gt(recv, 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); + if (UNDEF_P(val)) { + CALL_SIMPLE_METHOD(); } } -/** - @c optimize - @e optimized X>=Y. - @j 最適化された X>=Y。 - */ +/* optimized X>=Y. */ DEFINE_INSN opt_ge -(CALL_INFO ci) +(CALL_DATA cd) (VALUE recv, VALUE obj) (VALUE val) +// attr bool zjit_profile = true; { - if (FIXNUM_2_P(recv, obj) && - BASIC_OP_UNREDEFINED_P(BOP_GE, FIXNUM_REDEFINED_OP_FLAG)) { - SIGNED_VALUE a = recv, b = obj; + val = vm_opt_ge(recv, 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); + if (UNDEF_P(val)) { + CALL_SIMPLE_METHOD(); } } -/** - @c optimize - @e << - @j 最適化された X<<Y。 - */ +/* << */ DEFINE_INSN opt_ltlt -(CALL_INFO ci) +(CALL_DATA cd) (VALUE recv, VALUE obj) (VALUE val) +/* This instruction can append an integer, as a codepoint, into a + * string. Then what happens if that codepoint does not exist in the + * string's encoding? Of course an exception. That's not a leaf. */ +// attr bool leaf = false; /* has "invalid codepoint" exception */ +// attr bool zjit_profile = true; { - if (!SPECIAL_CONST_P(recv)) { - if (RBASIC_CLASS(recv) == rb_cString && - BASIC_OP_UNREDEFINED_P(BOP_LTLT, STRING_REDEFINED_OP_FLAG)) { - val = rb_str_concat(recv, obj); - } - else if (RBASIC_CLASS(recv) == rb_cArray && - BASIC_OP_UNREDEFINED_P(BOP_LTLT, ARRAY_REDEFINED_OP_FLAG)) { - val = rb_ary_push(recv, obj); - } - else { - goto INSN_LABEL(normal_dispatch); - } - } - else { - INSN_LABEL(normal_dispatch): - PUSH(recv); - PUSH(obj); - CALL_SIMPLE_METHOD(recv); + val = vm_opt_ltlt(recv, obj); + + if (UNDEF_P(val)) { + CALL_SIMPLE_METHOD(); } } -/** - @c optimize - @e [] - @j 最適化された recv[obj]。 - */ +/* optimized X&Y. */ DEFINE_INSN -opt_aref -(CALL_INFO ci) +opt_and +(CALL_DATA cd) (VALUE recv, VALUE obj) (VALUE val) +// attr bool zjit_profile = true; { - if (!SPECIAL_CONST_P(recv)) { - if (RBASIC_CLASS(recv) == rb_cArray && BASIC_OP_UNREDEFINED_P(BOP_AREF, ARRAY_REDEFINED_OP_FLAG) && FIXNUM_P(obj)) { - val = rb_ary_entry(recv, FIX2LONG(obj)); - } - else if (RBASIC_CLASS(recv) == rb_cHash && BASIC_OP_UNREDEFINED_P(BOP_AREF, HASH_REDEFINED_OP_FLAG)) { - val = rb_hash_aref(recv, obj); - } - else { - goto INSN_LABEL(normal_dispatch); - } - } - else { - INSN_LABEL(normal_dispatch): - PUSH(recv); - PUSH(obj); - CALL_SIMPLE_METHOD(recv); + val = vm_opt_and(recv, obj); + + if (UNDEF_P(val)) { + CALL_SIMPLE_METHOD(); } } -/** - @c optimize - @e recv[obj] = set - @j 最適化された recv[obj] = set。 - */ +/* optimized X|Y. */ DEFINE_INSN -opt_aset -(CALL_INFO ci) -(VALUE recv, VALUE obj, VALUE set) +opt_or +(CALL_DATA cd) +(VALUE recv, VALUE obj) (VALUE val) +// attr bool zjit_profile = true; { - if (!SPECIAL_CONST_P(recv)) { - if (RBASIC_CLASS(recv) == rb_cArray && BASIC_OP_UNREDEFINED_P(BOP_ASET, ARRAY_REDEFINED_OP_FLAG) && FIXNUM_P(obj)) { - rb_ary_store(recv, FIX2LONG(obj), set); - val = set; - } - else if (RBASIC_CLASS(recv) == rb_cHash && BASIC_OP_UNREDEFINED_P(BOP_ASET, HASH_REDEFINED_OP_FLAG)) { - rb_hash_aset(recv, obj, set); - val = set; - } - else { - goto INSN_LABEL(normal_dispatch); - } - } - else { - INSN_LABEL(normal_dispatch): - PUSH(recv); - PUSH(obj); - PUSH(set); - CALL_SIMPLE_METHOD(recv); + val = vm_opt_or(recv, obj); + + if (UNDEF_P(val)) { + CALL_SIMPLE_METHOD(); } } -/** - @c optimize - @e recv[str] = set - @j 最適化された recv[str] = set。 - */ +/* [] */ DEFINE_INSN -opt_aset_with -(CALL_INFO ci, VALUE key) -(VALUE recv, VALUE val) +opt_aref +(CALL_DATA cd) +(VALUE recv, VALUE obj) (VALUE val) +/* This is complicated. In case of hash, vm_opt_aref() resorts to + * rb_hash_aref(). If `recv` has no `obj`, this function then yields + * default_proc. This is a method call. So opt_aref is + * (surprisingly) not leaf. */ +// attr bool leaf = false; /* has rb_funcall() */ /* calls #yield */ +// attr bool zjit_profile = true; { - if (!SPECIAL_CONST_P(recv) && RBASIC_CLASS(recv) == rb_cHash && BASIC_OP_UNREDEFINED_P(BOP_ASET, HASH_REDEFINED_OP_FLAG)) { - rb_hash_aset(recv, key, val); - } - else { - PUSH(recv); - PUSH(rb_str_resurrect(key)); - PUSH(val); - CALL_SIMPLE_METHOD(recv); + val = vm_opt_aref(recv, obj); + + if (UNDEF_P(val)) { + CALL_SIMPLE_METHOD(); } } -/** - @c optimize - @e recv[str] - @j 最適化された recv[str]。 - */ +/* recv[obj] = set */ DEFINE_INSN -opt_aref_with -(CALL_INFO ci, VALUE key) -(VALUE recv) +opt_aset +(CALL_DATA cd) +(VALUE recv, VALUE obj, VALUE set) (VALUE val) +/* This is another story than opt_aref. When vm_opt_aset() resorts + * to rb_hash_aset(), which should call #hash for `obj`. */ +// attr bool leaf = false; /* has rb_funcall() */ /* calls #hash */ +// attr bool zjit_profile = true; { - if (!SPECIAL_CONST_P(recv) && RBASIC_CLASS(recv) == rb_cHash && BASIC_OP_UNREDEFINED_P(BOP_AREF, HASH_REDEFINED_OP_FLAG)) { - val = rb_hash_aref(recv, key); - } - else { - PUSH(recv); - PUSH(rb_str_resurrect(key)); - CALL_SIMPLE_METHOD(recv); + val = vm_opt_aset(recv, obj, set); + + if (UNDEF_P(val)) { + CALL_SIMPLE_METHOD(); } } -/** - @c optimize - @e optimized length - @j 最適化された recv.length()。 - */ +/* optimized length */ DEFINE_INSN opt_length -(CALL_INFO ci) +(CALL_DATA cd) (VALUE recv) (VALUE val) +// attr bool zjit_profile = true; { - if (!SPECIAL_CONST_P(recv)) { - if (RBASIC_CLASS(recv) == rb_cString && - BASIC_OP_UNREDEFINED_P(BOP_LENGTH, STRING_REDEFINED_OP_FLAG)) { - val = rb_str_length(recv); - } - else if (RBASIC_CLASS(recv) == rb_cArray && - BASIC_OP_UNREDEFINED_P(BOP_LENGTH, ARRAY_REDEFINED_OP_FLAG)) { - val = LONG2NUM(RARRAY_LEN(recv)); - } - else if (RBASIC_CLASS(recv) == rb_cHash && - BASIC_OP_UNREDEFINED_P(BOP_LENGTH, HASH_REDEFINED_OP_FLAG)) { - val = INT2FIX(RHASH_SIZE(recv)); - } - else { - goto INSN_LABEL(normal_dispatch); - } - } - else { - INSN_LABEL(normal_dispatch): - PUSH(recv); - CALL_SIMPLE_METHOD(recv); + val = vm_opt_length(recv, BOP_LENGTH); + + if (UNDEF_P(val)) { + CALL_SIMPLE_METHOD(); } } -/** - @c optimize - @e optimized size - @j 最適化された recv.size()。 - */ +/* optimized size */ DEFINE_INSN opt_size -(CALL_INFO ci) +(CALL_DATA cd) (VALUE recv) (VALUE val) +// attr bool zjit_profile = true; { - if (!SPECIAL_CONST_P(recv)) { - if (RBASIC_CLASS(recv) == rb_cString && - BASIC_OP_UNREDEFINED_P(BOP_SIZE, STRING_REDEFINED_OP_FLAG)) { - val = rb_str_length(recv); - } - else if (RBASIC_CLASS(recv) == rb_cArray && - BASIC_OP_UNREDEFINED_P(BOP_SIZE, ARRAY_REDEFINED_OP_FLAG)) { - val = LONG2NUM(RARRAY_LEN(recv)); - } - else if (RBASIC_CLASS(recv) == rb_cHash && - BASIC_OP_UNREDEFINED_P(BOP_SIZE, HASH_REDEFINED_OP_FLAG)) { - val = INT2FIX(RHASH_SIZE(recv)); - } - else { - goto INSN_LABEL(normal_dispatch); - } - } - else { - INSN_LABEL(normal_dispatch): - PUSH(recv); - CALL_SIMPLE_METHOD(recv); + val = vm_opt_length(recv, BOP_SIZE); + + if (UNDEF_P(val)) { + CALL_SIMPLE_METHOD(); } } -/** - @c optimize - @e optimized empty? - @j 最適化された recv.empty?()。 - */ +/* optimized empty? */ DEFINE_INSN opt_empty_p -(CALL_INFO ci) +(CALL_DATA cd) (VALUE recv) (VALUE val) +// attr bool zjit_profile = true; { - if (!SPECIAL_CONST_P(recv)) { - if (RBASIC_CLASS(recv) == rb_cString && - BASIC_OP_UNREDEFINED_P(BOP_EMPTY_P, STRING_REDEFINED_OP_FLAG)) { - if (RSTRING_LEN(recv) == 0) val = Qtrue; - else val = Qfalse; - } - else if (RBASIC_CLASS(recv) == rb_cArray && - BASIC_OP_UNREDEFINED_P(BOP_EMPTY_P, ARRAY_REDEFINED_OP_FLAG)) { - if (RARRAY_LEN(recv) == 0) val = Qtrue; - else val = Qfalse; - } - else if (RBASIC_CLASS(recv) == rb_cHash && - BASIC_OP_UNREDEFINED_P(BOP_EMPTY_P, HASH_REDEFINED_OP_FLAG)) { - if (RHASH_EMPTY_P(recv)) val = Qtrue; - else val = Qfalse; - } - else { - goto INSN_LABEL(normal_dispatch); - } - } - else { - INSN_LABEL(normal_dispatch): - PUSH(recv); - CALL_SIMPLE_METHOD(recv); + val = vm_opt_empty_p(recv); + + if (UNDEF_P(val)) { + CALL_SIMPLE_METHOD(); } } -/** - @c optimize - @e optimized succ - @j 最適化された recv.succ()。 - */ +/* optimized succ */ DEFINE_INSN opt_succ -(CALL_INFO ci) +(CALL_DATA cd) (VALUE recv) (VALUE val) +// attr bool zjit_profile = true; { - if (SPECIAL_CONST_P(recv)) { - if (FIXNUM_P(recv) && - BASIC_OP_UNREDEFINED_P(BOP_SUCC, FIXNUM_REDEFINED_OP_FLAG)) { - const VALUE obj = INT2FIX(1); - /* fixnum + INT2FIX(1) */ - val = (recv + (obj & (~1))); - if ((~(recv ^ obj) & (recv ^ val)) & ((unsigned long)LONG_MAX + 1)) { - val = rb_big_plus(rb_int2big(FIX2LONG(recv)), - rb_int2big(FIX2LONG(obj))); - } - } - else { - goto INSN_LABEL(normal_dispatch); - } + val = vm_opt_succ(recv); + + if (UNDEF_P(val)) { + CALL_SIMPLE_METHOD(); } - else { - if (RBASIC_CLASS(recv) == rb_cString && - BASIC_OP_UNREDEFINED_P(BOP_SUCC, STRING_REDEFINED_OP_FLAG)) { - val = rb_str_succ(recv); - } - else - { - goto INSN_LABEL(normal_dispatch); - } - } - if (0) { - INSN_LABEL(normal_dispatch): - PUSH(recv); - CALL_SIMPLE_METHOD(recv); - } -} - -/** - @c optimize - @e optimized not - @j 最適化された recv.!()。 - */ +} + +/* optimized not */ DEFINE_INSN opt_not -(CALL_INFO ci) +(CALL_DATA cd) (VALUE recv) (VALUE val) +// attr bool zjit_profile = true; { - extern VALUE rb_obj_not(VALUE obj); - vm_search_method(ci, recv); + val = vm_opt_not(GET_CFP(), cd, recv); - if (check_cfunc(ci->me, rb_obj_not)) { - val = RTEST(recv) ? Qfalse : Qtrue; - } - else { - PUSH(recv); - CALL_SIMPLE_METHOD(recv); + if (UNDEF_P(val)) { + CALL_SIMPLE_METHOD(); } } - -/** - @c optimize - @e optimized regexp match - @j 最適化された正規表現マッチ。 - */ +/* optimized regexp match 2 */ DEFINE_INSN -opt_regexpmatch1 -(VALUE r) -(VALUE obj) +opt_regexpmatch2 +(CALL_DATA cd) +(VALUE obj2, VALUE obj1) (VALUE val) +// attr bool leaf = false; /* match_at() has rb_thread_check_ints() */ +// attr bool zjit_profile = true; { - 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); + val = vm_opt_regexpmatch2(obj2, obj1); + + if (UNDEF_P(val)) { + CALL_SIMPLE_METHOD(); } } -/** - @c optimize - @e optimized regexp match 2 - @j 最適化された正規表現マッチ 2 - */ +/* call specific function with args */ DEFINE_INSN -opt_regexpmatch2 -(CALL_INFO ci) -(VALUE obj2, VALUE obj1) +invokebuiltin +(RB_BUILTIN bf) +(...) (VALUE val) +// attr bool leaf = false; /* anything can happen inside */ +// attr rb_snum_t sp_inc = 1 - bf->argc; { - 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); - } + val = vm_invoke_builtin(ec, reg_cfp, bf, STACK_ADDR_FROM_TOP(bf->argc)); } -/** - @c optimize - @e call native compiled method - @j ネイティブコンパイルしたメソッドを起動。 - */ +/* call specific function with args (same parameters) */ DEFINE_INSN -opt_call_c_function -(rb_insn_func_t funcptr) +opt_invokebuiltin_delegate +(RB_BUILTIN bf, rb_num_t index) () +(VALUE val) +// attr bool leaf = false; /* anything can happen inside */ +{ + val = vm_invoke_builtin_delegate(ec, reg_cfp, bf, (unsigned int)index); +} + +/* call specific function with args (same parameters) and leave */ +DEFINE_INSN +opt_invokebuiltin_delegate_leave +(RB_BUILTIN bf, rb_num_t index) () +(VALUE val) +// attr bool leaf = false; /* anything can happen inside */ { - reg_cfp = (funcptr)(th, reg_cfp); + val = vm_invoke_builtin_delegate(ec, reg_cfp, bf, (unsigned int)index); - if (reg_cfp == 0) { - VALUE err = th->errinfo; - th->errinfo = Qnil; - THROW_EXCEPTION(err); + /* leave fastpath */ + /* TracePoint/return fallbacks this insn to opt_invokebuiltin_delegate */ + if (vm_pop_frame(ec, GET_CFP(), GET_EP())) { +#if OPT_CALL_THREADED_CODE + rb_ec_thread_ptr(ec)->retval = val; + return 0; +#else + return val; +#endif + } + else { + RESTORE_REGS(); } - - RESTORE_REGS(); - NEXT_INSN(); } -/** - @c joke - @e BLT - @j BLT - */ -DEFINE_INSN +/* BLT */ +DEFINE_INSN_IF(SUPPORT_JOKE) bitblt () () @@ -2144,12 +1771,8 @@ bitblt 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 +/* The Answer to Life, the Universe, and Everything */ +DEFINE_INSN_IF(SUPPORT_JOKE) answer () () @@ -2157,4 +1780,3 @@ answer { ret = INT2FIX(42); } - |
