summaryrefslogtreecommitdiff
path: root/compile.c
diff options
context:
space:
mode:
authorJeremy Evans <code@jeremyevans.net>2024-01-26 11:53:49 -0800
committerJeremy Evans <code@jeremyevans.net>2024-01-27 14:04:55 -0800
commitd917bb8e60c283981626876c496cb2670f74ffb7 (patch)
tree0ef4e156f57f89d74927f6d7fd7ac19dd83279a0 /compile.c
parenta591e11a7a6f6749044b6bef9c35633ad1930db5 (diff)
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.
Diffstat (limited to 'compile.c')
-rw-r--r--compile.c24
1 files changed, 22 insertions, 2 deletions
diff --git a/compile.c b/compile.c
index a27f488373..79b17abd93 100644
--- a/compile.c
+++ b/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);
if (vm_ci_flag(ci) & VM_CALL_ARGS_SPLAT) {
int argc = vm_ci_argc(ci);
+ bool dupsplat = false;
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;
RB_OBJ_WRITTEN(iseq, Qundef, iobj);
- INSERT_BEFORE_INSN1(iobj, line_node, newarray, INT2FIX(1));
- INSERT_BEFORE_INSN(iobj, line_node, concatarray);
+ /* Given: h[*a], h[*b, 1] = ary
+ * 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);
if (argc != 1) {