mirror of
https://github.com/ruby/ruby.git
synced 2025-08-26 06:25:31 +02:00
Optimize instructions when creating an array just to call include?
(#12123)
* Add opt_duparray_send insn to skip the allocation on `#include?` If the method isn't going to modify the array we don't need to copy it. This avoids the allocation / array copy for things like `[:a, :b].include?(x)`. This adds a BOP for include? and tracks redefinition for it on Array. Co-authored-by: Andrew Novoselac <andrew.novoselac@shopify.com> * YJIT: Implement opt_duparray_send include_p Co-authored-by: Andrew Novoselac <andrew.novoselac@shopify.com> * Update opt_newarray_send to support simple forms of include?(arg) Similar to opt_duparray_send but for non-static arrays. * YJIT: Implement opt_newarray_send include_p --------- Co-authored-by: Andrew Novoselac <andrew.novoselac@shopify.com>
This commit is contained in:
parent
c1dcd1d496
commit
1dd40ec18a
Notes:
git
2024-11-26 19:31:33 +00:00
Merged-By: maximecb <maximecb@ruby-lang.org>
14 changed files with 573 additions and 156 deletions
|
@ -1114,6 +1114,33 @@ class TestArray < Test::Unit::TestCase
|
|||
assert_not_include(a, [1,2])
|
||||
end
|
||||
|
||||
def test_monkey_patch_include?
|
||||
assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}", timeout: 30)
|
||||
begin;
|
||||
$-w = false
|
||||
class Array
|
||||
alias :old_include? :include?
|
||||
def include? x
|
||||
return true if x == :always
|
||||
old_include?(x)
|
||||
end
|
||||
end
|
||||
def test
|
||||
a, c, always = :a, :c, :always
|
||||
[
|
||||
[:a, :b].include?(a),
|
||||
[:a, :b].include?(c),
|
||||
[:a, :b].include?(always),
|
||||
]
|
||||
end
|
||||
v = test
|
||||
class Array
|
||||
alias :include? :old_include?
|
||||
end
|
||||
assert_equal [true, false, true], v
|
||||
end;
|
||||
end
|
||||
|
||||
def test_intersect?
|
||||
a = @cls[ 1, 2, 3]
|
||||
assert_send([a, :intersect?, [3]])
|
||||
|
|
|
@ -1107,4 +1107,113 @@ class TestRubyOptimization < Test::Unit::TestCase
|
|||
def o.to_s; 1; end
|
||||
assert_match %r{\A#<Object:0x[0-9a-f]+>\z}, "#{o}"
|
||||
end
|
||||
|
||||
def test_opt_duparray_send_include_p
|
||||
[
|
||||
'x = :b; [:a, :b].include?(x)',
|
||||
'@c = :b; [:a, :b].include?(@c)',
|
||||
'@c = "b"; %i[a b].include?(@c.to_sym)',
|
||||
'[:a, :b].include?(self) == false',
|
||||
].each do |code|
|
||||
iseq = RubyVM::InstructionSequence.compile(code)
|
||||
insn = iseq.disasm
|
||||
assert_match(/opt_duparray_send/, insn)
|
||||
assert_no_match(/\bduparray\b/, insn)
|
||||
assert_equal(true, eval(code))
|
||||
end
|
||||
|
||||
x, y = :b, :c
|
||||
assert_equal(true, [:a, :b].include?(x))
|
||||
assert_equal(false, [:a, :b].include?(y))
|
||||
|
||||
assert_in_out_err([], <<~RUBY, ["1,2", "3,3", "1,2", "4,4"])
|
||||
class Array
|
||||
prepend(Module.new do
|
||||
def include?(i)
|
||||
puts self.join(",")
|
||||
# Modify self to prove that we are operating on a copy.
|
||||
map! { i }
|
||||
puts self.join(",")
|
||||
end
|
||||
end)
|
||||
end
|
||||
def x(i)
|
||||
[1, 2].include?(i)
|
||||
end
|
||||
x(3)
|
||||
x(4)
|
||||
RUBY
|
||||
|
||||
# Ensure raises happen correctly.
|
||||
assert_in_out_err([], <<~RUBY, ["will raise", "int 1 not 3"])
|
||||
class Integer
|
||||
undef_method :==
|
||||
def == x
|
||||
raise "int \#{self} not \#{x}"
|
||||
end
|
||||
end
|
||||
x = 3
|
||||
puts "will raise"
|
||||
begin
|
||||
p [1, 2].include?(x)
|
||||
rescue
|
||||
puts $!
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
|
||||
def test_opt_newarray_send_include_p
|
||||
[
|
||||
'b = :b; [:a, b].include?(:b)',
|
||||
# Use Object.new to ensure that we get newarray rather than duparray.
|
||||
'value = 1; [Object.new, true, "true", 1].include?(value)',
|
||||
'value = 1; [Object.new, "1"].include?(value.to_s)',
|
||||
'[Object.new, "1"].include?(self) == false',
|
||||
].each do |code|
|
||||
iseq = RubyVM::InstructionSequence.compile(code)
|
||||
insn = iseq.disasm
|
||||
assert_match(/opt_newarray_send/, insn)
|
||||
assert_no_match(/\bnewarray\b/, insn)
|
||||
assert_equal(true, eval(code))
|
||||
end
|
||||
|
||||
x, y = :b, :c
|
||||
assert_equal(true, [:a, x].include?(x))
|
||||
assert_equal(false, [:a, x].include?(y))
|
||||
|
||||
assert_in_out_err([], <<~RUBY, ["1,3", "3,3", "1,4", "4,4"])
|
||||
class Array
|
||||
prepend(Module.new do
|
||||
def include?(i)
|
||||
puts self.join(",")
|
||||
# Modify self to prove that we are operating on a copy.
|
||||
map! { i }
|
||||
puts self.join(",")
|
||||
end
|
||||
end)
|
||||
end
|
||||
def x(i)
|
||||
[1, i].include?(i)
|
||||
end
|
||||
x(3)
|
||||
x(4)
|
||||
RUBY
|
||||
|
||||
# Ensure raises happen correctly.
|
||||
assert_in_out_err([], <<~RUBY, ["will raise", "int 1 not 3"])
|
||||
class Integer
|
||||
undef_method :==
|
||||
def == x
|
||||
raise "int \#{self} not \#{x}"
|
||||
end
|
||||
end
|
||||
x = 3
|
||||
puts "will raise"
|
||||
begin
|
||||
p [1, x].include?(x)
|
||||
rescue
|
||||
puts $!
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue