summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--test/ruby/test_jit.rb462
1 files changed, 270 insertions, 192 deletions
diff --git a/test/ruby/test_jit.rb b/test/ruby/test_jit.rb
index d0a3323dc5..dadb8c3afd 100644
--- a/test/ruby/test_jit.rb
+++ b/test/ruby/test_jit.rb
@@ -1,8 +1,7 @@
# frozen_string_literal: true
require 'test/unit'
-# Test for --jit option
-class TestJIT < Test::Unit::TestCase
+module TestJITSupport
JIT_TIMEOUT = 600 # 10min for each...
JIT_SUCCESS_PREFIX = 'JIT success \(\d+\.\dms\)'
SUPPORTED_COMPILERS = [
@@ -10,24 +9,55 @@ class TestJIT < Test::Unit::TestCase
'clang',
]
+ module_function
+ def eval_with_jit(script, verbose: 0, min_calls: 5, timeout: JIT_TIMEOUT)
+ EnvUtil.invoke_ruby(
+ ['--disable-gems', '--jit-wait', "--jit-verbose=#{verbose}", "--jit-min-calls=#{min_calls}", '-e', script],
+ '', true, true, timeout: timeout,
+ )
+ end
+
+ def supported?
+ # Experimental. If you want to ensure JIT is working with this test, please set this for now.
+ if ENV.key?('RUBY_FORCE_TEST_JIT')
+ return true
+ end
+
+ # Very pessimistic check. With this check, we can't ensure JIT is working.
+ begin
+ _, err = TestJITSupport.eval_with_jit('proc {}.call', verbose: 1, min_calls: 1, timeout: 10)
+ rescue Timeout::Error
+ $stderr.puts "TestJIT: #jit_supported? check timed out"
+ false
+ else
+ err.match?(JIT_SUCCESS_PREFIX)
+ end
+ end
+end
+
+return unless TestJITSupport.supported?
+
+# Test for --jit option
+class TestJIT < Test::Unit::TestCase
+ include TestJITSupport
# Ensure all supported insns can be compiled. Only basic tests are included.
# TODO: ensure --dump=insns includes the expected insn
- def test_compile_insns
- skip unless jit_supported?
- # nop
+ def test_compile_insn_nop
assert_compile_once('nil rescue true', result_inspect: 'nil')
+ end
- # getlocal
- # setlocal
- assert_compile_once(<<~RUBY, result_inspect: '1')
+ def test_compile_insn_local
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: '1')
+ begin;
foo = 1
foo
- RUBY
+ end;
+ end
- # getblockparam
- # setblockparam
- assert_eval_with_jit(<<~RUBY, stdout: '3', success_count: 2)
+ def test_compile_insn_blockparam
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '3', success_count: 2)
+ begin;
def foo(&b)
a = b
b = 2
@@ -35,185 +65,243 @@ class TestJIT < Test::Unit::TestCase
end
print foo { 1 }
- RUBY
+ end;
+ end
- # getblockparamproxy
- # TODO: support this in mjit_compile
+ def test_compile_insn_getblockparamproxy
+ skip "support this in mjit_compile"
+ end
- # getspecial
+ def test_compile_insn_getspecial
assert_compile_once('$1', result_inspect: 'nil')
+ end
- # setspecial
- assert_compile_once(<<~RUBY, result_inspect: 'true')
+ def test_compile_insn_setspecial
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: 'true')
+ begin;
true if nil.nil?..nil.nil?
- RUBY
+ end;
+ end
- # getinstancevariable
- # setinstancevariable
- assert_compile_once(<<~RUBY, result_inspect: '1')
+ def test_compile_insn_instancevariable
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: '1')
+ begin;
@foo = 1
@foo
- RUBY
+ end;
+ end
- # getclassvariable
- # setclassvariable
- assert_compile_once(<<~RUBY, result_inspect: '1')
+ def test_compile_insn_classvariable
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: '1')
+ begin;
@@foo = 1
@@foo
- RUBY
+ end;
+ end
- # getconstant
- # setconstant
- assert_compile_once(<<~RUBY, result_inspect: '1')
+ def test_compile_insn_constant
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: '1')
+ begin;
FOO = 1
FOO
- RUBY
+ end;
+ end
- # getglobal
- # setglobal
- assert_compile_once(<<~RUBY, result_inspect: '1')
+ def test_compile_insn_global
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: '1')
+ begin;
$foo = 1
$foo
- RUBY
+ end;
+ end
- # putnil
+ def test_compile_insn_putnil
assert_compile_once('nil', result_inspect: 'nil')
+ end
- # putself
- assert_eval_with_jit(<<~RUBY, stdout: 'hello', success_count: 1)
+ def test_compile_insn_putself
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: 'hello', success_count: 1)
+ begin;
proc { print "hello" }.call
- RUBY
+ end;
+ end
- # putobject
+ def test_compile_insn_putobject
assert_compile_once('0', result_inspect: '0') # putobject_OP_INT2FIX_O_0_C_
assert_compile_once('1', result_inspect: '1') # putobject_OP_INT2FIX_O_1_C_
assert_compile_once('2', result_inspect: '2')
+ end
- # putspecialobject
- # putiseq
- assert_eval_with_jit(<<~RUBY, stdout: 'hello', success_count: 2)
+ def test_compile_insn_putspecialobject_putiseq
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: 'hello', success_count: 2)
+ begin;
print proc {
def method_definition
'hello'
end
method_definition
}.call
- RUBY
+ end;
+ end
- # putstring
- # concatstrings
- # tostring
+ def test_compile_insn_putstring_concatstrings_tostring
assert_compile_once('"a#{}b" + "c"', result_inspect: '"abc"')
+ end
- # freezestring
- assert_eval_with_jit(<<~'RUBY', stdout: 'true', success_count: 1)
+ def test_compile_insn_freezestring
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~'end;'}", stdout: 'true', success_count: 1)
+ begin;
# frozen_string_literal: true
print proc { "#{true}".frozen? }.call
- RUBY
+ end;
+ end
- # toregexp
+ def test_compile_insn_toregexp
assert_compile_once('/#{true}/ =~ "true"', result_inspect: '0')
+ end
- # intern
- # newarray
- # duparray
+ def test_compile_insn_intern_newarray_duparray
assert_compile_once('[:"#{0}"] + [1,2,3]', result_inspect: '[:"0", 1, 2, 3]')
+ end
- # expandarray
+ def test_compile_insn_expandarray
assert_compile_once('y = [ true, false, nil ]; x, = y; x', result_inspect: 'true')
+ end
- # concatarray
+ def test_compile_insn_concatarray
assert_compile_once('["t", "r", *x = "u", "e"].join', result_inspect: '"true"')
+ end
- # splatarray
+ def test_compile_insn_splatarray
assert_compile_once('[*(1..2)]', result_inspect: '[1, 2]')
+ end
- # newhash
+ def test_compile_insn_newhash
assert_compile_once('a = 1; { a: a }', result_inspect: '{:a=>1}')
+ end
- # newrange
+ def test_compile_insn_newrange
assert_compile_once('a = 1; 0..a', result_inspect: '0..1')
+ end
- # pop
- assert_compile_once(<<~RUBY, result_inspect: '1')
+ def test_compile_insn_pop
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: '1')
+ begin;
a = false
b = 1
a || b
- RUBY
+ end;
+ end
- # dup
- assert_compile_once(<<~RUBY, result_inspect: '3')
+ def test_compile_insn_dup
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: '3')
+ begin;
a = 1
a&.+(2)
- RUBY
+ end;
+ end
- # dupn
- assert_compile_once(<<~RUBY, result_inspect: 'true')
+ def test_compile_insn_dupn
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: 'true')
+ begin;
klass = Class.new
klass::X ||= true
- RUBY
+ end;
+ end
- # swap
- # topn
+ def test_compile_insn_swap_topn
assert_compile_once('{}["true"] = true', result_inspect: 'true')
+ end
- # reverse
+ def test_compile_insn_reverse
assert_compile_once('q, (w, e), r = 1, [2, 3], 4; e == 3', result_inspect: 'true')
+ end
- # reput
- # TODO: write test
+ def test_compile_insn_reput
+ skip "write test"
+ end
- # setn
+ def test_compile_insn_setn
assert_compile_once('[nil][0] = 1', result_inspect: '1')
+ end
- # adjuststack
- assert_compile_once(<<~RUBY, result_inspect: 'true')
+ def test_compile_insn_adjuststack
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: 'true')
+ begin;
x = [true]
x[0] ||= nil
x[0]
- RUBY
+ end;
+ end
- # defined
+ def test_compile_insn_defined
assert_compile_once('defined?(a)', result_inspect: 'nil')
+ end
- # checkkeyword
- assert_eval_with_jit(<<~RUBY, stdout: 'true', success_count: 1)
+ def test_compile_insn_checkkeyword
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: 'true', success_count: 1)
+ begin;
def test(x: rand)
x
end
print test(x: true)
- RUBY
+ end;
+ end
- # tracecoverage
- # TODO: write test
+ def test_compile_insn_tracecoverage
+ skip "write test"
+ end
- # defineclass
- # TODO: support this in mjit_compile (low priority)
+ def test_compile_insn_defineclass
+ skip "support this in mjit_compile (low priority)"
+ end
- # send
- assert_eval_with_jit(<<~RUBY, stdout: '1', success_count: 2)
+ def test_compile_insn_send
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '1', success_count: 2)
+ begin;
print proc { yield_self { 1 } }.call
- RUBY
+ end;
+ end
+
+ def test_compile_insn_opt_str_freeze
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: '"foo"')
+ begin;
+ 'foo'.freeze
+ end;
+ end
- # opt_str_freeze
- # opt_str_uminus
- assert_compile_once(<<~RUBY, result_inspect: '"foobar"')
- 'foo'.freeze + -'bar'
- RUBY
+ def test_compile_insn_opt_str_uminus
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: '"bar"')
+ begin;
+ -'bar'
+ end;
+ end
- # opt_newarray_max
- # opt_newarray_min
- assert_compile_once(<<~RUBY, result_inspect: '3')
+ def test_compile_insn_opt_newarray_max
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: '2')
+ begin;
a = 1
b = 2
- [a, b].max + [a, b].min
- RUBY
+ [a, b].max
+ end;
+ end
- # opt_send_without_block
+ def test_compile_insn_opt_newarray_min
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: '1')
+ begin;
+ a = 1
+ b = 2
+ [a, b].min
+ end;
+ end
+
+ def test_compile_insn_opt_send_without_block
assert_compile_once('print', result_inspect: 'nil')
+ end
- # invokesuper
- assert_eval_with_jit(<<~RUBY, stdout: '3', success_count: 4)
+ def test_compile_insn_invokesuper
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '3', success_count: 4)
+ begin;
mod = Module.new {
def test
super + 2
@@ -226,19 +314,22 @@ class TestJIT < Test::Unit::TestCase
end
}
print klass.new.test
- RUBY
+ end;
+ end
- # invokeblock
- # leave
- assert_eval_with_jit(<<~RUBY, stdout: '2', success_count: 2)
+ def test_compile_insn_invokeblock_leave
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '2', success_count: 2)
+ begin;
def foo
yield
end
print foo { 2 }
- RUBY
+ end;
+ end
- # throw
- assert_eval_with_jit(<<~RUBY, stdout: '4', success_count: 2)
+ def test_compile_insn_throw
+ assert_eval_with_jit("#{<<~"begin;"}\n#{<<~"end;"}", stdout: '4', success_count: 2)
+ begin;
def test
proc do
if 1+1 == 1
@@ -250,111 +341,121 @@ class TestJIT < Test::Unit::TestCase
end.call
end
print test
- RUBY
+ end;
+ end
- # jump
- # branchif
- assert_compile_once(<<~'RUBY', result_inspect: 'nil')
+ def test_compile_insn_jump_branchif
+ assert_compile_once("#{<<~"begin;"}\n#{<<~'end;'}", result_inspect: 'nil')
+ begin;
a = false
1 + 1 while false
- RUBY
+ end;
+ end
- # branchunless
- assert_compile_once(<<~'RUBY', result_inspect: '1')
+ def test_compile_insn_branchunless
+ assert_compile_once("#{<<~"begin;"}\n#{<<~'end;'}", result_inspect: '1')
+ begin;
a = true
if a
1
else
2
end
- RUBY
+ end;
+ end
- # branchnil
- assert_compile_once(<<~'RUBY', result_inspect: '3')
+ def test_compile_insn_branchnil
+ assert_compile_once("#{<<~"begin;"}\n#{<<~'end;'}", result_inspect: '3')
+ begin;
a = 2
a&.+(1)
- RUBY
+ end;
+ end
- # branchiftype
- assert_compile_once(<<~'RUBY', result_inspect: '"42"')
+ def test_compile_insn_branchiftype
+ assert_compile_once("#{<<~"begin;"}\n#{<<~'end;'}", result_inspect: '"42"')
+ begin;
a = '2'
"4#{a}"
- RUBY
+ end;
+ end
- # getinlinecache
- # setinlinecache
+ def test_compile_insn_inlinecache
assert_compile_once('Struct', result_inspect: 'Struct')
+ end
- # once
+ def test_compile_insn_once
assert_compile_once('/#{true}/o =~ "true" && $~.to_a', result_inspect: '["true"]')
+ end
- # checkmatch
- # opt_case_dispatch
- assert_compile_once(<<~RUBY, result_inspect: '"world"')
+ def test_compile_insn_checkmatch_opt_case_dispatch
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: '"world"')
+ begin;
case 'hello'
when /hello/
'world'
end
- RUBY
+ end;
+ end
- # opt_plus
- # opt_minus
- # opt_mult
- # opt_div
- # opt_mod
+ def test_compile_insn_opt_calc
assert_compile_once('4 + 2 - ((2 * 3 / 2) % 2)', result_inspect: '5')
+ assert_compile_once('4 + 2', result_inspect: '6')
+ end
- # opt_eq
- # opt_neq
+ def test_compile_insn_opt_cmp
assert_compile_once('(1 == 1) && (1 != 2)', result_inspect: 'true')
+ end
- # opt_lt
- # opt_le
- # opt_gt
- # opt_ge
+ def test_compile_insn_opt_rel
assert_compile_once('1 < 2 && 1 <= 1 && 2 > 1 && 1 >= 1', result_inspect: 'true')
+ end
- # opt_ltlt
+ def test_compile_insn_opt_ltlt
assert_compile_once('[1] << 2', result_inspect: '[1, 2]')
+ end
- # opt_aref
- # opt_aset
- # opt_aset_with
- # opt_aref_with
- assert_compile_once(<<~RUBY, result_inspect: '8')
+ def test_compile_insn_opt_aref_aset
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: '8')
+ begin;
hash = { '1' => 2 }
hash['1'] + hash[1.to_s] + (hash['2'] = 2) + (hash[2.to_s] = 2)
- RUBY
+ end;
+ end
- # opt_length
- # opt_size
- assert_compile_once(<<~RUBY, result_inspect: '4')
+ def test_compile_insn_opt_length_size
+ assert_compile_once("#{<<~"begin;"}\n#{<<~"end;"}", result_inspect: '4')
+ begin;
array = [1, 2]
- array.size + array.length
- RUBY
+ array.length + array.size
+ end;
+ end
- # opt_empty_p
+ def test_compile_insn_opt_empty_p
assert_compile_once('[].empty?', result_inspect: 'true')
+ end
- # opt_succ
+ def test_compile_insn_opt_succ
assert_compile_once('1.succ', result_inspect: '2')
+ end
- # opt_not
+ def test_compile_insn_opt_not
assert_compile_once('!!true', result_inspect: 'true')
+ end
- # opt_regexpmatch1
+ def test_compile_insn_opt_regexpmatch1
assert_compile_once("/true/ =~ 'true'", result_inspect: '0')
+ end
- # opt_regexpmatch2
+ def test_compile_insn_opt_regexpmatch2
assert_compile_once("'true' =~ /true/", result_inspect: '0')
+ end
- # opt_call_c_function
- # TODO: support this in opt_call_c_function (low priority)
+ def test_compile_insn_opt_call_c_function
+ skip "support this in opt_call_c_function (low priority)"
end
def test_jit_output
- skip unless jit_supported?
-
out, err = eval_with_jit('5.times { puts "MJIT" }', verbose: 1, min_calls: 5)
assert_equal("MJIT\n" * 5, out)
assert_match(/^#{JIT_SUCCESS_PREFIX}: block in <main>@-e:1 -> .+_ruby_mjit_p\d+u\d+\.c$/, err)
@@ -371,14 +472,12 @@ class TestJIT < Test::Unit::TestCase
# Shorthand for normal test cases
def assert_eval_with_jit(script, stdout: nil, success_count:)
out, err = eval_with_jit(script, verbose: 1, min_calls: 1)
- if jit_supported?
- actual = err.scan(/^#{JIT_SUCCESS_PREFIX}:/).size
- assert_equal(
- success_count, actual,
- "Expected #{success_count} times of JIT success, but succeeded #{actual} times.\n\n"\
- "script:\n#{code_block(script)}\nstderr:\n#{code_block(err)}",
- )
- end
+ actual = err.scan(/^#{JIT_SUCCESS_PREFIX}:/).size
+ assert_equal(
+ success_count, actual,
+ "Expected #{success_count} times of JIT success, but succeeded #{actual} times.\n\n"\
+ "script:\n#{code_block(script)}\nstderr:\n#{code_block(err)}",
+ )
if stdout
assert_equal(stdout, out, "Expected stdout #{out.inspect} to match #{stdout.inspect} with script:\n#{code_block(script)}")
end
@@ -386,11 +485,8 @@ class TestJIT < Test::Unit::TestCase
# Run Ruby script with --jit-wait (Synchronous JIT compilation).
# Returns [stdout, stderr]
- def eval_with_jit(script, verbose: 0, min_calls: 5, timeout: JIT_TIMEOUT)
- stdout, stderr, status = EnvUtil.invoke_ruby(
- ['--disable-gems', '--jit-wait', "--jit-verbose=#{verbose}", "--jit-min-calls=#{min_calls}", '-e', script],
- '', true, true, timeout: timeout,
- )
+ def eval_with_jit(script, **opts)
+ stdout, stderr, status = super
assert_equal(true, status.success?, "Failed to run script with JIT:\n#{code_block(script)}\nstdout:\n#{code_block(stdout)}\nstderr:\n#{code_block(stderr)}")
[stdout, stderr]
end
@@ -398,22 +494,4 @@ class TestJIT < Test::Unit::TestCase
def code_block(code)
"```\n#{code}\n```\n\n"
end
-
- def jit_supported?
- return @jit_supported if defined?(@jit_supported)
-
- # Experimental. If you want to ensure JIT is working with this test, please set this for now.
- if ENV.key?('RUBY_FORCE_TEST_JIT')
- return @jit_supported = true
- end
-
- # Very pessimistic check. With this check, we can't ensure JIT is working.
- begin
- _, err = eval_with_jit('proc {}.call', verbose: 1, min_calls: 1, timeout: 10)
- @jit_supported = err.match?(JIT_SUCCESS_PREFIX)
- rescue Timeout::Error
- $stderr.puts "TestJIT: #jit_supported? check timed out"
- @jit_supported = false
- end
- end
end