summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAaron Patterson <tenderlove@ruby-lang.org>2022-06-06 17:27:56 -0700
committerAaron Patterson <aaron.patterson@gmail.com>2023-04-18 17:16:22 -0700
commitc5fc1ce975ecdf1c6818714e47579c5d3531c4ca (patch)
tree77a0f348d00a99281f826dac90e1d288c3139051
parent3016f30c956413268655dcb25dbe5041684f9528 (diff)
downloadruby-c5fc1ce975ecdf1c6818714e47579c5d3531c4ca.tar.gz
Emit special instruction for array literal + .(hash|min|max)
This commit introduces a new instruction `opt_newarray_send` which is used when there is an array literal followed by either the `hash`, `min`, or `max` method. ``` [a, b, c].hash ``` Will emit an `opt_newarray_send` instruction. This instruction falls back to a method call if the "interested" method has been monkey patched. Here are some examples of the instructions generated: ``` $ ./miniruby --dump=insns -e '[@a, @b].max' == disasm: #<ISeq:<main>@-e:1 (1,0)-(1,12)> (catch: FALSE) 0000 getinstancevariable :@a, <is:0> ( 1)[Li] 0003 getinstancevariable :@b, <is:1> 0006 opt_newarray_send 2, :max 0009 leave $ ./miniruby --dump=insns -e '[@a, @b].min' == disasm: #<ISeq:<main>@-e:1 (1,0)-(1,12)> (catch: FALSE) 0000 getinstancevariable :@a, <is:0> ( 1)[Li] 0003 getinstancevariable :@b, <is:1> 0006 opt_newarray_send 2, :min 0009 leave $ ./miniruby --dump=insns -e '[@a, @b].hash' == disasm: #<ISeq:<main>@-e:1 (1,0)-(1,13)> (catch: FALSE) 0000 getinstancevariable :@a, <is:0> ( 1)[Li] 0003 getinstancevariable :@b, <is:1> 0006 opt_newarray_send 2, :hash 0009 leave ``` [Feature #18897] [ruby-core:109147] Co-authored-by: John Hawthorn <jhawthorn@github.com>
-rw-r--r--array.c30
-rw-r--r--bootstraptest/test_insns.rb14
-rw-r--r--compile.c29
-rw-r--r--defs/id.def1
-rw-r--r--insns.def33
-rw-r--r--internal/array.h1
-rw-r--r--internal/basic_operators.h1
-rw-r--r--vm.c1
-rw-r--r--vm_insnhelper.c12
9 files changed, 76 insertions, 46 deletions
diff --git a/array.c b/array.c
index 1221b43923..b3b334dd68 100644
--- a/array.c
+++ b/array.c
@@ -5350,6 +5350,23 @@ rb_ary_eql(VALUE ary1, VALUE ary2)
return rb_exec_recursive_paired(recursive_eql, ary1, ary2, ary2);
}
+VALUE
+rb_ary_hash_values(long len, const VALUE *elements)
+{
+ long i;
+ st_index_t h;
+ VALUE n;
+
+ h = rb_hash_start(len);
+ h = rb_hash_uint(h, (st_index_t)rb_ary_hash_values);
+ for (i=0; i<len; i++) {
+ n = rb_hash(elements[i]);
+ h = rb_hash_uint(h, NUM2LONG(n));
+ }
+ h = rb_hash_end(h);
+ return ST2FIX(h);
+}
+
/*
* call-seq:
* array.hash -> integer
@@ -5366,18 +5383,7 @@ rb_ary_eql(VALUE ary1, VALUE ary2)
static VALUE
rb_ary_hash(VALUE ary)
{
- long i;
- st_index_t h;
- VALUE n;
-
- h = rb_hash_start(RARRAY_LEN(ary));
- h = rb_hash_uint(h, (st_index_t)rb_ary_hash);
- for (i=0; i<RARRAY_LEN(ary); i++) {
- n = rb_hash(RARRAY_AREF(ary, i));
- h = rb_hash_uint(h, NUM2LONG(n));
- }
- h = rb_hash_end(h);
- return ST2FIX(h);
+ return rb_ary_hash_values(RARRAY_LEN(ary), RARRAY_CONST_PTR(ary));
}
/*
diff --git a/bootstraptest/test_insns.rb b/bootstraptest/test_insns.rb
index 91fba9b011..d2e799f855 100644
--- a/bootstraptest/test_insns.rb
+++ b/bootstraptest/test_insns.rb
@@ -214,9 +214,11 @@ tests = [
'true'.freeze
},
- [ 'opt_newarray_max', %q{ [ ].max.nil? }, ],
- [ 'opt_newarray_max', %q{ [1, x = 2, 3].max == 3 }, ],
- [ 'opt_newarray_max', <<-'},', ], # {
+ [ 'opt_newarray_send', %q{ ![ ].hash.nil? }, ],
+
+ [ 'opt_newarray_send', %q{ [ ].max.nil? }, ],
+ [ 'opt_newarray_send', %q{ [1, x = 2, 3].max == 3 }, ],
+ [ 'opt_newarray_send', <<-'},', ], # {
class Array
def max
true
@@ -224,9 +226,9 @@ tests = [
end
[1, x = 2, 3].max
},
- [ 'opt_newarray_min', %q{ [ ].min.nil? }, ],
- [ 'opt_newarray_min', %q{ [3, x = 2, 1].min == 1 }, ],
- [ 'opt_newarray_min', <<-'},', ], # {
+ [ 'opt_newarray_send', %q{ [ ].min.nil? }, ],
+ [ 'opt_newarray_send', %q{ [3, x = 2, 1].min == 1 }, ],
+ [ 'opt_newarray_send', <<-'},', ], # {
class Array
def min
true
diff --git a/compile.c b/compile.c
index 819137006c..be5fd9c345 100644
--- a/compile.c
+++ b/compile.c
@@ -3706,18 +3706,23 @@ iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj)
if (IS_INSN_ID(niobj, send)) {
const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(niobj, 0);
if ((vm_ci_flag(ci) & VM_CALL_ARGS_SIMPLE) && vm_ci_argc(ci) == 0) {
- switch (vm_ci_mid(ci)) {
- case idMax:
- iobj->insn_id = BIN(opt_newarray_max);
- ELEM_REMOVE(&niobj->link);
- return COMPILE_OK;
- case idMin:
- iobj->insn_id = BIN(opt_newarray_min);
- ELEM_REMOVE(&niobj->link);
- return COMPILE_OK;
- }
- }
- }
+ switch (vm_ci_mid(ci)) {
+ case idMax:
+ case idMin:
+ case idHash:
+ {
+ rb_num_t num = (rb_num_t)iobj->operands[0];
+ iobj->insn_id = BIN(opt_newarray_send);
+ iobj->operands = compile_data_calloc2(iseq, insn_len(iobj->insn_id) - 1, sizeof(VALUE));
+ iobj->operands[0] = (VALUE)num;
+ iobj->operands[1] = (VALUE)rb_id2sym(vm_ci_mid(ci));
+ iobj->operand_size = insn_len(iobj->insn_id) - 1;
+ ELEM_REMOVE(&niobj->link);
+ return COMPILE_OK;
+ }
+ }
+ }
+ }
}
if (IS_INSN_ID(iobj, send)) {
diff --git a/defs/id.def b/defs/id.def
index c8d7b6af3e..2ddde7be70 100644
--- a/defs/id.def
+++ b/defs/id.def
@@ -2,6 +2,7 @@
firstline, predefined = __LINE__+1, %[\
max
min
+ hash
freeze
nil?
inspect
diff --git a/insns.def b/insns.def
index ba16d6365c..a457372112 100644
--- a/insns.def
+++ b/insns.def
@@ -899,8 +899,8 @@ opt_str_uminus
}
DEFINE_INSN
-opt_newarray_max
-(rb_num_t num)
+opt_newarray_send
+(rb_num_t num, ID method)
(...)
(VALUE val)
/* This instruction typically has no funcalls. But it compares array
@@ -909,20 +909,21 @@ opt_newarray_max
* cannot but mark it being not leaf. */
// attr bool leaf = false; /* has rb_funcall() */
// attr rb_snum_t sp_inc = 1 - (rb_snum_t)num;
-{
- val = vm_opt_newarray_max(ec, num, STACK_ADDR_FROM_TOP(num));
-}
-
-DEFINE_INSN
-opt_newarray_min
-(rb_num_t num)
-(...)
-(VALUE val)
-/* Same discussion as opt_newarray_max. */
-// attr bool leaf = false; /* has rb_funcall() */
-// attr rb_snum_t sp_inc = 1 - (rb_snum_t)num;
-{
- val = vm_opt_newarray_min(ec, num, STACK_ADDR_FROM_TOP(num));
+// attr rb_snum_t comptime_sp_inc = 1 - (rb_snum_t)num;
+{
+ switch(method) {
+ case idHash:
+ val = vm_opt_newarray_hash(ec, num, STACK_ADDR_FROM_TOP(num));
+ break;
+ case idMin:
+ val = vm_opt_newarray_min(ec, num, STACK_ADDR_FROM_TOP(num));
+ break;
+ case idMax:
+ val = vm_opt_newarray_max(ec, num, STACK_ADDR_FROM_TOP(num));
+ break;
+ default:
+ rb_bug("unreachable");
+ }
}
/* super(args) # args.size => num */
diff --git a/internal/array.h b/internal/array.h
index 3aeb1be2dd..7e55b66a87 100644
--- a/internal/array.h
+++ b/internal/array.h
@@ -23,6 +23,7 @@
#define RARRAY_PTR_IN_USE_FLAG FL_USER14
/* array.c */
+VALUE rb_ary_hash_values(long len, const VALUE *elements);
VALUE rb_ary_last(int, const VALUE *, VALUE);
void rb_ary_set_len(VALUE, long);
void rb_ary_delete_same(VALUE, VALUE);
diff --git a/internal/basic_operators.h b/internal/basic_operators.h
index 12a0475990..a59403631e 100644
--- a/internal/basic_operators.h
+++ b/internal/basic_operators.h
@@ -31,6 +31,7 @@ enum ruby_basic_operators {
BOP_UMINUS,
BOP_MAX,
BOP_MIN,
+ BOP_HASH,
BOP_CALL,
BOP_AND,
BOP_OR,
diff --git a/vm.c b/vm.c
index bc007e21e5..b3fce8d4fb 100644
--- a/vm.c
+++ b/vm.c
@@ -2051,6 +2051,7 @@ vm_init_redefined_flag(void)
OP(UMinus, UMINUS), (C(String));
OP(Max, MAX), (C(Array));
OP(Min, MIN), (C(Array));
+ OP(Hash, HASH), (C(Array));
OP(Call, CALL), (C(Proc));
OP(And, AND), (C(Integer));
OP(Or, OR), (C(Integer));
diff --git a/vm_insnhelper.c b/vm_insnhelper.c
index f4894225d7..c9805377ff 100644
--- a/vm_insnhelper.c
+++ b/vm_insnhelper.c
@@ -5377,6 +5377,18 @@ rb_vm_opt_newarray_min(rb_execution_context_t *ec, rb_num_t num, const VALUE *pt
return vm_opt_newarray_min(ec, num, ptr);
}
+static VALUE
+vm_opt_newarray_hash(rb_execution_context_t *ec, rb_num_t num, const VALUE *ptr)
+{
+ // If Array#hash is _not_ monkeypatched, use the optimized call
+ if (BASIC_OP_UNREDEFINED_P(BOP_HASH, ARRAY_REDEFINED_OP_FLAG)) {
+ return rb_ary_hash_values(num, ptr);
+ }
+ else {
+ return rb_vm_call_with_refinements(ec, rb_ary_new4(num, ptr), idHash, 0, NULL, RB_NO_KEYWORDS);
+ }
+}
+
#undef id_cmp
#define IMEMO_CONST_CACHE_SHAREABLE IMEMO_FL_USER0