diff options
author | Jeremy Evans <[email protected]> | 2023-11-23 10:47:24 -0800 |
---|---|---|
committer | Jeremy Evans <[email protected]> | 2024-01-24 18:25:55 -0800 |
commit | 22e488464a412afa58f201c49e54773aa8011320 (patch) | |
tree | 77f5adf2d7761cbcf1eb87e375663195e40fda44 /compile.c | |
parent | ef276858d9295208add48e27208c69184dc50472 (diff) |
Add VM_CALL_ARGS_SPLAT_MUT callinfo flag
This flag is set when the caller has already created a new array to
handle a splat, such as for `f(*a, b)` and `f(*a, *b)`. Previously,
if `f` was defined as `def f(*a)`, these calls would create an extra
array on the callee side, instead of using the new array created
by the caller.
This modifies `setup_args_core` to set the flag whenver it would add
a `splatarray true` instruction. However, when `splatarray true` is
changed to `splatarray false` in the peephole optimizer, to avoid
unnecessary allocations on the caller side, the flag must be removed.
Add `optimize_args_splat_no_copy` and have the peephole optimizer call
that. This significantly simplifies the related peephole optimizer
code.
On the callee side, in `setup_parameters_complex`, set
`args->rest_dupped` to true if the flag is set.
This takes a similar approach for optimizing regular splats that was
previiously used for keyword splats in
d2c41b1bff1f3102544bb0d03d4e82356d034d33 (via VM_CALL_KW_SPLAT_MUT).
Diffstat (limited to 'compile.c')
-rw-r--r-- | compile.c | 193 |
1 files changed, 89 insertions, 104 deletions
@@ -3190,6 +3190,30 @@ ci_argc_set(const rb_iseq_t *iseq, const struct rb_callinfo *ci, int argc) return nci; } +static bool +optimize_args_splat_no_copy(rb_iseq_t *iseq, INSN *insn, LINK_ELEMENT *niobj, + unsigned int set_flags, unsigned int unset_flags) +{ + LINK_ELEMENT *iobj = (LINK_ELEMENT *)insn; + if (!IS_NEXT_INSN_ID(niobj, send)) { + return false; + } + niobj = niobj->next; + + const struct rb_callinfo *ci = (const struct rb_callinfo *)OPERAND_AT(niobj, 0); + unsigned int flags = vm_ci_flag(ci); + if ((flags & set_flags) == set_flags && !(flags & unset_flags)) { + RUBY_ASSERT(flags & VM_CALL_ARGS_SPLAT_MUT); + OPERAND_AT(iobj, 0) = Qfalse; + const struct rb_callinfo *nci = vm_ci_new(vm_ci_mid(ci), + flags & ~VM_CALL_ARGS_SPLAT_MUT, vm_ci_argc(ci), vm_ci_kwarg(ci)); + RB_OBJ_WRITTEN(iseq, ci, nci); + OPERAND_AT(niobj, 0) = (VALUE)nci; + return true; + } + return false; +} + static int iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcallopt) { @@ -3879,58 +3903,46 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal * splatarray false * send */ - if (IS_NEXT_INSN_ID(niobj, send)) { - niobj = niobj->next; - unsigned int flag = vm_ci_flag((const struct rb_callinfo *)OPERAND_AT(niobj, 0)); - if ((flag & VM_CALL_ARGS_SPLAT) && !(flag & (VM_CALL_KW_SPLAT|VM_CALL_ARGS_BLOCKARG))) { - OPERAND_AT(iobj, 0) = Qfalse; - } - } - else if (IS_NEXT_INSN_ID(niobj, getlocal) || IS_NEXT_INSN_ID(niobj, getinstancevariable)) { + if (optimize_args_splat_no_copy(iseq, iobj, niobj, + VM_CALL_ARGS_SPLAT, VM_CALL_KW_SPLAT|VM_CALL_ARGS_BLOCKARG)) goto optimized_splat; + + if (IS_NEXT_INSN_ID(niobj, getlocal) || IS_NEXT_INSN_ID(niobj, getinstancevariable)) { niobj = niobj->next; - if (IS_NEXT_INSN_ID(niobj, send)) { - niobj = niobj->next; - unsigned int flag = vm_ci_flag((const struct rb_callinfo *)OPERAND_AT(niobj, 0)); + /* + * Eliminate array allocation for f(1, *a, &lvar) and f(1, *a, &@iv) + * + * splatarray true + * getlocal / getinstancevariable + * send ARGS_SPLAT|ARGS_BLOCKARG and not KW_SPLAT + * => + * splatarray false + * getlocal / getinstancevariable + * send + */ + if (optimize_args_splat_no_copy(iseq, iobj, niobj, + VM_CALL_ARGS_SPLAT|VM_CALL_ARGS_BLOCKARG, VM_CALL_KW_SPLAT)) goto optimized_splat; - if ((flag & VM_CALL_ARGS_SPLAT)) { - /* - * Eliminate array allocation for f(1, *a, &lvar) and f(1, *a, &@iv) - * - * splatarray true - * getlocal / getinstancevariable - * send ARGS_SPLAT|ARGS_BLOCKARG and not KW_SPLAT - * => - * splatarray false - * getlocal / getinstancevariable - * send - */ - if ((flag & VM_CALL_ARGS_BLOCKARG) && !(flag & VM_CALL_KW_SPLAT)) { - OPERAND_AT(iobj, 0) = Qfalse; - } + /* + * Eliminate array allocation for f(*a, **lvar) and f(*a, **@iv) + * + * splatarray true + * getlocal / getinstancevariable + * send ARGS_SPLAT|KW_SPLAT and not ARGS_BLOCKARG + * => + * splatarray false + * getlocal / getinstancevariable + * send + */ + if (optimize_args_splat_no_copy(iseq, iobj, niobj, + VM_CALL_ARGS_SPLAT|VM_CALL_KW_SPLAT, VM_CALL_ARGS_BLOCKARG)) goto optimized_splat; - /* - * Eliminate array allocation for f(*a, **lvar) and f(*a, **@iv) - * - * splatarray true - * getlocal / getinstancevariable - * send ARGS_SPLAT|KW_SPLAT and not ARGS_BLOCKARG - * => - * splatarray false - * getlocal / getinstancevariable - * send - */ - else if (!(flag & VM_CALL_ARGS_BLOCKARG) && (flag & VM_CALL_KW_SPLAT)) { - OPERAND_AT(iobj, 0) = Qfalse; - } - } - } - else if (IS_NEXT_INSN_ID(niobj, getlocal) || IS_NEXT_INSN_ID(niobj, getinstancevariable) || - IS_NEXT_INSN_ID(niobj, getblockparamproxy)) { + if (IS_NEXT_INSN_ID(niobj, getlocal) || IS_NEXT_INSN_ID(niobj, getinstancevariable) || + IS_NEXT_INSN_ID(niobj, getblockparamproxy)) { niobj = niobj->next; /* - * Eliminate array allocation for f(*a, **lvar, &lvar) and f(*a, **@iv, &@iv) + * Eliminate array allocation for f(*a, **lvar, &{arg,lvar,@iv}) * * splatarray true * getlocal / getinstancevariable @@ -3942,40 +3954,24 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal * getlocal / getinstancevariable / getblockparamproxy * send */ - if (IS_NEXT_INSN_ID(niobj, send)) { - niobj = niobj->next; - unsigned int flag = vm_ci_flag((const struct rb_callinfo *)OPERAND_AT(niobj, 0)); - - if ((flag & VM_CALL_ARGS_SPLAT) && (flag & VM_CALL_KW_SPLAT) && (flag & VM_CALL_ARGS_BLOCKARG)) { - OPERAND_AT(iobj, 0) = Qfalse; - } - } - } - } - else if (IS_NEXT_INSN_ID(niobj, getblockparamproxy)) { - niobj = niobj->next; - - if (IS_NEXT_INSN_ID(niobj, send)) { - niobj = niobj->next; - unsigned int flag = vm_ci_flag((const struct rb_callinfo *)OPERAND_AT(niobj, 0)); - - /* - * Eliminate array allocation for f(1, *a, &arg) - * - * splatarray true - * getblockparamproxy - * send ARGS_SPLAT|ARGS_BLOCKARG and not KW_SPLAT - * => - * splatarray false - * getblockparamproxy - * send - */ - if ((flag & VM_CALL_ARGS_BLOCKARG) & (flag & VM_CALL_ARGS_SPLAT) && !(flag & VM_CALL_KW_SPLAT)) { - OPERAND_AT(iobj, 0) = Qfalse; - } + optimize_args_splat_no_copy(iseq, iobj, niobj, + VM_CALL_ARGS_SPLAT|VM_CALL_KW_SPLAT|VM_CALL_ARGS_BLOCKARG, 0); } - } - else if (IS_NEXT_INSN_ID(niobj, duphash)) { + } else if (IS_NEXT_INSN_ID(niobj, getblockparamproxy)) { + /* + * Eliminate array allocation for f(1, *a, &arg) + * + * splatarray true + * getblockparamproxy + * send ARGS_SPLAT|ARGS_BLOCKARG and not KW_SPLAT + * => + * splatarray false + * getblockparamproxy + * send + */ + optimize_args_splat_no_copy(iseq, iobj, niobj, + VM_CALL_ARGS_SPLAT|VM_CALL_ARGS_BLOCKARG, VM_CALL_KW_SPLAT); + } else if (IS_NEXT_INSN_ID(niobj, duphash)) { niobj = niobj->next; /* @@ -3989,21 +3985,13 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal * duphash * send */ - if (IS_NEXT_INSN_ID(niobj, send)) { - niobj = niobj->next; - unsigned int flag = vm_ci_flag((const struct rb_callinfo *)OPERAND_AT(niobj, 0)); - - if ((flag & VM_CALL_ARGS_SPLAT) && (flag & VM_CALL_KW_SPLAT) && - (flag & VM_CALL_KW_SPLAT_MUT) && !(flag & VM_CALL_ARGS_BLOCKARG)) { - OPERAND_AT(iobj, 0) = Qfalse; - } - } - else if (IS_NEXT_INSN_ID(niobj, getlocal) || IS_NEXT_INSN_ID(niobj, getinstancevariable) || - IS_NEXT_INSN_ID(niobj, getblockparamproxy)) { - niobj = niobj->next; + if (optimize_args_splat_no_copy(iseq, iobj, niobj->next, + VM_CALL_ARGS_SPLAT|VM_CALL_KW_SPLAT|VM_CALL_KW_SPLAT_MUT, VM_CALL_ARGS_BLOCKARG)) goto optimized_splat; + if (IS_NEXT_INSN_ID(niobj, getlocal) || IS_NEXT_INSN_ID(niobj, getinstancevariable) || + IS_NEXT_INSN_ID(niobj, getblockparamproxy)) { /* - * Eliminate array allocation for f(*a, kw: 1, &lvar) and f(*a, kw: 1, &@iv) + * Eliminate array allocation for f(*a, kw: 1, &{arg,lvar,@iv}) * * splatarray true * duphash @@ -4015,18 +4003,12 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal * getlocal / getinstancevariable / getblockparamproxy * send */ - if (IS_NEXT_INSN_ID(niobj, send)) { - niobj = niobj->next; - unsigned int flag = vm_ci_flag((const struct rb_callinfo *)OPERAND_AT(niobj, 0)); - - if ((flag & VM_CALL_ARGS_SPLAT) && (flag & VM_CALL_KW_SPLAT) && - (flag & VM_CALL_KW_SPLAT_MUT) && (flag & VM_CALL_ARGS_BLOCKARG)) { - OPERAND_AT(iobj, 0) = Qfalse; - } - } + optimize_args_splat_no_copy(iseq, iobj, niobj->next, + VM_CALL_ARGS_SPLAT|VM_CALL_KW_SPLAT|VM_CALL_KW_SPLAT_MUT|VM_CALL_ARGS_BLOCKARG, 0); } } } + optimized_splat: return COMPILE_OK; } @@ -6190,12 +6172,15 @@ setup_args_core(rb_iseq_t *iseq, LINK_ANCHOR *const args, const NODE *argn, // f(*a) NO_CHECK(COMPILE(args, "args (splat)", RNODE_SPLAT(argn)->nd_head)); ADD_INSN1(args, argn, splatarray, RBOOL(dup_rest)); - if (flag_ptr) *flag_ptr |= VM_CALL_ARGS_SPLAT; + if (flag_ptr) { + *flag_ptr |= VM_CALL_ARGS_SPLAT; + if (dup_rest) *flag_ptr |= VM_CALL_ARGS_SPLAT_MUT; + } 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; + if (flag_ptr) *flag_ptr |= VM_CALL_ARGS_SPLAT | VM_CALL_ARGS_SPLAT_MUT; int argc = setup_args_core(iseq, args, RNODE_ARGSCAT(argn)->nd_head, 1, NULL, NULL); if (nd_type_p(RNODE_ARGSCAT(argn)->nd_body, NODE_LIST)) { @@ -6229,7 +6214,7 @@ setup_args_core(rb_iseq_t *iseq, LINK_ANCHOR *const args, const NODE *argn, return argc; } case NODE_ARGSPUSH: { - if (flag_ptr) *flag_ptr |= VM_CALL_ARGS_SPLAT; + if (flag_ptr) *flag_ptr |= VM_CALL_ARGS_SPLAT | VM_CALL_ARGS_SPLAT_MUT; int argc = setup_args_core(iseq, args, RNODE_ARGSPUSH(argn)->nd_head, 1, NULL, NULL); if (nd_type_p(RNODE_ARGSPUSH(argn)->nd_body, NODE_LIST)) { @@ -9080,7 +9065,7 @@ compile_op_asgn1(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node } ADD_INSN1(ret, node, dupn, INT2FIX(dup_argn)); flag |= asgnflag; - ADD_SEND_R(ret, node, idAREF, argc, NULL, INT2FIX(flag & ~VM_CALL_KW_SPLAT_MUT), keywords); + ADD_SEND_R(ret, node, idAREF, argc, NULL, INT2FIX(flag & ~(VM_CALL_ARGS_SPLAT_MUT|VM_CALL_KW_SPLAT_MUT)), keywords); if (id == idOROP || id == idANDOP) { /* a[x] ||= y or a[x] &&= y |