Eliminate 1-2 array allocations for each splat used in a masgn method
Given code such as: ```ruby h[*a, :a], h[*b] = v ``` Ruby would previously allocate 5 arrays for the mass assignment: * splatarray true for a * newarray for v[0] * concatarray for [*a, a] and v[0] * newarray for v[1] * concatarray for b and v[1] This optimizes it to only allocate 2 arrays: * splatarray true for a * splatarray true for b Instead of the newarray/concatarray combination, pushtoarray is used. Note above that there was no splatarray true for b originally. The normal compilation uses splatarray false for b. Instead of trying to find and modify the splatarray false to splatarray true, this adds splatarray true for b, which requires a couple of swap instructions, before the pushtoarray. This could be further optimized to remove the need for those three instructions, but I'm not sure if the complexity is worth it. Additionally, this sets VM_CALL_ARGS_SPLAT_MUT on the call to []= in the h[*b] case, so that if []= has a splat parameter, the new array can be used directly, without additional duplication.
This commit is contained in:
parent
a591e11a7a
commit
d917bb8e60
24
compile.c
24
compile.c
@ -5401,11 +5401,31 @@ compile_massign_lhs(rb_iseq_t *iseq, LINK_ANCHOR *const pre, LINK_ANCHOR *const
|
|||||||
ADD_ELEM(lhs, (LINK_ELEMENT *)iobj);
|
ADD_ELEM(lhs, (LINK_ELEMENT *)iobj);
|
||||||
if (vm_ci_flag(ci) & VM_CALL_ARGS_SPLAT) {
|
if (vm_ci_flag(ci) & VM_CALL_ARGS_SPLAT) {
|
||||||
int argc = vm_ci_argc(ci);
|
int argc = vm_ci_argc(ci);
|
||||||
|
bool dupsplat = false;
|
||||||
ci = ci_argc_set(iseq, ci, argc - 1);
|
ci = ci_argc_set(iseq, ci, argc - 1);
|
||||||
|
if (!(vm_ci_flag(ci) & VM_CALL_ARGS_SPLAT_MUT)) {
|
||||||
|
/* Given h[*a], _ = ary
|
||||||
|
* setup_args sets VM_CALL_ARGS_SPLAT and not VM_CALL_ARGS_SPLAT_MUT
|
||||||
|
* `a` must be dupped, because it will be appended with ary[0]
|
||||||
|
* Since you are dupping `a`, you can set VM_CALL_ARGS_SPLAT_MUT
|
||||||
|
*/
|
||||||
|
dupsplat = true;
|
||||||
|
ci = ci_flag_set(iseq, ci, VM_CALL_ARGS_SPLAT_MUT);
|
||||||
|
}
|
||||||
OPERAND_AT(iobj, 0) = (VALUE)ci;
|
OPERAND_AT(iobj, 0) = (VALUE)ci;
|
||||||
RB_OBJ_WRITTEN(iseq, Qundef, iobj);
|
RB_OBJ_WRITTEN(iseq, Qundef, iobj);
|
||||||
INSERT_BEFORE_INSN1(iobj, line_node, newarray, INT2FIX(1));
|
/* Given: h[*a], h[*b, 1] = ary
|
||||||
INSERT_BEFORE_INSN(iobj, line_node, concatarray);
|
* h[*a] uses splatarray false and does not set VM_CALL_ARGS_SPLAT_MUT,
|
||||||
|
* so this uses splatarray true on a to dup it before using pushtoarray
|
||||||
|
* h[*b, 1] uses splatarray true and sets VM_CALL_ARGS_SPLAT_MUT,
|
||||||
|
* so you can use pushtoarray directly
|
||||||
|
*/
|
||||||
|
if (dupsplat) {
|
||||||
|
INSERT_BEFORE_INSN(iobj, line_node, swap);
|
||||||
|
INSERT_BEFORE_INSN1(iobj, line_node, splatarray, Qtrue);
|
||||||
|
INSERT_BEFORE_INSN(iobj, line_node, swap);
|
||||||
|
}
|
||||||
|
INSERT_BEFORE_INSN1(iobj, line_node, pushtoarray, INT2FIX(1));
|
||||||
}
|
}
|
||||||
ADD_INSN(lhs, line_node, pop);
|
ADD_INSN(lhs, line_node, pop);
|
||||||
if (argc != 1) {
|
if (argc != 1) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user