From 71fee9bc720ba7a117062bf3f78b6086527b656c Mon Sep 17 00:00:00 2001 From: Koichi Sasada Date: Fri, 15 Nov 2019 17:49:49 +0900 Subject: vm_invoke_builtin_delegate with start index. opt_invokebuiltin_delegate and opt_invokebuiltin_delegate_leave invokes builtin functions with same parameters of the method. This technique eliminate stack push operations. However, delegation parameters should be completely same as given parameters. (e.g. `def foo(a, b, c) __builtin_foo(a, b, c)` is okay, but __builtin_foo(b, c) is not allowed) This patch relaxes this restriction. ISeq has a local variables table which includes parameters. For example, the method defined as `def foo(a, b, c) x=y=nil`, then local variables table contains [a, b, c, x, y]. If calling builtin-function with arguments which are sub-array of the lvar table, use opt_invokebuiltin_delegate instruction with start index. For example, `__builtin_foo(b, c)`, `__builtin_bar(c, x, y)` is okay, and so on. --- compile.c | 77 ++++++++++++++++++++++++++++++++++++++------------------- insns.def | 10 ++++---- mini_builtin.c | 5 ++++ vm_insnhelper.c | 14 ++++++++--- 4 files changed, 73 insertions(+), 33 deletions(-) diff --git a/compile.c b/compile.c index 7de6068a7d..3cf5bb6680 100644 --- a/compile.c +++ b/compile.c @@ -6762,39 +6762,65 @@ iseq_builtin_function_name(ID mid) } static int -delegate_call_p(const rb_iseq_t *iseq, unsigned int argc, const LINK_ANCHOR *args) +delegate_call_p(const rb_iseq_t *iseq, unsigned int argc, const LINK_ANCHOR *args, unsigned int *pstart_index) { + if (argc == 0) { + *pstart_index = 0; return TRUE; } - else if (argc == iseq->body->param.size) { - const LINK_ELEMENT *elem = FIRST_ELEMENT(args); - - for (unsigned int i=0; itype == ISEQ_ELEMENT_INSN && - INSN_OF(elem) == BIN(getlocal)) { - int local_index = FIX2INT(OPERAND_AT(elem, 0)); - int local_level = FIX2INT(OPERAND_AT(elem, 1)); - if (local_level == 0) { - unsigned int index = iseq->body->local_table_size - (local_index - VM_ENV_DATA_SIZE + 1); -#if 0 - ID param_id = iseq->body->local_table[i]; - fprintf(stderr, "param_id:%s (%d), id:%s (%d) local_index:%d, local_size:%d\n", - rb_id2name(param_id), i, - rb_id2name(iseq->body->local_table[index]), index, - local_index, (int)iseq->body->local_table_size); -#endif - if (i == index) { - elem = elem->next; - continue; /* for */ + else if (argc <= iseq->body->local_table_size) { + unsigned int start=0; + + // local_table: [p1, p2, p3, l1, l2, l3] + // arguments: [p3, l1, l2] -> 2 + for (start = 0; + argc + start <= iseq->body->local_table_size; + start++) { + const LINK_ELEMENT *elem = FIRST_ELEMENT(args); + + for (unsigned int i=start; i-starttype == ISEQ_ELEMENT_INSN && + INSN_OF(elem) == BIN(getlocal)) { + int local_index = FIX2INT(OPERAND_AT(elem, 0)); + int local_level = FIX2INT(OPERAND_AT(elem, 1)); + + if (local_level == 0) { + unsigned int index = iseq->body->local_table_size - (local_index - VM_ENV_DATA_SIZE + 1); + if (0) { // for debug + fprintf(stderr, "lvar:%s (%d), id:%s (%d) local_index:%d, local_size:%d\n", + rb_id2name(iseq->body->local_table[i]), i, + rb_id2name(iseq->body->local_table[index]), index, + local_index, (int)iseq->body->local_table_size); + } + if (i == index) { + elem = elem->next; + continue; /* for */ + } + else { + goto next; + } + } + else { + goto fail; // level != 0 is unsupport } } + else { + goto fail; // insn is not a getlocal + } } - return FALSE; + goto success; + next:; } + fail: + return FALSE; + success: + *pstart_index = start; return TRUE; } - return FALSE; + else { + return FALSE; + } } static int @@ -6924,8 +6950,9 @@ compile_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, in return COMPILE_NG; } - if (delegate_call_p(iseq, FIX2INT(argc), args)) { - ADD_INSN1(ret, line, opt_invokebuiltin_delegate, bf); + unsigned int start_index; + if (delegate_call_p(iseq, FIX2INT(argc), args, &start_index)) { + ADD_INSN2(ret, line, opt_invokebuiltin_delegate, bf, INT2FIX(start_index)); } else { ADD_SEQ(ret, args); diff --git a/insns.def b/insns.def index df505808ce..0388a364da 100644 --- a/insns.def +++ b/insns.def @@ -1492,26 +1492,26 @@ invokebuiltin /* call specific function with args (same parameters) */ DEFINE_INSN opt_invokebuiltin_delegate -(RB_BUILTIN bf) +(RB_BUILTIN bf, rb_num_t index) () (VALUE ret) // attr bool leaf = false; /* anything can happen inside */ { - ret = vm_invoke_builtin_delegate(ec, reg_cfp, bf); + ret = vm_invoke_builtin_delegate(ec, reg_cfp, bf, index); } /* call specific function with args (same parameters) and leave */ DEFINE_INSN opt_invokebuiltin_delegate_leave -(RB_BUILTIN bf) +(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); + val = vm_invoke_builtin_delegate(ec, reg_cfp, bf, index); /* leave fastpath */ - /* TracePoint/return should fallback this insn to invokecfuncwparam */ + /* TracePoint/return should fallback 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; diff --git a/mini_builtin.c b/mini_builtin.c index 9dc32980ca..1254e5934f 100644 --- a/mini_builtin.c +++ b/mini_builtin.c @@ -21,6 +21,11 @@ rb_load_with_builtin_functions(const char *feature_name, const struct rb_builtin rb_ast_dispose(ast); + // for debug + if (0 && strcmp("prelude", feature_name) == 0) { + rb_io_write(rb_stdout, rb_iseq_disasm((const rb_iseq_t *)iseq)); + } + // register (loaded iseq will not be freed) st_insert(loaded_builtin_table, (st_data_t)feature_name, (st_data_t)iseq); rb_gc_register_mark_object((VALUE)iseq); diff --git a/vm_insnhelper.c b/vm_insnhelper.c index e5a7c77e91..a4d2fbcf41 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -4989,10 +4989,18 @@ vm_invoke_builtin(rb_execution_context_t *ec, rb_control_frame_t *cfp, const str } static VALUE -vm_invoke_builtin_delegate(rb_execution_context_t *ec, rb_control_frame_t *cfp, const struct rb_builtin_function *bf) +vm_invoke_builtin_delegate(rb_execution_context_t *ec, rb_control_frame_t *cfp, const struct rb_builtin_function *bf, int start_index) { - const VALUE *argv = cfp->ep - cfp->iseq->body->local_table_size - VM_ENV_DATA_SIZE + 1; - // fprintf(stderr, "%s %s(%d):%p\n", __func__, bf->name, bf->argc, bf->func_ptr); + if (0) { // debug print + fprintf(stderr, "vm_invoke_builtin_delegate: passing -> "); + for (int i=0; iargc; i++) { + fprintf(stderr, ":%s ", rb_id2name(cfp->iseq->body->local_table[i+start_index])); + } + fprintf(stderr, "\n"); + fprintf(stderr, "%s %s(%d):%p\n", __func__, bf->name, bf->argc, bf->func_ptr); + } + + const VALUE *argv = cfp->ep - cfp->iseq->body->local_table_size - VM_ENV_DATA_SIZE + 1 + start_index; return invoke_bf(ec, cfp, bf, argv); } -- cgit v1.2.3