summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy Evans <code@jeremyevans.net>2023-11-24 20:23:58 -0800
committerJeremy Evans <code@jeremyevans.net>2024-01-24 18:25:55 -0800
commit6e06d0d180001a79abadf48e2fe6baf3886f54c0 (patch)
tree93402c0051aa74046bef5a5b64407151ffbd6b43
parent22e488464a412afa58f201c49e54773aa8011320 (diff)
Add concattoarray VM instruction
This instruction is similar to concatarray, but assumes the first object is already an array, and appends to it directly. This is different than concatarray, which will create a new array instead of appending to an existing array. Additionally, for both concatarray and concattoarray, if the second argument cannot be converted to an array, then just push it onto the array, instead of creating a new array to wrap it, and then using concat array. This saves an array allocation in that case. This allows `f(*a, *a, *1)` to allocate only a single array on the caller side (which can be reused on the callee side in the case of `def f(*a)`). Prior to this commit, `f(*a, *a, *1)` would generate 4 arrays: * a dupped by splatarray true * a dupped again by first concatarray * 1 wrapped in array by third splatarray * result of [*a, *a] dupped by second concatarray Instructions Before for `a = []; f(*a, *a, *1)`: ``` 0000 newarray 0 ( 1)[Li] 0002 setlocal_WC_0 a@0 0004 putself 0005 getlocal_WC_0 a@0 0007 splatarray true 0009 getlocal_WC_0 a@0 0011 splatarray false 0013 concatarray 0014 putobject_INT2FIX_1_ 0015 splatarray false 0017 concatarray 0018 opt_send_without_block <calldata!mid:g, argc:1, ARGS_SPLAT|ARGS_SPLAT_MUT|FCALL> 0020 leave ``` Instructions After for `a = []; f(*a, *a, *1)`: ``` 0000 newarray 0 ( 1)[Li] 0002 setlocal_WC_0 a@0 0004 putself 0005 getlocal_WC_0 a@0 0007 splatarray true 0009 getlocal_WC_0 a@0 0011 concattoarray 0012 putobject_INT2FIX_1_ 0013 concattoarray 0014 opt_send_without_block <calldata!mid:f, argc:1, ARGS_SPLAT|ARGS_SPLAT_MUT|FCALL> 0016 leave ```
-rw-r--r--compile.c3
-rw-r--r--insns.def18
-rw-r--r--vm_insnhelper.c22
3 files changed, 36 insertions, 7 deletions
diff --git a/compile.c b/compile.c
index a2efc05438..f2701060d8 100644
--- a/compile.c
+++ b/compile.c
@@ -6198,8 +6198,7 @@ setup_args_core(rb_iseq_t *iseq, LINK_ANCHOR *const args, const NODE *argn,
argc += 1;
}
else {
- ADD_INSN1(args, argn, splatarray, Qfalse);
- ADD_INSN(args, argn, concatarray);
+ ADD_INSN(args, argn, concattoarray);
}
// f(..., *a, ..., k1:1, ...) #=> f(..., *[*a, ...], **{k1:1, ...})
diff --git a/insns.def b/insns.def
index 5f6cb314ff..2b03e3eb47 100644
--- a/insns.def
+++ b/insns.def
@@ -505,7 +505,9 @@ expandarray
vm_expandarray(GET_CFP(), ary, num, (int)flag);
}
-/* concat two arrays */
+/* concat two arrays, without modifying first array.
+ * attempts to convert both objects to arrays using to_a.
+ */
DEFINE_INSN
concatarray
()
@@ -516,6 +518,20 @@ concatarray
ary = vm_concat_array(ary1, ary2);
}
+/* concat second array to first array.
+ * first argument must already be an array.
+ * attempts to convert second object to array using to_a.
+ */
+DEFINE_INSN
+concattoarray
+()
+(VALUE ary1, VALUE ary2)
+(VALUE ary)
+// attr bool leaf = false; /* has rb_check_array_type() */
+{
+ ary = vm_concat_to_array(ary1, ary2);
+}
+
/* call to_a on array ary to splat */
DEFINE_INSN
splatarray
diff --git a/vm_insnhelper.c b/vm_insnhelper.c
index 97776f7a69..55e79acdd8 100644
--- a/vm_insnhelper.c
+++ b/vm_insnhelper.c
@@ -5204,15 +5204,29 @@ vm_concat_array(VALUE ary1, VALUE ary2st)
if (NIL_P(tmp1)) {
tmp1 = rb_ary_new3(1, ary1);
}
+ if (tmp1 == ary1) {
+ tmp1 = rb_ary_dup(ary1);
+ }
if (NIL_P(tmp2)) {
- tmp2 = rb_ary_new3(1, ary2);
+ return rb_ary_push(tmp1, ary2);
+ } else {
+ return rb_ary_concat(tmp1, tmp2);
}
+}
- if (tmp1 == ary1) {
- tmp1 = rb_ary_dup(ary1);
+static VALUE
+vm_concat_to_array(VALUE ary1, VALUE ary2st)
+{
+ /* ary1 must be a newly created array */
+ const VALUE ary2 = ary2st;
+ VALUE tmp2 = rb_check_to_array(ary2);
+
+ if (NIL_P(tmp2)) {
+ return rb_ary_push(ary1, ary2);
+ } else {
+ return rb_ary_concat(ary1, tmp2);
}
- return rb_ary_concat(tmp1, tmp2);
}
// YJIT implementation is using the C function