diff options
author | Aaron Patterson <[email protected]> | 2022-06-06 17:27:56 -0700 |
---|---|---|
committer | Aaron Patterson <[email protected]> | 2023-04-18 17:16:22 -0700 |
commit | c5fc1ce975ecdf1c6818714e47579c5d3531c4ca (patch) | |
tree | 77a0f348d00a99281f826dac90e1d288c3139051 | |
parent | 3016f30c956413268655dcb25dbe5041684f9528 (diff) |
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 <[email protected]>
Notes
Notes:
Merged: https://github.com/ruby/ruby/pull/6090
-rw-r--r-- | array.c | 30 | ||||
-rw-r--r-- | bootstraptest/test_insns.rb | 14 | ||||
-rw-r--r-- | compile.c | 29 | ||||
-rw-r--r-- | defs/id.def | 1 | ||||
-rw-r--r-- | insns.def | 33 | ||||
-rw-r--r-- | internal/array.h | 1 | ||||
-rw-r--r-- | internal/basic_operators.h | 1 | ||||
-rw-r--r-- | vm.c | 1 | ||||
-rw-r--r-- | vm_insnhelper.c | 12 |
9 files changed, 76 insertions, 46 deletions
@@ -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 @@ -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 @@ -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, @@ -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 |