Feature #10423 ยป opt_str_lit-v4.patch
benchmark/bm_vm2_array_delete_lit.rb | ||
---|---|---|
1 |
ary = [] |
|
2 |
i = 0 |
|
3 |
while i<6_000_000 # while loop 2 |
|
4 |
i += 1 |
|
5 |
ary.delete("foo") |
|
6 |
end |
benchmark/bm_vm2_array_include_lit.rb | ||
---|---|---|
1 |
ary = [] |
|
2 |
i = 0 |
|
3 |
while i<6_000_000 # while loop 2 |
|
4 |
i += 1 |
|
5 |
ary.include?("foo") |
|
6 |
end |
benchmark/bm_vm2_hash_aref_lit.rb | ||
---|---|---|
1 |
h = { "foo" => nil } |
|
2 |
i = 0 |
|
3 |
while i<6_000_000 # while loop 2 |
|
4 |
i += 1 |
|
5 |
h["foo"] |
|
6 |
end |
benchmark/bm_vm2_hash_aset_lit.rb | ||
---|---|---|
1 |
h = {} |
|
2 |
i = 0 |
|
3 |
while i<6_000_000 # while loop 2 |
|
4 |
i += 1 |
|
5 |
h["foo"] = nil |
|
6 |
end |
benchmark/bm_vm2_hash_delete_lit.rb | ||
---|---|---|
1 |
h = {} |
|
2 |
i = 0 |
|
3 |
while i<6_000_000 # while loop 2 |
|
4 |
i += 1 |
|
5 |
h.delete("foo") |
|
6 |
end |
benchmark/bm_vm2_set_include_lit.rb | ||
---|---|---|
1 |
require 'set' |
|
2 |
set = Set.new |
|
3 |
i = 0 |
|
4 |
while i<6_000_000 # while loop 2 |
|
5 |
i += 1 |
|
6 |
set.include?("foo") |
|
7 |
end |
benchmark/bm_vm2_str_delete.rb | ||
---|---|---|
1 |
str = '' |
|
2 |
i = 0 |
|
3 |
while i<6_000_000 # while loop 2 |
|
4 |
i += 1 |
|
5 |
str.delete("foo") |
|
6 |
end |
benchmark/bm_vm2_str_eq1.rb | ||
---|---|---|
1 |
i = 0 |
|
2 |
foo = "literal" |
|
3 |
while i<6_000_000 # benchmark loop 2 |
|
4 |
i += 1 |
|
5 |
foo == "literal" |
|
6 |
end |
benchmark/bm_vm2_str_eq2.rb | ||
---|---|---|
1 |
i = 0 |
|
2 |
foo = "literal" |
|
3 |
while i<6_000_000 # benchmark loop 2 |
|
4 |
i += 1 |
|
5 |
"literal" == foo |
|
6 |
end |
benchmark/bm_vm2_str_eqq1.rb | ||
---|---|---|
1 |
i = 0 |
|
2 |
foo = "literal" |
|
3 |
while i<6_000_000 # benchmark loop 2 |
|
4 |
i += 1 |
|
5 |
foo === "literal" |
|
6 |
end |
benchmark/bm_vm2_str_eqq2.rb | ||
---|---|---|
1 |
i = 0 |
|
2 |
foo = "literal" |
|
3 |
while i<6_000_000 # benchmark loop 2 |
|
4 |
i += 1 |
|
5 |
"literal" === foo |
|
6 |
end |
benchmark/bm_vm2_str_fmt.rb | ||
---|---|---|
1 |
i = 0 |
|
2 |
while i<6_000_000 # benchmark loop 2 |
|
3 |
i += 1 |
|
4 |
"%d" % i |
|
5 |
end |
benchmark/bm_vm2_str_gsub_bang_lit.rb | ||
---|---|---|
1 |
i = 0 |
|
2 |
str = "" |
|
3 |
while i<6_000_000 # benchmark loop 2 |
|
4 |
i += 1 |
|
5 |
str.gsub!("nomatch", "") |
|
6 |
end |
benchmark/bm_vm2_str_gsub_bang_re.rb | ||
---|---|---|
1 |
i = 0 |
|
2 |
str = "" |
|
3 |
while i<6_000_000 # benchmark loop 2 |
|
4 |
i += 1 |
|
5 |
str.gsub!(/a/, "") |
|
6 |
end |
benchmark/bm_vm2_str_gsub_re.rb | ||
---|---|---|
1 |
i = 0 |
|
2 |
str = "" |
|
3 |
while i<6_000_000 # benchmark loop 2 |
|
4 |
i += 1 |
|
5 |
str.gsub(/a/, "") |
|
6 |
end |
benchmark/bm_vm2_str_plus1.rb | ||
---|---|---|
1 |
i = 0 |
|
2 |
foo = "a" |
|
3 |
while i<6_000_000 # benchmark loop 2 |
|
4 |
i += 1 |
|
5 |
foo + "b" |
|
6 |
end |
benchmark/bm_vm2_str_plus2.rb | ||
---|---|---|
1 |
i = 0 |
|
2 |
foo = "a" |
|
3 |
while i<6_000_000 # benchmark loop 2 |
|
4 |
i += 1 |
|
5 |
"b" + foo |
|
6 |
end |
benchmark/bm_vm2_str_tr_bang.rb | ||
---|---|---|
1 |
i = 0 |
|
2 |
str = "a" |
|
3 |
while i<6_000_000 # benchmark loop 2 |
|
4 |
i += 1 |
|
5 |
str.tr!("a", "A") |
|
6 |
str.tr!("A", "a") |
|
7 |
end |
benchmark/bm_vm2_strcat.rb | ||
---|---|---|
1 |
i = 0 |
|
2 |
str = "" |
|
3 |
while i<6_000_000 # benchmark loop 2 |
|
4 |
i += 1 |
|
5 |
str << "const" |
|
6 |
str.clear |
|
7 |
end |
common.mk | ||
---|---|---|
640 | 640 |
VM_CORE_H_INCLUDES = {$(VPATH)}vm_core.h {$(VPATH)}thread_$(THREAD_MODEL).h \ |
641 | 641 |
{$(VPATH)}node.h {$(VPATH)}method.h {$(VPATH)}ruby_atomic.h \ |
642 | 642 |
{$(VPATH)}vm_debug.h {$(VPATH)}id.h {$(VPATH)}thread_native.h \ |
643 |
$(CCAN_LIST_INCLUDES) |
|
643 |
$(CCAN_LIST_INCLUDES) {$(VPATH)}opt_method.h
|
|
644 | 644 | |
645 | 645 |
### |
646 | 646 | |
... | ... | |
827 | 827 |
$(VM_CORE_H_INCLUDES) {$(VPATH)}vm_method.c {$(VPATH)}vm_eval.c \ |
828 | 828 |
{$(VPATH)}vm_insnhelper.c {$(VPATH)}vm_insnhelper.h {$(VPATH)}vm_exec.c \ |
829 | 829 |
{$(VPATH)}vm_exec.h {$(VPATH)}insns.def {$(VPATH)}vmtc.inc \ |
830 |
{$(VPATH)}vm.inc {$(VPATH)}insns.inc \ |
|
830 |
{$(VPATH)}vm.inc {$(VPATH)}insns.inc {$(VPATH)}opt_method.inc \
|
|
831 | 831 |
{$(VPATH)}internal.h {$(VPATH)}vm.h {$(VPATH)}constant.h \ |
832 | 832 |
$(PROBES_H_INCLUDES) {$(VPATH)}probes_helper.h {$(VPATH)}vm_opts.h |
833 | 833 |
vm_dump.$(OBJEXT): {$(VPATH)}vm_dump.c $(RUBY_H_INCLUDES) \ |
... | ... | |
939 | 939 | |
940 | 940 |
insns: $(INSNS) |
941 | 941 | |
942 |
opt_method.h: $(srcdir)/tool/generic_erb.rb \ |
|
943 |
$(srcdir)/template/opt_method.h.tmpl \ |
|
944 |
$(srcdir)/defs/opt_method.def |
|
945 |
$(ECHO) generating $@ |
|
946 |
$(Q) $(BASERUBY) $(srcdir)/tool/generic_erb.rb --output=$@ \ |
|
947 |
$(srcdir)/template/opt_method.h.tmpl |
|
948 | ||
949 |
opt_method.inc: $(srcdir)/tool/generic_erb.rb \ |
|
950 |
$(srcdir)/template/opt_method.inc.tmpl \ |
|
951 |
$(srcdir)/defs/opt_method.def |
|
952 |
$(ECHO) generating $@ |
|
953 |
$(Q) $(BASERUBY) $(srcdir)/tool/generic_erb.rb --output=$@ \ |
|
954 |
$(srcdir)/template/opt_method.inc.tmpl |
|
955 | ||
942 | 956 |
id.h: $(srcdir)/tool/generic_erb.rb $(srcdir)/template/id.h.tmpl $(srcdir)/defs/id.def |
943 | 957 |
$(ECHO) generating $@ |
944 | 958 |
$(Q) $(BASERUBY) $(srcdir)/tool/generic_erb.rb --output=$@ \ |
compile.c | ||
---|---|---|
1703 | 1703 |
return 0; |
1704 | 1704 |
} |
1705 | 1705 | |
1706 |
#define new_recvinfo_for_recv(iseq,str,mid,klass) \ |
|
1707 |
new_recvinfo_for_recv_(iseq,str,OM_##mid##__##klass) |
|
1708 |
static VALUE |
|
1709 |
new_recvinfo_for_recv_(rb_iseq_t *iseq, VALUE str, |
|
1710 |
enum ruby_optimized_method om) |
|
1711 |
{ |
|
1712 |
VALUE ri = rb_ary_new_from_args(2, str, INT2FIX(om)); |
|
1713 | ||
1714 |
hide_obj(ri); |
|
1715 |
iseq_add_mark_object(iseq, ri); |
|
1716 | ||
1717 |
return ri; |
|
1718 |
} |
|
1719 | ||
1720 |
#define new_recvinfo_for_arg(iseq,str,mid,klass,off) \ |
|
1721 |
new_recvinfo_for_arg_((iseq),(str),(OM_##mid##__##klass),\ |
|
1722 |
(OM_TMASK_##klass),(off)) |
|
1723 |
static VALUE |
|
1724 |
new_recvinfo_for_arg_(rb_iseq_t *iseq, VALUE str, |
|
1725 |
enum ruby_optimized_method om, |
|
1726 |
VALUE tmask, int recv_off) |
|
1727 |
{ |
|
1728 |
VALUE ri = rb_ary_new_from_args(4, str, INT2FIX(om), |
|
1729 |
tmask, INT2FIX(recv_off)); |
|
1730 | ||
1731 |
hide_obj(ri); |
|
1732 |
iseq_add_mark_object(iseq, ri); |
|
1733 | ||
1734 |
return ri; |
|
1735 |
} |
|
1736 | ||
1737 |
/* optimize allocation for: obj.method("literal string") */ |
|
1738 |
static void |
|
1739 |
opt_str_lit_1(rb_iseq_t *iseq, VALUE str, rb_call_info_t *ci, INSN *list) |
|
1740 |
{ |
|
1741 |
enum ruby_optimized_method om; |
|
1742 |
VALUE tmask; |
|
1743 |
VALUE ri; |
|
1744 |
int data_p = 0; |
|
1745 | ||
1746 |
switch (ci->mid) { |
|
1747 |
#define C(mid,klass) \ |
|
1748 |
case mid: \ |
|
1749 |
om = OM_##mid##__##klass; \ |
|
1750 |
tmask = OM_TMASK_##klass; \ |
|
1751 |
break |
|
1752 |
C(idAREF, Hash); |
|
1753 |
C(idEq, String); |
|
1754 |
C(idNeq, String); |
|
1755 |
C(idLTLT, String); |
|
1756 |
C(idPLUS, String); |
|
1757 |
C(idEqq, String); |
|
1758 |
C(idDelete, Array_Hash_String); |
|
1759 |
C(idIncludeP, Array_Hash_String); |
|
1760 |
C(idMemberP, Hash); |
|
1761 |
C(idHas_keyP, Hash); |
|
1762 |
C(idKeyP, Hash); |
|
1763 |
C(idFetch, Hash); /* TODO: hash.fetch("lit") { ... } block */ |
|
1764 |
C(idPack, Array); |
|
1765 |
C(idUnpack, String); |
|
1766 |
C(idSplit, String); /* TODO: str.split("lit", num) */ |
|
1767 |
C(idJoin, Array); |
|
1768 |
C(idCount, String); |
|
1769 |
C(idChomp, String); |
|
1770 |
C(idChomp_bang, String); |
|
1771 |
C(idSqueeze, String); |
|
1772 |
C(idSqueeze_bang, String); |
|
1773 |
C(idDelete_bang, String); |
|
1774 |
C(idEncode, String); |
|
1775 |
C(idEncode_bang, String); |
|
1776 |
C(idForce_encoding, String); |
|
1777 |
C(idIndex, String); /* TODO: str.index("lit", num) */ |
|
1778 |
C(idRindex, String); |
|
1779 |
C(idMatch, String); |
|
1780 |
C(idCasecmp, String); |
|
1781 |
C(idStart_withP, String); |
|
1782 |
C(idEnd_withP, String); |
|
1783 |
C(idPartition, String); |
|
1784 |
C(idRpartition, String); |
|
1785 |
#undef C |
|
1786 |
#define C(mid,klass) \ |
|
1787 |
case mid: \ |
|
1788 |
om = OM_##mid##__##klass; \ |
|
1789 |
tmask = OM_TMASK_##klass; \ |
|
1790 |
data_p = 1; \ |
|
1791 |
break |
|
1792 |
/* opt_str_lit_data oddities, maybe this is not worth supporting */ |
|
1793 |
C(idStrftime, Time); |
|
1794 |
#undef C |
|
1795 |
default: return; |
|
1796 |
} |
|
1797 | ||
1798 |
list->insn_id = data_p ? BIN(opt_str_lit_data) : BIN(opt_str_lit_tmask); |
|
1799 |
ri = new_recvinfo_for_arg_(iseq, str, om, tmask, 0); |
|
1800 |
list->operands[0] = ri; |
|
1801 |
} |
|
1802 | ||
1803 |
/* |
|
1804 |
* optimize common string calls which take one or two string literals: |
|
1805 |
* obj.method("lit 1", "lit 2") |
|
1806 |
* obj.method(any, "lit 2") # any may be regexp |
|
1807 |
*/ |
|
1808 |
static void |
|
1809 |
opt_str_lit_2(rb_iseq_t *iseq, VALUE str, rb_call_info_t *ci, INSN *list) |
|
1810 |
{ |
|
1811 |
INSN *piobj; |
|
1812 |
enum ruby_optimized_method om; |
|
1813 |
VALUE ri; |
|
1814 | ||
1815 |
switch (ci->mid) { |
|
1816 |
#define C(mid) case mid: om = OM_##mid##__String; break |
|
1817 |
C(idSub); |
|
1818 |
C(idSub_bang); |
|
1819 |
C(idGsub); |
|
1820 |
C(idGsub_bang); |
|
1821 |
C(idTr); |
|
1822 |
C(idTr_bang); |
|
1823 |
C(idTr_s); |
|
1824 |
C(idTr_s_bang); |
|
1825 |
C(idInsert); /* String#insert(num, "lit") */ |
|
1826 | ||
1827 |
/* String#encode("dst", "src") */ |
|
1828 |
C(idEncode); |
|
1829 |
C(idEncode_bang); |
|
1830 |
#undef C |
|
1831 |
default: return; |
|
1832 |
} |
|
1833 | ||
1834 |
/* |
|
1835 |
* previous arg may be a string literal, too: |
|
1836 |
* foo.gsub!("from", "to") |
|
1837 |
* foo.tr!("from", "to") |
|
1838 |
* .. |
|
1839 |
*/ |
|
1840 |
piobj = (INSN *)get_prev_insn(list); |
|
1841 |
if (piobj && piobj->insn_id == BIN(putstring)) { |
|
1842 |
VALUE pstr = piobj->operands[0]; |
|
1843 |
VALUE pri = new_recvinfo_for_arg_(iseq, pstr, om, OM_TMASK_String, 0); |
|
1844 |
piobj->operands[0] = pri; |
|
1845 |
piobj->insn_id = BIN(opt_str_lit_tmask); |
|
1846 |
} |
|
1847 | ||
1848 |
list->insn_id = BIN(opt_str_lit_tmask); |
|
1849 |
ri = new_recvinfo_for_arg_(iseq, str, om, OM_TMASK_String, 1); |
|
1850 |
list->operands[0] = ri; |
|
1851 |
} |
|
1852 | ||
1706 | 1853 |
static int |
1707 | 1854 |
iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcallopt) |
1708 | 1855 |
{ |
... | ... | |
1819 | 1966 |
} |
1820 | 1967 |
} |
1821 | 1968 |
} |
1969 | ||
1970 |
/* string literal optimizations */ |
|
1971 |
if (iobj->insn_id == BIN(putstring)) { |
|
1972 |
INSN *niobj = (INSN *)get_next_insn((INSN *)list); |
|
1973 | ||
1974 |
if (niobj && niobj->insn_id == BIN(send)) { |
|
1975 |
rb_call_info_t *ci = (rb_call_info_t *)niobj->operands[0]; |
|
1976 | ||
1977 |
if (!ci->blockiseq && !(ci->flag & ~VM_CALL_ARGS_SKIP_SETUP)) { |
|
1978 |
VALUE ri = Qfalse; |
|
1979 |
VALUE str = iobj->operands[0]; |
|
1980 | ||
1981 |
switch (ci->orig_argc) { |
|
1982 |
case 0: |
|
1983 |
/* |
|
1984 |
* optimize: |
|
1985 |
* "literal".freeze |
|
1986 |
* "literal".size |
|
1987 |
* "literal".length |
|
1988 |
*/ |
|
1989 |
switch (ci->mid) { |
|
1990 |
case idFreeze: |
|
1991 |
ri = new_recvinfo_for_recv(iseq, str, idFreeze, String); |
|
1992 |
break; |
|
1993 |
case idSize: |
|
1994 |
ri = new_recvinfo_for_recv(iseq, str, idSize, String); |
|
1995 |
break; |
|
1996 |
case idLength: |
|
1997 |
ri = new_recvinfo_for_recv(iseq, str, idLength, String); |
|
1998 |
break; |
|
1999 |
} |
|
2000 |
if (ri != Qfalse) { |
|
2001 |
iobj->insn_id = BIN(opt_str_lit_recv); |
|
2002 |
iobj->operands[0] = ri; |
|
2003 |
} |
|
2004 |
break; |
|
2005 |
case 1: |
|
2006 |
opt_str_lit_1(iseq, str, ci, (INSN *)list); |
|
2007 |
break; |
|
2008 |
case 2: |
|
2009 |
opt_str_lit_2(iseq, str, ci, (INSN *)list); |
|
2010 |
break; |
|
2011 |
} |
|
2012 |
} |
|
2013 |
} |
|
2014 |
} |
|
2015 | ||
1822 | 2016 |
return COMPILE_OK; |
1823 | 2017 |
} |
1824 | 2018 | |
... | ... | |
3096 | 3290 |
return Qnil; |
3097 | 3291 |
} |
3098 | 3292 | |
3293 |
static enum ruby_optimized_method |
|
3294 |
opt_str_lit_recv_om(ID mid) |
|
3295 |
{ |
|
3296 |
switch (mid) { |
|
3297 |
case idEq: return OM_idEq__String; |
|
3298 |
case idNeq: return OM_idNeq__String; |
|
3299 |
case idPLUS: return OM_idPLUS__String; |
|
3300 |
case idMULT: return OM_idMULT__String; |
|
3301 |
case idMOD: return OM_idMOD__String; |
|
3302 |
case idEqq: return OM_idEqq__String; |
|
3303 |
} |
|
3304 |
return OM_LAST_; |
|
3305 |
} |
|
3306 | ||
3099 | 3307 |
/** |
3100 | 3308 |
compile each node |
3101 | 3309 | |
... | ... | |
4238 | 4446 |
break; |
4239 | 4447 |
} |
4240 | 4448 |
case NODE_CALL: |
4241 |
/* optimization shortcut |
|
4242 |
* "literal".freeze -> opt_str_freeze("literal") |
|
4243 |
*/ |
|
4244 |
if (node->nd_recv && nd_type(node->nd_recv) == NODE_STR && |
|
4245 |
node->nd_mid == idFreeze && node->nd_args == NULL) |
|
4246 |
{ |
|
4247 |
VALUE str = rb_fstring(node->nd_recv->nd_lit); |
|
4248 |
iseq_add_mark_object(iseq, str); |
|
4249 |
ADD_INSN1(ret, line, opt_str_freeze, str); |
|
4250 |
if (poped) { |
|
4251 |
ADD_INSN(ret, line, pop); |
|
4252 |
} |
|
4253 |
break; |
|
4254 |
} |
|
4255 |
/* optimization shortcut |
|
4256 |
* obj["literal"] -> opt_aref_with(obj, "literal") |
|
4257 |
*/ |
|
4258 |
if (node->nd_mid == idAREF && !private_recv_p(node) && node->nd_args && |
|
4259 |
nd_type(node->nd_args) == NODE_ARRAY && node->nd_args->nd_alen == 1 && |
|
4260 |
nd_type(node->nd_args->nd_head) == NODE_STR) |
|
4261 |
{ |
|
4262 |
VALUE str = rb_fstring(node->nd_args->nd_head->nd_lit); |
|
4263 |
node->nd_args->nd_head->nd_lit = str; |
|
4264 |
COMPILE(ret, "recv", node->nd_recv); |
|
4265 |
ADD_INSN2(ret, line, opt_aref_with, |
|
4266 |
new_callinfo(iseq, idAREF, 1, 0, 0), str); |
|
4267 |
if (poped) { |
|
4268 |
ADD_INSN(ret, line, pop); |
|
4269 |
} |
|
4270 |
break; |
|
4271 |
} |
|
4272 | 4449 |
case NODE_FCALL: |
4273 | 4450 |
case NODE_VCALL:{ /* VCALL: variable or call */ |
4274 | 4451 |
/* |
... | ... | |
4352 | 4529 |
#endif |
4353 | 4530 |
/* receiver */ |
4354 | 4531 |
if (type == NODE_CALL) { |
4355 |
COMPILE(recv, "recv", node->nd_recv); |
|
4532 |
enum ruby_optimized_method om; |
|
4533 |
/* |
|
4534 |
* optimize: |
|
4535 |
* "string literal".method(...) |
|
4536 |
* "yoda" == other -> opt_str_lit("yoda").send(:==, other) |
|
4537 |
* "yoda" != other -> opt_str_lit("yoda").send(:!=, other) |
|
4538 |
* "str" + other -> opt_str_lit("str").send(:+, other) |
|
4539 |
* "str" * other -> opt_str_lit("str").send(:*, other) |
|
4540 |
* "fmt" % args -> opt_str_lit("str").send(:%, other) |
|
4541 |
* ... |
|
4542 |
*/ |
|
4543 |
if (iseq->compile_data->option->peephole_optimization && |
|
4544 |
((om = opt_str_lit_recv_om(mid)) != OM_LAST_) && |
|
4545 |
!private_recv_p(node) && |
|
4546 |
node->nd_recv && nd_type(node->nd_recv) == NODE_STR && |
|
4547 |
node->nd_args && nd_type(node->nd_args) == NODE_ARRAY && |
|
4548 |
node->nd_args->nd_alen == 1) |
|
4549 |
{ |
|
4550 |
VALUE yoda = rb_fstring(node->nd_recv->nd_lit); |
|
4551 |
VALUE recv_info = new_recvinfo_for_recv_(iseq, yoda, om); |
|
4552 | ||
4553 |
node->nd_recv->nd_lit = yoda; |
|
4554 |
ADD_INSN1(recv, line, opt_str_lit_recv, recv_info); |
|
4555 |
} else { |
|
4556 |
COMPILE(recv, "recv", node->nd_recv); |
|
4557 |
} |
|
4356 | 4558 |
} |
4357 | 4559 |
else if (type == NODE_FCALL || type == NODE_VCALL) { |
4358 | 4560 |
ADD_CALL_RECEIVER(recv, line); |
... | ... | |
5241 | 5443 |
int asgnflag; |
5242 | 5444 | |
5243 | 5445 |
/* optimization shortcut |
5244 |
* obj["literal"] = value -> opt_aset_with(obj, "literal", value) |
|
5446 |
* obj["literal"] = val -> send(obj, :[]=, opt_str_lit("lit"), val) |
|
5447 |
* TODO: ideally this should be done inside iseq_peephole_optimize, |
|
5448 |
* but that would require a lot of scanning as the `val' (2nd arg) |
|
5449 |
* is of variable distance between the :putstring and :send insns |
|
5245 | 5450 |
*/ |
5246 |
if (node->nd_mid == idASET && !private_recv_p(node) && node->nd_args && |
|
5451 |
if (iseq->compile_data->option->peephole_optimization && |
|
5452 |
node->nd_mid == idASET && !private_recv_p(node) && node->nd_args && |
|
5247 | 5453 |
nd_type(node->nd_args) == NODE_ARRAY && node->nd_args->nd_alen == 2 && |
5248 | 5454 |
nd_type(node->nd_args->nd_head) == NODE_STR) |
5249 | 5455 |
{ |
5250 | 5456 |
VALUE str = rb_fstring(node->nd_args->nd_head->nd_lit); |
5457 |
VALUE recv_info = new_recvinfo_for_arg(iseq, str, idASET, Hash, 0); |
|
5458 | ||
5251 | 5459 |
node->nd_args->nd_head->nd_lit = str; |
5252 |
iseq_add_mark_object(iseq, str); |
|
5460 |
if (!poped) { |
|
5461 |
ADD_INSN(ret, line, putnil); |
|
5462 |
} |
|
5253 | 5463 |
COMPILE(ret, "recv", node->nd_recv); |
5464 |
ADD_INSN1(ret, line, opt_str_lit_tmask, recv_info); |
|
5254 | 5465 |
COMPILE(ret, "value", node->nd_args->nd_next->nd_head); |
5255 | 5466 |
if (!poped) { |
5256 |
ADD_INSN(ret, line, swap); |
|
5257 |
ADD_INSN1(ret, line, topn, INT2FIX(1)); |
|
5467 |
ADD_INSN1(ret, line, setn, INT2FIX(3)); |
|
5258 | 5468 |
} |
5259 |
ADD_INSN2(ret, line, opt_aset_with,
|
|
5260 |
new_callinfo(iseq, idASET, 2, 0, 0), str);
|
|
5469 |
flag = VM_CALL_ARGS_SKIP_SETUP;
|
|
5470 |
ADD_SEND_R(ret, line, node->nd_mid, 2, 0, INT2FIX(flag));
|
|
5261 | 5471 |
ADD_INSN(ret, line, pop); |
5262 | 5472 |
break; |
5263 | 5473 |
} |
... | ... | |
5906 | 6116 |
{ |
5907 | 6117 |
return GET_THREAD()->parse_in_eval < 0; |
5908 | 6118 |
} |
6119 | ||
6120 |
/* |
|
6121 |
* Live bytecode patch: |
|
6122 |
* - opt_str_lit(recv_info) |
|
6123 |
* + putstring(str) # str is recv_info[0] |
|
6124 |
* |
|
6125 |
* If allocation optimization fails at this call site once, assume it |
|
6126 |
* will fail in the future. This prevents performance regressions for |
|
6127 |
* things like #include? calls which may be used with unoptimized |
|
6128 |
* classes (Set,*DBM and many others) as well as optimized core classes |
|
6129 |
* (Array/Hash/String). Call sites which only use optimized core |
|
6130 |
* classes will never get here. |
|
6131 |
*/ |
|
6132 |
void |
|
6133 |
rb_undo_opt_str_lit(rb_control_frame_t *cfp) |
|
6134 |
{ |
|
6135 |
VALUE *insn = cfp->pc - insn_len(BIN(opt_str_lit_tmask)); |
|
6136 | ||
6137 |
#if OPT_DIRECT_THREADED_CODE || OPT_CALL_THREADED_CODE |
|
6138 |
const void * const *table = rb_vm_get_insns_address_table(); |
|
6139 | ||
6140 |
assert(((VALUE)table[BIN(opt_str_lit_tmask)] == insn[0] || |
|
6141 |
(VALUE)table[BIN(opt_str_lit_data)] == insn[0] || |
|
6142 |
(VALUE)table[BIN(opt_str_lit_recv)] == insn[0]) && |
|
6143 |
"mismatch"); |
|
6144 |
insn[0] = (VALUE)table[BIN(putstring)]; |
|
6145 |
#else |
|
6146 |
assert(((VALUE)BIN(opt_str_lit_tmask) == insn[0] || |
|
6147 |
(VALUE)BIN(opt_str_lit_data) == insn[0] || |
|
6148 |
(VALUE)BIN(opt_str_lit_recv) == insn[0]) && |
|
6149 |
"mismatch"); |
|
6150 |
insn[0] = (VALUE)BIN(putstring); |
|
6151 |
#endif |
|
6152 |
assert(insn_len(BIN(putstring)) == insn_len(BIN(opt_str_lit_tmask))); |
|
6153 |
assert(insn_len(BIN(putstring)) == insn_len(BIN(opt_str_lit_data))); |
|
6154 |
assert(insn_len(BIN(putstring)) == insn_len(BIN(opt_str_lit_recv))); |
|
6155 | ||
6156 |
assert(T_ARRAY == BUILTIN_TYPE(insn[1])); |
|
6157 | ||
6158 |
/* n.b.: recv_info remains marked */ |
|
6159 |
insn[1] = RARRAY_AREF(insn[1], 0); /* recv_info[0] == str */ |
|
6160 |
} |
defs/id.def | ||
---|---|---|
57 | 57 |
core#hash_merge_ary |
58 | 58 |
core#hash_merge_ptr |
59 | 59 |
core#hash_merge_kwd |
60 |
gsub |
|
61 |
gsub! |
|
62 |
sub |
|
63 |
sub! |
|
64 |
tr |
|
65 |
tr! |
|
66 |
tr_s |
|
67 |
tr_s! |
|
68 |
delete |
|
69 |
delete! |
|
70 |
include? |
|
71 |
member? |
|
72 |
has_key? |
|
73 |
key? |
|
74 |
fetch |
|
75 |
count |
|
76 |
chomp |
|
77 |
chomp! |
|
78 |
squeeze |
|
79 |
squeeze! |
|
80 |
strftime |
|
81 |
pack |
|
82 |
unpack |
|
83 |
split |
|
84 |
join |
|
85 |
encode |
|
86 |
encode! |
|
87 |
force_encoding |
|
88 |
index |
|
89 |
rindex |
|
90 |
match |
|
91 |
casecmp |
|
92 |
insert |
|
93 |
start_with? |
|
94 |
end_with? |
|
95 |
partition |
|
96 |
rpartition |
|
60 | 97 |
] |
61 | 98 | |
62 | 99 |
class KeywordError < RuntimeError |
... | ... | |
83 | 120 |
token = "_#{token.gsub(/\W+/, '_')}" |
84 | 121 |
else |
85 | 122 |
token = token.sub(/\?/, 'P').sub(/\A[a-z]/) {$&.upcase} |
123 |
token.sub!(/!\z/, "_bang") |
|
86 | 124 |
token.sub!(/\A\$/, "_G_") |
87 | 125 |
token.sub!(/\A@@/, "_C_") |
88 | 126 |
token.sub!(/\A@/, "_I_") |
defs/opt_method.def | ||
---|---|---|
1 |
# byte align the bitmap for now, maybe some arches do better with long or int |
|
2 |
# we may also use a larger size (in the unlikely case) we need more than |
|
3 |
# 7 optimized classes per mid. Currently this caps us to 256 optimized |
|
4 |
# (mid, klass) combinations (tested with OM_SHIFT=4, giving us 64K) |
|
5 |
OM_SHIFT = 3 |
|
6 |
OM_ALIGN = 1 << OM_SHIFT |
|
7 |
OM_ALIGN_MASK = ~(OM_ALIGN - 1) |
|
8 |
OPT_METHODS = [ |
|
9 |
%w(idPLUS Fixnum Float String Array), |
|
10 |
%w(idMINUS Fixnum Float), |
|
11 |
%w(idMULT Fixnum Float String), |
|
12 |
%w(idDIV Fixnum Float), |
|
13 |
%w(idMOD Fixnum Float String), |
|
14 |
%w(idEq Fixnum Float String), |
|
15 |
%w(idNeq Fixnum Float String), |
|
16 |
# id, mask classes |
|
17 |
[ 'idEqq', %w(Bignum Fixnum Float Symbol), *%w(String) ], |
|
18 |
%w(idLT Fixnum Float), |
|
19 |
%w(idLE Fixnum Float), |
|
20 |
%w(idGT Fixnum Float), |
|
21 |
%w(idGE Fixnum Float), |
|
22 |
%w(idLTLT Array String), |
|
23 |
%w(idAREF Array Hash), |
|
24 |
%w(idASET Array Hash), |
|
25 |
%w(idLength Array Hash String), |
|
26 |
%w(idSize Array Hash String), |
|
27 |
%w(idEmptyP Array Hash String), |
|
28 |
%w(idSucc Fixnum String Time), |
|
29 |
%w(idEqTilde Regexp String), |
|
30 |
%w(idFreeze String), |
|
31 |
%w(idGsub String), |
|
32 |
%w(idGsub_bang String), |
|
33 |
%w(idSub String), |
|
34 |
%w(idSub_bang String), |
|
35 |
%w(idTr String), |
|
36 |
%w(idTr_bang String), |
|
37 |
%w(idTr_s String), |
|
38 |
%w(idTr_s_bang String), |
|
39 |
[ "idDelete", %w(Array Hash String) ], |
|
40 |
[ "idIncludeP", %w(Array Hash String) ], |
|
41 |
%w(idMemberP Hash), |
|
42 |
%w(idHas_keyP Hash), |
|
43 |
%w(idKeyP Hash), |
|
44 |
%w(idFetch Hash), |
|
45 |
%w(idStrftime Time), |
|
46 |
%w(idUnpack String), |
|
47 |
%w(idPack Array), |
|
48 |
%w(idSplit String), |
|
49 |
%w(idJoin Array), |
|
50 |
%w(idCount String), |
|
51 |
%w(idChomp String), |
|
52 |
%w(idChomp_bang String), |
|
53 |
%w(idSqueeze String), |
|
54 |
%w(idSqueeze_bang String), |
|
55 |
%w(idDelete_bang String), |
|
56 |
%w(idEncode String), |
|
57 |
%w(idEncode_bang String), |
|
58 |
%w(idForce_encoding String), |
|
59 |
%w(idIndex String), |
|
60 |
%w(idRindex String), |
|
61 |
%w(idMatch String), |
|
62 |
%w(idCasecmp String), |
|
63 |
%w(idInsert String), |
|
64 |
%w(idStart_withP String), |
|
65 |
%w(idEnd_withP String), |
|
66 |
%w(idPartition String), |
|
67 |
%w(idRpartition String), |
|
68 |
] |
|
69 | ||
70 |
# for checking optimized classes, |
|
71 |
# speeds up method definitions of non-core classes |
|
72 |
def opt_classes |
|
73 |
rv = {} |
|
74 |
OPT_METHODS.each do |(_, *classes)| |
|
75 |
classes.flatten.each { |c| rv[c] = true } |
|
76 |
end |
|
77 |
rv |
|
78 |
end |
|
79 | ||
80 |
def om(mid, klass) |
|
81 |
if Array === klass |
|
82 |
"OM_#{mid}__#{klass.join('_')}" |
|
83 |
else |
|
84 |
"OM_#{mid}__#{klass}" |
|
85 |
end |
|
86 |
end |
|
87 | ||
88 |
IS_T_DATA = { |
|
89 |
"Time" => true |
|
90 |
} |
insns.def | ||
---|---|---|
357 | 357 |
} |
358 | 358 | |
359 | 359 |
/** |
360 |
@c optimize |
|
361 |
@e put string val. string may be created depending on recv_info conditions |
|
362 |
*/ |
|
363 |
DEFINE_INSN |
|
364 |
opt_str_lit_tmask |
|
365 |
(VALUE recv_info) |
|
366 |
() |
|
367 |
(VALUE val) |
|
368 |
{ |
|
369 |
/* |
|
370 |
* recv_info: |
|
371 |
* 0 - str |
|
372 |
* 1 - optimized method flag (OM_*) |
|
373 |
* 2 - tmask (optimized receiver classes) |
|
374 |
* 3 - stack offset (Fixint), |
|
375 |
* -1 stack offset means receiver is the frozen string literal itself |
|
376 |
*/ |
|
377 |
const VALUE *ri = RARRAY_CONST_PTR(recv_info); |
|
378 |
enum ruby_optimized_method om = FIX2UINT(ri[1]); |
|
379 |
int tmask = FIX2INT(ri[2]); |
|
380 |
int n = FIX2INT(ri[3]); |
|
381 |
VALUE recv; |
|
382 | ||
383 |
val = ri[0]; /* hopefully, this is the only val assignment we need */ |
|
384 |
recv = n < 0 ? val : TOPN(n); |
|
385 | ||
386 |
if (!SPECIAL_CONST_P(recv)) { |
|
387 |
enum ruby_value_type btype = BUILTIN_TYPE(recv); |
|
388 |
int rmask = 1 << btype; |
|
389 | ||
390 |
if ((rmask & tmask) && |
|
391 |
(rb_opt_method_class(btype) == RBASIC_CLASS(recv))) { |
|
392 |
if (rb_opt_method_is_mask(om)) { |
|
393 |
if (rb_basic_mask_unredefined_p(om)) { |
|
394 |
goto out_tmask; /* fast path */ |
|
395 |
} |
|
396 |
} |