summaryrefslogtreecommitdiff
path: root/compile.c
diff options
context:
space:
mode:
authorRandy Stauner <[email protected]>2024-11-26 12:31:08 -0700
committerGitHub <[email protected]>2024-11-26 14:31:08 -0500
commit1dd40ec18a55ff46f52d0ba44ff5d7923f57c08f (patch)
treeeac4304c90dfa9df36a657af57eccd6bf3e8543c /compile.c
parentc1dcd1d4965100292e8f649042c74e10d58e6c0f (diff)
Optimize instructions when creating an array just to call `include?` (#12123)
* Add opt_duparray_send insn to skip the allocation on `#include?` If the method isn't going to modify the array we don't need to copy it. This avoids the allocation / array copy for things like `[:a, :b].include?(x)`. This adds a BOP for include? and tracks redefinition for it on Array. Co-authored-by: Andrew Novoselac <[email protected]> * YJIT: Implement opt_duparray_send include_p Co-authored-by: Andrew Novoselac <[email protected]> * Update opt_newarray_send to support simple forms of include?(arg) Similar to opt_duparray_send but for non-static arrays. * YJIT: Implement opt_newarray_send include_p --------- Co-authored-by: Andrew Novoselac <[email protected]>
Notes
Notes: Merged-By: maximecb <[email protected]>
Diffstat (limited to 'compile.c')
-rw-r--r--compile.c79
1 files changed, 79 insertions, 0 deletions
diff --git a/compile.c b/compile.c
index 10d02b2447..7de791a033 100644
--- a/compile.c
+++ b/compile.c
@@ -4170,8 +4170,87 @@ iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj)
return COMPILE_OK;
}
}
+
+ // Break the "else if" chain since some prior checks abort after sub-ifs.
+ // We already found "newarray". To match `[...].include?(arg)` we look for
+ // the instruction(s) representing the argument followed by a "send".
+ if ((IS_INSN_ID(niobj, putstring) || IS_INSN_ID(niobj, putchilledstring) ||
+ IS_INSN_ID(niobj, putobject) ||
+ IS_INSN_ID(niobj, putself) ||
+ IS_INSN_ID(niobj, getlocal) ||
+ IS_INSN_ID(niobj, getinstancevariable)) &&
+ IS_NEXT_INSN_ID(&niobj->link, send)) {
+
+ LINK_ELEMENT *sendobj = &(niobj->link); // Below we call ->next;
+ const struct rb_callinfo *ci;
+ // Allow any number (0 or more) of simple method calls on the argument
+ // (as in `[...].include?(arg.method1.method2)`.
+ do {
+ sendobj = sendobj->next;
+ ci = (struct rb_callinfo *)OPERAND_AT(sendobj, 0);
+ } while (vm_ci_simple(ci) && vm_ci_argc(ci) == 0 && IS_NEXT_INSN_ID(sendobj, send));
+
+ // If this send is for .include? with one arg we can do our opt.
+ if (vm_ci_simple(ci) && vm_ci_argc(ci) == 1 && vm_ci_mid(ci) == idIncludeP) {
+ VALUE num = iobj->operands[0];
+ INSN *sendins = (INSN *)sendobj;
+ sendins->insn_id = BIN(opt_newarray_send);
+ sendins->operand_size = insn_len(sendins->insn_id) - 1;
+ sendins->operands = compile_data_calloc2(iseq, sendins->operand_size, sizeof(VALUE));
+ sendins->operands[0] = FIXNUM_INC(num, 1);
+ sendins->operands[1] = INT2FIX(VM_OPT_NEWARRAY_SEND_INCLUDE_P);
+ // Remove the original "newarray" insn.
+ ELEM_REMOVE(&iobj->link);
+ return COMPILE_OK;
+ }
+ }
+ }
+
+ /*
+ * duparray [...]
+ * some insn for the arg...
+ * send <calldata!mid:include?, argc:1, ARGS_SIMPLE>, nil
+ * =>
+ * arg insn...
+ * opt_duparray_send [...], :include?, 1
+ */
+ if (IS_INSN_ID(iobj, duparray) && iobj->link.next && IS_INSN(iobj->link.next)) {
+ INSN *niobj = (INSN *)iobj->link.next;
+ if ((IS_INSN_ID(niobj, getlocal) ||
+ IS_INSN_ID(niobj, getinstancevariable) ||
+ IS_INSN_ID(niobj, putself)) &&
+ IS_NEXT_INSN_ID(&niobj->link, send)) {
+
+ LINK_ELEMENT *sendobj = &(niobj->link); // Below we call ->next;
+ const struct rb_callinfo *ci;
+ // Allow any number (0 or more) of simple method calls on the argument
+ // (as in `[...].include?(arg.method1.method2)`.
+ do {
+ sendobj = sendobj->next;
+ ci = (struct rb_callinfo *)OPERAND_AT(sendobj, 0);
+ } while (vm_ci_simple(ci) && vm_ci_argc(ci) == 0 && IS_NEXT_INSN_ID(sendobj, send));
+
+ if (vm_ci_simple(ci) && vm_ci_argc(ci) == 1 && vm_ci_mid(ci) == idIncludeP) {
+ // Move the array arg from duparray to opt_duparray_send.
+ VALUE ary = iobj->operands[0];
+ rb_obj_reveal(ary, rb_cArray);
+
+ INSN *sendins = (INSN *)sendobj;
+ sendins->insn_id = BIN(opt_duparray_send);
+ sendins->operand_size = insn_len(sendins->insn_id) - 1;;
+ sendins->operands = compile_data_calloc2(iseq, sendins->operand_size, sizeof(VALUE));
+ sendins->operands[0] = ary;
+ sendins->operands[1] = rb_id2sym(idIncludeP);
+ sendins->operands[2] = INT2FIX(1);
+
+ // Remove the duparray insn.
+ ELEM_REMOVE(&iobj->link);
+ return COMPILE_OK;
+ }
+ }
}
+
if (IS_INSN_ID(iobj, send)) {
const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(iobj, 0);
const rb_iseq_t *blockiseq = (rb_iseq_t *)OPERAND_AT(iobj, 1);