Feature #18897
closedAdd a new instruction for sending messages to ephemeral stack arrays
Description
This feature is an alternative to Feature #18825. Instead of adding a new instruction specifically for the .hash
method, this patch replaces opt_newarray_max
and opt_newarray_min
with a new instruction opt_newarray_send
. opt_newarray_send
only operates on arrays that will temporarily be on the Ruby stack.
Take the following code for example:
[@a, @b].max
[@a, @b].min
[@a, @b].hash
In all three cases the compiler can detect that we have an array literal pushed on the stack and instead of allocating a new array and calling the corresponding method, we emit the opt_newarray_send
instruction which knows that the contents of the array will temporarily be on the stack (as min
, max
, and hash
will not push the array back on the stack).
If any of these methods are monkeypatched, opt_newarray_send
will allocate a Ruby array and fall back to doing a normal method call.
The benefit of this instruction is that we can avoid allocating a Ruby array and also avoid pushing a stack frame. The downside is that elimination of the frame might be a problem for profilers, but min
and max
already eliminate this frame so maybe it's fine.
The above code will emit the following instructions:
$ cat test.rb
[@a, @b].max
[@a, @b].min
[@a, @b].hash
$ ./miniruby --dump=insns test.rb
== disasm: #<ISeq:<main>@test.rb:1 (1,0)-(3,13)> (catch: FALSE)
0000 getinstancevariable :@a, <is:0> ( 1)[Li]
0003 getinstancevariable :@b, <is:1>
0006 opt_newarray_send 2, :max
0009 pop
0010 getinstancevariable :@a, <is:0> ( 2)[Li]
0013 getinstancevariable :@b, <is:1>
0016 opt_newarray_send 2, :min
0019 pop
0020 getinstancevariable :@a, <is:0> ( 3)[Li]
0023 getinstancevariable :@b, <is:1>
0026 opt_newarray_send 2, :hash
0029 leave
Files
Updated by tenderlovemaking (Aaron Patterson) over 2 years ago
Just to be complete with regard to stack frames, max
doesn't show up in the stack trace in the following code even though <=>
is called via .max
:
class Foo
attr_reader :x
def initialize x
@x = x
end
def <=> other
puts caller
x <=> other.x
end
end
def check_max
[Foo.new(1), Foo.new(2)].max
end
check_max
We could probably detect that the <=>
method is implemented in "user" code and push a frame, but it seems like nobody notices (I can't find any bug reports). 😅
Updated by mame (Yusuke Endoh) over 2 years ago
@tenderlovemaking (Aaron Patterson) Thank you! Your patch looks good to me.
Updated by tenderlovemaking (Aaron Patterson) over 1 year ago
- Status changed from Open to Closed
Applied in changeset git|c5fc1ce975ecdf1c6818714e47579c5d3531c4ca.
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