diff options
author | Takashi Kokubun <[email protected]> | 2025-03-07 12:55:47 -0800 |
---|---|---|
committer | Takashi Kokubun <[email protected]> | 2025-04-18 21:52:59 +0900 |
commit | 33a052486baa54ab858bd0a06033e90a3c66d2ac (patch) | |
tree | d2f0602755e3e1df114be2c882dc4071128d362d /test | |
parent | d2115562b92e70bea71cfaca175f59587c6a77da (diff) |
Assert everything is compiled in test_zjit (https://github.com/Shopify/zjit/pull/40)
* Assert everything is compiled in test_zjit
* Update a comment on rb_zjit_assert_compiles
Co-authored-by: Maxime Chevalier-Boisvert <[email protected]>
* Add a comment about assert_compiles
* Actually use pipe_fd
---------
Co-authored-by: Maxime Chevalier-Boisvert <[email protected]>
Notes
Notes:
Merged: https://github.com/ruby/ruby/pull/13131
Diffstat (limited to 'test')
-rw-r--r-- | test/lib/jit_support.rb | 6 | ||||
-rw-r--r-- | test/ruby/test_zjit.rb | 117 |
2 files changed, 123 insertions, 0 deletions
diff --git a/test/lib/jit_support.rb b/test/lib/jit_support.rb index 1b15f685a0..79fdbcce48 100644 --- a/test/lib/jit_support.rb +++ b/test/lib/jit_support.rb @@ -16,4 +16,10 @@ module JITSupport def yjit_force_enabled? "#{RbConfig::CONFIG['CFLAGS']} #{RbConfig::CONFIG['CPPFLAGS']}".match?(/(\A|\s)-D ?YJIT_FORCE_ENABLE\b/) end + + def zjit_supported? + return @zjit_supported if defined?(@zjit_supported) + # nil in mswin + @zjit_supported = ![nil, 'no'].include?(RbConfig::CONFIG['ZJIT_SUPPORT']) + end end diff --git a/test/ruby/test_zjit.rb b/test/ruby/test_zjit.rb new file mode 100644 index 0000000000..7365aa9e57 --- /dev/null +++ b/test/ruby/test_zjit.rb @@ -0,0 +1,117 @@ +# frozen_string_literal: true +# +# This set of tests can be run with: +# make test-all TESTS=test/ruby/test_zjit.rb + +require 'test/unit' +require 'envutil' +require_relative '../lib/jit_support' +return unless JITSupport.zjit_supported? + +class TestZJIT < Test::Unit::TestCase + def test_nil + assert_compiles nil, %q{ + def test = nil + test + } + end + + def test_putobject + assert_compiles 1, %q{ + def test = 1 + test + } + end + + def test_opt_plus_const + assert_compiles 3, %q{ + def test = 1 + 2 + test # profile opt_plus + test + }, call_threshold: 2 + end + + def test_opt_plus_fixnum + assert_compiles 3, %q{ + def test(a, b) = a + b + test(0, 1) # profile opt_plus + test(1, 2) + }, call_threshold: 2 + end + + def test_opt_plus_chain + assert_compiles 6, %q{ + def test(a, b, c) = a + b + c + test(0, 1, 2) # profile opt_plus + test(1, 2, 3) + }, call_threshold: 2 + end + + # Test argument ordering + def test_opt_minus + omit 'FixnumSub is not implemented yet' + assert_compiles 2, %q{ + def test(a, b) = a - b + test(2, 1) # profile opt_minus + test(6, 4) + }, call_threshold: 2 + end + + private + + # Assert that every method call in `test_script` can be compiled by ZJIT + # at a given call_threshold + def assert_compiles(expected, test_script, call_threshold: 1) + pipe_fd = 3 + + script = <<~RUBY + _test_proc = -> { + RubyVM::ZJIT.assert_compiles + #{test_script} + } + result = _test_proc.call + IO.open(#{pipe_fd}).write(Marshal.dump(result)) + RUBY + + status, out, err, pipe_out = eval_with_jit(script, call_threshold:, pipe_fd:) + + message = "exited with status #{status.to_i}" + message << "\nstdout:\n```\n#{out}```\n" unless out.empty? + message << "\nstderr:\n```\n#{err}```\n" unless err.empty? + assert status.success?, message + + actual = Marshal.load(pipe_out) + assert_equal expected, actual + end + + # Run a Ruby process with ZJIT options and a pipe for writing test results + def eval_with_jit(script, call_threshold: 1, timeout: 1000, pipe_fd:) + args = [ + "--disable-gems", + "--zjit-call-threshold=#{call_threshold}", + ] + args << "-e" << script_shell_encode(script) + pipe_r, pipe_w = IO.pipe + # Separate thread so we don't deadlock when + # the child ruby blocks writing the output to pipe_fd + pipe_out = nil + pipe_reader = Thread.new do + pipe_out = pipe_r.read + pipe_r.close + end + out, err, status = EnvUtil.invoke_ruby(args, '', true, true, rubybin: RbConfig.ruby, timeout: timeout, ios: { pipe_fd => pipe_w }) + pipe_w.close + pipe_reader.join(timeout) + [status, out, err, pipe_out] + ensure + pipe_reader&.kill + pipe_reader&.join(timeout) + pipe_r&.close + pipe_w&.close + end + + def script_shell_encode(s) + # We can't pass utf-8-encoded characters directly in a shell arg. But we can use Ruby \u constants. + s.chars.map { |c| c.ascii_only? ? c : "\\u%x" % c.codepoints[0] }.join + end +end |