From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on dcvr.yhbt.net X-Spam-Level: X-Spam-ASN: AS43350 46.166.144.0/21 X-Spam-Status: No, score=-2.9 required=3.0 tests=AWL,BAYES_00, RCVD_IN_MSPIKE_BL,RCVD_IN_MSPIKE_ZBI,RCVD_IN_XBL,RDNS_NONE,SPF_FAIL, SPF_HELO_FAIL,TO_EQ_FM_DOM_SPF_FAIL shortcircuit=no autolearn=no autolearn_force=no version=3.4.0 Received: from 80x24.org (unknown [46.166.148.176]) by dcvr.yhbt.net (Postfix) with ESMTP id 72EC02095B for ; Thu, 23 Mar 2017 08:20:36 +0000 (UTC) From: Eric Wong To: spew@80x24.org Subject: [PATCH] compile.c: optimize literal String range in case/when dispatch Date: Thu, 23 Mar 2017 08:20:34 +0000 Message-Id: <20170323082034.9486-1-e@80x24.org> List-Id: This is similar in spirit to opt_case_dispatch as the literal Range here is guaranteed to be immutable when used for checkmatch. Normal range literals with non-frozen strings are actually mutable, as Range#begin and Range#end exposes the strings to modification. So those Range objects cannot be frozen without breaking compatibility. * compile.c (iseq_peephole_optimize): persistent Range creation when String literals are used as beginning and end of range when used for case/when dispatch. --- compile.c | 31 +++++++++++++++++++++++++++++++ test/ruby/test_optimization.rb | 15 +++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/compile.c b/compile.c index e3d66b6809..496e4d8857 100644 --- a/compile.c +++ b/compile.c @@ -2144,6 +2144,37 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal } } + /* + * putstring "beg" + * putstring "end" + * newrange excl + * + * ==> + * + * putobject "beg".."end" + */ + if (IS_INSN_ID(iobj, checkmatch)) { + INSN *range = (INSN *)get_prev_insn(iobj); + INSN *beg, *end; + + if (range && IS_INSN_ID(range, newrange) && + (end = (INSN *)get_prev_insn(range)) != 0 && + IS_INSN_ID(end, putstring) && + (beg = (INSN *)get_prev_insn(end)) != 0 && + IS_INSN_ID(beg, putstring)) { + VALUE sbeg = OPERAND_AT(beg, 0); + VALUE send = OPERAND_AT(end, 0); + int excl = FIX2INT(OPERAND_AT(range, 0)); + VALUE lit_range = rb_range_new(sbeg, send, excl); + + iseq_add_mark_object_compile_time(iseq, lit_range); + REMOVE_ELEM(&beg->link); + REMOVE_ELEM(&end->link); + range->insn_id = BIN(putobject); + OPERAND_AT(range, 0) = lit_range; + } + } + if (IS_INSN_ID(iobj, leave)) { remove_unreachable_chunk(iseq, iobj->link.next); } diff --git a/test/ruby/test_optimization.rb b/test/ruby/test_optimization.rb index 502d12389e..aa3c82fdfd 100644 --- a/test/ruby/test_optimization.rb +++ b/test/ruby/test_optimization.rb @@ -490,4 +490,19 @@ def test_nil_safe_conditional_assign bug11816 = '[ruby-core:74993] [Bug #11816]' assert_ruby_status([], 'nil&.foo &&= false', bug11816) end + + def test_peephole_string_literal_range + code = <<-EOF + case ver + when "2.0.0".."2.3.2" then :foo + when "1.8.0"..."1.8.8" then :bar + end + EOF + iseq = RubyVM::InstructionSequence.compile(code) + insn = iseq.disasm + assert_match %r{putobject\s+#{Regexp.quote('"1.8.0"..."1.8.8"')}}, insn + assert_match %r{putobject\s+#{Regexp.quote('"2.0.0".."2.3.2"')}}, insn + assert_no_match /putstring/, insn + assert_no_match /newrange/, insn + end end -- EW