1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

test_jit.rb: split test_compile_insns

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@62378 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
nobu 2018-02-12 03:33:00 +00:00
parent 8547caede1
commit 30bcbfd339

View file

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