Skip to content

Commit c5fc1ce

Browse files
tenderlovejhawthorn
andcommitted
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>
1 parent 3016f30 commit c5fc1ce

File tree

9 files changed

+76
-46
lines changed

9 files changed

+76
-46
lines changed

array.c

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5350,6 +5350,23 @@ rb_ary_eql(VALUE ary1, VALUE ary2)
53505350
return rb_exec_recursive_paired(recursive_eql, ary1, ary2, ary2);
53515351
}
53525352

5353+
VALUE
5354+
rb_ary_hash_values(long len, const VALUE *elements)
5355+
{
5356+
long i;
5357+
st_index_t h;
5358+
VALUE n;
5359+
5360+
h = rb_hash_start(len);
5361+
h = rb_hash_uint(h, (st_index_t)rb_ary_hash_values);
5362+
for (i=0; i<len; i++) {
5363+
n = rb_hash(elements[i]);
5364+
h = rb_hash_uint(h, NUM2LONG(n));
5365+
}
5366+
h = rb_hash_end(h);
5367+
return ST2FIX(h);
5368+
}
5369+
53535370
/*
53545371
* call-seq:
53555372
* array.hash -> integer
@@ -5366,18 +5383,7 @@ rb_ary_eql(VALUE ary1, VALUE ary2)
53665383
static VALUE
53675384
rb_ary_hash(VALUE ary)
53685385
{
5369-
long i;
5370-
st_index_t h;
5371-
VALUE n;
5372-
5373-
h = rb_hash_start(RARRAY_LEN(ary));
5374-
h = rb_hash_uint(h, (st_index_t)rb_ary_hash);
5375-
for (i=0; i<RARRAY_LEN(ary); i++) {
5376-
n = rb_hash(RARRAY_AREF(ary, i));
5377-
h = rb_hash_uint(h, NUM2LONG(n));
5378-
}
5379-
h = rb_hash_end(h);
5380-
return ST2FIX(h);
5386+
return rb_ary_hash_values(RARRAY_LEN(ary), RARRAY_CONST_PTR(ary));
53815387
}
53825388

53835389
/*

bootstraptest/test_insns.rb

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -214,19 +214,21 @@ def freeze
214214
'true'.freeze
215215
},
216216

217-
[ 'opt_newarray_max', %q{ [ ].max.nil? }, ],
218-
[ 'opt_newarray_max', %q{ [1, x = 2, 3].max == 3 }, ],
219-
[ 'opt_newarray_max', <<-'},', ], # {
217+
[ 'opt_newarray_send', %q{ ![ ].hash.nil? }, ],
218+
219+
[ 'opt_newarray_send', %q{ [ ].max.nil? }, ],
220+
[ 'opt_newarray_send', %q{ [1, x = 2, 3].max == 3 }, ],
221+
[ 'opt_newarray_send', <<-'},', ], # {
220222
class Array
221223
def max
222224
true
223225
end
224226
end
225227
[1, x = 2, 3].max
226228
},
227-
[ 'opt_newarray_min', %q{ [ ].min.nil? }, ],
228-
[ 'opt_newarray_min', %q{ [3, x = 2, 1].min == 1 }, ],
229-
[ 'opt_newarray_min', <<-'},', ], # {
229+
[ 'opt_newarray_send', %q{ [ ].min.nil? }, ],
230+
[ 'opt_newarray_send', %q{ [3, x = 2, 1].min == 1 }, ],
231+
[ 'opt_newarray_send', <<-'},', ], # {
230232
class Array
231233
def min
232234
true

compile.c

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3706,18 +3706,23 @@ iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj)
37063706
if (IS_INSN_ID(niobj, send)) {
37073707
const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(niobj, 0);
37083708
if ((vm_ci_flag(ci) & VM_CALL_ARGS_SIMPLE) && vm_ci_argc(ci) == 0) {
3709-
switch (vm_ci_mid(ci)) {
3710-
case idMax:
3711-
iobj->insn_id = BIN(opt_newarray_max);
3712-
ELEM_REMOVE(&niobj->link);
3713-
return COMPILE_OK;
3714-
case idMin:
3715-
iobj->insn_id = BIN(opt_newarray_min);
3716-
ELEM_REMOVE(&niobj->link);
3717-
return COMPILE_OK;
3718-
}
3719-
}
3720-
}
3709+
switch (vm_ci_mid(ci)) {
3710+
case idMax:
3711+
case idMin:
3712+
case idHash:
3713+
{
3714+
rb_num_t num = (rb_num_t)iobj->operands[0];
3715+
iobj->insn_id = BIN(opt_newarray_send);
3716+
iobj->operands = compile_data_calloc2(iseq, insn_len(iobj->insn_id) - 1, sizeof(VALUE));
3717+
iobj->operands[0] = (VALUE)num;
3718+
iobj->operands[1] = (VALUE)rb_id2sym(vm_ci_mid(ci));
3719+
iobj->operand_size = insn_len(iobj->insn_id) - 1;
3720+
ELEM_REMOVE(&niobj->link);
3721+
return COMPILE_OK;
3722+
}
3723+
}
3724+
}
3725+
}
37213726
}
37223727

37233728
if (IS_INSN_ID(iobj, send)) {

defs/id.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
firstline, predefined = __LINE__+1, %[\
33
max
44
min
5+
hash
56
freeze
67
nil?
78
inspect

insns.def

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -899,8 +899,8 @@ opt_str_uminus
899899
}
900900

901901
DEFINE_INSN
902-
opt_newarray_max
903-
(rb_num_t num)
902+
opt_newarray_send
903+
(rb_num_t num, ID method)
904904
(...)
905905
(VALUE val)
906906
/* This instruction typically has no funcalls. But it compares array
@@ -909,20 +909,21 @@ opt_newarray_max
909909
* cannot but mark it being not leaf. */
910910
// attr bool leaf = false; /* has rb_funcall() */
911911
// attr rb_snum_t sp_inc = 1 - (rb_snum_t)num;
912-
{
913-
val = vm_opt_newarray_max(ec, num, STACK_ADDR_FROM_TOP(num));
914-
}
915-
916-
DEFINE_INSN
917-
opt_newarray_min
918-
(rb_num_t num)
919-
(...)
920-
(VALUE val)
921-
/* Same discussion as opt_newarray_max. */
922-
// attr bool leaf = false; /* has rb_funcall() */
923-
// attr rb_snum_t sp_inc = 1 - (rb_snum_t)num;
924-
{
925-
val = vm_opt_newarray_min(ec, num, STACK_ADDR_FROM_TOP(num));
912+
// attr rb_snum_t comptime_sp_inc = 1 - (rb_snum_t)num;
913+
{
914+
switch(method) {
915+
case idHash:
916+
val = vm_opt_newarray_hash(ec, num, STACK_ADDR_FROM_TOP(num));
917+
break;
918+
case idMin:
919+
val = vm_opt_newarray_min(ec, num, STACK_ADDR_FROM_TOP(num));
920+
break;
921+
case idMax:
922+
val = vm_opt_newarray_max(ec, num, STACK_ADDR_FROM_TOP(num));
923+
break;
924+
default:
925+
rb_bug("unreachable");
926+
}
926927
}
927928

928929
/* super(args) # args.size => num */

internal/array.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#define RARRAY_PTR_IN_USE_FLAG FL_USER14
2424

2525
/* array.c */
26+
VALUE rb_ary_hash_values(long len, const VALUE *elements);
2627
VALUE rb_ary_last(int, const VALUE *, VALUE);
2728
void rb_ary_set_len(VALUE, long);
2829
void rb_ary_delete_same(VALUE, VALUE);

internal/basic_operators.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ enum ruby_basic_operators {
3131
BOP_UMINUS,
3232
BOP_MAX,
3333
BOP_MIN,
34+
BOP_HASH,
3435
BOP_CALL,
3536
BOP_AND,
3637
BOP_OR,

vm.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2051,6 +2051,7 @@ vm_init_redefined_flag(void)
20512051
OP(UMinus, UMINUS), (C(String));
20522052
OP(Max, MAX), (C(Array));
20532053
OP(Min, MIN), (C(Array));
2054+
OP(Hash, HASH), (C(Array));
20542055
OP(Call, CALL), (C(Proc));
20552056
OP(And, AND), (C(Integer));
20562057
OP(Or, OR), (C(Integer));

vm_insnhelper.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5377,6 +5377,18 @@ rb_vm_opt_newarray_min(rb_execution_context_t *ec, rb_num_t num, const VALUE *pt
53775377
return vm_opt_newarray_min(ec, num, ptr);
53785378
}
53795379

5380+
static VALUE
5381+
vm_opt_newarray_hash(rb_execution_context_t *ec, rb_num_t num, const VALUE *ptr)
5382+
{
5383+
// If Array#hash is _not_ monkeypatched, use the optimized call
5384+
if (BASIC_OP_UNREDEFINED_P(BOP_HASH, ARRAY_REDEFINED_OP_FLAG)) {
5385+
return rb_ary_hash_values(num, ptr);
5386+
}
5387+
else {
5388+
return rb_vm_call_with_refinements(ec, rb_ary_new4(num, ptr), idHash, 0, NULL, RB_NO_KEYWORDS);
5389+
}
5390+
}
5391+
53805392
#undef id_cmp
53815393

53825394
#define IMEMO_CONST_CACHE_SHAREABLE IMEMO_FL_USER0

0 commit comments

Comments
 (0)