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>
This commit is contained in:
parent
3016f30c95
commit
c5fc1ce975
Notes:
git
2023-04-19 00:16:56 +00:00
30
array.c
30
array.c
@ -5350,6 +5350,23 @@ rb_ary_eql(VALUE ary1, VALUE ary2)
|
|||||||
return rb_exec_recursive_paired(recursive_eql, ary1, ary2, 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:
|
* call-seq:
|
||||||
* array.hash -> integer
|
* array.hash -> integer
|
||||||
@ -5366,18 +5383,7 @@ rb_ary_eql(VALUE ary1, VALUE ary2)
|
|||||||
static VALUE
|
static VALUE
|
||||||
rb_ary_hash(VALUE ary)
|
rb_ary_hash(VALUE ary)
|
||||||
{
|
{
|
||||||
long i;
|
return rb_ary_hash_values(RARRAY_LEN(ary), RARRAY_CONST_PTR(ary));
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -214,9 +214,11 @@ tests = [
|
|||||||
'true'.freeze
|
'true'.freeze
|
||||||
},
|
},
|
||||||
|
|
||||||
[ 'opt_newarray_max', %q{ [ ].max.nil? }, ],
|
[ 'opt_newarray_send', %q{ ![ ].hash.nil? }, ],
|
||||||
[ 'opt_newarray_max', %q{ [1, x = 2, 3].max == 3 }, ],
|
|
||||||
[ 'opt_newarray_max', <<-'},', ], # {
|
[ 'opt_newarray_send', %q{ [ ].max.nil? }, ],
|
||||||
|
[ 'opt_newarray_send', %q{ [1, x = 2, 3].max == 3 }, ],
|
||||||
|
[ 'opt_newarray_send', <<-'},', ], # {
|
||||||
class Array
|
class Array
|
||||||
def max
|
def max
|
||||||
true
|
true
|
||||||
@ -224,9 +226,9 @@ tests = [
|
|||||||
end
|
end
|
||||||
[1, x = 2, 3].max
|
[1, x = 2, 3].max
|
||||||
},
|
},
|
||||||
[ 'opt_newarray_min', %q{ [ ].min.nil? }, ],
|
[ 'opt_newarray_send', %q{ [ ].min.nil? }, ],
|
||||||
[ 'opt_newarray_min', %q{ [3, x = 2, 1].min == 1 }, ],
|
[ 'opt_newarray_send', %q{ [3, x = 2, 1].min == 1 }, ],
|
||||||
[ 'opt_newarray_min', <<-'},', ], # {
|
[ 'opt_newarray_send', <<-'},', ], # {
|
||||||
class Array
|
class Array
|
||||||
def min
|
def min
|
||||||
true
|
true
|
||||||
|
13
compile.c
13
compile.c
@ -3708,17 +3708,22 @@ iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj)
|
|||||||
if ((vm_ci_flag(ci) & VM_CALL_ARGS_SIMPLE) && vm_ci_argc(ci) == 0) {
|
if ((vm_ci_flag(ci) & VM_CALL_ARGS_SIMPLE) && vm_ci_argc(ci) == 0) {
|
||||||
switch (vm_ci_mid(ci)) {
|
switch (vm_ci_mid(ci)) {
|
||||||
case idMax:
|
case idMax:
|
||||||
iobj->insn_id = BIN(opt_newarray_max);
|
|
||||||
ELEM_REMOVE(&niobj->link);
|
|
||||||
return COMPILE_OK;
|
|
||||||
case idMin:
|
case idMin:
|
||||||
iobj->insn_id = BIN(opt_newarray_min);
|
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);
|
ELEM_REMOVE(&niobj->link);
|
||||||
return COMPILE_OK;
|
return COMPILE_OK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (IS_INSN_ID(iobj, send)) {
|
if (IS_INSN_ID(iobj, send)) {
|
||||||
const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(iobj, 0);
|
const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(iobj, 0);
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
firstline, predefined = __LINE__+1, %[\
|
firstline, predefined = __LINE__+1, %[\
|
||||||
max
|
max
|
||||||
min
|
min
|
||||||
|
hash
|
||||||
freeze
|
freeze
|
||||||
nil?
|
nil?
|
||||||
inspect
|
inspect
|
||||||
|
29
insns.def
29
insns.def
@ -899,8 +899,8 @@ opt_str_uminus
|
|||||||
}
|
}
|
||||||
|
|
||||||
DEFINE_INSN
|
DEFINE_INSN
|
||||||
opt_newarray_max
|
opt_newarray_send
|
||||||
(rb_num_t num)
|
(rb_num_t num, ID method)
|
||||||
(...)
|
(...)
|
||||||
(VALUE val)
|
(VALUE val)
|
||||||
/* This instruction typically has no funcalls. But it compares array
|
/* This instruction typically has no funcalls. But it compares array
|
||||||
@ -909,20 +909,21 @@ opt_newarray_max
|
|||||||
* cannot but mark it being not leaf. */
|
* cannot but mark it being not leaf. */
|
||||||
// attr bool leaf = false; /* has rb_funcall() */
|
// attr bool leaf = false; /* has rb_funcall() */
|
||||||
// attr rb_snum_t sp_inc = 1 - (rb_snum_t)num;
|
// 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));
|
switch(method) {
|
||||||
}
|
case idHash:
|
||||||
|
val = vm_opt_newarray_hash(ec, num, STACK_ADDR_FROM_TOP(num));
|
||||||
DEFINE_INSN
|
break;
|
||||||
opt_newarray_min
|
case idMin:
|
||||||
(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));
|
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 */
|
/* super(args) # args.size => num */
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#define RARRAY_PTR_IN_USE_FLAG FL_USER14
|
#define RARRAY_PTR_IN_USE_FLAG FL_USER14
|
||||||
|
|
||||||
/* array.c */
|
/* array.c */
|
||||||
|
VALUE rb_ary_hash_values(long len, const VALUE *elements);
|
||||||
VALUE rb_ary_last(int, const VALUE *, VALUE);
|
VALUE rb_ary_last(int, const VALUE *, VALUE);
|
||||||
void rb_ary_set_len(VALUE, long);
|
void rb_ary_set_len(VALUE, long);
|
||||||
void rb_ary_delete_same(VALUE, VALUE);
|
void rb_ary_delete_same(VALUE, VALUE);
|
||||||
|
@ -31,6 +31,7 @@ enum ruby_basic_operators {
|
|||||||
BOP_UMINUS,
|
BOP_UMINUS,
|
||||||
BOP_MAX,
|
BOP_MAX,
|
||||||
BOP_MIN,
|
BOP_MIN,
|
||||||
|
BOP_HASH,
|
||||||
BOP_CALL,
|
BOP_CALL,
|
||||||
BOP_AND,
|
BOP_AND,
|
||||||
BOP_OR,
|
BOP_OR,
|
||||||
|
1
vm.c
1
vm.c
@ -2051,6 +2051,7 @@ vm_init_redefined_flag(void)
|
|||||||
OP(UMinus, UMINUS), (C(String));
|
OP(UMinus, UMINUS), (C(String));
|
||||||
OP(Max, MAX), (C(Array));
|
OP(Max, MAX), (C(Array));
|
||||||
OP(Min, MIN), (C(Array));
|
OP(Min, MIN), (C(Array));
|
||||||
|
OP(Hash, HASH), (C(Array));
|
||||||
OP(Call, CALL), (C(Proc));
|
OP(Call, CALL), (C(Proc));
|
||||||
OP(And, AND), (C(Integer));
|
OP(And, AND), (C(Integer));
|
||||||
OP(Or, OR), (C(Integer));
|
OP(Or, OR), (C(Integer));
|
||||||
|
@ -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);
|
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
|
#undef id_cmp
|
||||||
|
|
||||||
#define IMEMO_CONST_CACHE_SHAREABLE IMEMO_FL_USER0
|
#define IMEMO_CONST_CACHE_SHAREABLE IMEMO_FL_USER0
|
||||||
|
Loading…
x
Reference in New Issue
Block a user