ruby-changes:40156
From: nobu <ko1@a...>
Date: Fri, 23 Oct 2015 11:58:29 +0900 (JST)
Subject: [ruby-changes:40156] nobu:r52237 (trunk): compile.c: optimize method chain
nobu 2015-10-23 11:58:22 +0900 (Fri, 23 Oct 2015) New Revision: 52237 http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=52237 Log: compile.c: optimize method chain * compile.c (iseq_peephole_optimize): optimize lengthy safe navigation method chain. [Feature #11537] Modified files: trunk/ChangeLog trunk/compile.c trunk/test/ruby/test_iseq.rb Index: ChangeLog =================================================================== --- ChangeLog (revision 52236) +++ ChangeLog (revision 52237) @@ -1,3 +1,8 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1 +Fri Oct 23 11:58:21 2015 Nobuyoshi Nakada <nobu@r...> + + * compile.c (iseq_peephole_optimize): optimize lengthy safe + navigation method chain. [Feature #11537] + Fri Oct 23 10:58:41 2015 Shugo Maeda <shugo@r...> * lib/matrix/eigenvalue_decomposition.rb (tridiagonalize): fix Index: compile.c =================================================================== --- compile.c (revision 52236) +++ compile.c (revision 52237) @@ -1967,33 +1967,36 @@ iseq_peephole_optimize(rb_iseq_t *iseq, https://github.com/ruby/ruby/blob/trunk/compile.c#L1967 * if L2 */ INSN *nobj = (INSN *)get_destination_insn(iobj); - if (nobj->insn_id == BIN(jump)) { - OPERAND_AT(iobj, 0) = OPERAND_AT(nobj, 0); - } + INSN *pobj = (INSN *)iobj->link.prev; + int prev_dup = (pobj && pobj->insn_id == BIN(dup)); - if (nobj->insn_id == BIN(dup)) { - /* - * dup - * if L1 - * ... - * L1: - * dup - * if L2 - * => - * dup - * if L2 - * ... - * L1: - * dup - * if L2 - */ - INSN *pobj = (INSN *)iobj->link.prev; - nobj = (INSN *)nobj->link.next; - /* basic blocks, with no labels in the middle */ - if ((pobj && pobj->insn_id == BIN(dup)) && - (nobj && nobj->insn_id == iobj->insn_id)) { + for (;;) { + if (nobj->insn_id == BIN(jump)) { + OPERAND_AT(iobj, 0) = OPERAND_AT(nobj, 0); + } + else if (prev_dup && nobj->insn_id == BIN(dup) && + !!(nobj = (INSN *)nobj->link.next) && + /* basic blocks, with no labels in the middle */ + nobj->insn_id == iobj->insn_id) { + /* + * dup + * if L1 + * ... + * L1: + * dup + * if L2 + * => + * dup + * if L2 + * ... + * L1: + * dup + * if L2 + */ OPERAND_AT(iobj, 0) = OPERAND_AT(nobj, 0); } + else break; + nobj = (INSN *)get_destination_insn(nobj); } } Index: test/ruby/test_iseq.rb =================================================================== --- test/ruby/test_iseq.rb (revision 52236) +++ test/ruby/test_iseq.rb (revision 52237) @@ -8,8 +8,12 @@ class TestISeq < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_iseq.rb#L8 assert_normal_exit('p RubyVM::InstructionSequence.compile("1", "mac", "", 0).to_a', bug5894) end + def compile(src, line = nil, opt = nil) + RubyVM::InstructionSequence.new(src, __FILE__, __FILE__, line, opt) + end + def lines src - body = RubyVM::InstructionSequence.new(src).to_a[13] + body = compile(src).to_a[13] body.find_all{|e| e.kind_of? Fixnum} end @@ -52,7 +56,7 @@ class TestISeq < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_iseq.rb#L56 end if defined?(RubyVM::InstructionSequence.load) def test_loaded_cdhash_mark - iseq = RubyVM::InstructionSequence.compile(<<-'end;', __FILE__, __FILE__, __LINE__+1) + iseq = compile(<<-'end;', __LINE__+1) def bug(kw) case kw when "false" then false @@ -73,11 +77,11 @@ class TestISeq < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_iseq.rb#L77 def test_disasm_encoding src = "\u{3042} = 1; \u{3042}; \u{3043}" - asm = RubyVM::InstructionSequence.compile(src).disasm + asm = compile(src).disasm assert_equal(src.encoding, asm.encoding) assert_predicate(asm, :valid_encoding?) src.encode!(Encoding::Shift_JIS) - asm = RubyVM::InstructionSequence.compile(src).disasm + asm = compile(src).disasm assert_equal(src.encoding, asm.encoding) assert_predicate(asm, :valid_encoding?) end @@ -147,7 +151,7 @@ class TestISeq < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_iseq.rb#L151 def test_disable_opt src = "a['foo'] = a['bar']; 'a'.freeze" - _,_,_,_,_,_,_,_,_,_,_,_,_,body= RubyVM::InstructionSequence.compile(src, __FILE__, __FILE__, __LINE__, false).to_a + body= compile(src, __LINE__, false).to_a[13] body.each{|insn| next unless Array === insn op = insn.first @@ -168,11 +172,18 @@ class TestISeq < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_iseq.rb#L172 code = <<-'EOS' ['foo', 'foo', "#{$f}foo", "#{'foo'}"] EOS - s1, s2, s3, s4 = RubyVM::InstructionSequence.compile(code, __FILE__, __FILE__, line, {frozen_string_literal: true}).eval + s1, s2, s3, s4 = compile(code, line, {frozen_string_literal: true}).eval assert_predicate(s1, :frozen?) assert_predicate(s2, :frozen?) assert_not_predicate(s3, :frozen?) assert_predicate(s4, :frozen?) assert_same(s1, s2) end + + def test_safe_call_chain + src = "a.?a.?a.?a.?a.?a" + body = compile(src, __LINE__, {peephole_optimization: true}).to_a[13] + labels = body.select {|op, arg| op == :branchnil}.map {|op, arg| arg} + assert_equal(1, labels.uniq.size) + end end -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/