diff options
author | Koichi Sasada <ko1@atdot.net> | 2023-01-13 17:52:59 +0900 |
---|---|---|
committer | Koichi Sasada <ko1@atdot.net> | 2023-03-06 15:03:06 +0900 |
commit | e87d0882910001ef3b0c2ccd43bf00cee8c34a0c (patch) | |
tree | 85a4cab782f6660530e100117183e0cd7d583130 /vm_args.c | |
parent | 0463c5806ac63bbd082f4abb1e3ceeae6ffc39ce (diff) |
Change bytecode of `f(*a, **kw)`
`f(*a, **kw)` is compiled to `f([*a, kw])` but it makes an dummy
array, so change it to pass two arguments `a` and `kw` with calling
flags.
```
ruby 3.2.0 (2022-12-29 revision a7d467a792) [x86_64-linux]
Calculating -------------------------------------
foo() 15.354M (± 4.2%) i/s - 77.295M in 5.043650s
dele() 13.439M (± 3.9%) i/s - 67.109M in 5.001974s
dele(*) 6.265M (± 4.5%) i/s - 31.730M in 5.075649s
dele(*a) 6.286M (± 3.3%) i/s - 31.719M in 5.051516s
dele(*a, **kw) 1.926M (± 4.5%) i/s - 9.753M in 5.076487s
dele(*, **) 1.927M (± 4.2%) i/s - 9.710M in 5.048224s
dele(...) 5.871M (± 3.9%) i/s - 29.471M in 5.028023s
forwardable 4.969M (± 4.1%) i/s - 25.233M in 5.087498s
ruby 3.3.0dev (2023-01-13T01:28:00Z master 7e8802fa5b) [x86_64-linux]
Calculating -------------------------------------
foo() 16.354M (± 4.7%) i/s - 81.799M in 5.014561s
dele() 14.256M (± 3.5%) i/s - 71.656M in 5.032883s
dele(*) 6.701M (± 3.8%) i/s - 33.948M in 5.074938s
dele(*a) 6.681M (± 3.3%) i/s - 33.578M in 5.031720s
dele(*a, **kw) 4.200M (± 4.4%) i/s - 21.258M in 5.072583s
dele(*, **) 4.197M (± 5.3%) i/s - 21.322M in 5.096684s
dele(...) 6.039M (± 6.8%) i/s - 30.355M in 5.052662s
forwardable 4.788M (± 3.2%) i/s - 24.033M in 5.024875s
```
Diffstat (limited to 'vm_args.c')
-rw-r--r-- | vm_args.c | 128 |
1 files changed, 55 insertions, 73 deletions
@@ -440,9 +440,10 @@ ignore_keyword_hash_p(VALUE keyword_hash, const rb_iseq_t * const iseq, unsigned if (!RB_TYPE_P(keyword_hash, T_HASH)) { keyword_hash = rb_to_hash_type(keyword_hash); } + if (!(*kw_flag & VM_CALL_KW_SPLAT_MUT) && - (ISEQ_BODY(iseq)->param.flags.has_kwrest || - ISEQ_BODY(iseq)->param.flags.ruby2_keywords)) { + (ISEQ_BODY(iseq)->param.flags.has_kwrest || + ISEQ_BODY(iseq)->param.flags.ruby2_keywords)) { *kw_flag |= VM_CALL_KW_SPLAT_MUT; keyword_hash = rb_hash_dup(keyword_hash); } @@ -520,54 +521,78 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co args->kw_argv = NULL; } - if (vm_ci_flag(ci) & VM_CALL_ARGS_SPLAT) { - int len; + if ((vm_ci_flag(ci) & VM_CALL_ARGS_SPLAT) && (vm_ci_flag(ci) & VM_CALL_KW_SPLAT)) { + // f(*a, **kw) + args->rest_index = 0; + keyword_hash = locals[--args->argc]; args->rest = locals[--args->argc]; + + if (ignore_keyword_hash_p(keyword_hash, iseq, &kw_flag, &converted_keyword_hash)) { + keyword_hash = Qnil; + } + else if (UNLIKELY(ISEQ_BODY(iseq)->param.flags.ruby2_keywords)) { + flag_keyword_hash = keyword_hash; + rb_ary_push(args->rest, keyword_hash); + keyword_hash = Qnil; + } + else if (!ISEQ_BODY(iseq)->param.flags.has_kwrest && !ISEQ_BODY(iseq)->param.flags.has_kw) { + rb_ary_push(args->rest, keyword_hash); + keyword_hash = Qnil; + } + + int len = RARRAY_LENINT(args->rest); + given_argc += len - 2; + } + else if (vm_ci_flag(ci) & VM_CALL_ARGS_SPLAT) { + // f(*a) args->rest_index = 0; - len = RARRAY_LENINT(args->rest); + args->rest = locals[--args->argc]; + int len = RARRAY_LENINT(args->rest); given_argc += len - 1; rest_last = RARRAY_AREF(args->rest, len - 1); if (!kw_flag && len > 0) { if (RB_TYPE_P(rest_last, T_HASH) && (((struct RHash *)rest_last)->basic.flags & RHASH_PASS_AS_KEYWORDS)) { + // def f(**kw); a = [..., kw]; g(*a) splat_flagged_keyword_hash = rest_last; rest_last = rb_hash_dup(rest_last); kw_flag |= VM_CALL_KW_SPLAT | VM_CALL_KW_SPLAT_MUT; - } - else { - rest_last = 0; - } - } - if (kw_flag & VM_CALL_KW_SPLAT) { - if (ignore_keyword_hash_p(rest_last, iseq, &kw_flag, &converted_keyword_hash)) { - arg_rest_dup(args); - rb_ary_pop(args->rest); - given_argc--; - kw_flag &= ~(VM_CALL_KW_SPLAT | VM_CALL_KW_SPLAT_MUT); - } - else { - if (rest_last != converted_keyword_hash) { - rest_last = converted_keyword_hash; - arg_rest_dup(args); - RARRAY_ASET(args->rest, len - 1, rest_last); - } - - if (ISEQ_BODY(iseq)->param.flags.ruby2_keywords && rest_last) { - flag_keyword_hash = rest_last; - } - else if (ISEQ_BODY(iseq)->param.flags.has_kw || ISEQ_BODY(iseq)->param.flags.has_kwrest) { + if (ignore_keyword_hash_p(rest_last, iseq, &kw_flag, &converted_keyword_hash)) { arg_rest_dup(args); rb_ary_pop(args->rest); given_argc--; - keyword_hash = rest_last; + kw_flag &= ~(VM_CALL_KW_SPLAT | VM_CALL_KW_SPLAT_MUT); + } + else { + if (rest_last != converted_keyword_hash) { + rest_last = converted_keyword_hash; + arg_rest_dup(args); + RARRAY_ASET(args->rest, len - 1, rest_last); + } + + if (ISEQ_BODY(iseq)->param.flags.ruby2_keywords && rest_last) { + flag_keyword_hash = rest_last; + } + else if (ISEQ_BODY(iseq)->param.flags.has_kw || ISEQ_BODY(iseq)->param.flags.has_kwrest) { + arg_rest_dup(args); + rb_ary_pop(args->rest); + given_argc--; + keyword_hash = rest_last; + } } } } + else { + rest_last = 0; + } } else { - if (kw_flag & VM_CALL_KW_SPLAT) { + args->rest = Qfalse; + + if (args->argc > 0 && (kw_flag & VM_CALL_KW_SPLAT)) { + // f(**kw) VALUE last_arg = args->argv[args->argc-1]; if (ignore_keyword_hash_p(last_arg, iseq, &kw_flag, &converted_keyword_hash)) { args->argc--; @@ -590,7 +615,6 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co } } } - args->rest = Qfalse; } if (flag_keyword_hash && RB_TYPE_P(flag_keyword_hash, T_HASH)) { @@ -778,48 +802,6 @@ argument_kw_error(rb_execution_context_t *ec, const rb_iseq_t *iseq, const char raise_argument_error(ec, iseq, rb_keyword_error_new(error, keys)); } -static inline void -vm_caller_setup_arg_splat(rb_control_frame_t *cfp, struct rb_calling_info *calling) -{ - int argc = calling->argc; - VALUE *argv = cfp->sp - argc; - VALUE ary = argv[argc-1]; - - vm_check_canary(GET_EC(), cfp->sp); - cfp->sp--; - - if (!NIL_P(ary)) { - const VALUE *ptr = RARRAY_CONST_PTR_TRANSIENT(ary); - long len = RARRAY_LEN(ary), i; - - CHECK_VM_STACK_OVERFLOW(cfp, len); - - for (i = 0; i < len; i++) { - *cfp->sp++ = ptr[i]; - } - calling->argc += i - 1; - } -} - -static inline void -vm_caller_setup_arg_kw(rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_callinfo *ci) -{ - const VALUE *const passed_keywords = vm_ci_kwarg(ci)->keywords; - const int kw_len = vm_ci_kwarg(ci)->keyword_len; - const VALUE h = rb_hash_new_with_size(kw_len); - VALUE *sp = cfp->sp; - int i; - - for (i=0; i<kw_len; i++) { - rb_hash_aset(h, passed_keywords[i], (sp - kw_len)[i]); - } - (sp-kw_len)[0] = h; - - cfp->sp -= kw_len - 1; - calling->argc -= kw_len - 1; - calling->kw_splat = 1; -} - static VALUE vm_to_proc(VALUE proc) { |