mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
compile.c: optimize literal String range in case/when dispatch
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, but Ranges in case/when dispatch can be frozen at compile time. * 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. [ruby-core:80290] [Feature #13355] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@58233 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
48bc5004c0
commit
120976b9f9
2 changed files with 46 additions and 0 deletions
31
compile.c
31
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 str_beg = OPERAND_AT(beg, 0);
|
||||||
|
VALUE str_end = OPERAND_AT(end, 0);
|
||||||
|
int excl = FIX2INT(OPERAND_AT(range, 0));
|
||||||
|
VALUE lit_range = rb_range_new(str_beg, str_end, excl);
|
||||||
|
|
||||||
|
iseq_add_mark_object(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)) {
|
if (IS_INSN_ID(iobj, leave)) {
|
||||||
remove_unreachable_chunk(iseq, iobj->link.next);
|
remove_unreachable_chunk(iseq, iobj->link.next);
|
||||||
}
|
}
|
||||||
|
|
|
@ -495,4 +495,19 @@ EOS
|
||||||
bug11816 = '[ruby-core:74993] [Bug #11816]'
|
bug11816 = '[ruby-core:74993] [Bug #11816]'
|
||||||
assert_ruby_status([], 'nil&.foo &&= false', bug11816)
|
assert_ruby_status([], 'nil&.foo &&= false', bug11816)
|
||||||
end
|
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
|
end
|
||||||
|
|
Loading…
Reference in a new issue