Feature #18897


Add a new instruction for sending messages to ephemeral stack arrays

Added by tenderlovemaking (Aaron Patterson) about 1 month ago. Updated 21 days ago.

Target version:


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


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 .max:

class Foo
  attr_reader :x

  def initialize x
    @x = x

  def <=> other
    puts caller
    x <=> other.x

def 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) 21 days ago

@tenderlovemaking (Aaron Patterson) Thank you! Your patch looks good to me.


Also available in: Atom PDF