From ce1c768b63390166d39282ffc0b9f085ba64c222 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 6 Jun 2022 17:27:56 -0700 Subject: [PATCH] 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: #@-e:1 (1,0)-(1,12)> (catch: FALSE) 0000 getinstancevariable :@a, ( 1)[Li] 0003 getinstancevariable :@b, 0006 opt_newarray_send 2, :max 0009 leave $ ./miniruby --dump=insns -e '[@a, @b].min' == disasm: #@-e:1 (1,0)-(1,12)> (catch: FALSE) 0000 getinstancevariable :@a, ( 1)[Li] 0003 getinstancevariable :@b, 0006 opt_newarray_send 2, :min 0009 leave $ ./miniruby --dump=insns -e '[@a, @b].hash' == disasm: #@-e:1 (1,0)-(1,13)> (catch: FALSE) 0000 getinstancevariable :@a, ( 1)[Li] 0003 getinstancevariable :@b, 0006 opt_newarray_send 2, :hash 0009 leave ``` Co-authored-by: John Hawthorn --- array.c | 30 ++++++++++++++++++------------ bootstraptest/test_insns.rb | 14 ++++++++------ compile.c | 17 +++++++++++------ defs/id.def | 1 + insns.def | 31 ++++++++++++++++--------------- internal/array.h | 1 + vm.c | 1 + vm_core.h | 1 + vm_insnhelper.c | 12 ++++++++++++ 9 files changed, 69 insertions(+), 39 deletions(-) diff --git a/array.c b/array.c index 7b3f5bd0b0a..e714652520c 100644 --- a/array.c +++ b/array.c @@ -5306,6 +5306,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 integer @@ -5322,18 +5339,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; iinsn_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; + 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; + } } } } diff --git a/defs/id.def b/defs/id.def index 097e34e4056..1ce67100949 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 b5dea9c10c7..87165a7ad09 100644 --- a/insns.def +++ b/insns.def @@ -841,8 +841,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 @@ -851,20 +851,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; +// attr rb_snum_t comptime_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)); + 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 60f66f31bfb..acd286941d5 100644 --- a/internal/array.h +++ b/internal/array.h @@ -21,6 +21,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/vm.c b/vm.c index c2dda3fdc4b..659fd223d85 100644 --- a/vm.c +++ b/vm.c @@ -1969,6 +1969,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_core.h b/vm_core.h index b7d980e0ec8..19959cb853b 100644 --- a/vm_core.h +++ b/vm_core.h @@ -586,6 +586,7 @@ enum ruby_basic_operators { BOP_UMINUS, BOP_MAX, BOP_MIN, + BOP_HASH, BOP_CALL, BOP_AND, BOP_OR, diff --git a/vm_insnhelper.c b/vm_insnhelper.c index abc8aef0536..e8f2b80b711 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -4932,6 +4932,18 @@ vm_opt_newarray_min(rb_execution_context_t *ec, rb_num_t num, const VALUE *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 -- 2.36.0