Skip to content

Support opt_newarray_send #378

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions lib/syntax_tree/node.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1299,7 +1299,7 @@ def format(q)
end
end

# [nil | VarRef] the optional constant wrapper
# [nil | VarRef | ConstPathRef] the optional constant wrapper
attr_reader :constant

# [Array[ Node ]] the regular positional arguments that this array
Expand Down Expand Up @@ -2849,7 +2849,10 @@ def format_chain(q, children)
# to print the operator trailing in order to keep it working.
last_child = children.last
if last_child.is_a?(CallNode) && last_child.message != :call &&
last_child.message.comments.any? && last_child.operator
(
(last_child.message.comments.any? && last_child.operator) ||
(last_child.operator && last_child.operator.comments.any?)
)
q.format(CallOperatorFormatter.new(last_child.operator))
skip_operator = true
else
Expand Down Expand Up @@ -5413,7 +5416,7 @@ def ===(other)
# end
#
class FndPtn < Node
# [nil | Node] the optional constant wrapper
# [nil | VarRef | ConstPathRef] the optional constant wrapper
attr_reader :constant

# [VarField] the splat on the left-hand side
Expand Down Expand Up @@ -6035,7 +6038,7 @@ def format(q)
end
end

# [nil | Node] the optional constant wrapper
# [nil | VarRef | ConstPathRef] the optional constant wrapper
attr_reader :constant

# [Array[ [DynaSymbol | Label, nil | Node] ]] the set of tuples
Expand Down
25 changes: 22 additions & 3 deletions lib/syntax_tree/yarv/instruction_sequence.rb
Original file line number Diff line number Diff line change
Expand Up @@ -353,11 +353,27 @@ def specialize_instructions!
next unless calldata.argc == 0

case calldata.method
when :min
node.value =
if RUBY_VERSION < "3.3"
Legacy::OptNewArrayMin.new(value.number)
else
OptNewArraySend.new(value.number, :min)
end

node.next_node = next_node.next_node
when :max
node.value = OptNewArrayMax.new(value.number)
node.value =
if RUBY_VERSION < "3.3"
Legacy::OptNewArrayMax.new(value.number)
else
OptNewArraySend.new(value.number, :max)
end

node.next_node = next_node.next_node
when :min
node.value = OptNewArrayMin.new(value.number)
when :hash
next if RUBY_VERSION < "3.3"
node.value = OptNewArraySend.new(value.number, :hash)
node.next_node = next_node.next_node
end
when PutObject, PutString
Expand Down Expand Up @@ -1174,6 +1190,9 @@ def self.from(source, options = Compiler::Options.new, parent_iseq = nil)
when :opt_newarray_min
iseq.newarray(opnds[0])
iseq.send(YARV.calldata(:min))
when :opt_newarray_send
iseq.newarray(opnds[0])
iseq.send(CallData.new(opnds[1]))
when :opt_neq
iseq.push(
OptNEq.new(CallData.from(opnds[0]), CallData.from(opnds[1]))
Expand Down
82 changes: 18 additions & 64 deletions lib/syntax_tree/yarv/instructions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3818,93 +3818,47 @@ def call(vm)

# ### Summary
#
# `opt_newarray_max` is a specialization that occurs when the `max` method
# is called on an array literal. It pops the values of the array off the
# stack and pushes on the result.
# `opt_newarray_send` is a specialization that occurs when a dynamic array
# literal is created and immediately sent the `min`, `max`, or `hash`
# methods. It pops the values of the array off the stack and pushes on the
# result of the method call.
#
# ### Usage
#
# ~~~ruby
# [a, b, c].max
# ~~~
#
class OptNewArrayMax < Instruction
attr_reader :number

def initialize(number)
@number = number
end

def disasm(fmt)
fmt.instruction("opt_newarray_max", [fmt.object(number)])
end

def to_a(_iseq)
[:opt_newarray_max, number]
end

def deconstruct_keys(_keys)
{ number: number }
end

def ==(other)
other.is_a?(OptNewArrayMax) && other.number == number
end

def length
2
end

def pops
number
end
class OptNewArraySend < Instruction
attr_reader :number, :method

def pushes
1
end

def call(vm)
vm.push(vm.pop(number).max)
end
end

# ### Summary
#
# `opt_newarray_min` is a specialization that occurs when the `min` method
# is called on an array literal. It pops the values of the array off the
# stack and pushes on the result.
#
# ### Usage
#
# ~~~ruby
# [a, b, c].min
# ~~~
#
class OptNewArrayMin < Instruction
attr_reader :number

def initialize(number)
def initialize(number, method)
@number = number
@method = method
end

def disasm(fmt)
fmt.instruction("opt_newarray_min", [fmt.object(number)])
fmt.instruction(
"opt_newarray_send",
[fmt.object(number), fmt.object(method)]
)
end

def to_a(_iseq)
[:opt_newarray_min, number]
[:opt_newarray_send, number, method]
end

def deconstruct_keys(_keys)
{ number: number }
{ number: number, method: method }
end

def ==(other)
other.is_a?(OptNewArrayMin) && other.number == number
other.is_a?(OptNewArraySend) && other.number == number &&
other.method == method
end

def length
2
3
end

def pops
Expand All @@ -3916,7 +3870,7 @@ def pushes
end

def call(vm)
vm.push(vm.pop(number).min)
vm.push(vm.pop(number).__send__(method))
end
end

Expand Down
104 changes: 104 additions & 0 deletions lib/syntax_tree/yarv/legacy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,110 @@ def falls_through?
end
end

# ### Summary
#
# `opt_newarray_max` is a specialization that occurs when the `max` method
# is called on an array literal. It pops the values of the array off the
# stack and pushes on the result.
#
# ### Usage
#
# ~~~ruby
# [a, b, c].max
# ~~~
#
class OptNewArrayMax < Instruction
attr_reader :number

def initialize(number)
@number = number
end

def disasm(fmt)
fmt.instruction("opt_newarray_max", [fmt.object(number)])
end

def to_a(_iseq)
[:opt_newarray_max, number]
end

def deconstruct_keys(_keys)
{ number: number }
end

def ==(other)
other.is_a?(OptNewArrayMax) && other.number == number
end

def length
2
end

def pops
number
end

def pushes
1
end

def call(vm)
vm.push(vm.pop(number).max)
end
end

# ### Summary
#
# `opt_newarray_min` is a specialization that occurs when the `min` method
# is called on an array literal. It pops the values of the array off the
# stack and pushes on the result.
#
# ### Usage
#
# ~~~ruby
# [a, b, c].min
# ~~~
#
class OptNewArrayMin < Instruction
attr_reader :number

def initialize(number)
@number = number
end

def disasm(fmt)
fmt.instruction("opt_newarray_min", [fmt.object(number)])
end

def to_a(_iseq)
[:opt_newarray_min, number]
end

def deconstruct_keys(_keys)
{ number: number }
end

def ==(other)
other.is_a?(OptNewArrayMin) && other.number == number
end

def length
2
end

def pops
number
end

def pushes
1
end

def call(vm)
vm.push(vm.pop(number).min)
end
end

# ### Summary
#
# `opt_setinlinecache` sets an inline cache for a constant lookup. It pops
Expand Down
6 changes: 6 additions & 0 deletions test/compiler_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,12 @@ class CompilerTest < Minitest::Test
"[1, 2, 3].min",
"[foo, bar, baz].min",
"[foo, bar, baz].min(1)",
"[1, 2, 3].hash",
"[foo, bar, baz].hash",
"[foo, bar, baz].hash(1)",
"[1, 2, 3].foo",
"[foo, bar, baz].foo",
"[foo, bar, baz].foo(1)",
"[**{ x: true }][0][:x]",
# Core method calls
"alias foo bar",
Expand Down