diff --git a/ChangeLog b/ChangeLog index 3771b534a1..430a7fb8de 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,35 @@ +Mon Oct 15 01:38:06 2012 Koichi Sasada + + * insns.def (send, invokesuper, invokeblock, opt_*), vm_core.h: + use only a `ci' (rb_call_info_t) parameter instead of using + parameters such as `op_id', 'op_argc', `blockiseq' and flag. + These information are stored in rb_call_info_t at the compile + time. + This technique simplifies parameter passings at related + function calls (~10% speedups for simple mehtod invocation at + my machine). + `rb_call_info_t' also has new function pointer variable `call'. + This `call' variable enables to customize method (block) + invocation process for each place. However, it always call + `vm_call_general()' at this changes. + `rb_call_info_t' also has temporary variables for method + (block) invocation. + + * vm_core.h, compile.c, insns.def: introduce VM_CALL_ARGS_SKIP_SETUP + VM_CALL macro. This flag indicates that this call can skip + caller_setup (block arg and splat arg). + + * compile.c: catch up above changes. + + * iseq.c: catch up above changes (especially for TS_CALLINFO). + + * tool/instruction.rb: catch up above chagnes. + + * vm_insnhelper.c, vm_insnhelper.h: ditto. Macros and functions + parameters are changed. + + * vm_eval.c (vm_call0): ditto (it will be rewriten soon). + Sun Oct 14 12:30:44 2012 Nobuyoshi Nakada * ruby.c (rb_f_sub, rb_f_gsub): pass the given block. diff --git a/compile.c b/compile.c index e03d0f9ac6..f878eda8bb 100644 --- a/compile.c +++ b/compile.c @@ -935,20 +935,36 @@ new_insn_body(rb_iseq_t *iseq, int line_no, int insn_id, int argc, ...) return new_insn_core(iseq, line_no, insn_id, argc, operands); } -static INSN * -new_insn_send(rb_iseq_t *iseq, int line_no, - VALUE id, VALUE argc, VALUE block, VALUE flag) +static rb_call_info_t * +new_callinfo(rb_iseq_t *iseq, ID mid, int argc, VALUE block, unsigned long flag) { - INSN *iobj = 0; - VALUE *operands = - (VALUE *)compile_data_alloc(iseq, sizeof(VALUE) * 5); - operands[0] = id; - operands[1] = argc; - operands[2] = block; - operands[3] = flag; - operands[4] = INT2FIX(iseq->callinfo_size++); - iobj = new_insn_core(iseq, line_no, BIN(send), 5, operands); - return iobj; + rb_call_info_t *ci = (rb_call_info_t *)compile_data_alloc(iseq, sizeof(rb_call_info_t)); + ci->mid = mid; + ci->flag = flag; + ci->orig_argc = argc; + + if (block) { + GetISeqPtr(block, ci->blockiseq); + } + else { + ci->blockiseq = 0; + if (!(ci->flag & (VM_CALL_ARGS_BLOCKARG_BIT | VM_CALL_ARGS_SPLAT_BIT))) { + ci->flag |= VM_CALL_ARGS_SKIP_SETUP; + } + } + ci->vmstat = 0; + ci->argc = iseq->callinfo_size++; /* index of callinfo in this iseq */ + + return ci; +} + +static INSN * +new_insn_send(rb_iseq_t *iseq, int line_no, VALUE id, VALUE argc, VALUE block, VALUE flag) +{ + + VALUE *operands = (VALUE *)compile_data_alloc(iseq, sizeof(VALUE) * 1); + operands[0] = (VALUE)new_callinfo(iseq, SYM2ID(id), FIX2INT(argc), block, FIX2INT(flag)); + return new_insn_core(iseq, line_no, BIN(send), 1, operands); } static VALUE @@ -1399,7 +1415,7 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *anchor) iseq->ic_entries = ALLOC_N(struct iseq_inline_cache_entry, iseq->ic_size); MEMZERO(iseq->ic_entries, struct iseq_inline_cache_entry, iseq->ic_size); iseq->callinfo_entries = ALLOC_N(rb_call_info_t, iseq->callinfo_size); - MEMZERO(iseq->callinfo_entries, rb_call_info_t, iseq->callinfo_size); + /* MEMZERO(iseq->callinfo_entries, rb_call_info_t, iseq->callinfo_size); */ list = FIRST_ELEMENT(anchor); k = pos = sp = 0; @@ -1504,10 +1520,12 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *anchor) } case TS_CALLINFO: /* call info */ { - int ci_index = FIX2INT(operands[j]); - CALL_INFO ci = &iseq->callinfo_entries[ci_index]; - if (UNLIKELY(ci_index >= iseq->callinfo_size)) { - rb_bug("iseq_set_sequence: ci_index overflow: index: %d, size: %d", ci_index, iseq->callinfo_size); + rb_call_info_t *base_ci = (rb_call_info_t *)operands[j]; + rb_call_info_t *ci = &iseq->callinfo_entries[base_ci->argc]; + *ci = *base_ci; + + if (UNLIKELY(base_ci->argc >= iseq->callinfo_size)) { + rb_bug("iseq_set_sequence: ci_index overflow: index: %d, size: %d", base_ci->argc, iseq->callinfo_size); } generated_iseq[pos + 1 + j] = (VALUE)ci; break; @@ -1847,10 +1865,11 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal */ INSN *piobj = (INSN *)get_prev_insn((INSN *)list); - if (piobj->insn_id == BIN(send) && - piobj->operands[2] == 0 /* block */ - ) { - piobj->operands[3] = FIXNUM_OR(piobj->operands[3], VM_CALL_TAILCALL_BIT); + if (piobj->insn_id == BIN(send)) { + rb_call_info_t *ci = (rb_call_info_t *)piobj->operands[0]; + if (ci->blockiseq == 0) { + ci->flag |= VM_CALL_TAILCALL_BIT; + } } } return COMPILE_OK; @@ -1859,18 +1878,18 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal static int insn_set_specialized_instruction(rb_iseq_t *iseq, INSN *iobj, int insn_id) { - int i, old_opsize = iobj->operand_size; - + int old_opsize = iobj->operand_size; iobj->insn_id = insn_id; iobj->operand_size = insn_len(insn_id) - 1; - /* printf("iobj->operand_size: %d\n", iobj->operand_size); */ if (iobj->operand_size > old_opsize) { - iobj->operands = (VALUE *)compile_data_alloc(iseq, iobj->operand_size); - } - - for (i=0; ioperand_size; i++) { - iobj->operands[i] = INT2FIX(iseq->callinfo_size++); + VALUE *old_operands = iobj->operands; + if (insn_id != BIN(opt_neq)) { + rb_bug("insn_set_specialized_instruction: unknown insn: %d", insn_id); + } + iobj->operands = (VALUE *)compile_data_alloc(iseq, iobj->operand_size * sizeof(VALUE)); + iobj->operands[0] = old_operands[0]; + iobj->operands[1] = (VALUE)new_callinfo(iseq, idEq, 1, 0, 0); } return COMPILE_OK; @@ -1880,17 +1899,13 @@ static int iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj) { if (iobj->insn_id == BIN(send)) { - ID mid = SYM2ID(OPERAND_AT(iobj, 0)); - int argc = FIX2INT(OPERAND_AT(iobj, 1)); - VALUE block = OPERAND_AT(iobj, 2); - VALUE flag = OPERAND_AT(iobj, 3); + rb_call_info_t *ci = (rb_call_info_t *)OPERAND_AT(iobj, 0); #define SP_INSN(opt) insn_set_specialized_instruction(iseq, iobj, BIN(opt_##opt)) - - if (block == 0 && flag == INT2FIX(0)) { - switch (argc) { + if (ci->blockiseq == 0 && (ci->flag & ~VM_CALL_ARGS_SKIP_SETUP) == 0) { + switch (ci->orig_argc) { case 0: - switch (mid) { + switch (ci->mid) { case idLength: SP_INSN(length); break; case idSize: SP_INSN(size); break; case idEmptyP: SP_INSN(empty_p);break; @@ -1899,7 +1914,7 @@ iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj) } break; case 1: - switch (mid) { + switch (ci->mid) { case idPLUS: SP_INSN(plus); break; case idMINUS: SP_INSN(minus); break; case idMULT: SP_INSN(mult); break; @@ -1918,8 +1933,9 @@ iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj) } } } - return COMPILE_OK; #undef SP_INSN + + return COMPILE_OK; } static int @@ -2502,15 +2518,16 @@ compile_massign_lhs(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE *node) switch (nd_type(node)) { case NODE_ATTRASGN: { INSN *iobj; + rb_call_info_t *ci; VALUE dupidx; COMPILE_POPED(ret, "masgn lhs (NODE_ATTRASGN)", node); + POP_ELEMENT(ret); /* pop pop insn */ iobj = (INSN *)POP_ELEMENT(ret); /* pop send insn */ - - dupidx = iobj->operands[1]; - dupidx = FIXNUM_INC(dupidx, 1); - iobj->operands[1] = dupidx; + ci = (rb_call_info_t *)iobj->operands[0]; + ci->orig_argc += 1; + dupidx = INT2FIX(ci->orig_argc); ADD_INSN1(ret, nd_line(node), topn, dupidx); ADD_ELEM(ret, (LINK_ELEMENT *)iobj); @@ -4355,11 +4372,10 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped) } /* dummy receiver */ - ADD_INSN1(ret, nd_line(node), putobject, - nd_type(node) == NODE_ZSUPER ? Qfalse : Qtrue); + ADD_INSN1(ret, nd_line(node), putobject, nd_type(node) == NODE_ZSUPER ? Qfalse : Qtrue); ADD_SEQ(ret, args); - ADD_INSN3(ret, nd_line(node), invokesuper, - argc, parent_block, LONG2FIX(flag)); + ADD_INSN1(ret, nd_line(node), invokesuper, new_callinfo(iseq, 0, FIX2INT(argc), parent_block, + flag | VM_CALL_SUPER_BIT | VM_CALL_FCALL_BIT)); if (poped) { ADD_INSN(ret, nd_line(node), pop); @@ -4468,7 +4484,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped) } ADD_SEQ(ret, args); - ADD_INSN2(ret, nd_line(node), invokeblock, argc, LONG2FIX(flag)); + ADD_INSN1(ret, nd_line(node), invokeblock, new_callinfo(iseq, 0, FIX2INT(argc), 0, flag)); if (poped) { ADD_INSN(ret, nd_line(node), pop); @@ -5225,7 +5241,7 @@ insn_data_to_s_detail(INSN *iobj) { rb_iseq_t *iseq = (rb_iseq_t *)OPERAND_AT(iobj, j); VALUE val = Qnil; - if (iseq) { + if (0 && iseq) { /* TODO: invalidate now */ val = iseq->self; } rb_str_concat(str, rb_inspect(val)); @@ -5234,8 +5250,11 @@ insn_data_to_s_detail(INSN *iobj) case TS_LINDEX: case TS_NUM: /* ulong */ case TS_VALUE: /* VALUE */ - rb_str_concat(str, rb_inspect(OPERAND_AT(iobj, j))); - break; + { + VALUE v = OPERAND_AT(iobj, j); + rb_str_concat(str, rb_inspect(v)); + break; + } case TS_ID: /* ID */ rb_str_concat(str, rb_inspect(OPERAND_AT(iobj, j))); break; @@ -5244,13 +5263,20 @@ insn_data_to_s_detail(INSN *iobj) struct rb_global_entry *entry = (struct rb_global_entry *) (OPERAND_AT(iobj, j) & (~1)); rb_str_cat2(str, rb_id2name(entry->id)); + break; } case TS_IC: /* inline cache */ rb_str_catf(str, "", FIX2INT(OPERAND_AT(iobj, j))); break; case TS_CALLINFO: /* call info */ - rb_str_catf(str, "", FIX2INT(OPERAND_AT(iobj, j))); - break; + { + rb_call_info_t *ci = (rb_call_info_t *)OPERAND_AT(iobj, j); + rb_str_catf(str, "", ci->mid ? rb_id2name(ci->mid) : "", ci->orig_argc); + if (ci->orig_argc > 60) { + rb_bug("xyzzy"); + } + break; + } case TS_CDHASH: /* case/when condition cache */ rb_str_cat2(str, ""); break; @@ -5282,8 +5308,7 @@ dump_disasm_list(struct iseq_link_element *link) { iobj = (INSN *)link; str = insn_data_to_s_detail(iobj); - printf("%04d %-65s(%4d)\n", pos, StringValueCStr(str), - insn_data_line_no(iobj)); + printf("%04d %-65s(%4d)\n", pos, StringValueCStr(str), insn_data_line_no(iobj)); pos += insn_data_length(iobj); break; } @@ -5422,6 +5447,23 @@ insn_make_insn_table(void) return table; } +static VALUE +iseq_build_load_iseq(rb_iseq_t *iseq, VALUE op) +{ + VALUE iseqval; + if (RB_TYPE_P(op, T_ARRAY)) { + iseqval = rb_iseq_load(op, iseq->self, Qnil); + } + else if (CLASS_OF(op) == rb_cISeq) { + iseqval = op; + } + else { + rb_raise(rb_eSyntaxError, "ISEQ is required"); + } + iseq_add_mark_object(iseq, iseqval); + return iseqval; +} + static int iseq_build_from_ary_body(rb_iseq_t *iseq, LINK_ANCHOR *anchor, VALUE body, struct st_table *labels_table) @@ -5492,16 +5534,7 @@ iseq_build_from_ary_body(rb_iseq_t *iseq, LINK_ANCHOR *anchor, case TS_ISEQ: { if (op != Qnil) { - if (RB_TYPE_P(op, T_ARRAY)) { - argv[j] = rb_iseq_load(op, iseq->self, Qnil); - } - else if (CLASS_OF(op) == rb_cISeq) { - argv[j] = op; - } - else { - rb_raise(rb_eSyntaxError, "ISEQ is required"); - } - iseq_add_mark_object(iseq, argv[j]); + argv[j] = iseq_build_load_iseq(iseq, op); } else { argv[j] = 0; @@ -5519,10 +5552,26 @@ iseq_build_from_ary_body(rb_iseq_t *iseq, LINK_ANCHOR *anchor, } break; case TS_CALLINFO: - argv[j] = op; - if (NUM2INT(op) >= iseq->callinfo_size) { - iseq->callinfo_size = NUM2INT(op) + 1; + { + ID mid = 0; + int orig_argc = 0; + VALUE block = 0; + unsigned long flag = 0; + + if (!NIL_P(op)) { + VALUE vmid = rb_hash_aref(op, ID2SYM(rb_intern("mid"))); + VALUE vflag = rb_hash_aref(op, ID2SYM(rb_intern("flag"))); + VALUE vorig_argc = rb_hash_aref(op, ID2SYM(rb_intern("orig_argc"))); + VALUE vblock = rb_hash_aref(op, ID2SYM(rb_intern("block"))); + + if (!NIL_P(vmid)) mid = SYM2ID(vmid); + if (!NIL_P(vflag)) flag = NUM2ULONG(vflag); + if (!NIL_P(vorig_argc)) orig_argc = FIX2INT(vorig_argc); + if (!NIL_P(vblock)) block = iseq_build_load_iseq(iseq, vblock); + } + argv[j] = (VALUE)new_callinfo(iseq, mid, orig_argc, block, flag); } + break; case TS_ID: argv[j] = rb_convert_type(op, T_SYMBOL, "Symbol", "to_sym"); diff --git a/insns.def b/insns.def index ae1998af63..6e516fbd8d 100644 --- a/insns.def +++ b/insns.def @@ -952,100 +952,62 @@ defineclass /** @c method/iterator - @e obj.send(id, args..) # args.size => num - @j メソッド呼び出しを行う。 - obj.send(id, args..) # args.size => num - flag & VM_CALL_ARGS_SPLAT_BIT != 0 -> splat last arg - flag & VM_CALL_ARGS_BLOCKARG_BIT != 0 -> Proc as Block - flag & VM_CALL_FCALL_BIT != 0 -> FCALL ( func() ) - flag & VM_CALL_VCALL_BIT != 0 -> VCALL ( func ) - ... + @e invoke method. + @j メソッド呼び出しを行う。ci に必要な情報が格納されている。 */ DEFINE_INSN send -(ID op_id, rb_num_t op_argc, ISEQ blockiseq, rb_num_t op_flag, CALL_INFO ci) +(CALL_INFO ci) (...) -(VALUE val) // inc += - (int)(op_argc + ((op_flag & VM_CALL_ARGS_BLOCKARG_BIT) ? 1 : 0)); +(VALUE val) // inc += - (int)(ci->orig_argc + ((ci->flag & VM_CALL_ARGS_BLOCKARG_BIT) ? 1 : 0)); { - const rb_method_entry_t *me; - VALUE recv, klass, defined_class; - rb_block_t *blockptr = 0; - VALUE flag = op_flag; - int num = caller_setup_args(th, GET_CFP(), flag, (int)op_argc, - (rb_iseq_t *)blockiseq, &blockptr); - ID id = op_id; + ci->argc = ci->orig_argc; + ci->blockptr = 0; - /* get receiver */ - recv = TOPN(num); - klass = CLASS_OF(recv); - me = vm_method_search(id, klass, ci, &defined_class); - CALL_METHOD(num, blockptr, flag, id, me, recv, defined_class); + if (!LIKELY(ci->flag & VM_CALL_ARGS_SKIP_SETUP)) { + vm_caller_setup_args(th, reg_cfp, ci); + } + vm_search_method(ci, ci->recv = TOPN(ci->argc)); + CALL_METHOD(ci); } /** @c method/iterator @e super(args) # args.size => num - @j super を実行する。 - super(args) # args.size => num - flag 等オペランドの意味は send と同じ。 + @j super を実行する。ci に必要な情報が格納されている。 */ DEFINE_INSN invokesuper -(rb_num_t op_argc, ISEQ blockiseq, rb_num_t op_flag) +(CALL_INFO ci) (...) -(VALUE val) // inc += - (int)(op_argc + ((op_flag & VM_CALL_ARGS_BLOCKARG_BIT) ? 1 : 0)); +(VALUE val) // inc += - (int)(ci->orig_argc + ((ci->flag & VM_CALL_ARGS_BLOCKARG_BIT) ? 1 : 0)); { - rb_block_t *blockptr = !(op_flag & VM_CALL_ARGS_BLOCKARG_BIT) ? GET_BLOCK_PTR() : 0; - VALUE flag = op_flag; - int num = caller_setup_args(th, GET_CFP(), flag, - (int)op_argc, blockiseq, &blockptr); - VALUE recv, klass; - ID id; - const rb_method_entry_t *me; - rb_iseq_t *ip; + ci->argc = ci->orig_argc; + ci->blockptr = !(ci->flag & VM_CALL_ARGS_BLOCKARG_BIT) ? GET_BLOCK_PTR() : 0; - flag = VM_CALL_SUPER_BIT | VM_CALL_FCALL_BIT; - - klass = GET_CFP()->klass; - if (NIL_P(klass)) { - vm_super_outside(); + if (!LIKELY(ci->flag & VM_CALL_ARGS_SKIP_SETUP)) { + vm_caller_setup_args(th, reg_cfp, ci); } - if (!NIL_P(RCLASS_REFINED_CLASS(klass))) { - klass = RCLASS_REFINED_CLASS(klass); - } - recv = GET_SELF(); - if (!rb_obj_is_kind_of(recv, klass)) { - rb_raise(rb_eNotImpError, "super from singleton method that is defined to multiple classes is not supported; this will be fixed in 2.0.0 or later"); - } - vm_search_superclass(GET_CFP(), GET_ISEQ(), TOPN(num), &id, &klass); - - ip = GET_ISEQ(); - while (ip && !ip->klass) { - ip = ip->parent_iseq; - } - me = rb_method_entry(klass, id, &klass); - if (me && me->def->type == VM_METHOD_TYPE_ISEQ && - me->def->body.iseq == ip) { - klass = RCLASS_SUPER(klass); - me = rb_method_entry_get_with_refinements(Qnil, klass, id, &klass); - } - - CALL_METHOD(num, blockptr, flag, id, me, recv, klass); + ci->recv = GET_SELF(); + vm_search_super_method(th, GET_CFP(), ci); + CALL_METHOD(ci); } /** @c method/iterator - @e yield(args) # args.size => num, flag shows expand argument or not + @e yield(args) @j yield を実行する。 - yield(args) # args.size => num */ DEFINE_INSN invokeblock -(rb_num_t num, rb_num_t flag) +(CALL_INFO ci) (...) -(VALUE val) // inc += 1 - num; +(VALUE val) // inc += 1 - ci->orig_argc; { - val = vm_invoke_block(th, GET_CFP(), num, flag); + 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(); @@ -1338,7 +1300,7 @@ opt_plus INSN_LABEL(normal_dispatch): PUSH(recv); PUSH(obj); - CALL_SIMPLE_METHOD(1, idPLUS, recv); + CALL_SIMPLE_METHOD(recv); } } @@ -1386,7 +1348,7 @@ opt_minus INSN_LABEL(normal_dispatch): PUSH(recv); PUSH(obj); - CALL_SIMPLE_METHOD(1, idMINUS, recv); + CALL_SIMPLE_METHOD(recv); } } @@ -1439,7 +1401,7 @@ opt_mult INSN_LABEL(normal_dispatch): PUSH(recv); PUSH(obj); - CALL_SIMPLE_METHOD(1, idMULT, recv); + CALL_SIMPLE_METHOD(recv); } } @@ -1502,7 +1464,7 @@ opt_div INSN_LABEL(normal_dispatch): PUSH(recv); PUSH(obj); - CALL_SIMPLE_METHOD(1, idDIV, recv); + CALL_SIMPLE_METHOD(recv); } } @@ -1566,7 +1528,7 @@ opt_mod INSN_LABEL(normal_dispatch): PUSH(recv); PUSH(obj); - CALL_SIMPLE_METHOD(1, idMOD, recv); + CALL_SIMPLE_METHOD(recv); } } @@ -1587,7 +1549,7 @@ opt_eq /* other */ PUSH(recv); PUSH(obj); - CALL_SIMPLE_METHOD(1, idEq, recv); + CALL_SIMPLE_METHOD(recv); } } @@ -1603,10 +1565,10 @@ opt_neq (VALUE val) { extern VALUE rb_obj_not_equal(VALUE obj1, VALUE obj2); - const rb_method_entry_t *me = vm_method_search(idNeq, CLASS_OF(recv), ci, 0); + vm_search_method(ci, recv); val = Qundef; - if (check_cfunc(me, rb_obj_not_equal)) { + if (check_cfunc(ci->me, rb_obj_not_equal)) { val = opt_eq_func(recv, obj, ci_eq); if (val != Qundef) { @@ -1618,7 +1580,7 @@ opt_neq /* other */ PUSH(recv); PUSH(obj); - CALL_SIMPLE_METHOD(1, idNeq, recv); + CALL_SIMPLE_METHOD(recv); } } @@ -1662,7 +1624,7 @@ opt_lt INSN_LABEL(normal_dispatch): PUSH(recv); PUSH(obj); - CALL_SIMPLE_METHOD(1, idLT, recv); + CALL_SIMPLE_METHOD(recv); } } @@ -1697,7 +1659,7 @@ opt_le /* other */ PUSH(recv); PUSH(obj); - CALL_SIMPLE_METHOD(1, idLE, recv); + CALL_SIMPLE_METHOD(recv); } } @@ -1741,7 +1703,7 @@ opt_gt INSN_LABEL(normal_dispatch): PUSH(recv); PUSH(obj); - CALL_SIMPLE_METHOD(1, idGT, recv); + CALL_SIMPLE_METHOD(recv); } } @@ -1775,7 +1737,7 @@ opt_ge else { PUSH(recv); PUSH(obj); - CALL_SIMPLE_METHOD(1, idGE, recv); + CALL_SIMPLE_METHOD(recv); } } @@ -1807,7 +1769,7 @@ opt_ltlt INSN_LABEL(normal_dispatch): PUSH(recv); PUSH(obj); - CALL_SIMPLE_METHOD(1, idLTLT, recv); + CALL_SIMPLE_METHOD(recv); } } @@ -1837,7 +1799,7 @@ opt_aref INSN_LABEL(normal_dispatch): PUSH(recv); PUSH(obj); - CALL_SIMPLE_METHOD(1, idAREF, recv); + CALL_SIMPLE_METHOD(recv); } } @@ -1870,7 +1832,7 @@ opt_aset PUSH(recv); PUSH(obj); PUSH(set); - CALL_SIMPLE_METHOD(2, idASET, recv); + CALL_SIMPLE_METHOD(recv); } } @@ -1905,7 +1867,7 @@ opt_length else { INSN_LABEL(normal_dispatch): PUSH(recv); - CALL_SIMPLE_METHOD(0, idLength, recv); + CALL_SIMPLE_METHOD(recv); } } @@ -1940,7 +1902,7 @@ opt_size else { INSN_LABEL(normal_dispatch): PUSH(recv); - CALL_SIMPLE_METHOD(0, idSize, recv); + CALL_SIMPLE_METHOD(recv); } } @@ -1978,7 +1940,7 @@ opt_empty_p else { INSN_LABEL(normal_dispatch): PUSH(recv); - CALL_SIMPLE_METHOD(0, idEmptyP, recv); + CALL_SIMPLE_METHOD(recv); } } @@ -2025,7 +1987,7 @@ opt_succ if (0) { INSN_LABEL(normal_dispatch): PUSH(recv); - CALL_SIMPLE_METHOD(0, idSucc, recv); + CALL_SIMPLE_METHOD(recv); } } @@ -2041,14 +2003,14 @@ opt_not (VALUE val) { extern VALUE rb_obj_not(VALUE obj); - const rb_method_entry_t *me = vm_method_search(idNot, CLASS_OF(recv), ci, 0); + vm_search_method(ci, recv); - if (check_cfunc(me, rb_obj_not)) { + if (check_cfunc(ci->me, rb_obj_not)) { val = RTEST(recv) ? Qfalse : Qtrue; } else { PUSH(recv); - CALL_SIMPLE_METHOD(0, idNot, recv); + CALL_SIMPLE_METHOD(recv); } } diff --git a/iseq.c b/iseq.c index 792f401bde..3244e44ab8 100644 --- a/iseq.c +++ b/iseq.c @@ -149,6 +149,7 @@ iseq_memsize(const void *ptr) size += iseq->catch_table_size * sizeof(struct iseq_catch_table_entry); size += iseq->arg_opts * sizeof(VALUE); size += iseq->ic_size * sizeof(struct iseq_inline_cache_entry); + size += iseq->callinfo_size * sizeof(rb_call_info_t); if (iseq->compile_data) { struct iseq_compile_data_storage *cur; @@ -1041,7 +1042,37 @@ insn_operand_intern(rb_iseq_t *iseq, break; case TS_CALLINFO: - ret = rb_sprintf("", (rb_call_info_t *)op - iseq->callinfo_entries); + { + rb_call_info_t *ci = (rb_call_info_t *)op; + VALUE ary = rb_ary_new(); + + if (ci->mid) { + rb_ary_push(ary, rb_sprintf("mid:%s", rb_id2name(ci->mid))); + } + + rb_ary_push(ary, rb_sprintf("argc:%d", ci->orig_argc)); + + if (ci->blockiseq) { + if (child) { + rb_ary_push(child, ci->blockiseq->self); + } + rb_ary_push(ary, rb_sprintf("block:%"PRIsVALUE, ci->blockiseq->location.label)); + } + + if (ci->flag) { + VALUE flags = rb_ary_new(); + if (ci->flag & VM_CALL_ARGS_SPLAT_BIT) rb_ary_push(flags, rb_str_new2("ARGS_SPLAT")); + if (ci->flag & VM_CALL_ARGS_BLOCKARG_BIT) rb_ary_push(flags, rb_str_new2("ARGS_BLOCKARG")); + if (ci->flag & VM_CALL_FCALL_BIT) rb_ary_push(flags, rb_str_new2("FCALL")); + if (ci->flag & VM_CALL_VCALL_BIT) rb_ary_push(flags, rb_str_new2("VCALL")); + if (ci->flag & VM_CALL_TAILCALL_BIT) rb_ary_push(flags, rb_str_new2("TAILCALL")); + if (ci->flag & VM_CALL_SUPER_BIT) rb_ary_push(flags, rb_str_new2("SUPER")); + if (ci->flag & VM_CALL_OPT_SEND_BIT) rb_ary_push(flags, rb_str_new2("SNED")); /* maybe not reachable */ + if (ci->flag & VM_CALL_ARGS_SKIP_SETUP) rb_ary_push(flags, rb_str_new2("ARGS_SKIP")); + rb_ary_push(ary, rb_ary_join(flags, rb_str_new2("|"))); + } + ret = rb_sprintf("", rb_ary_join(ary, rb_str_new2(", "))); + } break; case TS_CDHASH: @@ -1542,6 +1573,17 @@ iseq_data_to_ary(rb_iseq_t *iseq) rb_ary_push(ary, INT2FIX(ic - iseq->ic_entries)); } break; + case TS_CALLINFO: + { + rb_call_info_t *ci = (rb_call_info_t *)*seq; + VALUE e = rb_hash_new(); + rb_hash_aset(e, ID2SYM(rb_intern("mid")), ci->mid ? ID2SYM(ci->mid) : Qnil); + rb_hash_aset(e, ID2SYM(rb_intern("flag")), ULONG2NUM(ci->flag)); + rb_hash_aset(e, ID2SYM(rb_intern("orig_argc")), INT2FIX(ci->orig_argc)); + rb_hash_aset(e, ID2SYM(rb_intern("blockptr")), ci->blockiseq ? iseq_data_to_ary(ci->blockiseq) : Qnil); + rb_ary_push(ary, e); + } + break; case TS_ID: rb_ary_push(ary, ID2SYM(*seq)); break; diff --git a/tool/instruction.rb b/tool/instruction.rb index 6934b33e80..c6e0c40c0c 100755 --- a/tool/instruction.rb +++ b/tool/instruction.rb @@ -66,11 +66,15 @@ class RubyVM ret = "int inc = 0;\n" @opes.each_with_index{|(t, v), i| - if t == 'rb_num_t' && ((re = /\b#{v}\b/n) =~ @sp_inc || - @defopes.any?{|t, val| re =~ val}) + if (t == 'rb_num_t' && ((re = /\b#{v}\b/n) =~ @sp_inc)) || + (@defopes.any?{|t, val| re =~ val}) ret << " int #{v} = FIX2INT(opes[#{i}]);\n" + elsif (t == 'CALL_INFO' && ((re = /\b#{v}\b/n) =~ @sp_inc)) + ret << " CALL_INFO #{v} = (CALL_INFO)(opes[#{i}]);\n" end + } + @defopes.each_with_index{|((t, var), val), i| if t == 'rb_num_t' && val != '*' && /\b#{var}\b/ =~ @sp_inc ret << " #{t} #{var} = #{val};\n" diff --git a/vm_core.h b/vm_core.h index 6213863da4..704989ce3f 100644 --- a/vm_core.h +++ b/vm_core.h @@ -138,15 +138,31 @@ struct iseq_inline_cache_entry { } ic_value; }; +/* to avoid warning */ +struct rb_thread_struct; +struct rb_control_frame_struct; + /* rb_call_info_t contains calling information including inline cache */ typedef struct rb_call_info_struct { + /* fixed at compile time */ + ID mid; + VALUE flag; + int orig_argc; + rb_iseq_t *blockiseq; + /* inline cache: keys */ - VALUE ic_vmstat; - VALUE ic_class; + VALUE vmstat; + VALUE klass; /* inline cache: values */ - rb_method_entry_t *method; + const rb_method_entry_t *me; VALUE defined_class; + + /* temporary values for method calling */ + int argc; + struct rb_block_struct *blockptr; + VALUE recv; + VALUE (*call)(struct rb_thread_struct *th, struct rb_control_frame_struct *cfp, struct rb_call_info_struct *ci); } rb_call_info_t; #if 1 @@ -367,7 +383,7 @@ typedef struct rb_vm_struct { #define VM_DEBUG_BP_CHECK 1 #endif -typedef struct { +typedef struct rb_control_frame_struct { VALUE *pc; /* cfp[0] */ VALUE *sp; /* cfp[1] */ rb_iseq_t *iseq; /* cfp[2] */ @@ -640,6 +656,7 @@ enum vm_check_match_type { #define VM_CALL_TAILRECURSION_BIT (0x01 << 6) #define VM_CALL_SUPER_BIT (0x01 << 7) #define VM_CALL_OPT_SEND_BIT (0x01 << 8) +#define VM_CALL_ARGS_SKIP_SETUP (0x01 << 9) enum vm_special_object_type { VM_SPECIAL_OBJECT_VMCORE = 1, diff --git a/vm_eval.c b/vm_eval.c index 1346aea3c3..c42703f78f 100644 --- a/vm_eval.c +++ b/vm_eval.c @@ -37,14 +37,23 @@ vm_call0(rb_thread_t* th, VALUE recv, VALUE id, int argc, const VALUE *argv, { const rb_method_definition_t *def = me->def; VALUE val; - VALUE klass = defined_class; - const rb_block_t *blockptr = 0; + rb_call_info_t ci_entry, *ci = &ci_entry; + + ci->flag = 0; + ci->mid = id; + ci->recv = recv; + ci->defined_class = defined_class; + ci->argc = argc; + ci->me = me; if (!def) return Qnil; if (th->passed_block) { - blockptr = th->passed_block; + ci->blockptr = (rb_block_t *)th->passed_block; th->passed_block = 0; } + else { + ci->blockptr = 0; + } again: switch (def->type) { @@ -59,20 +68,19 @@ vm_call0(rb_thread_t* th, VALUE recv, VALUE id, int argc, const VALUE *argv, *reg_cfp->sp++ = argv[i]; } - vm_setup_method(th, reg_cfp, recv, argc, blockptr, 0 /* flag */, - me, klass); + vm_setup_method(th, reg_cfp, ci); th->cfp->flag |= VM_FRAME_FLAG_FINISH; val = vm_exec(th); break; } case VM_METHOD_TYPE_NOTIMPLEMENTED: case VM_METHOD_TYPE_CFUNC: { - EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, recv, id, klass); + EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, ci->recv, ci->mid, ci->defined_class); { rb_control_frame_t *reg_cfp = th->cfp; rb_control_frame_t *cfp = vm_push_frame(th, 0, VM_FRAME_MAGIC_CFUNC, - recv, klass, VM_ENVVAL_BLOCK_PTR(blockptr), + ci->recv, ci->defined_class, VM_ENVVAL_BLOCK_PTR(ci->blockptr), 0, reg_cfp->sp, 1, me); cfp->me = me; @@ -83,7 +91,7 @@ vm_call0(rb_thread_t* th, VALUE recv, VALUE id, int argc, const VALUE *argv, } vm_pop_frame(th); } - EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, recv, id, klass); + EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, ci->recv, ci->mid, ci->defined_class); break; } case VM_METHOD_TYPE_ATTRSET: { @@ -97,13 +105,13 @@ vm_call0(rb_thread_t* th, VALUE recv, VALUE id, int argc, const VALUE *argv, break; } case VM_METHOD_TYPE_BMETHOD: { - val = vm_call_bmethod(th, recv, argc, argv, blockptr, me, klass); + val = vm_call_bmethod(th, recv, argc, argv, ci->blockptr, me, ci->defined_class); break; } case VM_METHOD_TYPE_ZSUPER: { - klass = RCLASS_SUPER(klass); - if (!klass || !(me = rb_method_entry(klass, id, &klass))) { - return method_missing(recv, id, argc, argv, NOEX_SUPER); + ci->defined_class = RCLASS_SUPER(ci->defined_class); + if (!ci->defined_class || !(ci->me = rb_method_entry(ci->defined_class, id, &ci->defined_class))) { + return method_missing(recv, ci->mid, ci->argc, argv, NOEX_SUPER); } RUBY_VM_CHECK_INTS(th); if (!(def = me->def)) return Qnil; @@ -113,10 +121,9 @@ vm_call0(rb_thread_t* th, VALUE recv, VALUE id, int argc, const VALUE *argv, VALUE new_args = rb_ary_new4(argc, argv); RB_GC_GUARD(new_args); - rb_ary_unshift(new_args, ID2SYM(id)); - th->passed_block = blockptr; - return rb_funcall2(recv, idMethodMissing, - argc+1, RARRAY_PTR(new_args)); + rb_ary_unshift(new_args, ID2SYM(ci->mid)); + th->passed_block = ci->blockptr; + return rb_funcall2(ci->recv, idMethodMissing, argc+1, RARRAY_PTR(new_args)); } case VM_METHOD_TYPE_OPTIMIZED: { switch (def->body.optimize_type) { @@ -126,7 +133,7 @@ vm_call0(rb_thread_t* th, VALUE recv, VALUE id, int argc, const VALUE *argv, case OPTIMIZED_METHOD_TYPE_CALL: { rb_proc_t *proc; GetProcPtr(recv, proc); - val = rb_vm_invoke_proc(th, proc, argc, argv, blockptr); + val = rb_vm_invoke_proc(th, proc, argc, argv, ci->blockptr); break; } default: diff --git a/vm_insnhelper.c b/vm_insnhelper.c index 06cc85934a..921e41ecae 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -158,27 +158,26 @@ rb_error_arity(int argc, int min, int max) rb_exc_raise(rb_arg_error_new(argc, min, max)); } -#define VM_CALLEE_SETUP_ARG(ret, th, iseq, orig_argc, orig_argv, block) \ +#define VM_CALLEE_SETUP_ARG(ret, th, ci, iseq, argv) \ if (LIKELY((iseq)->arg_simple & 0x01)) { \ /* simple check */ \ - if ((orig_argc) != (iseq)->argc) { \ - argument_error((iseq), (orig_argc), (iseq)->argc, (iseq)->argc); \ + if ((ci)->argc != (iseq)->argc) { \ + argument_error((iseq), ((ci)->argc), (iseq)->argc, (iseq)->argc); \ } \ (ret) = 0; \ } \ else { \ - (ret) = vm_callee_setup_arg_complex((th), (iseq), (orig_argc), (orig_argv), (block)); \ + (ret) = vm_callee_setup_arg_complex((th), (ci), (iseq), (argv)); \ } static inline int -vm_callee_setup_arg_complex(rb_thread_t *th, const rb_iseq_t * iseq, - int orig_argc, VALUE * orig_argv, - const rb_block_t **block) +vm_callee_setup_arg_complex(rb_thread_t *th, rb_call_info_t *ci, const rb_iseq_t * iseq, VALUE *orig_argv) { const int m = iseq->argc; const int opts = iseq->arg_opts - (iseq->arg_opts > 0); const int min = m + iseq->arg_post_len; const int max = (iseq->arg_rest == -1) ? m + opts + iseq->arg_post_len : UNLIMITED_ARGUMENTS; + const int orig_argc = ci->argc; int argc = orig_argc; VALUE *argv = orig_argv; rb_num_t opt_pc = 0; @@ -255,9 +254,9 @@ vm_callee_setup_arg_complex(rb_thread_t *th, const rb_iseq_t * iseq, } /* block arguments */ - if (block && iseq->arg_block != -1) { + if (iseq->arg_block != -1) { VALUE blockval = Qnil; - const rb_block_t *blockptr = *block; + const rb_block_t *blockptr = ci->blockptr; if (blockptr) { /* make Proc object */ @@ -265,7 +264,7 @@ vm_callee_setup_arg_complex(rb_thread_t *th, const rb_iseq_t * iseq, rb_proc_t *proc; blockval = rb_vm_make_proc(th, blockptr, rb_cProc); GetProcPtr(blockval, proc); - *block = &proc->block; + ci->blockptr = &proc->block; } else { blockval = blockptr->proc; @@ -279,45 +278,39 @@ vm_callee_setup_arg_complex(rb_thread_t *th, const rb_iseq_t * iseq, return (int)opt_pc; } -static inline int -caller_setup_args(const rb_thread_t *th, rb_control_frame_t *cfp, VALUE flag, - int argc, rb_iseq_t *blockiseq, rb_block_t **block) +static void +vm_caller_setup_args(const rb_thread_t *th, rb_control_frame_t *cfp, rb_call_info_t *ci) { - rb_block_t *blockptr = 0; + if (UNLIKELY(ci->flag & VM_CALL_ARGS_BLOCKARG_BIT)) { + rb_proc_t *po; + VALUE proc; - if (block) { - if (flag & VM_CALL_ARGS_BLOCKARG_BIT) { - rb_proc_t *po; - VALUE proc; + proc = *(--cfp->sp); - proc = *(--cfp->sp); - - if (proc != Qnil) { - if (!rb_obj_is_proc(proc)) { - VALUE b = rb_check_convert_type(proc, T_DATA, "Proc", "to_proc"); - if (NIL_P(b) || !rb_obj_is_proc(b)) { - rb_raise(rb_eTypeError, - "wrong argument type %s (expected Proc)", - rb_obj_classname(proc)); - } - proc = b; + if (proc != Qnil) { + if (!rb_obj_is_proc(proc)) { + VALUE b = rb_check_convert_type(proc, T_DATA, "Proc", "to_proc"); + if (NIL_P(b) || !rb_obj_is_proc(b)) { + rb_raise(rb_eTypeError, + "wrong argument type %s (expected Proc)", + rb_obj_classname(proc)); } - GetProcPtr(proc, po); - blockptr = &po->block; - RUBY_VM_GET_BLOCK_PTR_IN_CFP(cfp)->proc = proc; - *block = blockptr; + proc = b; } + GetProcPtr(proc, po); + ci->blockptr = &po->block; + RUBY_VM_GET_BLOCK_PTR_IN_CFP(cfp)->proc = proc; } - else if (blockiseq) { - blockptr = RUBY_VM_GET_BLOCK_PTR_IN_CFP(cfp); - blockptr->iseq = blockiseq; - blockptr->proc = 0; - *block = blockptr; - } + } + else if (UNLIKELY(ci->blockiseq != 0)) { + ci->blockptr = RUBY_VM_GET_BLOCK_PTR_IN_CFP(cfp); + ci->blockptr->iseq = ci->blockiseq; + ci->blockptr->proc = 0; } /* expand top of stack? */ - if (flag & VM_CALL_ARGS_SPLAT_BIT) { + + if (UNLIKELY(ci->flag & VM_CALL_ARGS_SPLAT_BIT)) { VALUE ary = *(cfp->sp - 1); VALUE *ptr; int i; @@ -336,11 +329,9 @@ caller_setup_args(const rb_thread_t *th, rb_control_frame_t *cfp, VALUE flag, for (i = 0; i < len; i++) { *cfp->sp++ = ptr[i]; } - argc += i-1; + ci->argc += i-1; } } - - return argc; } static inline VALUE @@ -428,21 +419,20 @@ call_cfunc(VALUE (*func)(), VALUE recv, } static inline VALUE -vm_call_cfunc(rb_thread_t *th, rb_control_frame_t *reg_cfp, - int num, volatile VALUE recv, const rb_block_t *blockptr, - const rb_method_entry_t *me, VALUE defined_class) +vm_call_cfunc(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_info_t *ci) { volatile VALUE val = 0; + const rb_method_entry_t *me = ci->me; const rb_method_definition_t *def = me->def; - EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, recv, me->called_id, me->klass); + EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, ci->recv, me->called_id, me->klass); - vm_push_frame(th, 0, VM_FRAME_MAGIC_CFUNC, recv, defined_class, - VM_ENVVAL_BLOCK_PTR(blockptr), 0, th->cfp->sp, 1, me); + vm_push_frame(th, 0, VM_FRAME_MAGIC_CFUNC, ci->recv, ci->defined_class, + VM_ENVVAL_BLOCK_PTR(ci->blockptr), 0, th->cfp->sp, 1, me); - reg_cfp->sp -= num + 1; + reg_cfp->sp -= ci->argc + 1; - val = call_cfunc(def->body.cfunc.func, recv, (int)def->body.cfunc.argc, num, reg_cfp->sp + 1); + val = call_cfunc(def->body.cfunc.func, ci->recv, (int)def->body.cfunc.argc, ci->argc, reg_cfp->sp + 1); if (reg_cfp != th->cfp + 1) { rb_bug("cfp consistency error - send"); @@ -450,7 +440,7 @@ vm_call_cfunc(rb_thread_t *th, rb_control_frame_t *reg_cfp, vm_pop_frame(th); - EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, recv, me->called_id, me->klass); + EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, ci->recv, me->called_id, me->klass); return val; } @@ -491,20 +481,18 @@ vm_method_missing(rb_thread_t *th, rb_control_frame_t *const reg_cfp, } static inline void -vm_setup_method(rb_thread_t *th, rb_control_frame_t *cfp, - VALUE recv, int argc, const rb_block_t *blockptr, VALUE flag, - const rb_method_entry_t *me, VALUE defined_class) +vm_setup_method(rb_thread_t *th, rb_control_frame_t *cfp, rb_call_info_t *ci) { int opt_pc, i; - VALUE *argv = cfp->sp - argc; - rb_iseq_t *iseq = me->def->body.iseq; + VALUE *argv = cfp->sp - ci->argc; + rb_iseq_t *iseq = ci->me->def->body.iseq; - VM_CALLEE_SETUP_ARG(opt_pc, th, iseq, argc, argv, &blockptr); + VM_CALLEE_SETUP_ARG(opt_pc, th, ci, iseq, argv); /* stack overflow check */ CHECK_STACK_OVERFLOW(cfp, iseq->stack_max); - if (LIKELY(!(flag & VM_CALL_TAILCALL_BIT))) { + if (LIKELY(!(ci->flag & VM_CALL_TAILCALL_BIT))) { VALUE *sp = argv + iseq->arg_size; /* clear local variables */ @@ -512,9 +500,9 @@ vm_setup_method(rb_thread_t *th, rb_control_frame_t *cfp, *sp++ = Qnil; } - vm_push_frame(th, iseq, VM_FRAME_MAGIC_METHOD, recv, defined_class, - VM_ENVVAL_BLOCK_PTR(blockptr), - iseq->iseq_encoded + opt_pc, sp, 0, me); + vm_push_frame(th, iseq, VM_FRAME_MAGIC_METHOD, ci->recv, ci->defined_class, + VM_ENVVAL_BLOCK_PTR(ci->blockptr), + iseq->iseq_encoded + opt_pc, sp, 0, ci->me); cfp->sp = argv - 1 /* recv */; } @@ -527,7 +515,7 @@ vm_setup_method(rb_thread_t *th, rb_control_frame_t *cfp, sp_orig = sp = cfp->sp; /* push self */ - sp[0] = recv; + sp[0] = ci->recv; sp++; /* copy arguments */ @@ -541,187 +529,13 @@ vm_setup_method(rb_thread_t *th, rb_control_frame_t *cfp, } vm_push_frame(th, iseq, VM_FRAME_MAGIC_METHOD | finish_flag, - recv, defined_class, VM_ENVVAL_BLOCK_PTR(blockptr), - iseq->iseq_encoded + opt_pc, sp, 0, me); + ci->recv, ci->defined_class, VM_ENVVAL_BLOCK_PTR(ci->blockptr), + iseq->iseq_encoded + opt_pc, sp, 0, ci->me); cfp->sp = sp_orig; } } -static inline VALUE -vm_call_method(rb_thread_t *th, rb_control_frame_t *cfp, - int num, const rb_block_t *blockptr, VALUE flag, - ID id, const rb_method_entry_t *me, - VALUE recv, VALUE defined_class) -{ - VALUE val; - - start_method_dispatch: - - if (me != 0) { - if ((me->flag == 0)) { - normal_method_dispatch: - switch (me->def->type) { - case VM_METHOD_TYPE_ISEQ:{ - vm_setup_method(th, cfp, recv, num, blockptr, flag, me, - defined_class); - return Qundef; - } - case VM_METHOD_TYPE_NOTIMPLEMENTED: - case VM_METHOD_TYPE_CFUNC:{ - val = vm_call_cfunc(th, cfp, num, recv, blockptr, me, - defined_class); - break; - } - case VM_METHOD_TYPE_ATTRSET:{ - rb_check_arity(num, 1, 1); - val = rb_ivar_set(recv, me->def->body.attr.id, *(cfp->sp - 1)); - cfp->sp -= 2; - break; - } - case VM_METHOD_TYPE_IVAR:{ - rb_check_arity(num, 0, 0); - val = rb_attr_get(recv, me->def->body.attr.id); - cfp->sp -= 1; - break; - } - case VM_METHOD_TYPE_MISSING:{ - VALUE *argv = ALLOCA_N(VALUE, num+1); - argv[0] = ID2SYM(me->def->original_id); - MEMCPY(argv+1, cfp->sp - num, VALUE, num); - cfp->sp += - num - 1; - th->passed_block = blockptr; - val = rb_funcall2(recv, rb_intern("method_missing"), num+1, argv); - break; - } - case VM_METHOD_TYPE_BMETHOD:{ - VALUE *argv = ALLOCA_N(VALUE, num); - MEMCPY(argv, cfp->sp - num, VALUE, num); - cfp->sp += - num - 1; - val = vm_call_bmethod(th, recv, num, argv, blockptr, me, - defined_class); - break; - } - case VM_METHOD_TYPE_ZSUPER:{ - VALUE klass = RCLASS_SUPER(me->klass); - me = rb_method_entry(klass, id, &defined_class); - - if (me != 0) { - goto normal_method_dispatch; - } - else { - goto start_method_dispatch; - } - } - case VM_METHOD_TYPE_OPTIMIZED:{ - switch (me->def->body.optimize_type) { - case OPTIMIZED_METHOD_TYPE_SEND: { - rb_control_frame_t *reg_cfp = cfp; - rb_num_t i = num - 1; - VALUE sym; - - if (num == 0) { - rb_raise(rb_eArgError, "no method name given"); - } - - sym = TOPN(i); - if (SYMBOL_P(sym)) { - id = SYM2ID(sym); - } - else if (!(id = rb_check_id(&sym))) { - if (rb_method_basic_definition_p(CLASS_OF(recv), idMethodMissing)) { - VALUE exc = make_no_method_exception(rb_eNoMethodError, NULL, recv, - rb_long2int(num), &TOPN(i)); - rb_exc_raise(exc); - } - id = rb_to_id(sym); - } - /* shift arguments */ - if (i > 0) { - MEMMOVE(&TOPN(i), &TOPN(i-1), VALUE, i); - } - me = rb_method_entry(CLASS_OF(recv), id, &defined_class); - num -= 1; - DEC_SP(1); - flag |= VM_CALL_FCALL_BIT | VM_CALL_OPT_SEND_BIT; - - goto start_method_dispatch; - } - case OPTIMIZED_METHOD_TYPE_CALL: { - rb_proc_t *proc; - int argc = num; - VALUE *argv = ALLOCA_N(VALUE, num); - GetProcPtr(recv, proc); - MEMCPY(argv, cfp->sp - num, VALUE, num); - cfp->sp -= num + 1; - - val = rb_vm_invoke_proc(th, proc, argc, argv, blockptr); - break; - } - default: - rb_bug("eval_invoke_method: unsupported optimized method type (%d)", - me->def->body.optimize_type); - } - break; - } - default:{ - rb_bug("eval_invoke_method: unsupported method type (%d)", me->def->type); - break; - } - } - } - else { - int noex_safe; - - if (!(flag & VM_CALL_FCALL_BIT) && - (me->flag & NOEX_MASK) & NOEX_PRIVATE) { - int stat = NOEX_PRIVATE; - - if (flag & VM_CALL_VCALL_BIT) { - stat |= NOEX_VCALL; - } - val = vm_method_missing(th, cfp, id, recv, num, blockptr, stat); - } - else if (!(flag & VM_CALL_OPT_SEND_BIT) && (me->flag & NOEX_MASK) & NOEX_PROTECTED) { - if (!rb_obj_is_kind_of(cfp->self, defined_class)) { - val = vm_method_missing(th, cfp, id, recv, num, blockptr, NOEX_PROTECTED); - } - else { - goto normal_method_dispatch; - } - } - else if ((noex_safe = NOEX_SAFE(me->flag)) > th->safe_level && - (noex_safe > 2)) { - rb_raise(rb_eSecurityError, "calling insecure method: %s", rb_id2name(id)); - } - else { - goto normal_method_dispatch; - } - } - } - else { - /* method missing */ - int stat = 0; - if (flag & VM_CALL_VCALL_BIT) { - stat |= NOEX_VCALL; - } - if (flag & VM_CALL_SUPER_BIT) { - stat |= NOEX_SUPER; - } - if (id == idMethodMissing) { - rb_control_frame_t *reg_cfp = cfp; - VALUE *argv = STACK_ADDR_FROM_TOP(num); - rb_raise_method_missing(th, num, argv, recv, stat); - } - else { - val = vm_method_missing(th, cfp, id, recv, num, blockptr, stat); - } - } - - RUBY_VM_CHECK_INTS(th); - return val; -} - /* yield */ static inline int @@ -938,8 +752,7 @@ vm_yield_setup_block_args(rb_thread_t *th, const rb_iseq_t * iseq, static inline int vm_yield_setup_args(rb_thread_t * const th, const rb_iseq_t *iseq, - int argc, VALUE *argv, - const rb_block_t *blockptr, int lambda) + int argc, VALUE *argv, const rb_block_t *blockptr, int lambda) { if (0) { /* for debug */ printf(" argc: %d\n", argc); @@ -955,7 +768,11 @@ vm_yield_setup_args(rb_thread_t * const th, const rb_iseq_t *iseq, if (lambda) { /* call as method */ int opt_pc; - VM_CALLEE_SETUP_ARG(opt_pc, th, iseq, argc, argv, &blockptr); + rb_call_info_t ci_entry; + ci_entry.flag = 0; + ci_entry.argc = argc; + ci_entry.blockptr = (rb_block_t *)blockptr; + VM_CALLEE_SETUP_ARG(opt_pc, th, &ci_entry, iseq, argv); return opt_pc; } else { @@ -964,11 +781,10 @@ vm_yield_setup_args(rb_thread_t * const th, const rb_iseq_t *iseq, } static VALUE -vm_invoke_block(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_num_t num, rb_num_t flag) +vm_invoke_block(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_info_t *ci) { const rb_block_t *block = VM_CF_BLOCK_PTR(reg_cfp); rb_iseq_t *iseq; - int argc = (int)num; VALUE type = GET_ISEQ()->local_iseq->type; if ((type != ISEQ_TYPE_METHOD && type != ISEQ_TYPE_CLASS) || block == 0) { @@ -976,17 +792,16 @@ vm_invoke_block(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_num_t num, rb_n } iseq = block->iseq; - argc = caller_setup_args(th, GET_CFP(), flag, argc, 0, 0); + vm_caller_setup_args(th, GET_CFP(), ci); if (BUILTIN_TYPE(iseq) != T_NODE) { int opt_pc; const int arg_size = iseq->arg_size; - VALUE * const rsp = GET_SP() - argc; + VALUE * const rsp = GET_SP() - ci->argc; SET_SP(rsp); CHECK_STACK_OVERFLOW(GET_CFP(), iseq->stack_max); - opt_pc = vm_yield_setup_args(th, iseq, argc, rsp, 0, - block_proc_is_lambda(block->proc)); + opt_pc = vm_yield_setup_args(th, iseq, ci->argc, rsp, 0, block_proc_is_lambda(block->proc)); vm_push_frame(th, iseq, VM_FRAME_MAGIC_BLOCK, block->self, block->klass, @@ -998,8 +813,8 @@ vm_invoke_block(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_num_t num, rb_n return Qundef; } else { - VALUE val = vm_yield_with_cfunc(th, block, block->self, argc, STACK_ADDR_FROM_TOP(argc), 0); - POPN(argc); /* TODO: should put before C/yield? */ + VALUE val = vm_yield_with_cfunc(th, block, block->self, ci->argc, STACK_ADDR_FROM_TOP(ci->argc), 0); + POPN(ci->argc); /* TODO: should put before C/yield? */ return val; } } @@ -1429,105 +1244,6 @@ vm_setivar(VALUE obj, ID id, VALUE val, IC ic) #endif } -static inline const rb_method_entry_t * -vm_method_search(VALUE id, VALUE klass, CALL_INFO ci, VALUE *defined_class_ptr) -{ - rb_method_entry_t *me; -#if OPT_INLINE_METHOD_CACHE - if (LIKELY(klass == ci->ic_class && - GET_VM_STATE_VERSION() == ci->ic_vmstat)) { - me = ci->method; - if (defined_class_ptr) { - *defined_class_ptr = ci->defined_class; - } - } - else { - VALUE defined_class; - me = rb_method_entry(klass, id, &defined_class); - if (defined_class_ptr) { - *defined_class_ptr = defined_class; - } - ci->ic_class = klass; - ci->method = me; - ci->defined_class = defined_class; - ci->ic_vmstat = GET_VM_STATE_VERSION(); - } -#else - me = rb_method_entry(klass, id, defined_class_ptr); -#endif - return me; -} - -static inline VALUE -vm_search_normal_superclass(VALUE klass) -{ - klass = RCLASS_ORIGIN(klass); - return RCLASS_SUPER(klass); -} - -static void -vm_super_outside(void) -{ - rb_raise(rb_eNoMethodError, "super called outside of method"); -} - -static void -vm_search_superclass(rb_control_frame_t *reg_cfp, rb_iseq_t *iseq, - VALUE sigval, - ID *idp, VALUE *klassp) -{ - ID id; - VALUE klass; - - while (iseq && !iseq->klass) { - iseq = iseq->parent_iseq; - } - - if (iseq == 0) { - vm_super_outside(); - } - - id = iseq->defined_method_id; - - if (iseq != iseq->local_iseq) { - /* defined by Module#define_method() */ - rb_control_frame_t *lcfp = GET_CFP(); - - if (!sigval) { - /* zsuper */ - rb_raise(rb_eRuntimeError, "implicit argument passing of super from method defined by define_method() is not supported. Specify all arguments explicitly."); - } - - while (lcfp->iseq != iseq) { - rb_thread_t *th = GET_THREAD(); - VALUE *tep = VM_EP_PREV_EP(lcfp->ep); - while (1) { - lcfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(lcfp); - if (RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(th, lcfp)) { - vm_super_outside(); - } - if (lcfp->ep == tep) { - break; - } - } - } - - /* temporary measure for [Bug #2420] [Bug #3136] */ - if (!lcfp->me) { - vm_super_outside(); - } - - id = lcfp->me->def->original_id; - klass = vm_search_normal_superclass(lcfp->klass); - } - else { - klass = vm_search_normal_superclass(reg_cfp->klass); - } - - *idp = id; - *klassp = klass; -} - static VALUE vm_throw(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_num_t throw_state, VALUE throwobj) @@ -1758,6 +1474,32 @@ vm_expandarray(rb_control_frame_t *cfp, VALUE ary, rb_num_t num, int flag) RB_GC_GUARD(ary); } +static VALUE vm_call_general(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_info_t *ci); + +static void +vm_search_method(rb_call_info_t *ci, VALUE recv) +{ + VALUE klass = CLASS_OF(recv); + rb_method_entry_t *me; + +#if OPT_INLINE_METHOD_CACHE + if (LIKELY(GET_VM_STATE_VERSION() == ci->vmstat && klass == ci->klass)) { + /* cache hit! */ + } + else { + me = rb_method_entry(klass, ci->mid, &ci->defined_class); + ci->me = me; + ci->klass = klass; + ci->vmstat = GET_VM_STATE_VERSION(); + ci->call = vm_call_general; + } +#else + ci->method = rb_method_entry(klass, id, &ci->defined_class); + ci->call = vm_call_general; + ci->klass = klass; +#endif +} + static inline int check_cfunc(const rb_method_entry_t *me, VALUE (*func)()) { @@ -1805,9 +1547,9 @@ opt_eq_func(VALUE recv, VALUE obj, CALL_INFO ci) } { - const rb_method_entry_t *me = vm_method_search(idEq, CLASS_OF(recv), ci, 0); + vm_search_method(ci, recv); - if (check_cfunc(me, rb_obj_equal)) { + if (check_cfunc(ci->me, rb_obj_equal)) { return recv == obj ? Qtrue : Qfalse; } } @@ -1927,3 +1669,280 @@ vm_base_ptr(rb_control_frame_t *cfp) return bp; } + +static inline VALUE +vm_call_method(rb_thread_t *th, rb_control_frame_t *cfp, rb_call_info_t *ci) +{ + VALUE val; + + start_method_dispatch: + if (ci->me != 0) { + if ((ci->me->flag == 0)) { + normal_method_dispatch: + switch (ci->me->def->type) { + case VM_METHOD_TYPE_ISEQ:{ + vm_setup_method(th, cfp, ci); + return Qundef; + } + case VM_METHOD_TYPE_NOTIMPLEMENTED: + case VM_METHOD_TYPE_CFUNC:{ + val = vm_call_cfunc(th, cfp, ci); + break; + } + case VM_METHOD_TYPE_ATTRSET:{ + rb_check_arity(ci->argc, 1, 1); + val = rb_ivar_set(ci->recv, ci->me->def->body.attr.id, *(cfp->sp - 1)); + cfp->sp -= 2; + break; + } + case VM_METHOD_TYPE_IVAR:{ + rb_check_arity(ci->argc, 0, 0); + val = rb_attr_get(ci->recv, ci->me->def->body.attr.id); + cfp->sp -= 1; + break; + } + case VM_METHOD_TYPE_MISSING:{ + VALUE *argv = ALLOCA_N(VALUE, ci->argc+1); + argv[0] = ID2SYM(ci->me->def->original_id); + MEMCPY(argv+1, cfp->sp - ci->argc, VALUE, ci->argc); + cfp->sp += - ci->argc - 1; + th->passed_block = ci->blockptr; + val = rb_funcall2(ci->recv, rb_intern("method_missing"), ci->argc+1, argv); + break; + } + case VM_METHOD_TYPE_BMETHOD:{ + VALUE *argv = ALLOCA_N(VALUE, ci->argc); + MEMCPY(argv, cfp->sp - ci->argc, VALUE, ci->argc); + cfp->sp += - ci->argc - 1; + val = vm_call_bmethod(th, ci->recv, ci->argc, argv, + ci->blockptr, ci->me, ci->defined_class); + break; + } + case VM_METHOD_TYPE_ZSUPER:{ + VALUE klass = RCLASS_SUPER(ci->me->klass); + rb_call_info_t cie = *ci; + ci = &cie; + + ci->me = rb_method_entry(klass, ci->mid, &ci->defined_class); + + if (ci->me != 0) { + goto normal_method_dispatch; + } + else { + goto start_method_dispatch; + } + } + case VM_METHOD_TYPE_OPTIMIZED:{ + switch (ci->me->def->body.optimize_type) { + case OPTIMIZED_METHOD_TYPE_SEND: { + rb_control_frame_t *reg_cfp = cfp; + int i = ci->argc - 1; + VALUE sym; + rb_call_info_t ci_entry; + + if (ci->argc == 0) { + rb_raise(rb_eArgError, "no method name given"); + } + + ci_entry = *ci; /* copy ci entry */ + ci = &ci_entry; + + sym = TOPN(i); + + if (SYMBOL_P(sym)) { + ci->mid = SYM2ID(sym); + } + else if (!(ci->mid = rb_check_id(&sym))) { + if (rb_method_basic_definition_p(CLASS_OF(ci->recv), idMethodMissing)) { + VALUE exc = make_no_method_exception(rb_eNoMethodError, NULL, ci->recv, rb_long2int(ci->argc), &TOPN(i)); + rb_exc_raise(exc); + } + ci->mid = rb_to_id(sym); + } + /* shift arguments */ + if (i > 0) { + MEMMOVE(&TOPN(i), &TOPN(i-1), VALUE, i); + } + ci->me = rb_method_entry(CLASS_OF(ci->recv), ci->mid, &ci->defined_class); + ci->argc -= 1; + DEC_SP(1); + /* TODO: fixme */ + ci->flag |= VM_CALL_FCALL_BIT | VM_CALL_OPT_SEND_BIT; + + goto start_method_dispatch; + } + case OPTIMIZED_METHOD_TYPE_CALL: { + rb_proc_t *proc; + int argc = ci->argc; + VALUE *argv = ALLOCA_N(VALUE, argc); + GetProcPtr(ci->recv, proc); + MEMCPY(argv, cfp->sp - argc, VALUE, argc); + cfp->sp -= argc + 1; + + val = rb_vm_invoke_proc(th, proc, argc, argv, ci->blockptr); + break; + } + default: + rb_bug("eval_invoke_method: unsupported optimized method type (%d)", + ci->me->def->body.optimize_type); + } + break; + } + default:{ + rb_bug("eval_invoke_method: unsupported method type (%d)", ci->me->def->type); + break; + } + } + } + else { + int noex_safe; + if (!(ci->flag & VM_CALL_FCALL_BIT) && (ci->me->flag & NOEX_MASK) & NOEX_PRIVATE) { + int stat = NOEX_PRIVATE; + + if (ci->flag & VM_CALL_VCALL_BIT) { + stat |= NOEX_VCALL; + } + val = vm_method_missing(th, cfp, ci->mid, ci->recv, ci->argc, ci->blockptr, stat); + } + else if (!(ci->flag & VM_CALL_OPT_SEND_BIT) && (ci->me->flag & NOEX_MASK) & NOEX_PROTECTED) { + if (!rb_obj_is_kind_of(cfp->self, ci->defined_class)) { + val = vm_method_missing(th, cfp, ci->mid, ci->recv, ci->argc, ci->blockptr, NOEX_PROTECTED); + } + else { + goto normal_method_dispatch; + } + } + else if ((noex_safe = NOEX_SAFE(ci->me->flag)) > th->safe_level && (noex_safe > 2)) { + rb_raise(rb_eSecurityError, "calling insecure method: %s", rb_id2name(ci->mid)); + } + else { + goto normal_method_dispatch; + } + } + } + else { + /* method missing */ + int stat = 0; + if (ci->flag & VM_CALL_VCALL_BIT) { + stat |= NOEX_VCALL; + } + if (ci->flag & VM_CALL_SUPER_BIT) { + stat |= NOEX_SUPER; + } + if (ci->mid == idMethodMissing) { + rb_control_frame_t *reg_cfp = cfp; + VALUE *argv = STACK_ADDR_FROM_TOP(ci->argc); + rb_raise_method_missing(th, ci->argc, argv, ci->recv, stat); + } + else { + val = vm_method_missing(th, cfp, ci->mid, ci->recv, ci->argc, ci->blockptr, stat); + } + } + + RUBY_VM_CHECK_INTS(th); + return val; +} + +static inline VALUE +vm_search_normal_superclass(VALUE klass) +{ + klass = RCLASS_ORIGIN(klass); + return RCLASS_SUPER(klass); +} + +static void +vm_super_outside(void) +{ + rb_raise(rb_eNoMethodError, "super called outside of method"); +} + +static void +vm_search_superclass(rb_control_frame_t *reg_cfp, rb_iseq_t *iseq, VALUE sigval, rb_call_info_t *ci) +{ + while (iseq && !iseq->klass) { + iseq = iseq->parent_iseq; + } + + if (iseq == 0) { + vm_super_outside(); + } + + ci->mid = iseq->defined_method_id; + + if (iseq != iseq->local_iseq) { + /* defined by Module#define_method() */ + rb_control_frame_t *lcfp = GET_CFP(); + + if (!sigval) { + /* zsuper */ + rb_raise(rb_eRuntimeError, "implicit argument passing of super from method defined by define_method() is not supported. Specify all arguments explicitly."); + } + + while (lcfp->iseq != iseq) { + rb_thread_t *th = GET_THREAD(); + VALUE *tep = VM_EP_PREV_EP(lcfp->ep); + while (1) { + lcfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(lcfp); + if (RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(th, lcfp)) { + vm_super_outside(); + } + if (lcfp->ep == tep) { + break; + } + } + } + + /* temporary measure for [Bug #2420] [Bug #3136] */ + if (!lcfp->me) { + vm_super_outside(); + } + + ci->mid = lcfp->me->def->original_id; + ci->klass = vm_search_normal_superclass(lcfp->klass); + } + else { + ci->klass = vm_search_normal_superclass(reg_cfp->klass); + } +} + +static void +vm_search_super_method(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_info_t *ci) +{ + VALUE current_defind_class; + rb_iseq_t *iseq = GET_ISEQ(); + VALUE sigval = TOPN(ci->orig_argc); + + current_defind_class = GET_CFP()->klass; + if (NIL_P(current_defind_class)) { + vm_super_outside(); + } + + if (!NIL_P(RCLASS_REFINED_CLASS(current_defind_class))) { + current_defind_class = RCLASS_REFINED_CLASS(current_defind_class); + } + + if (!rb_obj_is_kind_of(ci->recv, current_defind_class)) { + rb_raise(rb_eNotImpError, "super from singleton method that is defined to multiple classes is not supported; this will be fixed in 2.0.0 or later"); + } + + vm_search_superclass(GET_CFP(), iseq, sigval, ci); + + /* TODO: use inline cache */ + ci->me = rb_method_entry(ci->klass, ci->mid, &ci->defined_class); + ci->call = vm_call_general; + + while (iseq && !iseq->klass) { + iseq = iseq->parent_iseq; + } + + if (ci->me && ci->me->def->type == VM_METHOD_TYPE_ISEQ && ci->me->def->body.iseq == iseq) { + ci->klass = RCLASS_SUPER(ci->defined_class); + ci->me = rb_method_entry_get_with_refinements(Qnil, ci->klass, ci->mid, &ci->defined_class); + } +} + +static VALUE +vm_call_general(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_info_t *ci) +{ + return vm_call_method(th, reg_cfp, ci); +} diff --git a/vm_insnhelper.h b/vm_insnhelper.h index 8b8da831a3..c6b2fbefdb 100644 --- a/vm_insnhelper.h +++ b/vm_insnhelper.h @@ -189,8 +189,8 @@ enum vm_regan_acttype { } \ } while (0) -#define CALL_METHOD(num, blockptr, flag, id, me, recv, defined_class) do { \ - VALUE v = vm_call_method(th, GET_CFP(), (num), (blockptr), (flag), (id), (me), (recv), (defined_class)); \ +#define CALL_METHOD(ci) do { \ + VALUE v = (*(ci)->call)(th, GET_CFP(), (ci)); \ if (v == Qundef) { \ RESTORE_REGS(); \ NEXT_INSN(); \ @@ -235,23 +235,12 @@ enum vm_regan_acttype { #define USE_IC_FOR_SPECIALIZED_METHOD 1 #endif -#if USE_IC_FOR_SPECIALIZED_METHOD - -#define CALL_SIMPLE_METHOD(num, id, recv) do { \ - VALUE klass = CLASS_OF(recv), defined_class; \ - const rb_method_entry_t *me = vm_method_search((id), klass, ci, &defined_class); \ - CALL_METHOD((num), 0, 0, (id), me, (recv), defined_class); \ +#define CALL_SIMPLE_METHOD(recv) do { \ + ci->blockptr = 0; ci->argc = ci->orig_argc; \ + vm_search_method(ci, ci->recv = (recv)); \ + CALL_METHOD(ci); \ } while (0) -#else - -#define CALL_SIMPLE_METHOD(num, id, recv) do { \ - VALUE klass = CLASS_OF(recv); \ - CALL_METHOD((num), 0, 0, (id), rb_method_entry(klass, (id)), (recv)); \ -} while (0) - -#endif - static VALUE ruby_vm_global_state_version = 1; #define GET_VM_STATE_VERSION() (ruby_vm_global_state_version)