From fb80f6c7ba450993419eba58e30f4f62cb99c338 Mon Sep 17 00:00:00 2001 From: k0kubun Date: Mon, 24 Sep 2018 12:40:28 +0000 Subject: [PATCH] insns.def: optimize & and | of Integer [experimental] not optimizing Array#& and Array#| because vm_insnhelper.c can't easily inline it (large amount of array.c code would be needed in vm_insnhelper.c) and the method body is a little complicated compared to Integer's ones. So I thought only Integer#& and Integer#| have a significant impact, and eliminating unnecessary branches would contribute to JIT's performance. vm_insnhelper.c: ditto tool/transform_mjit_header.rb: make sure these instructions are inlined on JIT. compile.c: compile vm_opt_and and vm_opt_or. id.def: define id for them to be used in compile.c and vm*.c vm.c: track redefinition of Integer#& and Integer#| vm_core.h: allow detecting redefinition of & and | test/ruby/test_jit.rb: test new insns test/ruby/test_optimization.rb: ditto * Optcarrot benchmark This is a kind of experimental thing but I'm committing this since the performance impact is significant especially on Optcarrot with JIT. $ benchmark-driver benchmark.yml --rbenv 'before::before --disable-gems;before+JIT::before --disable-gems --jit;after::after --disable-gems;after+JIT::after --disable-gems --jit' -v --repeat-count 24 before: ruby 2.6.0dev (2018-09-24 trunk 64821) [x86_64-linux] before+JIT: ruby 2.6.0dev (2018-09-24 trunk 64821) +JIT [x86_64-linux] after: ruby 2.6.0dev (2018-09-24 opt_and 64821) [x86_64-linux] last_commit=opt_or after+JIT: ruby 2.6.0dev (2018-09-24 opt_and 64821) +JIT [x86_64-linux] last_commit=opt_or Calculating ------------------------------------- before before+JIT after after+JIT Optcarrot Lan_Master.nes 51.460 66.315 53.023 71.173 fps Comparison: Optcarrot Lan_Master.nes after+JIT: 71.2 fps before+JIT: 66.3 fps - 1.07x slower after: 53.0 fps - 1.34x slower before: 51.5 fps - 1.38x slower [close https://github.com/ruby/ruby/pull/1963] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@64824 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- compile.c | 2 ++ defs/id.def | 2 ++ insns.def | 28 ++++++++++++++++++++++++++++ test/ruby/test_jit.rb | 8 ++++++++ test/ruby/test_optimization.rb | 10 ++++++++++ tool/transform_mjit_header.rb | 2 ++ vm.c | 2 ++ vm_core.h | 2 ++ vm_insnhelper.c | 24 ++++++++++++++++++++++++ 9 files changed, 80 insertions(+) diff --git a/compile.c b/compile.c index 83d992a2e9..0a2e076b39 100644 --- a/compile.c +++ b/compile.c @@ -3245,6 +3245,8 @@ iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj) case idGE: SP_INSN(ge); return COMPILE_OK; case idLTLT: SP_INSN(ltlt); return COMPILE_OK; case idAREF: SP_INSN(aref); return COMPILE_OK; + case idAnd: SP_INSN(and); return COMPILE_OK; + case idOr: SP_INSN(or); return COMPILE_OK; } break; case 2: diff --git a/defs/id.def b/defs/id.def index e1a66f5030..fe61d254c9 100644 --- a/defs/id.def +++ b/defs/id.def @@ -97,6 +97,8 @@ token_ops = %[\ Eqq === EQQ Neq != NEQ Not ! + And & + Or | Backquote ` EqTilde =~ MATCH NeqTilde !~ NMATCH diff --git a/insns.def b/insns.def index 742cd87960..cacc5b709f 100644 --- a/insns.def +++ b/insns.def @@ -1216,6 +1216,34 @@ opt_ltlt } } +/* optimized X&Y. */ +DEFINE_INSN +opt_and +(CALL_INFO ci, CALL_CACHE cc) +(VALUE recv, VALUE obj) +(VALUE val) +{ + val = vm_opt_and(recv, obj); + + if (val == Qundef) { + CALL_SIMPLE_METHOD(); + } +} + +/* optimized X|Y. */ +DEFINE_INSN +opt_or +(CALL_INFO ci, CALL_CACHE cc) +(VALUE recv, VALUE obj) +(VALUE val) +{ + val = vm_opt_or(recv, obj); + + if (val == Qundef) { + CALL_SIMPLE_METHOD(); + } +} + /* [] */ DEFINE_INSN opt_aref diff --git a/test/ruby/test_jit.rb b/test/ruby/test_jit.rb index 50f318c2a7..3770faf038 100644 --- a/test/ruby/test_jit.rb +++ b/test/ruby/test_jit.rb @@ -478,6 +478,14 @@ class TestJIT < Test::Unit::TestCase assert_compile_once('[1] << 2', result_inspect: '[1, 2]', insns: %i[opt_ltlt]) end + def test_compile_insn_opt_and + assert_compile_once('1 & 3', result_inspect: '1', insns: %i[opt_and]) + end + + def test_compile_insn_opt_or + assert_compile_once('1 | 3', result_inspect: '3', insns: %i[opt_or]) + end + def test_compile_insn_opt_aref skip_on_mswin # optimized call (optimized JIT) -> send call diff --git a/test/ruby/test_optimization.rb b/test/ruby/test_optimization.rb index d4a1fdbcea..c425254661 100644 --- a/test/ruby/test_optimization.rb +++ b/test/ruby/test_optimization.rb @@ -187,6 +187,16 @@ class TestRubyOptimization < Test::Unit::TestCase assert_redefine_method('String', '<<', 'assert_equal "b", "a" << "b"') end + def test_fixnum_and + assert_equal 1, 1&3 + assert_redefine_method('Integer', '&', 'assert_equal 3, 1&3') + end + + def test_fixnum_or + assert_equal 3, 1|3 + assert_redefine_method('Integer', '|', 'assert_equal 1, 3|1') + end + def test_array_plus assert_equal [1,2], [1]+[2] assert_redefine_method('Array', '+', 'assert_equal [2], [1]+[2]') diff --git a/tool/transform_mjit_header.rb b/tool/transform_mjit_header.rb index 936a0e1945..1000172352 100644 --- a/tool/transform_mjit_header.rb +++ b/tool/transform_mjit_header.rb @@ -52,6 +52,8 @@ module MJITHeader 'vm_opt_gt', 'vm_opt_ge', 'vm_opt_ltlt', + 'vm_opt_and', + 'vm_opt_or', 'vm_opt_aref', 'vm_opt_aset', 'vm_opt_aref_with', diff --git a/vm.c b/vm.c index da9afe2fc7..eb47a83d5f 100644 --- a/vm.c +++ b/vm.c @@ -1610,6 +1610,8 @@ vm_init_redefined_flag(void) OP(Max, MAX), (C(Array)); OP(Min, MIN), (C(Array)); OP(Call, CALL), (C(Proc)); + OP(And, AND), (C(Integer)); + OP(Or, OR), (C(Integer)); #undef C #undef OP } diff --git a/vm_core.h b/vm_core.h index bb6409719b..1ab49b05de 100644 --- a/vm_core.h +++ b/vm_core.h @@ -531,6 +531,8 @@ enum ruby_basic_operators { BOP_MAX, BOP_MIN, BOP_CALL, + BOP_AND, + BOP_OR, BOP_LAST_ }; diff --git a/vm_insnhelper.c b/vm_insnhelper.c index 29b935803b..29e6545398 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -3652,6 +3652,30 @@ vm_opt_ltlt(VALUE recv, VALUE obj) } } +static VALUE +vm_opt_and(VALUE recv, VALUE obj) +{ + if (FIXNUM_2_P(recv, obj) && + BASIC_OP_UNREDEFINED_P(BOP_AND, INTEGER_REDEFINED_OP_FLAG)) { + return LONG2NUM(FIX2LONG(recv) & FIX2LONG(obj)); + } + else { + return Qundef; + } +} + +static VALUE +vm_opt_or(VALUE recv, VALUE obj) +{ + if (FIXNUM_2_P(recv, obj) && + BASIC_OP_UNREDEFINED_P(BOP_OR, INTEGER_REDEFINED_OP_FLAG)) { + return LONG2NUM(FIX2LONG(recv) | FIX2LONG(obj)); + } + else { + return Qundef; + } +} + static VALUE vm_opt_aref(VALUE recv, VALUE obj) {