From b3457654ee196f752c7c936b426a99f6888b1608 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Wed, 31 May 2023 11:01:20 -0400 Subject: [PATCH] Support opt_newarray_send --- lib/syntax_tree/node.rb | 11 +- lib/syntax_tree/yarv/instruction_sequence.rb | 25 ++++- lib/syntax_tree/yarv/instructions.rb | 82 ++++----------- lib/syntax_tree/yarv/legacy.rb | 104 +++++++++++++++++++ test/compiler_test.rb | 6 ++ 5 files changed, 157 insertions(+), 71 deletions(-) diff --git a/lib/syntax_tree/node.rb b/lib/syntax_tree/node.rb index 54d132e6..ac6092ca 100644 --- a/lib/syntax_tree/node.rb +++ b/lib/syntax_tree/node.rb @@ -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 @@ -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 @@ -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 @@ -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 diff --git a/lib/syntax_tree/yarv/instruction_sequence.rb b/lib/syntax_tree/yarv/instruction_sequence.rb index 7ce7bcdd..df92799b 100644 --- a/lib/syntax_tree/yarv/instruction_sequence.rb +++ b/lib/syntax_tree/yarv/instruction_sequence.rb @@ -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 @@ -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])) diff --git a/lib/syntax_tree/yarv/instructions.rb b/lib/syntax_tree/yarv/instructions.rb index ceb237dc..ffeebe65 100644 --- a/lib/syntax_tree/yarv/instructions.rb +++ b/lib/syntax_tree/yarv/instructions.rb @@ -3818,9 +3818,10 @@ 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 # @@ -3828,83 +3829,36 @@ def call(vm) # [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 @@ -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 diff --git a/lib/syntax_tree/yarv/legacy.rb b/lib/syntax_tree/yarv/legacy.rb index e20729d9..8715993a 100644 --- a/lib/syntax_tree/yarv/legacy.rb +++ b/lib/syntax_tree/yarv/legacy.rb @@ -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 diff --git a/test/compiler_test.rb b/test/compiler_test.rb index 1922f8c6..ca3e8dde 100644 --- a/test/compiler_test.rb +++ b/test/compiler_test.rb @@ -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",