Add a new instruction for sending messages to ephemeral stack arrays
This feature is an alternative to Feature #18825. Instead of adding a new instruction specifically for the
.hash method, this patch replaces
opt_newarray_min with a new instruction
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
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
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
Updated by tenderlovemaking (Aaron Patterson) about 1 month 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
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). 😅