mirror of
https://github.com/ruby/ruby.git
synced 2025-08-15 21:49:06 +02:00
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 revisiona7d467a792
) [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 master7e8802fa5b
) [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 ```
This commit is contained in:
parent
0463c5806a
commit
e87d088291
4 changed files with 332 additions and 257 deletions
192
compile.c
192
compile.c
|
@ -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)
|
||||
int dup_rest, unsigned int *flag_ptr, struct rb_callinfo_kwarg **kwarg_ptr)
|
||||
{
|
||||
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);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue