Project

General

Profile

Actions

Feature #18897

closed

Add a new instruction for sending messages to ephemeral stack arrays

Added by tenderlovemaking (Aaron Patterson) over 2 years ago. Updated over 1 year ago.

Status:
Closed
Assignee:
-
Target version:
-
[ruby-core:109147]

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.

Actions #3

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

Actions

Also available in: Atom PDF

Like0
Like0Like0Like0