From 30bcbfd339d0b7176cc00a044d4682a300510ff3 Mon Sep 17 00:00:00 2001 From: nobu Date: Mon, 12 Feb 2018 03:33:00 +0000 Subject: test_jit.rb: split test_compile_insns git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@62378 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- test/ruby/test_jit.rb | 462 +++++++++++++++++++++++++++++--------------------- 1 file 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
@-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 -- cgit v1.2.3