2018-02-08 07:40:33 -05:00
# frozen_string_literal: true
require 'test/unit'
2018-05-02 09:11:53 -04:00
require 'tmpdir'
2018-02-22 10:11:12 -05:00
require_relative '../lib/jit_support'
2018-02-11 22:33:00 -05:00
# Test for --jit option
class TestJIT < Test :: Unit :: TestCase
2018-02-22 10:11:12 -05:00
include JITSupport
2018-05-02 11:41:43 -04:00
# trace_* insns are not compiled for now...
TEST_PENDING_INSNS = RubyVM :: INSTRUCTION_NAMES . select { | n | n . start_with? ( 'trace_' ) } . map ( & :to_sym ) + [
# not supported yet
:getblockparamproxy ,
:defineclass ,
:opt_call_c_function ,
# joke
:bitblt ,
:answer ,
# TODO: write tests for them
:getlocal ,
:setlocal ,
:getlocal_WC_1 ,
:setlocal_WC_1 ,
:reput ,
:tracecoverage ,
:opt_aref_with ,
]
def self . untested_insns
@untested_insns || = ( RubyVM :: INSTRUCTION_NAMES . map ( & :to_sym ) - TEST_PENDING_INSNS )
end
2018-02-08 08:56:32 -05:00
2018-02-11 23:00:45 -05:00
def setup
2018-02-22 10:11:12 -05:00
unless JITSupport . supported?
2018-02-11 23:00:45 -05:00
skip 'JIT seems not supported on this platform'
end
2018-05-02 11:41:43 -04:00
# ruby -w -Itest/lib test/ruby/test_jit.rb
if $VERBOSE && ! defined? ( @@at_exit_hooked )
at_exit do
unless TestJIT . untested_insns . empty?
warn " untested insns are found!: #{ TestJIT . untested_insns . join ( ' ' ) } "
end
end
@@at_exit_hooked = true
end
2018-02-11 23:00:45 -05:00
end
2018-02-11 22:33:00 -05:00
def test_compile_insn_nop
2018-05-02 11:12:55 -04:00
assert_compile_once ( 'nil rescue true' , result_inspect : 'nil' , insns : % i [ nop ] )
2018-02-11 22:33:00 -05:00
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_local
2018-05-02 11:12:55 -04:00
assert_compile_once ( " #{ << ~ " begin; " } \n #{ << ~ " end; " } " , result_inspect : '1' , insns : % i [ setlocal_WC_0 getlocal_WC_0 ] )
2018-02-11 22:33:00 -05:00
begin ;
2018-02-10 11:09:55 -05:00
foo = 1
foo
2018-02-11 22:33:00 -05:00
end ;
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_blockparam
2018-05-02 11:12:55 -04:00
assert_eval_with_jit ( " #{ << ~ " begin; " } \n #{ << ~ " end; " } " , stdout : '3' , success_count : 2 , insns : % i [ getblockparam setblockparam ] )
2018-02-11 22:33:00 -05:00
begin ;
2018-02-10 11:09:55 -05:00
def foo ( & b )
a = b
b = 2
a . call + 2
end
print foo { 1 }
2018-02-11 22:33:00 -05:00
end ;
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_getblockparamproxy
skip " support this in mjit_compile "
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_getspecial
2018-05-02 11:12:55 -04:00
assert_compile_once ( '$1' , result_inspect : 'nil' , insns : % i [ getspecial ] )
2018-02-11 22:33:00 -05:00
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_setspecial
2018-05-02 11:12:55 -04:00
assert_compile_once ( " #{ << ~ " begin; " } \n #{ << ~ " end; " } " , result_inspect : 'true' , insns : % i [ setspecial ] )
2018-02-11 22:33:00 -05:00
begin ;
2018-02-10 11:09:55 -05:00
true if nil . nil? .. nil . nil?
2018-02-11 22:33:00 -05:00
end ;
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_instancevariable
2018-05-02 11:12:55 -04:00
assert_compile_once ( " #{ << ~ " begin; " } \n #{ << ~ " end; " } " , result_inspect : '1' , insns : % i [ getinstancevariable setinstancevariable ] )
2018-02-11 22:33:00 -05:00
begin ;
2018-02-10 11:09:55 -05:00
@foo = 1
@foo
2018-02-11 22:33:00 -05:00
end ;
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_classvariable
2018-05-02 11:12:55 -04:00
assert_compile_once ( " #{ << ~ " begin; " } \n #{ << ~ " end; " } " , result_inspect : '1' , insns : % i [ getclassvariable setclassvariable ] )
2018-02-11 22:33:00 -05:00
begin ;
2018-02-10 11:09:55 -05:00
@@foo = 1
@@foo
2018-02-11 22:33:00 -05:00
end ;
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_constant
2018-05-02 11:12:55 -04:00
assert_compile_once ( " #{ << ~ " begin; " } \n #{ << ~ " end; " } " , result_inspect : '1' , insns : % i [ getconstant setconstant ] )
2018-02-11 22:33:00 -05:00
begin ;
2018-02-10 11:09:55 -05:00
FOO = 1
FOO
2018-02-11 22:33:00 -05:00
end ;
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_global
2018-05-02 11:12:55 -04:00
assert_compile_once ( " #{ << ~ " begin; " } \n #{ << ~ " end; " } " , result_inspect : '1' , insns : % i [ getglobal setglobal ] )
2018-02-11 22:33:00 -05:00
begin ;
2018-02-10 11:09:55 -05:00
$foo = 1
$foo
2018-02-11 22:33:00 -05:00
end ;
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_putnil
2018-05-02 11:12:55 -04:00
assert_compile_once ( 'nil' , result_inspect : 'nil' , insns : % i [ putnil ] )
2018-02-11 22:33:00 -05:00
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_putself
2018-05-02 11:12:55 -04:00
assert_eval_with_jit ( " #{ << ~ " begin; " } \n #{ << ~ " end; " } " , stdout : 'hello' , success_count : 1 , insns : % i [ putself ] )
2018-02-11 22:33:00 -05:00
begin ;
2018-02-10 11:09:55 -05:00
proc { print " hello " } . call
2018-02-11 22:33:00 -05:00
end ;
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_putobject
2018-05-02 11:12:55 -04:00
assert_compile_once ( '0' , result_inspect : '0' , insns : % i [ putobject_INT2FIX_0_ ] )
assert_compile_once ( '1' , result_inspect : '1' , insns : % i [ putobject_INT2FIX_1_ ] )
assert_compile_once ( '2' , result_inspect : '2' , insns : % i [ putobject ] )
2018-02-11 22:33:00 -05:00
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_putspecialobject_putiseq
2018-05-02 11:12:55 -04:00
assert_eval_with_jit ( " #{ << ~ " begin; " } \n #{ << ~ " end; " } " , stdout : 'hello' , success_count : 2 , insns : % i [ putspecialobject putiseq ] )
2018-02-11 22:33:00 -05:00
begin ;
2018-02-10 11:09:55 -05:00
print proc {
def method_definition
'hello'
end
method_definition
} . call
2018-02-11 22:33:00 -05:00
end ;
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_putstring_concatstrings_tostring
2018-05-02 11:41:43 -04:00
assert_compile_once ( '"a#{}b" + "c"' , result_inspect : '"abc"' , insns : % i [ putstring concatstrings tostring ] )
2018-02-11 22:33:00 -05:00
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_freezestring
2018-05-02 11:12:55 -04:00
assert_eval_with_jit ( " #{ << ~ " begin; " } \n #{ << ~ 'end;' } " , stdout : 'true' , success_count : 1 , insns : % i [ freezestring ] )
2018-02-11 22:33:00 -05:00
begin ;
2018-02-10 11:09:55 -05:00
# frozen_string_literal: true
print proc { " #{ true } " . frozen? } . call
2018-02-11 22:33:00 -05:00
end ;
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_toregexp
2018-05-02 11:12:55 -04:00
assert_compile_once ( '/#{true}/ =~ "true"' , result_inspect : '0' , insns : % i [ toregexp ] )
2018-02-11 22:33:00 -05:00
end
2018-02-10 11:09:55 -05:00
mjit_compile.c: use local variables for stack
if catch_except_p is FALSE. If catch_except_p is TRUE, stack values
should be on VM's stack when exception is thrown and the JIT-ed frame
is re-executed by VM's exception handler. If it's FALSE, the JIT-ed
frame won't be re-executed and don't need to keep values on VM's stack.
Using local variables allows us to reduce cfp->sp motion. Moving cfp->sp
is needed only for insns whose handles_frame? is false. So it improves
performance.
_mjit_compile_insn.erb: Prepare `stack_size` variable for GET_SP,
STACK_ADDR_FROM_TOP, TOPN macros. Share pc and sp motion partial view.
Use cancel handler created in mjit_compile.c.
_mjit_compile_send.erb: ditto. Also, when iseq->body->catch_except_p is
TRUE, this stops to call mjit_exec directly. I described the reason in
vm_insnhelper.h's comment for EXEC_EC_CFP.
_mjit_compile_pc_and_sp.erb: Shared logic for moving sp and pc. As you
can see from thsi file, when status->local_stack_p is TRUE and
insn.handles_frame? is false, moving sp is skipped. But if
insn.handles_frame? is true, values should be rolled back to VM's stack.
common.mk: add dependency for the file
_mjit_compile_insn_body.erb: Set sp value before canceling JIT on
DISPATCH_ORIGINAL_INSN. Replace GET_SP, STACK_ADDR_FROM_TOP, TOPN macros
for the case ocal_stack_p is TRUE and insn.handles_frame? is false.
In that case, values are not available on VM's stack and those macros
should be replaced.
mjit_compile.inc.erb: updated comments of macros which are supported by
JIT compiler. All references to `cfp->sp` should be replaced and thus
INC_SP, SET_SV, PUSH are no longer supported for now, because they are
not used now.
vm_exec.h: moved EXEC_EC_CFP definition to vm_insnhelper.h because it's
tighly coupled to CALL_METHOD.
vm_insnhelper.h: Have revised EXEC_EC_CFP definition moved from vm_exec.h.
Now it triggers mjit_exec for VM, and has the guard for catch_except_p
on JIT-ed code. See comments for details. CALL_METHOD delegates
triggering mjit_exec to EXEC_EC_CFP.
insns.def: Stopped using EXEC_EC_CFP for the case we don't want to
trigger mjit_exec. Those insns (defineclass, opt_call_c_function) are
not supported by JIT and it's safe to use RESTORE_REGS(), NEXT_INSN().
expandarray is changed to pass GET_SP() to replace the macro in
_mjit_compile_insn_body.erb.
vm_insnhelper.c: change to take sp for the above reason.
[close https://github.com/ruby/ruby/pull/1828]
This patch resurrects the performance which was attached in
[Feature #14235].
* Benchmark
Optcarrot (with configuration for benchmark_driver.gem)
https://github.com/benchmark-driver/optcarrot
$ benchmark-driver benchmark.yml --verbose 1 --rbenv 'before;before+JIT::before,--jit;after;after+JIT::after,--jit' --repeat-count 10
before: ruby 2.6.0dev (2018-03-04 trunk 62652) [x86_64-linux]
before+JIT: ruby 2.6.0dev (2018-03-04 trunk 62652) +JIT [x86_64-linux]
after: ruby 2.6.0dev (2018-03-04 local-variable.. 62652) [x86_64-linux]
last_commit=mjit_compile.c: use local variables for stack
after+JIT: ruby 2.6.0dev (2018-03-04 local-variable.. 62652) +JIT [x86_64-linux]
last_commit=mjit_compile.c: use local variables for stack
Calculating -------------------------------------
before before+JIT after after+JIT
optcarrot 53.552 59.680 53.697 63.358 fps
Comparison:
optcarrot
after+JIT: 63.4 fps
before+JIT: 59.7 fps - 1.06x slower
after: 53.7 fps - 1.18x slower
before: 53.6 fps - 1.18x slower
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@62655 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-03-04 02:04:40 -05:00
def test_compile_insn_newarray
2018-05-02 11:12:55 -04:00
assert_compile_once ( " #{ << ~ " begin; " } \n #{ << ~ " end; " } " , result_inspect : '[1, 2, 3]' , insns : % i [ newarray ] )
mjit_compile.c: use local variables for stack
if catch_except_p is FALSE. If catch_except_p is TRUE, stack values
should be on VM's stack when exception is thrown and the JIT-ed frame
is re-executed by VM's exception handler. If it's FALSE, the JIT-ed
frame won't be re-executed and don't need to keep values on VM's stack.
Using local variables allows us to reduce cfp->sp motion. Moving cfp->sp
is needed only for insns whose handles_frame? is false. So it improves
performance.
_mjit_compile_insn.erb: Prepare `stack_size` variable for GET_SP,
STACK_ADDR_FROM_TOP, TOPN macros. Share pc and sp motion partial view.
Use cancel handler created in mjit_compile.c.
_mjit_compile_send.erb: ditto. Also, when iseq->body->catch_except_p is
TRUE, this stops to call mjit_exec directly. I described the reason in
vm_insnhelper.h's comment for EXEC_EC_CFP.
_mjit_compile_pc_and_sp.erb: Shared logic for moving sp and pc. As you
can see from thsi file, when status->local_stack_p is TRUE and
insn.handles_frame? is false, moving sp is skipped. But if
insn.handles_frame? is true, values should be rolled back to VM's stack.
common.mk: add dependency for the file
_mjit_compile_insn_body.erb: Set sp value before canceling JIT on
DISPATCH_ORIGINAL_INSN. Replace GET_SP, STACK_ADDR_FROM_TOP, TOPN macros
for the case ocal_stack_p is TRUE and insn.handles_frame? is false.
In that case, values are not available on VM's stack and those macros
should be replaced.
mjit_compile.inc.erb: updated comments of macros which are supported by
JIT compiler. All references to `cfp->sp` should be replaced and thus
INC_SP, SET_SV, PUSH are no longer supported for now, because they are
not used now.
vm_exec.h: moved EXEC_EC_CFP definition to vm_insnhelper.h because it's
tighly coupled to CALL_METHOD.
vm_insnhelper.h: Have revised EXEC_EC_CFP definition moved from vm_exec.h.
Now it triggers mjit_exec for VM, and has the guard for catch_except_p
on JIT-ed code. See comments for details. CALL_METHOD delegates
triggering mjit_exec to EXEC_EC_CFP.
insns.def: Stopped using EXEC_EC_CFP for the case we don't want to
trigger mjit_exec. Those insns (defineclass, opt_call_c_function) are
not supported by JIT and it's safe to use RESTORE_REGS(), NEXT_INSN().
expandarray is changed to pass GET_SP() to replace the macro in
_mjit_compile_insn_body.erb.
vm_insnhelper.c: change to take sp for the above reason.
[close https://github.com/ruby/ruby/pull/1828]
This patch resurrects the performance which was attached in
[Feature #14235].
* Benchmark
Optcarrot (with configuration for benchmark_driver.gem)
https://github.com/benchmark-driver/optcarrot
$ benchmark-driver benchmark.yml --verbose 1 --rbenv 'before;before+JIT::before,--jit;after;after+JIT::after,--jit' --repeat-count 10
before: ruby 2.6.0dev (2018-03-04 trunk 62652) [x86_64-linux]
before+JIT: ruby 2.6.0dev (2018-03-04 trunk 62652) +JIT [x86_64-linux]
after: ruby 2.6.0dev (2018-03-04 local-variable.. 62652) [x86_64-linux]
last_commit=mjit_compile.c: use local variables for stack
after+JIT: ruby 2.6.0dev (2018-03-04 local-variable.. 62652) +JIT [x86_64-linux]
last_commit=mjit_compile.c: use local variables for stack
Calculating -------------------------------------
before before+JIT after after+JIT
optcarrot 53.552 59.680 53.697 63.358 fps
Comparison:
optcarrot
after+JIT: 63.4 fps
before+JIT: 59.7 fps - 1.06x slower
after: 53.7 fps - 1.18x slower
before: 53.6 fps - 1.18x slower
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@62655 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-03-04 02:04:40 -05:00
begin ;
a , b , c = 1 , 2 , 3
[ a , b , c ]
end ;
end
def test_compile_insn_intern_duparray
2018-05-02 11:41:43 -04:00
assert_compile_once ( '[:"#{0}"] + [1,2,3]' , result_inspect : '[:"0", 1, 2, 3]' , insns : % i [ intern duparray ] )
2018-02-11 22:33:00 -05:00
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_expandarray
2018-05-02 11:12:55 -04:00
assert_compile_once ( 'y = [ true, false, nil ]; x, = y; x' , result_inspect : 'true' , insns : % i [ expandarray ] )
2018-02-11 22:33:00 -05:00
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_concatarray
2018-05-02 11:12:55 -04:00
assert_compile_once ( '["t", "r", *x = "u", "e"].join' , result_inspect : '"true"' , insns : % i [ concatarray ] )
2018-02-11 22:33:00 -05:00
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_splatarray
2018-05-02 11:12:55 -04:00
assert_compile_once ( '[*(1..2)]' , result_inspect : '[1, 2]' , insns : % i [ splatarray ] )
2018-02-11 22:33:00 -05:00
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_newhash
2018-05-02 11:12:55 -04:00
assert_compile_once ( 'a = 1; { a: a }' , result_inspect : '{:a=>1}' , insns : % i [ newhash ] )
2018-02-11 22:33:00 -05:00
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_newrange
2018-05-02 11:12:55 -04:00
assert_compile_once ( 'a = 1; 0..a' , result_inspect : '0..1' , insns : % i [ newrange ] )
2018-02-11 22:33:00 -05:00
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_pop
2018-05-02 11:12:55 -04:00
assert_compile_once ( " #{ << ~ " begin; " } \n #{ << ~ " end; " } " , result_inspect : '1' , insns : % i [ pop ] )
2018-02-11 22:33:00 -05:00
begin ;
2018-02-10 11:09:55 -05:00
a = false
b = 1
a || b
2018-02-11 22:33:00 -05:00
end ;
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_dup
2018-05-02 11:12:55 -04:00
assert_compile_once ( " #{ << ~ " begin; " } \n #{ << ~ " end; " } " , result_inspect : '3' , insns : % i [ dup ] )
2018-02-11 22:33:00 -05:00
begin ;
2018-02-10 11:09:55 -05:00
a = 1
a & . + ( 2 )
2018-02-11 22:33:00 -05:00
end ;
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_dupn
2018-05-02 11:12:55 -04:00
assert_compile_once ( " #{ << ~ " begin; " } \n #{ << ~ " end; " } " , result_inspect : 'true' , insns : % i [ dupn ] )
2018-02-11 22:33:00 -05:00
begin ;
2018-02-10 11:09:55 -05:00
klass = Class . new
klass :: X || = true
2018-02-11 22:33:00 -05:00
end ;
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_swap_topn
2018-05-02 11:12:55 -04:00
assert_compile_once ( '{}["true"] = true' , result_inspect : 'true' , insns : % i [ swap topn ] )
2018-02-11 22:33:00 -05:00
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_reverse
2018-05-02 11:41:43 -04:00
assert_compile_once ( 'q, (w, e), r = 1, [2, 3], 4; [q, w, e, r]' , result_inspect : '[1, 2, 3, 4]' , insns : % i [ reverse ] )
2018-02-11 22:33:00 -05:00
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_reput
skip " write test "
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_setn
2018-05-02 11:12:55 -04:00
assert_compile_once ( '[nil][0] = 1' , result_inspect : '1' , insns : % i [ setn ] )
2018-02-11 22:33:00 -05:00
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_adjuststack
2018-05-02 11:12:55 -04:00
assert_compile_once ( " #{ << ~ " begin; " } \n #{ << ~ " end; " } " , result_inspect : 'true' , insns : % i [ adjuststack ] )
2018-02-11 22:33:00 -05:00
begin ;
2018-02-10 11:09:55 -05:00
x = [ true ]
x [ 0 ] || = nil
x [ 0 ]
2018-02-11 22:33:00 -05:00
end ;
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_defined
2018-05-02 11:12:55 -04:00
assert_compile_once ( 'defined?(a)' , result_inspect : 'nil' , insns : % i [ defined ] )
2018-02-11 22:33:00 -05:00
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_checkkeyword
2018-05-02 11:12:55 -04:00
assert_eval_with_jit ( " #{ << ~ " begin; " } \n #{ << ~ " end; " } " , stdout : 'true' , success_count : 1 , insns : % i [ checkkeyword ] )
2018-02-11 22:33:00 -05:00
begin ;
2018-02-10 11:09:55 -05:00
def test ( x : rand )
x
end
print test ( x : true )
2018-02-11 22:33:00 -05:00
end ;
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_tracecoverage
skip " write test "
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_defineclass
skip " support this in mjit_compile (low priority) "
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_send
2018-05-02 11:12:55 -04:00
assert_eval_with_jit ( " #{ << ~ " begin; " } \n #{ << ~ " end; " } " , stdout : '1' , success_count : 2 , insns : % i [ send ] )
2018-02-11 22:33:00 -05:00
begin ;
2018-02-10 11:09:55 -05:00
print proc { yield_self { 1 } } . call
2018-02-11 22:33:00 -05:00
end ;
end
def test_compile_insn_opt_str_freeze
2018-05-02 11:12:55 -04:00
assert_compile_once ( " #{ << ~ " begin; " } \n #{ << ~ " end; " } " , result_inspect : '"foo"' , insns : % i [ opt_str_freeze ] )
2018-02-11 22:33:00 -05:00
begin ;
'foo' . freeze
end ;
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_opt_str_uminus
2018-05-02 11:12:55 -04:00
assert_compile_once ( " #{ << ~ " begin; " } \n #{ << ~ " end; " } " , result_inspect : '"bar"' , insns : % i [ opt_str_uminus ] )
2018-02-11 22:33:00 -05:00
begin ;
- 'bar'
end ;
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_opt_newarray_max
2018-05-02 11:12:55 -04:00
assert_compile_once ( " #{ << ~ " begin; " } \n #{ << ~ " end; " } " , result_inspect : '2' , insns : % i [ opt_newarray_max ] )
2018-02-11 22:33:00 -05:00
begin ;
2018-02-10 11:09:55 -05:00
a = 1
b = 2
2018-02-11 22:33:00 -05:00
[ a , b ] . max
end ;
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_opt_newarray_min
2018-05-02 11:12:55 -04:00
assert_compile_once ( " #{ << ~ " begin; " } \n #{ << ~ " end; " } " , result_inspect : '1' , insns : % i [ opt_newarray_min ] )
2018-02-11 22:33:00 -05:00
begin ;
a = 1
b = 2
[ a , b ] . min
end ;
end
def test_compile_insn_opt_send_without_block
2018-05-02 11:12:55 -04:00
assert_compile_once ( 'print' , result_inspect : 'nil' , insns : % i [ opt_send_without_block ] )
2018-02-11 22:33:00 -05:00
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_invokesuper
2018-05-02 11:12:55 -04:00
assert_eval_with_jit ( " #{ << ~ " begin; " } \n #{ << ~ " end; " } " , stdout : '3' , success_count : 4 , insns : % i [ invokesuper ] )
2018-02-11 22:33:00 -05:00
begin ;
2018-02-10 11:09:55 -05:00
mod = Module . new {
def test
super + 2
end
}
klass = Class . new {
prepend mod
def test
1
end
}
print klass . new . test
2018-02-11 22:33:00 -05:00
end ;
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_invokeblock_leave
2018-05-02 11:12:55 -04:00
assert_eval_with_jit ( " #{ << ~ " begin; " } \n #{ << ~ " end; " } " , stdout : '2' , success_count : 2 , insns : % i [ invokeblock leave ] )
2018-02-11 22:33:00 -05:00
begin ;
2018-02-10 11:09:55 -05:00
def foo
yield
end
print foo { 2 }
2018-02-11 22:33:00 -05:00
end ;
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_throw
2018-05-02 11:12:55 -04:00
assert_eval_with_jit ( " #{ << ~ " begin; " } \n #{ << ~ " end; " } " , stdout : '4' , success_count : 2 , insns : % i [ throw ] )
2018-02-11 22:33:00 -05:00
begin ;
2018-02-10 11:09:55 -05:00
def test
proc do
if 1 + 1 == 1
return 3
else
return 4
end
5
end . call
end
print test
2018-02-11 22:33:00 -05:00
end ;
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_jump_branchif
2018-05-02 11:12:55 -04:00
assert_compile_once ( " #{ << ~ " begin; " } \n #{ << ~ 'end;' } " , result_inspect : 'nil' , insns : % i [ jump branchif ] )
2018-02-11 22:33:00 -05:00
begin ;
2018-02-10 11:09:55 -05:00
a = false
2018-05-02 11:12:55 -04:00
1 + 1 while a
2018-02-11 22:33:00 -05:00
end ;
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_branchunless
2018-05-02 11:12:55 -04:00
assert_compile_once ( " #{ << ~ " begin; " } \n #{ << ~ 'end;' } " , result_inspect : '1' , insns : % i [ branchunless ] )
2018-02-11 22:33:00 -05:00
begin ;
2018-02-10 11:09:55 -05:00
a = true
if a
1
else
2
end
2018-02-11 22:33:00 -05:00
end ;
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_branchnil
2018-05-02 11:12:55 -04:00
assert_compile_once ( " #{ << ~ " begin; " } \n #{ << ~ 'end;' } " , result_inspect : '3' , insns : % i [ branchnil ] )
2018-02-11 22:33:00 -05:00
begin ;
2018-02-10 11:09:55 -05:00
a = 2
a & . + ( 1 )
2018-02-11 22:33:00 -05:00
end ;
end
2018-02-10 11:09:55 -05:00
2018-04-21 09:15:43 -04:00
def test_compile_insn_checktype
2018-05-02 11:12:55 -04:00
assert_compile_once ( " #{ << ~ " begin; " } \n #{ << ~ 'end;' } " , result_inspect : '"42"' , insns : % i [ checktype ] )
2018-02-11 22:33:00 -05:00
begin ;
2018-02-10 11:09:55 -05:00
a = '2'
" 4 #{ a } "
2018-02-11 22:33:00 -05:00
end ;
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_inlinecache
2018-05-02 11:12:55 -04:00
assert_compile_once ( 'Struct' , result_inspect : 'Struct' , insns : % i [ getinlinecache setinlinecache ] )
2018-02-11 22:33:00 -05:00
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_once
2018-05-02 11:12:55 -04:00
assert_compile_once ( '/#{true}/o =~ "true" && $~.to_a' , result_inspect : '["true"]' , insns : % i [ once ] )
2018-02-11 22:33:00 -05:00
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_checkmatch_opt_case_dispatch
2018-05-02 11:41:43 -04:00
assert_compile_once ( " #{ << ~ " begin; " } \n #{ << ~ " end; " } " , result_inspect : '"world"' , insns : % i [ checkmatch opt_case_dispatch ] )
2018-02-11 22:33:00 -05:00
begin ;
2018-02-10 11:09:55 -05:00
case 'hello'
2018-05-02 11:12:55 -04:00
when 'hello'
2018-02-10 11:09:55 -05:00
'world'
end
2018-02-11 22:33:00 -05:00
end ;
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_opt_calc
2018-05-02 11:12:55 -04:00
assert_compile_once ( '4 + 2 - ((2 * 3 / 2) % 2)' , result_inspect : '5' , insns : % i [ opt_plus opt_minus opt_mult opt_div opt_mod ] )
2018-02-11 22:33:00 -05:00
assert_compile_once ( '4 + 2' , result_inspect : '6' )
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_opt_cmp
2018-05-02 11:12:55 -04:00
assert_compile_once ( '(1 == 1) && (1 != 2)' , result_inspect : 'true' , insns : % i [ opt_eq opt_neq ] )
2018-02-11 22:33:00 -05:00
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_opt_rel
2018-05-02 11:12:55 -04:00
assert_compile_once ( '1 < 2 && 1 <= 1 && 2 > 1 && 1 >= 1' , result_inspect : 'true' , insns : % i [ opt_lt opt_le opt_gt opt_ge ] )
2018-02-11 22:33:00 -05:00
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_opt_ltlt
2018-05-02 11:12:55 -04:00
assert_compile_once ( '[1] << 2' , result_inspect : '[1, 2]' , insns : % i [ opt_ltlt ] )
2018-02-11 22:33:00 -05:00
end
2018-02-10 11:09:55 -05:00
2018-02-13 10:58:38 -05:00
def test_compile_insn_opt_aref
# optimized call (optimized JIT) -> send call
2018-05-02 11:12:55 -04:00
assert_eval_with_jit ( " #{ << ~ " begin; " } \n #{ << ~ " end; " } " , stdout : '21' , success_count : 2 , min_calls : 1 , insns : % i [ opt_aref ] )
2018-02-13 10:58:38 -05:00
begin ;
obj = Object . new
def obj . [] ( h )
h
end
block = proc { | h | h [ 1 ] }
print block . call ( { 1 = > 2 } )
print block . call ( obj )
end ;
# send call -> optimized call (send JIT) -> optimized call
assert_eval_with_jit ( " #{ << ~ " begin; " } \n #{ << ~ " end; " } " , stdout : '122' , success_count : 1 , min_calls : 2 )
begin ;
obj = Object . new
def obj . [] ( h )
h
end
block = proc { | h | h [ 1 ] }
print block . call ( obj )
print block . call ( { 1 = > 2 } )
print block . call ( { 1 = > 2 } )
end ;
end
def test_compile_insn_opt_aset
2018-05-02 11:41:43 -04:00
assert_compile_once ( " #{ << ~ " begin; " } \n #{ << ~ " end; " } " , result_inspect : '5' , insns : % i [ opt_aset opt_aset_with ] )
2018-02-11 22:33:00 -05:00
begin ;
2018-02-10 11:09:55 -05:00
hash = { '1' = > 2 }
2018-02-13 10:58:38 -05:00
( hash [ '2' ] = 2 ) + ( hash [ 1 . to_s ] = 3 )
2018-02-11 22:33:00 -05:00
end ;
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_opt_length_size
2018-05-02 11:41:43 -04:00
assert_compile_once ( " #{ << ~ " begin; " } \n #{ << ~ " end; " } " , result_inspect : '4' , insns : % i [ opt_length opt_size ] )
2018-02-11 22:33:00 -05:00
begin ;
2018-02-10 11:09:55 -05:00
array = [ 1 , 2 ]
2018-02-11 22:33:00 -05:00
array . length + array . size
end ;
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_opt_empty_p
2018-05-02 11:12:55 -04:00
assert_compile_once ( '[].empty?' , result_inspect : 'true' , insns : % i [ opt_empty_p ] )
2018-02-11 22:33:00 -05:00
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_opt_succ
2018-05-02 11:12:55 -04:00
assert_compile_once ( '1.succ' , result_inspect : '2' , insns : % i [ opt_succ ] )
2018-02-11 22:33:00 -05:00
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_opt_not
2018-05-02 11:12:55 -04:00
assert_compile_once ( '!!true' , result_inspect : 'true' , insns : % i [ opt_not ] )
2018-02-11 22:33:00 -05:00
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_opt_regexpmatch1
2018-05-02 11:12:55 -04:00
assert_compile_once ( " /true/ =~ 'true' " , result_inspect : '0' , insns : % i [ opt_regexpmatch1 ] )
2018-02-11 22:33:00 -05:00
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_opt_regexpmatch2
2018-05-02 11:12:55 -04:00
assert_compile_once ( " 'true' =~ /true/ " , result_inspect : '0' , insns : % i [ opt_regexpmatch2 ] )
2018-02-11 22:33:00 -05:00
end
2018-02-10 11:09:55 -05:00
2018-02-11 22:33:00 -05:00
def test_compile_insn_opt_call_c_function
skip " support this in opt_call_c_function (low priority) "
2018-02-08 07:40:33 -05:00
end
def test_jit_output
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 )
assert_match ( / ^Successful MJIT finish$ / , err )
end
mjit_compile.c: use local variables for stack
if catch_except_p is FALSE. If catch_except_p is TRUE, stack values
should be on VM's stack when exception is thrown and the JIT-ed frame
is re-executed by VM's exception handler. If it's FALSE, the JIT-ed
frame won't be re-executed and don't need to keep values on VM's stack.
Using local variables allows us to reduce cfp->sp motion. Moving cfp->sp
is needed only for insns whose handles_frame? is false. So it improves
performance.
_mjit_compile_insn.erb: Prepare `stack_size` variable for GET_SP,
STACK_ADDR_FROM_TOP, TOPN macros. Share pc and sp motion partial view.
Use cancel handler created in mjit_compile.c.
_mjit_compile_send.erb: ditto. Also, when iseq->body->catch_except_p is
TRUE, this stops to call mjit_exec directly. I described the reason in
vm_insnhelper.h's comment for EXEC_EC_CFP.
_mjit_compile_pc_and_sp.erb: Shared logic for moving sp and pc. As you
can see from thsi file, when status->local_stack_p is TRUE and
insn.handles_frame? is false, moving sp is skipped. But if
insn.handles_frame? is true, values should be rolled back to VM's stack.
common.mk: add dependency for the file
_mjit_compile_insn_body.erb: Set sp value before canceling JIT on
DISPATCH_ORIGINAL_INSN. Replace GET_SP, STACK_ADDR_FROM_TOP, TOPN macros
for the case ocal_stack_p is TRUE and insn.handles_frame? is false.
In that case, values are not available on VM's stack and those macros
should be replaced.
mjit_compile.inc.erb: updated comments of macros which are supported by
JIT compiler. All references to `cfp->sp` should be replaced and thus
INC_SP, SET_SV, PUSH are no longer supported for now, because they are
not used now.
vm_exec.h: moved EXEC_EC_CFP definition to vm_insnhelper.h because it's
tighly coupled to CALL_METHOD.
vm_insnhelper.h: Have revised EXEC_EC_CFP definition moved from vm_exec.h.
Now it triggers mjit_exec for VM, and has the guard for catch_except_p
on JIT-ed code. See comments for details. CALL_METHOD delegates
triggering mjit_exec to EXEC_EC_CFP.
insns.def: Stopped using EXEC_EC_CFP for the case we don't want to
trigger mjit_exec. Those insns (defineclass, opt_call_c_function) are
not supported by JIT and it's safe to use RESTORE_REGS(), NEXT_INSN().
expandarray is changed to pass GET_SP() to replace the macro in
_mjit_compile_insn_body.erb.
vm_insnhelper.c: change to take sp for the above reason.
[close https://github.com/ruby/ruby/pull/1828]
This patch resurrects the performance which was attached in
[Feature #14235].
* Benchmark
Optcarrot (with configuration for benchmark_driver.gem)
https://github.com/benchmark-driver/optcarrot
$ benchmark-driver benchmark.yml --verbose 1 --rbenv 'before;before+JIT::before,--jit;after;after+JIT::after,--jit' --repeat-count 10
before: ruby 2.6.0dev (2018-03-04 trunk 62652) [x86_64-linux]
before+JIT: ruby 2.6.0dev (2018-03-04 trunk 62652) +JIT [x86_64-linux]
after: ruby 2.6.0dev (2018-03-04 local-variable.. 62652) [x86_64-linux]
last_commit=mjit_compile.c: use local variables for stack
after+JIT: ruby 2.6.0dev (2018-03-04 local-variable.. 62652) +JIT [x86_64-linux]
last_commit=mjit_compile.c: use local variables for stack
Calculating -------------------------------------
before before+JIT after after+JIT
optcarrot 53.552 59.680 53.697 63.358 fps
Comparison:
optcarrot
after+JIT: 63.4 fps
before+JIT: 59.7 fps - 1.06x slower
after: 53.7 fps - 1.18x slower
before: 53.6 fps - 1.18x slower
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@62655 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-03-04 02:04:40 -05:00
def test_local_stack_on_exception
assert_eval_with_jit ( " #{ << ~ " begin; " } \n #{ << ~ " end; " } " , stdout : '3' , success_count : 2 )
begin ;
def b
raise
rescue
2
end
def a
# Calling #b should be vm_exec, not direct mjit_exec.
# Otherwise `1` on local variable would be purged.
1 + b
end
print a
end ;
end
def test_local_stack_with_sp_motion_by_blockargs
assert_eval_with_jit ( " #{ << ~ " begin; " } \n #{ << ~ " end; " } " , stdout : '1' , success_count : 2 )
begin ;
def b ( base )
1
end
# This method is simple enough to have false in catch_except_p.
# So local_stack_p would be true in JIT compiler.
def a
m = method ( :b )
# ci->flag has VM_CALL_ARGS_BLOCKARG and cfp->sp is moved in vm_caller_setup_arg_block.
# So, for this send insn, JIT-ed code should use cfp->sp instead of local variables for stack.
Module . module_eval ( & m )
end
print a
end ;
end
compile.c: mark all ISeq ancestors as catch_except_p
This change assumes that continuously reading `parent_iseq` from block
ISeq would reach non-block ISeq finally.
test/ruby/test_jit.rb: add test that catches 2-depth exception
Combination of r62654 and r62678 caused following error in this test.
-e:12:in `wrapper': Stack consistency error (sp: 14, bp: 13) (fatal)
== disasm: #<ISeq:wrapper@-e:10 (10,0)-(12,3)> (catch: FALSE)===========
local table (size: 2, argc: 2 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
[ 2] paths<Arg> [ 1] prefixes<Arg>
0000 putself ( 11)[LiCa]
0001 getlocal_WC_0 paths
0003 getlocal_WC_0 prefixes
0005 opt_send_without_block <callinfo!mid:catch_true, argc:2, FCALL|ARGS_SIMPLE>, <callcache>
0008 leave ( 12)[Re]
As you can see, it says `catch: FALSE`, but obviously it catches
exception raised from `return path`.
As of r62655, it was kind of intentional because I only cared about
expiration of JIT-ed frame and I've thought calling `vm_exec` is only
needed once for it. So r62654 was NOT actually checking if it may catch
exception.
But for r62678, obviously we should set catch_except_p=TRUE for all
ISeqs which may catch exception. Otherwise catch table lookup would
fail.
With this bugfix, code generated by r62655 might be worse, but at least
while loop can be marked as `catch: FALSE` as expected.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@62717 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-03-10 09:52:12 -05:00
def test_catching_deep_exception
assert_eval_with_jit ( " #{ << ~ " begin; " } \n #{ << ~ " end; " } " , stdout : '1' , success_count : 4 )
begin ;
def catch_true ( paths , prefixes ) # catch_except_p: TRUE
prefixes . each do | prefix | # catch_except_p: TRUE
paths . each do | path | # catch_except_p: FALSE
return path
end
end
end
def wrapper ( paths , prefixes )
catch_true ( paths , prefixes )
end
print wrapper ( [ '1' ] , [ '2' ] )
end ;
end
_mjit_compile_send.erb: inline attr_reader call
_mjit_compile_send_guard.erb: carve out the shared logic to invalidate
inlined method call
common.mk: update dependency for this change
test_jit.rb: add test for attr_reader optimization
* Benchmark
```
require 'benchmark_driver'
Benchmark.driver do |x|
x.prelude %{
class C
attr_reader :a
def initialize
@a = 1
end
end
o = C.new
def l o
i = 0
while i < 1000000
o.a
i += 1
end
end
}
x.report 'aread', %{ l o }
x.loop_count 1000
x.rbenv 'before', 'before,--jit', 'after,--jit'
x.verbose
end
```
```
before: ruby 2.6.0dev (2018-04-20 trunk 63211) [x86_64-linux]
before,--jit: ruby 2.6.0dev (2018-04-20 trunk 63211) +JIT [x86_64-linux]
after,--jit: ruby 2.6.0dev (2018-04-20 trunk 63211) +JIT [x86_64-linux]
last_commit=_mjit_compile_send.erb: inline attr_reader call
Calculating -------------------------------------
before before,--jit after,--jit
aread 54.597 122.894 218.574 i/s - 1.000k times in 18.316102s 8.137089s 4.575106s
Comparison:
aread
after,--jit: 218.6 i/s
before,--jit: 122.9 i/s - 1.78x slower
before: 54.6 i/s - 4.00x slower
```
* Optcarrot
A little made faster?
fps: 71.35 -> 72.11
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@63212 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-04-20 09:42:08 -04:00
def test_attr_reader
assert_eval_with_jit ( " #{ << ~ " begin; " } \n #{ << ~ " end; " } " , stdout : " 4nil \n nil \n 6 " , success_count : 2 , min_calls : 2 )
begin ;
class A
attr_reader :a , :b
def initialize
@a = 2
end
def test
a
end
def undefined
b
end
end
a = A . new
print ( a . test * a . test )
p ( a . undefined )
p ( a . undefined )
# redefinition
class A
def test
3
end
end
print ( 2 * a . test )
end ;
end
2018-04-27 03:39:00 -04:00
def test_clean_so
Dir . mktmpdir ( " jit_test_clean_so_ " ) do | dir |
code = " x = 0; 10.times {|i|x+=i} "
eval_with_jit ( { " TMPDIR " = > dir } , code )
assert_send ( [ Dir , :empty? , dir ] )
eval_with_jit ( { " TMPDIR " = > dir } , code , save_temps : true )
assert_not_send ( [ Dir , :empty? , dir ] )
end
end
2018-05-02 08:55:57 -04:00
def test_lambda_longjmp
assert_eval_with_jit ( " #{ << ~ " begin; " } \n #{ << ~ " end; " } " , stdout : '5' , success_count : 1 )
begin ;
fib = lambda do | x |
return x if x == 0 || x == 1
fib . call ( x - 1 ) + fib . call ( x - 2 )
end
print fib . call ( 5 )
end ;
end
2018-02-08 07:40:33 -05:00
private
2018-02-10 11:09:55 -05:00
# The shortest way to test one proc
2018-05-02 11:12:55 -04:00
def assert_compile_once ( script , result_inspect : , insns : [ ] )
2018-02-11 23:35:16 -05:00
if script . match? ( / \ A \ n.+ \ n \ z /m )
script = script . gsub ( / ^ / , ' ' )
else
script = " #{ script } "
end
2018-05-02 11:12:55 -04:00
assert_eval_with_jit ( " p proc { #{ script } }.call " , stdout : " #{ result_inspect } \n " , success_count : 1 , insns : insns , uplevel : 4 )
2018-02-10 11:09:55 -05:00
end
2018-02-08 07:40:33 -05:00
# Shorthand for normal test cases
2018-05-02 11:12:55 -04:00
def assert_eval_with_jit ( script , stdout : nil , success_count : , min_calls : 1 , insns : [ ] , uplevel : 3 )
2018-02-13 10:58:38 -05:00
out , err = eval_with_jit ( script , verbose : 1 , min_calls : min_calls )
2018-02-11 22:33:00 -05:00
actual = err . scan ( / ^ #{ JIT_SUCCESS_PREFIX } : / ) . size
2018-03-11 05:32:48 -04:00
# Debugging on CI
2018-03-30 11:08:52 -04:00
if err . include? ( " error trying to exec 'cc1': execvp: No such file or directory " ) && RbConfig :: CONFIG [ 'CC' ] . start_with? ( 'gcc' )
2018-03-17 02:33:28 -04:00
$stderr . puts " \n test/ruby/test_jit.rb: DEBUG OUTPUT: "
cc1 = %x`gcc -print-prog-name=cc1` . rstrip
if $? . success?
$stderr . puts " cc1 path: #{ cc1 } "
$stderr . puts " executable?: #{ File . executable? ( cc1 ) } "
$stderr . puts " ls: \n #{ IO . popen ( [ 'ls' , '-la' , File . dirname ( cc1 ) ] , & :read ) } "
else
$stderr . puts 'Failed to fetch cc1 path'
end
2018-03-11 05:32:48 -04:00
end
2018-05-02 11:12:55 -04:00
# Make sure that the script has insns expected to be tested
used_insns = method_insns ( script )
insns . each do | insn |
unless used_insns . include? ( insn )
$stderr . puts
warn " ' #{ insn } ' insn is not included in the script. Actual insns are: #{ used_insns . join ( ' ' ) } \n " , uplevel : uplevel
end
2018-05-02 11:41:43 -04:00
TestJIT . untested_insns . delete ( insn )
2018-05-02 11:12:55 -04:00
end
2018-02-11 22:33:00 -05:00
assert_equal (
success_count , actual ,
" Expected #{ success_count } times of JIT success, but succeeded #{ actual } times. \n \n " \
" script: \n #{ code_block ( script ) } \n stderr: \n #{ code_block ( err ) } " ,
)
2018-02-08 07:40:33 -05:00
if stdout
2018-02-10 11:09:55 -05:00
assert_equal ( stdout , out , " Expected stdout #{ out . inspect } to match #{ stdout . inspect } with script: \n #{ code_block ( script ) } " )
2018-02-08 07:40:33 -05:00
end
end
2018-05-02 11:12:55 -04:00
# Collect block's insns or defined method's insns, which are expected to be JIT-ed.
2018-05-02 11:41:43 -04:00
# Note that this intentionally excludes insns in script's toplevel because they are not JIT-ed.
2018-05-02 11:12:55 -04:00
def method_insns ( script )
insns = [ ]
RubyVM :: InstructionSequence . compile ( script ) . to_a . last . each do | ( insn , * args ) |
case insn
when :putiseq , :send
insns += collect_insns ( args . last )
end
end
insns . uniq
end
# Recursively collect insns in iseq_array
def collect_insns ( iseq_array )
return [ ] if iseq_array . nil?
insns = iseq_array . last . select { | x | x . is_a? ( Array ) } . map ( & :first )
iseq_array . last . each do | ( insn , * args ) |
case insn
when :putiseq , :send
insns += collect_insns ( args . last )
end
end
insns
end
2018-02-08 07:40:33 -05:00
# Run Ruby script with --jit-wait (Synchronous JIT compilation).
# Returns [stdout, stderr]
2018-04-27 03:39:00 -04:00
def eval_with_jit ( env = nil , script , ** opts )
2018-02-11 22:33:00 -05:00
stdout , stderr , status = super
2018-02-08 07:58:27 -05:00
assert_equal ( true , status . success? , " Failed to run script with JIT: \n #{ code_block ( script ) } \n stdout: \n #{ code_block ( stdout ) } \n stderr: \n #{ code_block ( stderr ) } " )
2018-02-08 07:40:33 -05:00
[ stdout , stderr ]
end
def code_block ( code )
" ``` \n #{ code } \n ``` \n \n "
end
end