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 /compile.c | |
parent | 0463c5806ac63bbd082f4abb1e3ceeae6ffc39ce (diff) | |
download | ruby-e87d0882910001ef3b0c2ccd43bf00cee8c34a0c.tar.gz |
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 'compile.c')
-rw-r--r-- | compile.c | 194 |
1 files changed, 113 insertions, 81 deletions
@@ -4318,11 +4318,13 @@ keyword_node_p(const NODE *const node) static int compile_keyword_arg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, - const NODE *const root_node, - struct rb_callinfo_kwarg **const kw_arg_ptr, - unsigned int *flag) + const NODE *const root_node, + struct rb_callinfo_kwarg **const kw_arg_ptr, + unsigned int *flag) { - if (kw_arg_ptr == NULL) return FALSE; + RUBY_ASSERT(nd_type_p(root_node, NODE_HASH)); + RUBY_ASSERT(kw_arg_ptr != NULL); + RUBY_ASSERT(flag != NULL); if (root_node->nd_head && nd_type_p(root_node->nd_head, NODE_LIST)) { const NODE *node = root_node->nd_head; @@ -4379,8 +4381,7 @@ compile_keyword_arg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, } static int -compile_args(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, - struct rb_callinfo_kwarg **keywords_ptr, unsigned int *flag) +compile_args(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, NODE **kwnode_ptr) { int len = 0; @@ -4389,15 +4390,11 @@ compile_args(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, EXPECT_NODE("compile_args", node, NODE_LIST, -1); } - if (node->nd_next == NULL && keyword_node_p(node->nd_head)) { /* last node */ - if (compile_keyword_arg(iseq, ret, node->nd_head, keywords_ptr, flag)) { - len--; - } - else { - compile_hash(iseq, ret, node->nd_head, TRUE, FALSE); - } + if (node->nd_next == NULL && keyword_node_p(node->nd_head)) { /* last node is kwnode */ + *kwnode_ptr = node->nd_head; } else { + RUBY_ASSERT(!keyword_node_p(node->nd_head)); NO_CHECK(COMPILE_(ret, "array element", node->nd_head, FALSE)); } } @@ -5776,6 +5773,7 @@ add_ensure_iseq(LINK_ANCHOR *const ret, rb_iseq_t *iseq, int is_return) ADD_SEQ(ret, ensure); } +#if RUBY_DEBUG static int check_keyword(const NODE *node) { @@ -5790,65 +5788,111 @@ check_keyword(const NODE *node) return keyword_node_p(node); } +#endif -static VALUE +static int setup_args_core(rb_iseq_t *iseq, LINK_ANCHOR *const args, const NODE *argn, - int dup_rest, unsigned int *flag, struct rb_callinfo_kwarg **keywords) -{ - if (argn) { - switch (nd_type(argn)) { - case NODE_SPLAT: { - NO_CHECK(COMPILE(args, "args (splat)", argn->nd_head)); - ADD_INSN1(args, argn, splatarray, RBOOL(dup_rest)); - if (flag) *flag |= VM_CALL_ARGS_SPLAT; - return INT2FIX(1); + int dup_rest, unsigned int *flag_ptr, struct rb_callinfo_kwarg **kwarg_ptr) +{ + if (!argn) return 0; + + NODE *kwnode = NULL; + + switch (nd_type(argn)) { + case NODE_LIST: { + // f(x, y, z) + int len = compile_args(iseq, args, argn, &kwnode); + RUBY_ASSERT(flag_ptr == NULL || (*flag_ptr & VM_CALL_ARGS_SPLAT) == 0); + + if (kwnode) { + if (compile_keyword_arg(iseq, args, kwnode, kwarg_ptr, flag_ptr)) { + len -= 1; + } + else { + compile_hash(iseq, args, kwnode, TRUE, FALSE); + } } - case NODE_ARGSCAT: - case NODE_ARGSPUSH: { - int next_is_list = (nd_type_p(argn->nd_head, NODE_LIST)); - VALUE argc = setup_args_core(iseq, args, argn->nd_head, 1, NULL, NULL); - if (nd_type_p(argn->nd_body, NODE_LIST)) { - /* This branch is needed to avoid "newarraykwsplat" [Bug #16442] */ - int rest_len = compile_args(iseq, args, argn->nd_body, NULL, NULL); - ADD_INSN1(args, argn, newarray, INT2FIX(rest_len)); - } - else { - NO_CHECK(COMPILE(args, "args (cat: splat)", argn->nd_body)); - } - if (flag) { - *flag |= VM_CALL_ARGS_SPLAT; - /* This is a dirty hack. It traverses the AST twice. - * In a long term, it should be fixed by a redesign of keyword arguments */ - if (check_keyword(argn->nd_body)) - *flag |= VM_CALL_KW_SPLAT; - } - if (nd_type_p(argn, NODE_ARGSCAT)) { - if (next_is_list) { - ADD_INSN1(args, argn, splatarray, Qtrue); - return INT2FIX(FIX2INT(argc) + 1); - } - else { - ADD_INSN1(args, argn, splatarray, Qfalse); - ADD_INSN(args, argn, concatarray); - return argc; - } - } - else { - ADD_INSN1(args, argn, newarray, INT2FIX(1)); - ADD_INSN(args, argn, concatarray); - return argc; - } + + return len; + } + case NODE_SPLAT: { + // f(*a) + NO_CHECK(COMPILE(args, "args (splat)", argn->nd_head)); + ADD_INSN1(args, argn, splatarray, RBOOL(dup_rest)); + if (flag_ptr) *flag_ptr |= VM_CALL_ARGS_SPLAT; + RUBY_ASSERT(flag_ptr == NULL || (*flag_ptr & VM_CALL_KW_SPLAT) == 0); + return 1; + } + case NODE_ARGSCAT: { + if (flag_ptr) *flag_ptr |= VM_CALL_ARGS_SPLAT; + int argc = setup_args_core(iseq, args, argn->nd_head, 1, NULL, NULL); + + if (nd_type_p(argn->nd_body, NODE_LIST)) { + int rest_len = compile_args(iseq, args, argn->nd_body, &kwnode); + if (kwnode) rest_len--; + ADD_INSN1(args, argn, newarray, INT2FIX(rest_len)); } - case NODE_LIST: { - int len = compile_args(iseq, args, argn, keywords, flag); - return INT2FIX(len); + else { + RUBY_ASSERT(!check_keyword(argn->nd_body)); + NO_CHECK(COMPILE(args, "args (cat: splat)", argn->nd_body)); } - default: { - UNKNOWN_NODE("setup_arg", argn, Qnil); + + if (nd_type_p(argn->nd_head, NODE_LIST)) { + ADD_INSN1(args, argn, splatarray, Qtrue); + argc += 1; } - } + else { + ADD_INSN1(args, argn, splatarray, Qfalse); + ADD_INSN(args, argn, concatarray); + } + + // f(..., *a, ..., k1:1, ...) #=> f(..., *[*a, ...], **{k1:1, ...}) + if (kwnode) { + // kwsplat + *flag_ptr |= VM_CALL_KW_SPLAT; + *flag_ptr |= VM_CALL_KW_SPLAT_MUT; + compile_hash(iseq, args, kwnode, TRUE, FALSE); + argc += 1; + } + + return argc; + } + case NODE_ARGSPUSH: { + if (flag_ptr) *flag_ptr |= VM_CALL_ARGS_SPLAT; + int argc = setup_args_core(iseq, args, argn->nd_head, 1, NULL, NULL); + + if (nd_type_p(argn->nd_body, NODE_LIST)) { + int rest_len = compile_args(iseq, args, argn->nd_body, &kwnode); + if (kwnode) rest_len--; + ADD_INSN1(args, argn, newarray, INT2FIX(rest_len)); + ADD_INSN1(args, argn, newarray, INT2FIX(1)); + ADD_INSN(args, argn, concatarray); + } + else { + if (keyword_node_p(argn->nd_body)) { + kwnode = argn->nd_body; + } + else { + NO_CHECK(COMPILE(args, "args (cat: splat)", argn->nd_body)); + ADD_INSN1(args, argn, newarray, INT2FIX(1)); + ADD_INSN(args, argn, concatarray); + } + } + + if (kwnode) { + // f(*a, k:1) + *flag_ptr |= VM_CALL_KW_SPLAT; + *flag_ptr |= VM_CALL_KW_SPLAT_MUT; + compile_hash(iseq, args, kwnode, TRUE, FALSE); + argc += 1; + } + + return argc; + } + default: { + UNKNOWN_NODE("setup_arg", argn, Qnil); + } } - return INT2FIX(0); } static VALUE @@ -5874,11 +5918,11 @@ setup_args(rb_iseq_t *iseq, LINK_ANCHOR *const args, const NODE *argn, dup_rest = 0; } } - ret = setup_args_core(iseq, args, argn->nd_head, dup_rest, flag, keywords); + ret = INT2FIX(setup_args_core(iseq, args, argn->nd_head, dup_rest, flag, keywords)); ADD_SEQ(args, arg_block); } else { - ret = setup_args_core(iseq, args, argn, 0, flag, keywords); + ret = INT2FIX(setup_args_core(iseq, args, argn, 0, flag, keywords)); } return ret; } @@ -8990,25 +9034,13 @@ compile_super(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, i ADD_GETLOCAL(args, node, idx, lvar_level); } ADD_SEND(args, node, id_core_hash_merge_ptr, INT2FIX(i * 2 + 1)); - if (local_body->param.flags.has_rest) { - ADD_INSN1(args, node, newarray, INT2FIX(1)); - ADD_INSN (args, node, concatarray); - --argc; - } flag |= VM_CALL_KW_SPLAT; } else if (local_body->param.flags.has_kwrest) { int idx = local_body->local_table_size - local_kwd->rest_start; ADD_GETLOCAL(args, node, idx, lvar_level); - - if (local_body->param.flags.has_rest) { - ADD_INSN1(args, node, newarray, INT2FIX(1)); - ADD_INSN (args, node, concatarray); - } - else { - argc++; - } - flag |= VM_CALL_KW_SPLAT; + argc++; + flag |= VM_CALL_KW_SPLAT | VM_CALL_KW_SPLAT_MUT; } } |