[PRISM] Selectively dup array in foo(*splat, &block_arg) calls

This is essentially an adaptation of aae8223c707 ("Dup splat array in
certain cases where there is a block argument") to pass
`TestCall#test_call_block_order`.

This also makes PRISM emit `getblockparamproxy` in cases like
`def foo(&blk) = bar(&blk)` where it used the less efficient
`getblockparam` previously.

Remove the `popped` parameter from setup_args() because it should always
be ignored, and is in fact unused.
This commit is contained in:
Alan Wu 2024-02-02 19:29:50 -05:00 committed by Kevin Newton
parent 68b57ceb46
commit 0854d64862

View File

@ -1009,8 +1009,9 @@ pm_arg_compile_keyword_hash_node(pm_keyword_hash_node_t *node, rb_iseq_t *iseq,
}
}
// This is details. Users should call pm_setup_args() instead.
static int
pm_setup_args(const pm_arguments_node_t *arguments_node, int *flags, struct rb_callinfo_kwarg **kw_arg, rb_iseq_t *iseq, LINK_ANCHOR *const ret, bool popped, pm_scope_node_t *scope_node, NODE dummy_line_node, pm_parser_t *parser)
pm_setup_args_core(const pm_arguments_node_t *arguments_node, const pm_node_t *block, int *flags, const bool has_regular_blockarg, struct rb_callinfo_kwarg **kw_arg, rb_iseq_t *iseq, LINK_ANCHOR *const ret, pm_scope_node_t *scope_node, NODE dummy_line_node, pm_parser_t *parser)
{
int orig_argc = 0;
bool has_splat = false;
@ -1106,7 +1107,7 @@ pm_setup_args(const pm_arguments_node_t *arguments_node, int *flags, struct rb_c
//
// foo(a, *b, c)
// ^^
if (index + 1 < arguments_node_list.size) {
if (index + 1 < arguments_node_list.size || has_regular_blockarg) {
ADD_INSN1(ret, &dummy_line_node, splatarray, Qtrue);
*flags |= VM_CALL_ARGS_SPLAT_MUT;
}
@ -1225,6 +1226,42 @@ pm_setup_args(const pm_arguments_node_t *arguments_node, int *flags, struct rb_c
return orig_argc;
}
// Compile the argument parts of a call
static int
pm_setup_args(const pm_arguments_node_t *arguments_node, const pm_node_t *block, int *flags, struct rb_callinfo_kwarg **kw_arg, rb_iseq_t *iseq, LINK_ANCHOR *const ret, pm_scope_node_t *scope_node, NODE dummy_line_node, pm_parser_t *parser)
{
if (block && PM_NODE_TYPE_P(block, PM_BLOCK_ARGUMENT_NODE)) {
// We compile the `&block_arg` expression first and stitch it later
// since the nature of the expression influences whether splat should
// duplicate the array.
bool regular_block_arg = true;
DECL_ANCHOR(block_arg);
INIT_ANCHOR(block_arg);
pm_compile_node(iseq, block, block_arg, false, scope_node);
*flags |= VM_CALL_ARGS_BLOCKARG;
if (LIST_INSN_SIZE_ONE(block_arg)) {
LINK_ELEMENT *elem = FIRST_ELEMENT(block_arg);
if (IS_INSN(elem)) {
INSN *iobj = (INSN *)elem;
if (iobj->insn_id == BIN(getblockparam)) {
iobj->insn_id = BIN(getblockparamproxy);
}
// Allow splat without duplication for simple one-instruction
// block arguments like `&arg`. It is known that this optimization
// can be too aggressive in some cases. See [Bug #16504].
regular_block_arg = false;
}
}
int argc = pm_setup_args_core(arguments_node, block, flags, regular_block_arg, kw_arg, iseq, ret, scope_node, dummy_line_node, parser);
ADD_SEQ(ret, block_arg);
return argc;
}
return pm_setup_args_core(arguments_node, block, flags, false, kw_arg, iseq, ret, scope_node, dummy_line_node, parser);
}
/**
* Compile an index operator write node, which is a node that is writing a value
* using the [] and []= methods. It looks like:
@ -1250,12 +1287,7 @@ pm_compile_index_operator_write_node(pm_scope_node_t *scope_node, const pm_index
int boff = (node->block == NULL ? 0 : 1);
int flag = PM_NODE_TYPE_P(node->receiver, PM_SELF_NODE) ? VM_CALL_FCALL : 0;
struct rb_callinfo_kwarg *keywords = NULL;
int argc = pm_setup_args(node->arguments, &flag, &keywords, iseq, ret, popped, scope_node, dummy_line_node, scope_node->parser);
if (node->block != NULL) {
flag |= VM_CALL_ARGS_BLOCKARG;
PM_COMPILE_NOT_POPPED(node->block);
}
int argc = pm_setup_args(node->arguments, node->block, &flag, &keywords, iseq, ret, scope_node, dummy_line_node, scope_node->parser);
if ((argc > 0 || boff) && (flag & VM_CALL_KW_SPLAT)) {
if (boff) {
@ -1375,12 +1407,7 @@ pm_compile_index_control_flow_write_node(pm_scope_node_t *scope_node, const pm_n
int boff = (block == NULL ? 0 : 1);
int flag = PM_NODE_TYPE_P(receiver, PM_SELF_NODE) ? VM_CALL_FCALL : 0;
struct rb_callinfo_kwarg *keywords = NULL;
int argc = pm_setup_args(arguments, &flag, &keywords, iseq, ret, popped, scope_node, dummy_line_node, scope_node->parser);
if (block != NULL) {
flag |= VM_CALL_ARGS_BLOCKARG;
PM_COMPILE_NOT_POPPED(block);
}
int argc = pm_setup_args(arguments, block, &flag, &keywords, iseq, ret, scope_node, dummy_line_node, scope_node->parser);
if ((argc > 0 || boff) && (flag & VM_CALL_KW_SPLAT)) {
if (boff) {
@ -2867,7 +2894,7 @@ pm_compile_call(rb_iseq_t *iseq, const pm_call_node_t *call_node, LINK_ANCHOR *c
int flags = 0;
struct rb_callinfo_kwarg *kw_arg = NULL;
int orig_argc = pm_setup_args(call_node->arguments, &flags, &kw_arg, iseq, ret, popped, scope_node, dummy_line_node, parser);
int orig_argc = pm_setup_args(call_node->arguments, call_node->block, &flags, &kw_arg, iseq, ret, scope_node, dummy_line_node, parser);
const rb_iseq_t *block_iseq = NULL;
if (call_node->block != NULL && PM_NODE_TYPE_P(call_node->block, PM_BLOCK_NODE)) {
@ -2887,11 +2914,6 @@ pm_compile_call(rb_iseq_t *iseq, const pm_call_node_t *call_node, LINK_ANCHOR *c
flags |= VM_CALL_VCALL;
}
if (call_node->block != NULL) {
PM_COMPILE_NOT_POPPED(call_node->block);
flags |= VM_CALL_ARGS_BLOCKARG;
}
if (!flags) {
flags |= VM_CALL_ARGS_SIMPLE;
}
@ -3448,12 +3470,7 @@ pm_compile_target_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *cons
int flags = 0;
struct rb_callinfo_kwarg *kwargs = NULL;
int argc = pm_setup_args(cast->arguments, &flags, &kwargs, iseq, parents, false, scope_node, dummy_line_node, scope_node->parser);
if (cast->block != NULL) {
flags |= VM_CALL_ARGS_BLOCKARG;
if (cast->block != NULL) pm_compile_node(iseq, cast->block, parents, false, scope_node);
}
int argc = pm_setup_args(cast->arguments, cast->block, &flags, &kwargs, iseq, parents, scope_node, dummy_line_node, scope_node->parser);
if (state != NULL) {
ADD_INSN1(writes, &dummy_line_node, topn, INT2FIX(argc + 1));
@ -7429,27 +7446,14 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret,
PM_PUTSELF;
int argc = pm_setup_args(super_node->arguments, &flags, &keywords, iseq, ret, popped, scope_node, dummy_line_node, parser);
int argc = pm_setup_args(super_node->arguments, super_node->block, &flags, &keywords, iseq, ret, scope_node, dummy_line_node, parser);
flags |= VM_CALL_SUPER | VM_CALL_FCALL;
if (super_node->block) {
switch (PM_NODE_TYPE(super_node->block)) {
case PM_BLOCK_ARGUMENT_NODE: {
PM_COMPILE_NOT_POPPED(super_node->block);
flags |= VM_CALL_ARGS_BLOCKARG;
break;
}
case PM_BLOCK_NODE: {
pm_scope_node_t next_scope_node;
pm_scope_node_init(super_node->block, &next_scope_node, scope_node, parser);
parent_block = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, lineno);
pm_scope_node_destroy(&next_scope_node);
break;
}
default: {
rb_bug("Unexpected node type on a SuperNode's block: %s", pm_node_type_to_str(PM_NODE_TYPE(super_node->block)));
}
}
if (super_node->block && PM_NODE_TYPE_P(super_node->block, PM_BLOCK_NODE)) {
pm_scope_node_t next_scope_node;
pm_scope_node_init(super_node->block, &next_scope_node, scope_node, parser);
parent_block = NEW_CHILD_ISEQ(&next_scope_node, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, lineno);
pm_scope_node_destroy(&next_scope_node);
}
if ((flags & VM_CALL_ARGS_BLOCKARG) && (flags & VM_CALL_KW_SPLAT) && !(flags & VM_CALL_KW_SPLAT_MUT)) {
@ -7554,7 +7558,7 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret,
int argc = 0;
if (yield_node->arguments) {
argc = pm_setup_args(yield_node->arguments, &flags, &keywords, iseq, ret, popped, scope_node, dummy_line_node, parser);
argc = pm_setup_args(yield_node->arguments, NULL, &flags, &keywords, iseq, ret, scope_node, dummy_line_node, parser);
}
ADD_INSN1(ret, &dummy_line_node, invokeblock, new_callinfo(iseq, 0, argc, flags, keywords, FALSE));