From: Eric Wong <e@80x24.org>
To: spew@80x24.org
Subject: [PATCH 2/2] opt_str_lit-v8
Date: Sat, 15 Nov 2014 22:16:12 +0000 [thread overview]
Message-ID: <opt_str_lit-v8@1> (raw)
In-Reply-To: <opt_str_lit-v8-prepare@0>
Changes since -v7:
* opt_str_lit*: avoid string literal allocations
* rb_undo_opt_str_lit is now a VM option
* template/opt_method.h.tmpl: explicitly cast to signed
* remove Time#strftime and opt_str_lit_data insns
(too specialized)
* keep opt_aref_with and opt_aset_with (for speed)
---
.gitignore | 1 +
benchmark/bm_vm2_array_delete_lit.rb | 6 +
benchmark/bm_vm2_array_include_lit.rb | 6 +
benchmark/bm_vm2_hash_aref_lit.rb | 6 +
benchmark/bm_vm2_hash_aset_lit.rb | 6 +
benchmark/bm_vm2_hash_delete_lit.rb | 6 +
benchmark/bm_vm2_set_include_lit.rb | 7 +
benchmark/bm_vm2_str_delete.rb | 6 +
benchmark/bm_vm2_str_eq1.rb | 6 +
benchmark/bm_vm2_str_eq2.rb | 6 +
benchmark/bm_vm2_str_eqq1.rb | 6 +
benchmark/bm_vm2_str_eqq2.rb | 6 +
benchmark/bm_vm2_str_fmt.rb | 5 +
benchmark/bm_vm2_str_gsub_bang_lit.rb | 6 +
benchmark/bm_vm2_str_gsub_bang_re.rb | 6 +
benchmark/bm_vm2_str_gsub_re.rb | 6 +
benchmark/bm_vm2_str_plus1.rb | 6 +
benchmark/bm_vm2_str_plus2.rb | 6 +
benchmark/bm_vm2_str_tr_bang.rb | 7 +
benchmark/bm_vm2_strcat.rb | 7 +
common.mk | 17 +-
compile.c | 313 ++++++++++++++++++++++++++++++++--
defs/id.def | 37 ++++
defs/opt_method.def | 85 +++++++++
insns.def | 230 +++++++++++++++++--------
template/opt_method.h.tmpl | 108 ++++++++++++
template/opt_method.inc.tmpl | 42 +++++
vm.c | 67 +-------
vm_core.h | 44 +----
vm_insnhelper.c | 8 +-
vm_insnhelper.h | 32 ++++
vm_opts.h | 1 +
32 files changed, 905 insertions(+), 196 deletions(-)
create mode 100644 benchmark/bm_vm2_array_delete_lit.rb
create mode 100644 benchmark/bm_vm2_array_include_lit.rb
create mode 100644 benchmark/bm_vm2_hash_aref_lit.rb
create mode 100644 benchmark/bm_vm2_hash_aset_lit.rb
create mode 100644 benchmark/bm_vm2_hash_delete_lit.rb
create mode 100644 benchmark/bm_vm2_set_include_lit.rb
create mode 100644 benchmark/bm_vm2_str_delete.rb
create mode 100644 benchmark/bm_vm2_str_eq1.rb
create mode 100644 benchmark/bm_vm2_str_eq2.rb
create mode 100644 benchmark/bm_vm2_str_eqq1.rb
create mode 100644 benchmark/bm_vm2_str_eqq2.rb
create mode 100644 benchmark/bm_vm2_str_fmt.rb
create mode 100644 benchmark/bm_vm2_str_gsub_bang_lit.rb
create mode 100644 benchmark/bm_vm2_str_gsub_bang_re.rb
create mode 100644 benchmark/bm_vm2_str_gsub_re.rb
create mode 100644 benchmark/bm_vm2_str_plus1.rb
create mode 100644 benchmark/bm_vm2_str_plus2.rb
create mode 100644 benchmark/bm_vm2_str_tr_bang.rb
create mode 100644 benchmark/bm_vm2_strcat.rb
create mode 100644 defs/opt_method.def
create mode 100644 template/opt_method.h.tmpl
create mode 100644 template/opt_method.inc.tmpl
diff --git a/.gitignore b/.gitignore
index 847f6e6..9371fb7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -74,6 +74,7 @@ y.tab.c
/newdate.rb
/newline.c
/newver.rb
+/opt_method.h
/parse.c
/parse.h
/patches
diff --git a/benchmark/bm_vm2_array_delete_lit.rb b/benchmark/bm_vm2_array_delete_lit.rb
new file mode 100644
index 0000000..60d599a
--- /dev/null
+++ b/benchmark/bm_vm2_array_delete_lit.rb
@@ -0,0 +1,6 @@
+ary = []
+i = 0
+while i<6_000_000 # while loop 2
+ i += 1
+ ary.delete("foo")
+end
diff --git a/benchmark/bm_vm2_array_include_lit.rb b/benchmark/bm_vm2_array_include_lit.rb
new file mode 100644
index 0000000..c81e230
--- /dev/null
+++ b/benchmark/bm_vm2_array_include_lit.rb
@@ -0,0 +1,6 @@
+ary = []
+i = 0
+while i<6_000_000 # while loop 2
+ i += 1
+ ary.include?("foo")
+end
diff --git a/benchmark/bm_vm2_hash_aref_lit.rb b/benchmark/bm_vm2_hash_aref_lit.rb
new file mode 100644
index 0000000..a6d4d12
--- /dev/null
+++ b/benchmark/bm_vm2_hash_aref_lit.rb
@@ -0,0 +1,6 @@
+h = { "foo" => nil }
+i = 0
+while i<6_000_000 # while loop 2
+ i += 1
+ h["foo"]
+end
diff --git a/benchmark/bm_vm2_hash_aset_lit.rb b/benchmark/bm_vm2_hash_aset_lit.rb
new file mode 100644
index 0000000..58339ec
--- /dev/null
+++ b/benchmark/bm_vm2_hash_aset_lit.rb
@@ -0,0 +1,6 @@
+h = {}
+i = 0
+while i<6_000_000 # while loop 2
+ i += 1
+ h["foo"] = nil
+end
diff --git a/benchmark/bm_vm2_hash_delete_lit.rb b/benchmark/bm_vm2_hash_delete_lit.rb
new file mode 100644
index 0000000..22dd95f
--- /dev/null
+++ b/benchmark/bm_vm2_hash_delete_lit.rb
@@ -0,0 +1,6 @@
+h = {}
+i = 0
+while i<6_000_000 # while loop 2
+ i += 1
+ h.delete("foo")
+end
diff --git a/benchmark/bm_vm2_set_include_lit.rb b/benchmark/bm_vm2_set_include_lit.rb
new file mode 100644
index 0000000..25d8b89
--- /dev/null
+++ b/benchmark/bm_vm2_set_include_lit.rb
@@ -0,0 +1,7 @@
+require 'set'
+set = Set.new
+i = 0
+while i<6_000_000 # while loop 2
+ i += 1
+ set.include?("foo")
+end
diff --git a/benchmark/bm_vm2_str_delete.rb b/benchmark/bm_vm2_str_delete.rb
new file mode 100644
index 0000000..c242f29
--- /dev/null
+++ b/benchmark/bm_vm2_str_delete.rb
@@ -0,0 +1,6 @@
+str = ''
+i = 0
+while i<6_000_000 # while loop 2
+ i += 1
+ str.delete("foo")
+end
diff --git a/benchmark/bm_vm2_str_eq1.rb b/benchmark/bm_vm2_str_eq1.rb
new file mode 100644
index 0000000..2a4b0f8
--- /dev/null
+++ b/benchmark/bm_vm2_str_eq1.rb
@@ -0,0 +1,6 @@
+i = 0
+foo = "literal"
+while i<6_000_000 # benchmark loop 2
+ i += 1
+ foo == "literal"
+end
diff --git a/benchmark/bm_vm2_str_eq2.rb b/benchmark/bm_vm2_str_eq2.rb
new file mode 100644
index 0000000..986020d
--- /dev/null
+++ b/benchmark/bm_vm2_str_eq2.rb
@@ -0,0 +1,6 @@
+i = 0
+foo = "literal"
+while i<6_000_000 # benchmark loop 2
+ i += 1
+ "literal" == foo
+end
diff --git a/benchmark/bm_vm2_str_eqq1.rb b/benchmark/bm_vm2_str_eqq1.rb
new file mode 100644
index 0000000..9183466
--- /dev/null
+++ b/benchmark/bm_vm2_str_eqq1.rb
@@ -0,0 +1,6 @@
+i = 0
+foo = "literal"
+while i<6_000_000 # benchmark loop 2
+ i += 1
+ foo === "literal"
+end
diff --git a/benchmark/bm_vm2_str_eqq2.rb b/benchmark/bm_vm2_str_eqq2.rb
new file mode 100644
index 0000000..f48a9cd
--- /dev/null
+++ b/benchmark/bm_vm2_str_eqq2.rb
@@ -0,0 +1,6 @@
+i = 0
+foo = "literal"
+while i<6_000_000 # benchmark loop 2
+ i += 1
+ "literal" === foo
+end
diff --git a/benchmark/bm_vm2_str_fmt.rb b/benchmark/bm_vm2_str_fmt.rb
new file mode 100644
index 0000000..efb88b6
--- /dev/null
+++ b/benchmark/bm_vm2_str_fmt.rb
@@ -0,0 +1,5 @@
+i = 0
+while i<6_000_000 # benchmark loop 2
+ i += 1
+ "%d" % i
+end
diff --git a/benchmark/bm_vm2_str_gsub_bang_lit.rb b/benchmark/bm_vm2_str_gsub_bang_lit.rb
new file mode 100644
index 0000000..9251fb1
--- /dev/null
+++ b/benchmark/bm_vm2_str_gsub_bang_lit.rb
@@ -0,0 +1,6 @@
+i = 0
+str = ""
+while i<6_000_000 # benchmark loop 2
+ i += 1
+ str.gsub!("nomatch", "")
+end
diff --git a/benchmark/bm_vm2_str_gsub_bang_re.rb b/benchmark/bm_vm2_str_gsub_bang_re.rb
new file mode 100644
index 0000000..e5fc9ea
--- /dev/null
+++ b/benchmark/bm_vm2_str_gsub_bang_re.rb
@@ -0,0 +1,6 @@
+i = 0
+str = ""
+while i<6_000_000 # benchmark loop 2
+ i += 1
+ str.gsub!(/a/, "")
+end
diff --git a/benchmark/bm_vm2_str_gsub_re.rb b/benchmark/bm_vm2_str_gsub_re.rb
new file mode 100644
index 0000000..606f247
--- /dev/null
+++ b/benchmark/bm_vm2_str_gsub_re.rb
@@ -0,0 +1,6 @@
+i = 0
+str = ""
+while i<6_000_000 # benchmark loop 2
+ i += 1
+ str.gsub(/a/, "")
+end
diff --git a/benchmark/bm_vm2_str_plus1.rb b/benchmark/bm_vm2_str_plus1.rb
new file mode 100644
index 0000000..714efb8
--- /dev/null
+++ b/benchmark/bm_vm2_str_plus1.rb
@@ -0,0 +1,6 @@
+i = 0
+foo = "a"
+while i<6_000_000 # benchmark loop 2
+ i += 1
+ foo + "b"
+end
diff --git a/benchmark/bm_vm2_str_plus2.rb b/benchmark/bm_vm2_str_plus2.rb
new file mode 100644
index 0000000..c7f91ed
--- /dev/null
+++ b/benchmark/bm_vm2_str_plus2.rb
@@ -0,0 +1,6 @@
+i = 0
+foo = "a"
+while i<6_000_000 # benchmark loop 2
+ i += 1
+ "b" + foo
+end
diff --git a/benchmark/bm_vm2_str_tr_bang.rb b/benchmark/bm_vm2_str_tr_bang.rb
new file mode 100644
index 0000000..8065a65
--- /dev/null
+++ b/benchmark/bm_vm2_str_tr_bang.rb
@@ -0,0 +1,7 @@
+i = 0
+str = "a"
+while i<6_000_000 # benchmark loop 2
+ i += 1
+ str.tr!("a", "A")
+ str.tr!("A", "a")
+end
diff --git a/benchmark/bm_vm2_strcat.rb b/benchmark/bm_vm2_strcat.rb
new file mode 100644
index 0000000..b25ac6e
--- /dev/null
+++ b/benchmark/bm_vm2_strcat.rb
@@ -0,0 +1,7 @@
+i = 0
+str = ""
+while i<6_000_000 # benchmark loop 2
+ i += 1
+ str << "const"
+ str.clear
+end
diff --git a/common.mk b/common.mk
index bb0bebd..2d441e2 100644
--- a/common.mk
+++ b/common.mk
@@ -642,7 +642,7 @@ PROBES_H_INCLUDES = {$(VPATH)}probes.h
VM_CORE_H_INCLUDES = {$(VPATH)}vm_core.h {$(VPATH)}thread_$(THREAD_MODEL).h \
{$(VPATH)}node.h {$(VPATH)}method.h {$(VPATH)}ruby_atomic.h \
{$(VPATH)}vm_debug.h {$(VPATH)}id.h {$(VPATH)}thread_native.h \
- $(CCAN_LIST_INCLUDES)
+ $(CCAN_LIST_INCLUDES) {$(VPATH)}opt_method.h
###
@@ -752,6 +752,20 @@ incs: $(INSNS) {$(VPATH)}node_name.inc {$(VPATH)}encdb.h {$(VPATH)}transdb.h {$(
insns: $(INSNS)
+opt_method.h: $(srcdir)/tool/generic_erb.rb \
+ $(srcdir)/template/opt_method.h.tmpl \
+ $(srcdir)/defs/opt_method.def
+ $(ECHO) generating $@
+ $(Q) $(BASERUBY) $(srcdir)/tool/generic_erb.rb --output=$@ \
+ $(srcdir)/template/opt_method.h.tmpl
+
+opt_method.inc: $(srcdir)/tool/generic_erb.rb \
+ $(srcdir)/template/opt_method.inc.tmpl \
+ $(srcdir)/defs/opt_method.def
+ $(ECHO) generating $@
+ $(Q) $(BASERUBY) $(srcdir)/tool/generic_erb.rb --output=$@ \
+ $(srcdir)/template/opt_method.inc.tmpl
+
id.h: $(srcdir)/tool/generic_erb.rb $(srcdir)/template/id.h.tmpl $(srcdir)/defs/id.def
$(ECHO) generating $@
$(Q) $(BASERUBY) $(srcdir)/tool/generic_erb.rb --output=$@ \
@@ -1931,6 +1945,7 @@ vm.$(OBJEXT): {$(VPATH)}iseq.h
vm.$(OBJEXT): {$(VPATH)}method.h
vm.$(OBJEXT): {$(VPATH)}node.h
vm.$(OBJEXT): {$(VPATH)}oniguruma.h
+vm.$(OBJEXT): {$(VPATH)}opt_method.inc
vm.$(OBJEXT): {$(VPATH)}probes.h
vm.$(OBJEXT): {$(VPATH)}probes_helper.h
vm.$(OBJEXT): {$(VPATH)}ruby_atomic.h
diff --git a/compile.c b/compile.c
index 9936647..0fdb429 100644
--- a/compile.c
+++ b/compile.c
@@ -1797,6 +1797,154 @@ get_prev_insn(INSN *iobj)
return 0;
}
+#define new_recvinfo_for_recv(iseq,str,mid,klass) \
+ new_recvinfo_for_recv_(iseq,str,OM_##mid##__##klass)
+static VALUE
+new_recvinfo_for_recv_(rb_iseq_t *iseq, VALUE str,
+ enum ruby_optimized_method om)
+{
+ VALUE ri = rb_ary_new_from_args(2, str, INT2FIX(om));
+
+ hide_obj(ri);
+ iseq_add_mark_object(iseq, ri);
+
+ return ri;
+}
+
+static VALUE
+new_recvinfo_for_yoda(rb_iseq_t *iseq, VALUE str,
+ enum ruby_optimized_method om, int recv_off)
+{
+ VALUE ri = rb_ary_new_from_args(3, str, INT2FIX(om), INT2FIX(recv_off));
+
+ hide_obj(ri);
+ iseq_add_mark_object(iseq, ri);
+
+ return ri;
+}
+
+#define new_recvinfo_for_arg(iseq,str,mid,klass,off) \
+ new_recvinfo_for_arg_((iseq),(str),(OM_##mid##__##klass),\
+ (OM_TMASK_##klass),(off))
+static VALUE
+new_recvinfo_for_arg_(rb_iseq_t *iseq, VALUE str,
+ enum ruby_optimized_method om,
+ VALUE tmask, int recv_off)
+{
+ VALUE ri = rb_ary_new_from_args(4, str, INT2FIX(om),
+ tmask, INT2FIX(recv_off));
+
+ hide_obj(ri);
+ iseq_add_mark_object(iseq, ri);
+
+ return ri;
+}
+
+/* optimize allocation for: obj.method("literal string") */
+static void
+opt_str_lit_1(rb_iseq_t *iseq, VALUE str, rb_call_info_t *ci, INSN *list)
+{
+ enum ruby_optimized_method om;
+ VALUE tmask;
+ VALUE ri;
+
+ switch (ci->mid) {
+#define C(mid,klass) \
+ case mid: \
+ om = OM_##mid##__##klass; \
+ tmask = OM_TMASK_##klass; \
+ break
+ C(idEq, String);
+ C(idNeq, String);
+ C(idLTLT, String);
+ C(idPLUS, String);
+ C(idEqq, String);
+ C(idDelete, Array_Hash_String);
+ C(idIncludeP, Array_Hash_String);
+ C(idMemberP, Hash);
+ C(idHas_keyP, Hash);
+ C(idKeyP, Hash);
+ C(idFetch, Hash); /* TODO: hash.fetch("lit") { ... } block */
+ C(idPack, Array);
+ C(idUnpack, String);
+ C(idSplit, String); /* TODO: str.split("lit", num) */
+ C(idJoin, Array);
+ C(idCount, String);
+ C(idChomp, String);
+ C(idChomp_bang, String);
+ C(idSqueeze, String);
+ C(idSqueeze_bang, String);
+ C(idDelete_bang, String);
+ C(idEncode, String);
+ C(idEncode_bang, String);
+ C(idForce_encoding, String);
+ C(idIndex, String); /* TODO: str.index("lit", num) */
+ C(idRindex, String);
+ C(idMatch, String);
+ C(idCasecmp, String);
+ C(idStart_withP, String);
+ C(idEnd_withP, String);
+ C(idPartition, String);
+ C(idRpartition, String);
+#undef C
+ default: return;
+ }
+
+ list->insn_id = BIN(opt_str_lit_tmask);
+ ri = new_recvinfo_for_arg_(iseq, str, om, tmask, 0);
+ list->operands[0] = ri;
+}
+
+/*
+ * optimize common string calls which take one or two string literals:
+ * obj.method("lit 1", "lit 2")
+ * obj.method(any, "lit 2") # any may be regexp
+ */
+static void
+opt_str_lit_2(rb_iseq_t *iseq, VALUE str, rb_call_info_t *ci, INSN *list)
+{
+ INSN *piobj;
+ enum ruby_optimized_method om;
+ VALUE ri;
+
+ switch (ci->mid) {
+#define C(mid) case mid: om = OM_##mid##__String; break
+ C(idSub);
+ C(idSub_bang);
+ C(idGsub);
+ C(idGsub_bang);
+ C(idTr);
+ C(idTr_bang);
+ C(idTr_s);
+ C(idTr_s_bang);
+ C(idInsert); /* String#insert(num, "lit") */
+
+ /* String#encode("dst", "src") */
+ C(idEncode);
+ C(idEncode_bang);
+#undef C
+ default: return;
+ }
+
+ /*
+ * previous arg may be a string literal, too:
+ * foo.gsub!("from", "to")
+ * foo.tr!("from", "to")
+ * ..
+ */
+ piobj = (INSN *)get_prev_insn(list);
+ if (piobj && piobj->insn_id == BIN(putstring)) {
+ VALUE pstr = piobj->operands[0];
+ VALUE pri = new_recvinfo_for_arg_(iseq, pstr, om, OM_TMASK_String, 0);
+ piobj->operands[0] = pri;
+ piobj->insn_id = BIN(opt_str_lit_tmask);
+ }
+
+ list->insn_id = BIN(opt_str_lit_tmask);
+ ri = new_recvinfo_for_arg_(iseq, str, om, OM_TMASK_String, 1);
+ list->operands[0] = ri;
+}
+
static int
iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcallopt)
{
@@ -1935,11 +2083,42 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal
iobj->operands[1] = old_operands[0];
REMOVE_ELEM((LINK_ELEMENT *)niobj);
+ } else {
+ VALUE ri = Qfalse;
+ VALUE str = iobj->operands[0];
+
+ switch (ci->orig_argc) {
+ case 0:
+ /*
+ * optimize:
+ * "literal".freeze
+ * "literal".size
+ * "literal".length
+ */
+#define C(mid) \
+ case mid: ri = new_recvinfo_for_recv(iseq, str, mid, String); break
+ switch (ci->mid) {
+ C(idFreeze);
+ C(idSize);
+ C(idLength);
+ }
+#undef C
+ if (ri != Qfalse) {
+ iobj->insn_id = BIN(opt_str_lit_recv);
+ iobj->operands[0] = ri;
+ }
+ break;
+ case 1:
+ opt_str_lit_1(iseq, str, ci, (INSN *)list);
+ break;
+ case 2:
+ opt_str_lit_2(iseq, str, ci, (INSN *)list);
+ break;
+ }
}
}
}
}
-
return COMPILE_OK;
}
@@ -3266,6 +3445,20 @@ build_postexe_iseq(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE *body)
return Qnil;
}
+static enum ruby_optimized_method
+opt_str_lit_recv_om(ID mid)
+{
+ switch (mid) {
+ case idEq: return OM_idEq__String;
+ case idNeq: return OM_idNeq__String;
+ case idPLUS: return OM_idPLUS__String;
+ case idMULT: return OM_idMULT__String;
+ case idMOD: return OM_idMOD__String;
+ case idEqq: return OM_idEqq__String;
+ }
+ return OM_LAST_;
+}
+
/**
compile each node
@@ -4400,21 +4593,6 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
break;
}
case NODE_CALL:
- /* optimization shortcut
- * "literal".freeze -> opt_str_freeze("literal")
- */
- if (iseq->compile_data->option->peephole_optimization &&
- node->nd_recv && nd_type(node->nd_recv) == NODE_STR &&
- node->nd_mid == idFreeze && node->nd_args == NULL)
- {
- VALUE str = rb_fstring(node->nd_recv->nd_lit);
- iseq_add_mark_object(iseq, str);
- ADD_INSN1(ret, line, opt_str_freeze, str);
- if (poped) {
- ADD_INSN(ret, line, pop);
- }
- break;
- }
case NODE_FCALL:
case NODE_VCALL:{ /* VCALL: variable or call */
/*
@@ -4429,6 +4607,7 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
unsigned int flag = 0;
rb_call_info_kw_arg_t *keywords = NULL;
VALUE parent_block = iseq->compile_data->current_block;
+ INSN *yoda_insn = 0;
iseq->compile_data->current_block = Qfalse;
INIT_ANCHOR(recv);
@@ -4499,7 +4678,44 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
#endif
/* receiver */
if (type == NODE_CALL) {
- COMPILE(recv, "recv", node->nd_recv);
+ enum ruby_optimized_method om;
+ /*
+ * optimize:
+ * "string literal".method(arg)
+ * "yoda" == other
+ * "yoda" != other
+ * "str" + other
+ * "str" * other
+ * "fmt" % args
+ * ...
+ * result A:
+ * putobject str
+ * <setup_args>
+ * opt_str_lit_yoda([str, om, off])
+ * send mid ...
+ *
+ * result B:
+ * opt_str_lit_recv([str, om]) => str
+ * <setup_args (putobject|putstring|putspecial)>
+ * send mid ...
+ */
+ if (iseq->compile_data->option->peephole_optimization &&
+ ((om = opt_str_lit_recv_om(mid)) != OM_LAST_) &&
+ !private_recv_p(node) &&
+ node->nd_recv && nd_type(node->nd_recv) == NODE_STR &&
+ node->nd_args && nd_type(node->nd_args) == NODE_ARRAY &&
+ node->nd_args->nd_alen == 1)
+ {
+ VALUE yoda = rb_fstring(node->nd_recv->nd_lit);
+ int off = node->nd_args->nd_alen;
+ VALUE yoda_info = new_recvinfo_for_yoda(iseq, yoda, om, off);
+
+ node->nd_recv->nd_lit = yoda;
+ ADD_INSN1(recv, line, putobject, yoda_info /* temporary */);
+ yoda_insn = (INSN *)recv->last;
+ } else {
+ COMPILE(recv, "recv", node->nd_recv);
+ }
}
else if (type == NODE_FCALL || type == NODE_VCALL) {
ADD_CALL_RECEIVER(recv, line);
@@ -4527,6 +4743,28 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
flag |= VM_CALL_FCALL;
}
+ if (yoda_insn) {
+ INSN *last = (INSN *)ret->last;
+ VALUE yoda_info = yoda_insn->operands[0];
+
+ assert(yoda_insn->insn_id == BIN(putobject));
+ switch (last->insn_id) {
+ case BIN(putstring):
+ case BIN(putobject):
+ case BIN(getlocal):
+ /*
+ * [putobject, yoda_info] => [opt_str_lit_recv, recv_info]
+ * n.b.: opt_str_lit_recv ignores 3rd element of recv_info
+ */
+ yoda_insn->insn_id = BIN(opt_str_lit_recv);
+ break;
+ default:
+ /* [putobject, yoda_info] => [putobject, "str lit"] */
+ yoda_insn->operands[0] = RARRAY_AREF(yoda_info, 0); /* str */
+
+ ADD_INSN1(ret, line, opt_str_lit_yoda, yoda_info);
+ }
+ }
ADD_SEND_R(ret, line, mid, argc, parent_block, INT2FIX(flag), keywords);
if (poped) {
@@ -6052,3 +6290,44 @@ rb_parse_in_main(void)
{
return GET_THREAD()->parse_in_eval < 0;
}
+
+#if OPT_STR_LIT_UNDO
+/*
+ * Live bytecode patch:
+ * - opt_str_lit(recv_info)
+ * + putstring(str) # str is recv_info[0]
+ *
+ * If allocation optimization fails at this call site once, assume it
+ * will fail in the future. This prevents performance regressions for
+ * things like #include? calls which may be used with unoptimized
+ * classes (Set,*DBM and many others) as well as optimized core classes
+ * (Array/Hash/String). Call sites which only use optimized core
+ * classes will never get here.
+ */
+void
+rb_undo_opt_str_lit(rb_control_frame_t *cfp)
+{
+ VALUE *insn = cfp->pc - insn_len(BIN(opt_str_lit_tmask));
+
+#if OPT_DIRECT_THREADED_CODE || OPT_CALL_THREADED_CODE
+ const void * const *table = rb_vm_get_insns_address_table();
+
+ assert(((VALUE)table[BIN(opt_str_lit_tmask)] == insn[0] ||
+ (VALUE)table[BIN(opt_str_lit_recv)] == insn[0]) &&
+ "mismatch");
+ insn[0] = (VALUE)table[BIN(putstring)];
+#else
+ assert(((VALUE)BIN(opt_str_lit_tmask) == insn[0] ||
+ (VALUE)BIN(opt_str_lit_recv) == insn[0]) &&
+ "mismatch");
+ insn[0] = (VALUE)BIN(putstring);
+#endif
+ assert(insn_len(BIN(putstring)) == insn_len(BIN(opt_str_lit_tmask)));
+ assert(insn_len(BIN(putstring)) == insn_len(BIN(opt_str_lit_recv)));
+
+ assert(T_ARRAY == BUILTIN_TYPE(insn[1]));
+
+ /* n.b.: recv_info remains marked */
+ insn[1] = RARRAY_AREF(insn[1], 0); /* recv_info[0] == str */
+}
+#endif /* OPT_STR_LIT_UNDO */
diff --git a/defs/id.def b/defs/id.def
index 6bf1854..a1b57ac 100644
--- a/defs/id.def
+++ b/defs/id.def
@@ -58,6 +58,42 @@ firstline, predefined = __LINE__+1, %[\
core#hash_merge_ary
core#hash_merge_ptr
core#hash_merge_kwd
+ gsub
+ gsub!
+ sub
+ sub!
+ tr
+ tr!
+ tr_s
+ tr_s!
+ delete
+ delete!
+ include?
+ member?
+ has_key?
+ key?
+ fetch
+ count
+ chomp
+ chomp!
+ squeeze
+ squeeze!
+ pack
+ unpack
+ split
+ join
+ encode
+ encode!
+ force_encoding
+ index
+ rindex
+ match
+ casecmp
+ insert
+ start_with?
+ end_with?
+ partition
+ rpartition
]
class KeywordError < RuntimeError
@@ -85,6 +121,7 @@ predefined.split(/^/).each_with_index do |line, num|
token = "_#{token.gsub(/\W+/, '_')}"
else
token = token.sub(/\?/, 'P').sub(/\A[a-z]/) {$&.upcase}
+ token.sub!(/!\z/, "_bang")
token.sub!(/\A\$/, "_G_")
token.sub!(/\A@@/, "_C_")
token.sub!(/\A@/, "_I_")
diff --git a/defs/opt_method.def b/defs/opt_method.def
new file mode 100644
index 0000000..115c89b
--- /dev/null
+++ b/defs/opt_method.def
@@ -0,0 +1,85 @@
+# byte align the bitmap for now, maybe some arches do better with long or int
+# we may also use a larger size (in the unlikely case) we need more than
+# 7 optimized classes per mid. Currently this caps us to 256 optimized
+# (mid, klass) combinations (tested with OM_SHIFT=4, giving us 64K)
+OM_SHIFT = 3
+OM_ALIGN = 1 << OM_SHIFT
+OM_ALIGN_MASK = ~(OM_ALIGN - 1)
+OPT_METHODS = [
+ %w(idPLUS Fixnum Float String Array),
+ %w(idMINUS Fixnum Float),
+ %w(idMULT Fixnum Float String),
+ %w(idDIV Fixnum Float),
+ %w(idMOD Fixnum Float String),
+ %w(idEq Fixnum Float String),
+ %w(idNeq Fixnum Float String),
+ # id, mask classes
+ [ 'idEqq', %w(Bignum Fixnum Float Symbol), *%w(String) ],
+ %w(idLT Fixnum Float),
+ %w(idLE Fixnum Float),
+ %w(idGT Fixnum Float),
+ %w(idGE Fixnum Float),
+ %w(idLTLT Array String),
+ %w(idAREF Array Hash),
+ %w(idASET Array Hash),
+ %w(idLength Array Hash String),
+ %w(idSize Array Hash String),
+ %w(idEmptyP Array Hash String),
+ %w(idSucc Fixnum String),
+ %w(idEqTilde Regexp String),
+ %w(idFreeze String),
+ %w(idGsub String),
+ %w(idGsub_bang String),
+ %w(idSub String),
+ %w(idSub_bang String),
+ %w(idTr String),
+ %w(idTr_bang String),
+ %w(idTr_s String),
+ %w(idTr_s_bang String),
+ [ "idDelete", %w(Array Hash String) ],
+ [ "idIncludeP", %w(Array Hash String) ],
+ %w(idMemberP Hash),
+ %w(idHas_keyP Hash),
+ %w(idKeyP Hash),
+ %w(idFetch Hash),
+ %w(idUnpack String),
+ %w(idPack Array),
+ %w(idSplit String),
+ %w(idJoin Array),
+ %w(idCount String),
+ %w(idChomp String),
+ %w(idChomp_bang String),
+ %w(idSqueeze String),
+ %w(idSqueeze_bang String),
+ %w(idDelete_bang String),
+ %w(idEncode String),
+ %w(idEncode_bang String),
+ %w(idForce_encoding String),
+ %w(idIndex String),
+ %w(idRindex String),
+ %w(idMatch String),
+ %w(idCasecmp String),
+ %w(idInsert String),
+ %w(idStart_withP String),
+ %w(idEnd_withP String),
+ %w(idPartition String),
+ %w(idRpartition String),
+]
+
+# for checking optimized classes,
+# speeds up method definitions of non-core classes
+def opt_classes
+ rv = {}
+ OPT_METHODS.each do |(_, *classes)|
+ classes.flatten.each { |c| rv[c] = true }
+ end
+ rv
+end
+
+def om(mid, klass)
+ if Array === klass
+ "OM_#{mid}__#{klass.join('_')}"
+ else
+ "OM_#{mid}__#{klass}"
+ end
+end
diff --git a/insns.def b/insns.def
index eae0b8e..e1f2c97 100644
--- a/insns.def
+++ b/insns.def
@@ -357,6 +357,111 @@ putstring
}
/**
+ @c optimize
+ @e put string val. string may be created depending on recv_info conditions
+ */
+DEFINE_INSN
+opt_str_lit_tmask
+(VALUE recv_info)
+()
+(VALUE val)
+{
+ /*
+ * recv_info:
+ * 0 - str
+ * 1 - optimized method flag (OM_*)
+ * 2 - tmask (optimized receiver classes)
+ * 3 - stack offset (Fixint),
+ * -1 stack offset means receiver is the frozen string literal itself
+ */
+ const VALUE *ri = RARRAY_CONST_PTR(recv_info);
+ enum ruby_optimized_method om = FIX2UINT(ri[1]);
+ int tmask = FIX2INT(ri[2]);
+ int n = FIX2INT(ri[3]);
+ VALUE recv;
+
+ val = ri[0]; /* hopefully, this is the only val assignment we need */
+ recv = n < 0 ? val : TOPN(n);
+
+ if (!SPECIAL_CONST_P(recv)) {
+ enum ruby_value_type btype = BUILTIN_TYPE(recv);
+ int rmask = 1 << btype;
+
+ if ((rmask & tmask) &&
+ (rb_opt_method_class(btype) == RBASIC_CLASS(recv))) {
+ if (rb_opt_method_is_mask(om)) {
+ if (rb_basic_mask_unredefined_p(om)) {
+ goto out_tmask; /* fast path */
+ }
+ }
+ else if (rb_basic_op_unredefined_p(om)) {
+ goto out_tmask; /* fast path */
+ }
+ }
+ }
+ /* slow path */
+ val = rb_str_resurrect(val);
+ rb_undo_opt_str_lit(GET_CFP());
+out_tmask:
+}
+
+/**
+ @c optimize
+ @e put string val. string may be created depending on recv_info conditions
+ */
+DEFINE_INSN
+opt_str_lit_recv
+(VALUE recv_info)
+()
+(VALUE val)
+{
+ /*
+ * recv_info:
+ * 0 - str
+ * 1 - optimized method flag (OM_*)
+ */
+ const VALUE *ri = RARRAY_CONST_PTR(recv_info);
+ enum ruby_optimized_method om = FIX2UINT(ri[1]);
+
+ val = ri[0]; /* hopefully, this is the only val assignment we need */
+
+ if (!rb_basic_op_unredefined_p(om)) {
+ val = rb_str_resurrect(val);
+ /*
+ * do not bother with: rb_undo_opt_str_lit(GET_CFP());
+ * here, it is crazy to redefine core String methods :P
+ */
+ }
+}
+
+/**
+ @c optimize
+ @e put string val. string may be dup-ed depending on recv_info conditions
+ */
+DEFINE_INSN
+opt_str_lit_yoda
+(VALUE recv_info)
+()
+()
+{
+ /*
+ * recv_info:
+ * 0 - str (only for assert)
+ * 1 - tmask (optimized receiver classes)
+ * 2 - stack offset (Fixint)
+ */
+ const VALUE *ri = RARRAY_CONST_PTR(recv_info);
+ enum ruby_optimized_method om = FIX2UINT(ri[1]);
+ int off = FIX2INT(ri[2]);
+ VALUE recv = TOPN(off);
+
+ assert(ri[0] == recv);
+ if (!rb_basic_op_unredefined_p(om)) {
+ TOPN(off) = rb_str_resurrect(recv);
+ }
+}
+
+/**
@c put
@e put concatenate strings
@j スタックトップの文字列を n 個連結し,結果をスタックにプッシュする。
@@ -1024,20 +1129,6 @@ send
CALL_METHOD(ci);
}
-DEFINE_INSN
-opt_str_freeze
-(VALUE str)
-()
-(VALUE val)
-{
- if (BASIC_OP_UNREDEFINED_P(BOP_FREEZE, STRING_REDEFINED_OP_FLAG)) {
- val = str;
- }
- else {
- val = rb_funcall(rb_str_resurrect(str), idFreeze, 0);
- }
-}
-
/**
@c optimize
@e Invoke method without block
@@ -1307,11 +1398,7 @@ opt_case_dispatch
case T_FIXNUM:
case T_BIGNUM:
case T_STRING:
- if (BASIC_OP_UNREDEFINED_P(BOP_EQQ,
- SYMBOL_REDEFINED_OP_FLAG |
- FIXNUM_REDEFINED_OP_FLAG |
- BIGNUM_REDEFINED_OP_FLAG |
- STRING_REDEFINED_OP_FLAG)) {
+ if (rb_basic_mask_unredefined_p(OM_idEqq__Bignum_Fixnum_Float_Symbol)) {
st_data_t val;
if (st_lookup(RHASH_TBL_RAW(hash), key, &val)) {
JUMP(FIX2INT((VALUE)val));
@@ -1339,8 +1426,7 @@ opt_plus
(VALUE recv, VALUE obj)
(VALUE val)
{
- if (FIXNUM_2_P(recv, obj) &&
- BASIC_OP_UNREDEFINED_P(BOP_PLUS,FIXNUM_REDEFINED_OP_FLAG)) {
+ if (FIXNUM_2_P(recv, obj) && BASIC_OP_UNREDEFINED_P(idPLUS, Fixnum)) {
/* fixnum + fixnum */
#ifndef LONG_LONG_VALUE
val = (recv + (obj & (~1)));
@@ -1363,20 +1449,20 @@ opt_plus
#endif
}
else if (FLONUM_2_P(recv, obj) &&
- BASIC_OP_UNREDEFINED_P(BOP_PLUS, FLOAT_REDEFINED_OP_FLAG)) {
+ BASIC_OP_UNREDEFINED_P(idPLUS, Float)) {
val = DBL2NUM(RFLOAT_VALUE(recv) + RFLOAT_VALUE(obj));
}
else if (!SPECIAL_CONST_P(recv) && !SPECIAL_CONST_P(obj)) {
if (RBASIC_CLASS(recv) == rb_cFloat && RBASIC_CLASS(obj) == rb_cFloat &&
- BASIC_OP_UNREDEFINED_P(BOP_PLUS, FLOAT_REDEFINED_OP_FLAG)) {
+ BASIC_OP_UNREDEFINED_P(idPLUS, Float)) {
val = DBL2NUM(RFLOAT_VALUE(recv) + RFLOAT_VALUE(obj));
}
else if (RBASIC_CLASS(recv) == rb_cString && RBASIC_CLASS(obj) == rb_cString &&
- BASIC_OP_UNREDEFINED_P(BOP_PLUS, STRING_REDEFINED_OP_FLAG)) {
+ BASIC_OP_UNREDEFINED_P(idPLUS, String)) {
val = rb_str_plus(recv, obj);
}
else if (RBASIC_CLASS(recv) == rb_cArray &&
- BASIC_OP_UNREDEFINED_P(BOP_PLUS, ARRAY_REDEFINED_OP_FLAG)) {
+ BASIC_OP_UNREDEFINED_P(idPLUS, Array)) {
val = rb_ary_plus(recv, obj);
}
else {
@@ -1403,7 +1489,7 @@ opt_minus
(VALUE val)
{
if (FIXNUM_2_P(recv, obj) &&
- BASIC_OP_UNREDEFINED_P(BOP_MINUS, FIXNUM_REDEFINED_OP_FLAG)) {
+ BASIC_OP_UNREDEFINED_P(idMINUS, Fixnum)) {
long a, b, c;
a = FIX2LONG(recv);
@@ -1418,12 +1504,12 @@ opt_minus
}
}
else if (FLONUM_2_P(recv, obj) &&
- BASIC_OP_UNREDEFINED_P(BOP_MINUS, FLOAT_REDEFINED_OP_FLAG)) {
+ BASIC_OP_UNREDEFINED_P(idMINUS, Float)) {
val = DBL2NUM(RFLOAT_VALUE(recv) - RFLOAT_VALUE(obj));
}
else if (!SPECIAL_CONST_P(recv) && !SPECIAL_CONST_P(obj)) {
if (RBASIC_CLASS(recv) == rb_cFloat && RBASIC_CLASS(obj) == rb_cFloat &&
- BASIC_OP_UNREDEFINED_P(BOP_MINUS, FLOAT_REDEFINED_OP_FLAG)) {
+ BASIC_OP_UNREDEFINED_P(idMINUS, Float)) {
val = DBL2NUM(RFLOAT_VALUE(recv) - RFLOAT_VALUE(obj));
}
else {
@@ -1451,7 +1537,7 @@ opt_mult
(VALUE val)
{
if (FIXNUM_2_P(recv, obj) &&
- BASIC_OP_UNREDEFINED_P(BOP_MULT, FIXNUM_REDEFINED_OP_FLAG)) {
+ BASIC_OP_UNREDEFINED_P(idMULT, Fixnum)) {
long a, b;
a = FIX2LONG(recv);
@@ -1468,13 +1554,12 @@ opt_mult
}
}
}
- else if (FLONUM_2_P(recv, obj) &&
- BASIC_OP_UNREDEFINED_P(BOP_MULT, FLOAT_REDEFINED_OP_FLAG)) {
+ else if (FLONUM_2_P(recv, obj) && BASIC_OP_UNREDEFINED_P(idMULT, Float)) {
val = DBL2NUM(RFLOAT_VALUE(recv) * RFLOAT_VALUE(obj));
}
else if (!SPECIAL_CONST_P(recv) && !SPECIAL_CONST_P(obj)) {
if (RBASIC_CLASS(recv) == rb_cFloat && RBASIC_CLASS(obj) == rb_cFloat &&
- BASIC_OP_UNREDEFINED_P(BOP_MULT, FLOAT_REDEFINED_OP_FLAG)) {
+ BASIC_OP_UNREDEFINED_P(idMULT, Float)) {
val = DBL2NUM(RFLOAT_VALUE(recv) * RFLOAT_VALUE(obj));
}
else {
@@ -1500,8 +1585,7 @@ opt_div
(VALUE recv, VALUE obj)
(VALUE val)
{
- if (FIXNUM_2_P(recv, obj) &&
- BASIC_OP_UNREDEFINED_P(BOP_DIV, FIXNUM_REDEFINED_OP_FLAG)) {
+ if (FIXNUM_2_P(recv, obj) && BASIC_OP_UNREDEFINED_P(idDIV, Fixnum)) {
long x, y, div;
x = FIX2LONG(recv);
@@ -1531,13 +1615,12 @@ opt_div
}
val = LONG2NUM(div);
}
- else if (FLONUM_2_P(recv, obj) &&
- BASIC_OP_UNREDEFINED_P(BOP_DIV, FLOAT_REDEFINED_OP_FLAG)) {
+ else if (FLONUM_2_P(recv, obj) && BASIC_OP_UNREDEFINED_P(idDIV, Float)) {
val = DBL2NUM(RFLOAT_VALUE(recv) / RFLOAT_VALUE(obj));
}
else if (!SPECIAL_CONST_P(recv) && !SPECIAL_CONST_P(obj)) {
if (RBASIC_CLASS(recv) == rb_cFloat && RBASIC_CLASS(obj) == rb_cFloat &&
- BASIC_OP_UNREDEFINED_P(BOP_DIV, FLOAT_REDEFINED_OP_FLAG)) {
+ BASIC_OP_UNREDEFINED_P(idDIV, Float)) {
val = DBL2NUM(RFLOAT_VALUE(recv) / RFLOAT_VALUE(obj));
}
else {
@@ -1563,8 +1646,7 @@ opt_mod
(VALUE recv, VALUE obj)
(VALUE val)
{
- if (FIXNUM_2_P(recv, obj) &&
- BASIC_OP_UNREDEFINED_P(BOP_MOD, FIXNUM_REDEFINED_OP_FLAG )) {
+ if (FIXNUM_2_P(recv, obj) && BASIC_OP_UNREDEFINED_P(idMOD, Fixnum )) {
long x, y;
x = FIX2LONG(recv);
@@ -1598,13 +1680,12 @@ opt_mod
val = LONG2FIX(mod);
}
}
- else if (FLONUM_2_P(recv, obj) &&
- BASIC_OP_UNREDEFINED_P(BOP_MOD, FLOAT_REDEFINED_OP_FLAG)) {
+ else if (FLONUM_2_P(recv, obj) && BASIC_OP_UNREDEFINED_P(idMOD, Float)) {
val = DBL2NUM(ruby_float_mod(RFLOAT_VALUE(recv), RFLOAT_VALUE(obj)));
}
else if (!SPECIAL_CONST_P(recv) && !SPECIAL_CONST_P(obj)) {
if (RBASIC_CLASS(recv) == rb_cFloat && RBASIC_CLASS(obj) == rb_cFloat &&
- BASIC_OP_UNREDEFINED_P(BOP_MOD, FLOAT_REDEFINED_OP_FLAG)) {
+ BASIC_OP_UNREDEFINED_P(idMOD, Float)) {
val = DBL2NUM(ruby_float_mod(RFLOAT_VALUE(recv), RFLOAT_VALUE(obj)));
}
else {
@@ -1683,7 +1764,7 @@ opt_lt
(VALUE val)
{
if (FIXNUM_2_P(recv, obj) &&
- BASIC_OP_UNREDEFINED_P(BOP_LT, FIXNUM_REDEFINED_OP_FLAG)) {
+ BASIC_OP_UNREDEFINED_P(idLT, Fixnum)) {
SIGNED_VALUE a = recv, b = obj;
if (a < b) {
@@ -1694,13 +1775,13 @@ opt_lt
}
}
else if (FLONUM_2_P(recv, obj) &&
- BASIC_OP_UNREDEFINED_P(BOP_LT, FLOAT_REDEFINED_OP_FLAG)) {
+ BASIC_OP_UNREDEFINED_P(idLT, Float)) {
/* flonum is not NaN */
val = RFLOAT_VALUE(recv) < RFLOAT_VALUE(obj) ? Qtrue : Qfalse;
}
else if (!SPECIAL_CONST_P(recv) && !SPECIAL_CONST_P(obj)) {
if (RBASIC_CLASS(recv) == rb_cFloat && RBASIC_CLASS(obj) == rb_cFloat &&
- BASIC_OP_UNREDEFINED_P(BOP_LT, FLOAT_REDEFINED_OP_FLAG)) {
+ BASIC_OP_UNREDEFINED_P(idLT, Float)) {
val = double_cmp_lt(RFLOAT_VALUE(recv), RFLOAT_VALUE(obj));
}
else {
@@ -1727,7 +1808,7 @@ opt_le
(VALUE val)
{
if (FIXNUM_2_P(recv, obj) &&
- BASIC_OP_UNREDEFINED_P(BOP_LE, FIXNUM_REDEFINED_OP_FLAG)) {
+ BASIC_OP_UNREDEFINED_P(idLE, Fixnum)) {
SIGNED_VALUE a = recv, b = obj;
if (a <= b) {
@@ -1738,7 +1819,7 @@ opt_le
}
}
else if (FLONUM_2_P(recv, obj) &&
- BASIC_OP_UNREDEFINED_P(BOP_LE, FLOAT_REDEFINED_OP_FLAG)) {
+ BASIC_OP_UNREDEFINED_P(idLE, Float)) {
/* flonum is not NaN */
val = RFLOAT_VALUE(recv) <= RFLOAT_VALUE(obj) ? Qtrue : Qfalse;
}
@@ -1762,7 +1843,7 @@ opt_gt
(VALUE val)
{
if (FIXNUM_2_P(recv, obj) &&
- BASIC_OP_UNREDEFINED_P(BOP_GT, FIXNUM_REDEFINED_OP_FLAG)) {
+ BASIC_OP_UNREDEFINED_P(idGT, Fixnum)) {
SIGNED_VALUE a = recv, b = obj;
if (a > b) {
@@ -1773,13 +1854,13 @@ opt_gt
}
}
else if (FLONUM_2_P(recv, obj) &&
- BASIC_OP_UNREDEFINED_P(BOP_GT, FLOAT_REDEFINED_OP_FLAG)) {
+ BASIC_OP_UNREDEFINED_P(idGT, Float)) {
/* flonum is not NaN */
val = RFLOAT_VALUE(recv) > RFLOAT_VALUE(obj) ? Qtrue : Qfalse;
}
else if (!SPECIAL_CONST_P(recv) && !SPECIAL_CONST_P(obj)) {
if (RBASIC_CLASS(recv) == rb_cFloat && RBASIC_CLASS(obj) == rb_cFloat &&
- BASIC_OP_UNREDEFINED_P(BOP_GT, FLOAT_REDEFINED_OP_FLAG)) {
+ BASIC_OP_UNREDEFINED_P(idGT, Float)) {
val = double_cmp_gt(RFLOAT_VALUE(recv), RFLOAT_VALUE(obj));
}
else {
@@ -1806,7 +1887,7 @@ opt_ge
(VALUE val)
{
if (FIXNUM_2_P(recv, obj) &&
- BASIC_OP_UNREDEFINED_P(BOP_GE, FIXNUM_REDEFINED_OP_FLAG)) {
+ BASIC_OP_UNREDEFINED_P(idGE, Fixnum)) {
SIGNED_VALUE a = recv, b = obj;
if (a >= b) {
@@ -1816,8 +1897,7 @@ opt_ge
val = Qfalse;
}
}
- else if (FLONUM_2_P(recv, obj) &&
- BASIC_OP_UNREDEFINED_P(BOP_GE, FLOAT_REDEFINED_OP_FLAG)) {
+ else if (FLONUM_2_P(recv, obj) && BASIC_OP_UNREDEFINED_P(idGE, Float)) {
/* flonum is not NaN */
val = RFLOAT_VALUE(recv) >= RFLOAT_VALUE(obj) ? Qtrue : Qfalse;
}
@@ -1841,11 +1921,11 @@ opt_ltlt
{
if (!SPECIAL_CONST_P(recv)) {
if (RBASIC_CLASS(recv) == rb_cString &&
- BASIC_OP_UNREDEFINED_P(BOP_LTLT, STRING_REDEFINED_OP_FLAG)) {
+ BASIC_OP_UNREDEFINED_P(idLTLT, String)) {
val = rb_str_concat(recv, obj);
}
else if (RBASIC_CLASS(recv) == rb_cArray &&
- BASIC_OP_UNREDEFINED_P(BOP_LTLT, ARRAY_REDEFINED_OP_FLAG)) {
+ BASIC_OP_UNREDEFINED_P(idLTLT, Array)) {
val = rb_ary_push(recv, obj);
}
else {
@@ -1872,10 +1952,10 @@ opt_aref
(VALUE val)
{
if (!SPECIAL_CONST_P(recv)) {
- if (RBASIC_CLASS(recv) == rb_cArray && BASIC_OP_UNREDEFINED_P(BOP_AREF, ARRAY_REDEFINED_OP_FLAG) && FIXNUM_P(obj)) {
+ if (RBASIC_CLASS(recv) == rb_cArray && BASIC_OP_UNREDEFINED_P(idAREF, Array) && FIXNUM_P(obj)) {
val = rb_ary_entry(recv, FIX2LONG(obj));
}
- else if (RBASIC_CLASS(recv) == rb_cHash && BASIC_OP_UNREDEFINED_P(BOP_AREF, HASH_REDEFINED_OP_FLAG)) {
+ else if (RBASIC_CLASS(recv) == rb_cHash && BASIC_OP_UNREDEFINED_P(idAREF, Hash)) {
val = rb_hash_aref(recv, obj);
}
else {
@@ -1902,11 +1982,11 @@ opt_aset
(VALUE val)
{
if (!SPECIAL_CONST_P(recv)) {
- if (RBASIC_CLASS(recv) == rb_cArray && BASIC_OP_UNREDEFINED_P(BOP_ASET, ARRAY_REDEFINED_OP_FLAG) && FIXNUM_P(obj)) {
+ if (RBASIC_CLASS(recv) == rb_cArray && BASIC_OP_UNREDEFINED_P(idASET, Array) && FIXNUM_P(obj)) {
rb_ary_store(recv, FIX2LONG(obj), set);
val = set;
}
- else if (RBASIC_CLASS(recv) == rb_cHash && BASIC_OP_UNREDEFINED_P(BOP_ASET, HASH_REDEFINED_OP_FLAG)) {
+ else if (RBASIC_CLASS(recv) == rb_cHash && BASIC_OP_UNREDEFINED_P(idASET, Hash)) {
rb_hash_aset(recv, obj, set);
val = set;
}
@@ -1934,7 +2014,8 @@ opt_aset_with
(VALUE recv, VALUE val)
(VALUE val)
{
- if (!SPECIAL_CONST_P(recv) && RBASIC_CLASS(recv) == rb_cHash && BASIC_OP_UNREDEFINED_P(BOP_ASET, HASH_REDEFINED_OP_FLAG)) {
+ if (!SPECIAL_CONST_P(recv) && RBASIC_CLASS(recv) == rb_cHash &&
+ BASIC_OP_UNREDEFINED_P(idASET, Hash)) {
rb_hash_aset(recv, key, val);
}
else {
@@ -1956,7 +2037,8 @@ opt_aref_with
(VALUE recv)
(VALUE val)
{
- if (!SPECIAL_CONST_P(recv) && RBASIC_CLASS(recv) == rb_cHash && BASIC_OP_UNREDEFINED_P(BOP_AREF, HASH_REDEFINED_OP_FLAG)) {
+ if (!SPECIAL_CONST_P(recv) && RBASIC_CLASS(recv) == rb_cHash &&
+ BASIC_OP_UNREDEFINED_P(idAREF, Hash)) {
val = rb_hash_aref(recv, key);
}
else {
@@ -1979,15 +2061,15 @@ opt_length
{
if (!SPECIAL_CONST_P(recv)) {
if (RBASIC_CLASS(recv) == rb_cString &&
- BASIC_OP_UNREDEFINED_P(BOP_LENGTH, STRING_REDEFINED_OP_FLAG)) {
+ BASIC_OP_UNREDEFINED_P(idLength, String)) {
val = rb_str_length(recv);
}
else if (RBASIC_CLASS(recv) == rb_cArray &&
- BASIC_OP_UNREDEFINED_P(BOP_LENGTH, ARRAY_REDEFINED_OP_FLAG)) {
+ BASIC_OP_UNREDEFINED_P(idLength, Array)) {
val = LONG2NUM(RARRAY_LEN(recv));
}
else if (RBASIC_CLASS(recv) == rb_cHash &&
- BASIC_OP_UNREDEFINED_P(BOP_LENGTH, HASH_REDEFINED_OP_FLAG)) {
+ BASIC_OP_UNREDEFINED_P(idLength, Hash)) {
val = INT2FIX(RHASH_SIZE(recv));
}
else {
@@ -2014,15 +2096,15 @@ opt_size
{
if (!SPECIAL_CONST_P(recv)) {
if (RBASIC_CLASS(recv) == rb_cString &&
- BASIC_OP_UNREDEFINED_P(BOP_SIZE, STRING_REDEFINED_OP_FLAG)) {
+ BASIC_OP_UNREDEFINED_P(idSize, String)) {
val = rb_str_length(recv);
}
else if (RBASIC_CLASS(recv) == rb_cArray &&
- BASIC_OP_UNREDEFINED_P(BOP_SIZE, ARRAY_REDEFINED_OP_FLAG)) {
+ BASIC_OP_UNREDEFINED_P(idSize, Array)) {
val = LONG2NUM(RARRAY_LEN(recv));
}
else if (RBASIC_CLASS(recv) == rb_cHash &&
- BASIC_OP_UNREDEFINED_P(BOP_SIZE, HASH_REDEFINED_OP_FLAG)) {
+ BASIC_OP_UNREDEFINED_P(idSize, Hash)) {
val = INT2FIX(RHASH_SIZE(recv));
}
else {
@@ -2049,17 +2131,17 @@ opt_empty_p
{
if (!SPECIAL_CONST_P(recv)) {
if (RBASIC_CLASS(recv) == rb_cString &&
- BASIC_OP_UNREDEFINED_P(BOP_EMPTY_P, STRING_REDEFINED_OP_FLAG)) {
+ BASIC_OP_UNREDEFINED_P(idEmptyP, String)) {
if (RSTRING_LEN(recv) == 0) val = Qtrue;
else val = Qfalse;
}
else if (RBASIC_CLASS(recv) == rb_cArray &&
- BASIC_OP_UNREDEFINED_P(BOP_EMPTY_P, ARRAY_REDEFINED_OP_FLAG)) {
+ BASIC_OP_UNREDEFINED_P(idEmptyP, Array)) {
if (RARRAY_LEN(recv) == 0) val = Qtrue;
else val = Qfalse;
}
else if (RBASIC_CLASS(recv) == rb_cHash &&
- BASIC_OP_UNREDEFINED_P(BOP_EMPTY_P, HASH_REDEFINED_OP_FLAG)) {
+ BASIC_OP_UNREDEFINED_P(idEmptyP, Hash)) {
if (RHASH_EMPTY_P(recv)) val = Qtrue;
else val = Qfalse;
}
@@ -2087,7 +2169,7 @@ opt_succ
{
if (SPECIAL_CONST_P(recv)) {
if (FIXNUM_P(recv) &&
- BASIC_OP_UNREDEFINED_P(BOP_SUCC, FIXNUM_REDEFINED_OP_FLAG)) {
+ BASIC_OP_UNREDEFINED_P(idSucc, Fixnum)) {
const VALUE obj = INT2FIX(1);
/* fixnum + INT2FIX(1) */
val = (recv + (obj & (~1)));
@@ -2102,7 +2184,7 @@ opt_succ
}
else {
if (RBASIC_CLASS(recv) == rb_cString &&
- BASIC_OP_UNREDEFINED_P(BOP_SUCC, STRING_REDEFINED_OP_FLAG)) {
+ BASIC_OP_UNREDEFINED_P(idSucc, String)) {
val = rb_str_succ(recv);
}
else
@@ -2152,7 +2234,7 @@ opt_regexpmatch1
(VALUE obj)
(VALUE val)
{
- if (BASIC_OP_UNREDEFINED_P(BOP_MATCH, REGEXP_REDEFINED_OP_FLAG)) {
+ if (BASIC_OP_UNREDEFINED_P(idEqTilde, Regexp)) {
val = rb_reg_match(r, obj);
}
else {
@@ -2172,7 +2254,7 @@ opt_regexpmatch2
(VALUE val)
{
if (CLASS_OF(obj2) == rb_cString &&
- BASIC_OP_UNREDEFINED_P(BOP_MATCH, STRING_REDEFINED_OP_FLAG)) {
+ BASIC_OP_UNREDEFINED_P(idEqTilde, String)) {
val = rb_reg_match(obj1, obj2);
}
else {
diff --git a/template/opt_method.h.tmpl b/template/opt_method.h.tmpl
new file mode 100644
index 0000000..e49e8ba
--- /dev/null
+++ b/template/opt_method.h.tmpl
@@ -0,0 +1,108 @@
+/* DO NOT EDIT THIS FILE DIRECTLY: edit template/opt_method.h.tmpl instead */
+#ifndef RUBY_OPT_METHOD_H
+#define RUBY_OPT_METHOD_H
+<%
+defs = File.join(File.dirname(File.dirname(erb.filename)), "defs/opt_method.def")
+eval(File.read(defs), binding, defs)
+tmasks = []
+%>
+typedef uint<%= OM_ALIGN %>_t rb_om_bitmap_t;
+
+enum ruby_optimized_method {
+<%
+opt_masks = {}
+mask_classes = {}
+n = 0
+OPT_METHODS.each do |(mid, *classes)|
+ classes.each do |klass|
+ if Array === klass
+ opt_masks[mid] = klass.dup
+ # we will align these in the second loop, below
+ klass.each { |k| mask_classes[k] = true }
+ next
+ end %>
+ <%= om(mid, klass) %> = (int)<%= n += 1 %>,
+<%
+ end # classes.each
+end # OPT_METHODS.each
+
+# align multi-class bits so a single AND operation may
+# be byte-aligned and used to check an mid for up to 7 classes at once:
+opt_masks.each do |mid, classes|
+ # round up n to the next aligned byte slot
+ n = (n + OM_ALIGN) & OM_ALIGN_MASK
+
+ classes.each do |k|
+%>
+ <%= om(mid, k) %> = (int)<%= n += 1 %>,
+<%=
+# we need this macro to generate shifts for the masks enums below:
+"#define #{om(mid, k)} (#{n})"
+%>
+<%
+ end # classes.each
+end # opt_masks.each
+if n >= ((1 << OM_ALIGN) - 1)
+ raise "OM_ALIGN needs to be raised to support more optimized methods"
+end
+%>
+ OM_LAST_ = (int)<%= om_last = (n += 1) %>, /* for bitmap sizing */
+ /* special mask values below */
+<%
+# generate mask enums
+opt_masks.each do |mid, c|
+ # n.b.: negate masks to simplify the rb_opt_method_is_mask check:
+%>
+ <%= om(mid, c) %> = -(<%=
+ # pack into 16 bits so it may be a negative Fixnum
+ # 1) 8 byte offset
+ # 2) OM_ALIGN bytes mask (8 or 16)
+ sep = "|\n "
+ "/* offset: */\n((#{om(mid, c[0])} / #{OM_ALIGN}) << #{OM_ALIGN}) " \
+ "#{sep} /* mask: */ (" +
+ c.map do |k|
+ "(1U << (#{om(mid, k)} % #{OM_ALIGN}))"
+ end.join(sep) + # mask
+ ')'
+ %>),
+<%
+ # mask for type checking in insns.def, we name this like the OM_*
+ # enum so it is easy to get this name using CPP macros
+ tmasks << [
+ "OM_TMASK_#{c.join('_')}",
+ 'INT2FIX(' +
+ c.map {|k| "(1U << RUBY_T_#{k.upcase})" }.join("|\\\n\t") +
+ ')'
+ ]
+end # opt_masks.each
+opt_classes.each_key do |k|
+ tmasks << [ "OM_TMASK_#{k}", "INT2FIX(1U << RUBY_T_#{k.upcase})" ]
+end # opt_classes.each_key
+%>
+ OM_ALIGN_ = <%= OM_ALIGN %>,
+ OM_SIZE_ = <%= ((om_last + OM_ALIGN) & OM_ALIGN_MASK) / OM_ALIGN %>,
+ OM_GETMASK_ = (1 << OM_ALIGN_) - 1
+};
+
+/* macros */
+<% tmasks.each do |(k,v)| %>
+#define <%= k %> (<%= v %>)
+<% end %>
+
+/* map a raw type to the preferred (optimized) class */
+static inline VALUE
+rb_opt_method_class(enum ruby_value_type type)
+{
+ switch (type) {
+<%
+opt_classes.each_key do |k|
+%>
+ case RUBY_T_<%= k.upcase %>: return rb_c<%= k %>;
+<%
+end
+%>
+ default: return Qfalse;
+ }
+}
+
+#endif /* RUBY_OPT_METHOD_H */
diff --git a/template/opt_method.inc.tmpl b/template/opt_method.inc.tmpl
new file mode 100644
index 0000000..acbdc1a
--- /dev/null
+++ b/template/opt_method.inc.tmpl
@@ -0,0 +1,42 @@
+/* DO NOT EDIT THIS FILE DIRECTLY: edit template/opt_method.inc.tmpl instead */
+<%
+defs = File.join(File.dirname(File.dirname(erb.filename)), "defs/opt_method.def")
+eval(File.read(defs), binding, defs)
+%>
+
+static void
+add_opt_method(st_table *tbl, VALUE klass, ID mid,
+ enum ruby_optimized_method om)
+{
+ rb_method_entry_t *me = rb_method_entry_at(klass, mid);
+
+ if (me && me->def && me->def->type == VM_METHOD_TYPE_CFUNC) {
+ st_insert(tbl, (st_data_t)me, (st_data_t)om);
+ }
+ else if (mid != idNeq) {
+ rb_bug("undefined optimized method: %s", rb_id2name(mid));
+ }
+}
+
+static void
+vm_init_redefined_flags(void *tbl)
+{
+<%
+OPT_METHODS.each do |(mid, *classes)|
+ classes.flatten.each do |klass|
+%>
+ add_opt_method(tbl, rb_c<%= klass %>, <%= mid %>, <%= om(mid, klass) %>);
+<%
+ end # classes.each
+end # OPT_METHODS.each
+%>
+}
+
+static int
+vm_redefinition_check_flag(VALUE klass)
+{
+<% opt_classes.each_key do |klass| %>
+ if (klass == rb_c<%= klass %>) return 1;
+<% end %>
+ return 0;
+}
diff --git a/vm.c b/vm.c
index a495da0..5f953c8 100644
--- a/vm.c
+++ b/vm.c
@@ -18,6 +18,7 @@
#include "eval_intern.h"
#include "probes.h"
#include "probes_helper.h"
+#include "opt_method.inc"
static inline VALUE *
VM_EP_LEP(VALUE *ep)
@@ -1174,30 +1175,16 @@ rb_iter_break_value(VALUE val)
static st_table *vm_opt_method_table = 0;
-static int
-vm_redefinition_check_flag(VALUE klass)
-{
- if (klass == rb_cFixnum) return FIXNUM_REDEFINED_OP_FLAG;
- if (klass == rb_cFloat) return FLOAT_REDEFINED_OP_FLAG;
- if (klass == rb_cString) return STRING_REDEFINED_OP_FLAG;
- if (klass == rb_cArray) return ARRAY_REDEFINED_OP_FLAG;
- if (klass == rb_cHash) return HASH_REDEFINED_OP_FLAG;
- if (klass == rb_cBignum) return BIGNUM_REDEFINED_OP_FLAG;
- if (klass == rb_cSymbol) return SYMBOL_REDEFINED_OP_FLAG;
- if (klass == rb_cTime) return TIME_REDEFINED_OP_FLAG;
- if (klass == rb_cRegexp) return REGEXP_REDEFINED_OP_FLAG;
- return 0;
-}
-
static void
rb_vm_check_redefinition_opt_method(const rb_method_entry_t *me, VALUE klass)
{
- st_data_t bop;
+ st_data_t om;
if (!me->def || me->def->type == VM_METHOD_TYPE_CFUNC) {
- if (st_lookup(vm_opt_method_table, (st_data_t)me, &bop)) {
- int flag = vm_redefinition_check_flag(klass);
+ if (st_lookup(vm_opt_method_table, (st_data_t)me, &om)) {
+ unsigned int i = om / OM_ALIGN_;
+ rb_om_bitmap_t mask = (rb_om_bitmap_t)(1U << (om % OM_ALIGN_));
- ruby_vm_redefined_flag[bop] |= flag;
+ ruby_vm_redefined_flag[i] |= mask;
}
}
}
@@ -1224,51 +1211,11 @@ rb_vm_check_redefinition_by_prepend(VALUE klass)
}
static void
-add_opt_method(VALUE klass, ID mid, VALUE bop)
-{
- rb_method_entry_t *me = rb_method_entry_at(klass, mid);
-
- if (me && me->def &&
- me->def->type == VM_METHOD_TYPE_CFUNC) {
- st_insert(vm_opt_method_table, (st_data_t)me, (st_data_t)bop);
- }
- else {
- rb_bug("undefined optimized method: %s", rb_id2name(mid));
- }
-}
-
-static void
vm_init_redefined_flag(void)
{
- ID mid;
- VALUE bop;
-
vm_opt_method_table = st_init_numtable();
-#define OP(mid_, bop_) (mid = id##mid_, bop = BOP_##bop_, ruby_vm_redefined_flag[bop] = 0)
-#define C(k) add_opt_method(rb_c##k, mid, bop)
- OP(PLUS, PLUS), (C(Fixnum), C(Float), C(String), C(Array));
- OP(MINUS, MINUS), (C(Fixnum), C(Float));
- OP(MULT, MULT), (C(Fixnum), C(Float));
- OP(DIV, DIV), (C(Fixnum), C(Float));
- OP(MOD, MOD), (C(Fixnum), C(Float));
- OP(Eq, EQ), (C(Fixnum), C(Float), C(String));
- OP(Eqq, EQQ), (C(Fixnum), C(Bignum), C(Float), C(Symbol), C(String));
- OP(LT, LT), (C(Fixnum), C(Float));
- OP(LE, LE), (C(Fixnum), C(Float));
- OP(GT, GT), (C(Fixnum), C(Float));
- OP(GE, GE), (C(Fixnum), C(Float));
- OP(LTLT, LTLT), (C(String), C(Array));
- OP(AREF, AREF), (C(Array), C(Hash));
- OP(ASET, ASET), (C(Array), C(Hash));
- OP(Length, LENGTH), (C(Array), C(String), C(Hash));
- OP(Size, SIZE), (C(Array), C(String), C(Hash));
- OP(EmptyP, EMPTY_P), (C(Array), C(String), C(Hash));
- OP(Succ, SUCC), (C(Fixnum), C(String), C(Time));
- OP(EqTilde, MATCH), (C(Regexp), C(String));
- OP(Freeze, FREEZE), (C(String));
-#undef C
-#undef OP
+ vm_init_redefined_flags(vm_opt_method_table); /* opt_method.h.tmpl */
}
/* for vm development */
diff --git a/vm_core.h b/vm_core.h
index 37bfc75..fe9bb5d 100644
--- a/vm_core.h
+++ b/vm_core.h
@@ -24,6 +24,7 @@
#include "method.h"
#include "ruby_atomic.h"
#include "ccan/list/list.h"
+#include "opt_method.h"
#include "ruby/thread_native.h"
#if defined(_WIN32)
@@ -355,33 +356,6 @@ enum ruby_special_exceptions {
ruby_special_error_count
};
-enum ruby_basic_operators {
- BOP_PLUS,
- BOP_MINUS,
- BOP_MULT,
- BOP_DIV,
- BOP_MOD,
- BOP_EQ,
- BOP_EQQ,
- BOP_LT,
- BOP_LE,
- BOP_LTLT,
- BOP_AREF,
- BOP_ASET,
- BOP_LENGTH,
- BOP_SIZE,
- BOP_EMPTY_P,
- BOP_SUCC,
- BOP_GT,
- BOP_GE,
- BOP_NOT,
- BOP_NEQ,
- BOP_MATCH,
- BOP_FREEZE,
-
- BOP_LAST_
-};
-
#define GetVMPtr(obj, ptr) \
GetCoreDataFromValue((obj), rb_vm_t, (ptr))
@@ -476,7 +450,7 @@ typedef struct rb_vm_struct {
size_t fiber_machine_stack_size;
} default_params;
- short redefined_flag[BOP_LAST_];
+ rb_om_bitmap_t redefined_flag[OM_SIZE_];
} rb_vm_t;
/* default values */
@@ -493,18 +467,8 @@ typedef struct rb_vm_struct {
#define RUBY_VM_FIBER_MACHINE_STACK_SIZE ( 64 * 1024 * sizeof(VALUE)) /* 256 KB or 512 KB */
#define RUBY_VM_FIBER_MACHINE_STACK_SIZE_MIN ( 16 * 1024 * sizeof(VALUE)) /* 64 KB or 128 KB */
-/* optimize insn */
-#define FIXNUM_REDEFINED_OP_FLAG (1 << 0)
-#define FLOAT_REDEFINED_OP_FLAG (1 << 1)
-#define STRING_REDEFINED_OP_FLAG (1 << 2)
-#define ARRAY_REDEFINED_OP_FLAG (1 << 3)
-#define HASH_REDEFINED_OP_FLAG (1 << 4)
-#define BIGNUM_REDEFINED_OP_FLAG (1 << 5)
-#define SYMBOL_REDEFINED_OP_FLAG (1 << 6)
-#define TIME_REDEFINED_OP_FLAG (1 << 7)
-#define REGEXP_REDEFINED_OP_FLAG (1 << 8)
-
-#define BASIC_OP_UNREDEFINED_P(op, klass) (LIKELY((GET_VM()->redefined_flag[(op)]&(klass)) == 0))
+#define BASIC_OP_UNREDEFINED_P(mid, klass) \
+ rb_basic_op_unredefined_p(OM_##mid##__##klass)
#ifndef VM_DEBUG_BP_CHECK
#define VM_DEBUG_BP_CHECK 0
diff --git a/vm_insnhelper.c b/vm_insnhelper.c
index e442383a..40cf5a5 100644
--- a/vm_insnhelper.c
+++ b/vm_insnhelper.c
@@ -847,17 +847,17 @@ VALUE
opt_eq_func(VALUE recv, VALUE obj, CALL_INFO ci)
{
if (FIXNUM_2_P(recv, obj) &&
- BASIC_OP_UNREDEFINED_P(BOP_EQ, FIXNUM_REDEFINED_OP_FLAG)) {
+ BASIC_OP_UNREDEFINED_P(idEq, Fixnum)) {
return (recv == obj) ? Qtrue : Qfalse;
}
else if (FLONUM_2_P(recv, obj) &&
- BASIC_OP_UNREDEFINED_P(BOP_EQ, FLOAT_REDEFINED_OP_FLAG)) {
+ BASIC_OP_UNREDEFINED_P(idEq, Float)) {
return (recv == obj) ? Qtrue : Qfalse;
}
else if (!SPECIAL_CONST_P(recv) && !SPECIAL_CONST_P(obj)) {
if (RBASIC_CLASS(recv) == rb_cFloat &&
RBASIC_CLASS(obj) == rb_cFloat &&
- BASIC_OP_UNREDEFINED_P(BOP_EQ, FLOAT_REDEFINED_OP_FLAG)) {
+ BASIC_OP_UNREDEFINED_P(idEq, Float)) {
double a = RFLOAT_VALUE(recv);
double b = RFLOAT_VALUE(obj);
@@ -868,7 +868,7 @@ opt_eq_func(VALUE recv, VALUE obj, CALL_INFO ci)
}
else if (RBASIC_CLASS(recv) == rb_cString &&
RBASIC_CLASS(obj) == rb_cString &&
- BASIC_OP_UNREDEFINED_P(BOP_EQ, STRING_REDEFINED_OP_FLAG)) {
+ BASIC_OP_UNREDEFINED_P(idEq, String)) {
return rb_str_equal(recv, obj);
}
}
diff --git a/vm_insnhelper.h b/vm_insnhelper.h
index 31f8ffc..f526eb7 100644
--- a/vm_insnhelper.h
+++ b/vm_insnhelper.h
@@ -229,5 +229,37 @@ enum vm_regan_acttype {
static VALUE make_no_method_exception(VALUE exc, const char *format,
VALUE obj, int argc, const VALUE *argv);
+static inline int
+rb_basic_op_unredefined_p(enum ruby_optimized_method om)
+{
+ unsigned int uom = (unsigned int)om;
+ unsigned int i = uom / OM_ALIGN_;
+ rb_om_bitmap_t mask = (rb_om_bitmap_t)(1U << (uom % OM_ALIGN_));
+
+ return LIKELY((GET_VM()->redefined_flag[i] & mask) == 0);
+}
+
+static inline int
+rb_basic_mask_unredefined_p(enum ruby_optimized_method om)
+{
+ unsigned int uom = (unsigned int)-om;
+ unsigned int offset = 0xffU & (uom >> OM_ALIGN_);
+ rb_om_bitmap_t mask = (rb_om_bitmap_t)(OM_GETMASK_ & uom);
+
+ return LIKELY((GET_VM()->redefined_flag[offset] & mask) == 0);
+}
+
+static inline int
+rb_opt_method_is_mask(enum ruby_optimized_method om)
+{
+ return !!((int)om < 0);
+}
+
+#if OPT_STR_LIT_UNDO
+/* compile.c */
+void rb_undo_opt_str_lit(rb_control_frame_t *cfp);
+#else /* OPT_STR_LIT_UNDO */
+static inline void rb_undo_opt_str_lit(rb_control_frame_t *cfp) { /* noop */ }
+#endif /* OPT_STR_LIT_UNDO */
#endif /* RUBY_INSNHELPER_H */
diff --git a/vm_opts.h b/vm_opts.h
index b67e254..9faac01 100644
--- a/vm_opts.h
+++ b/vm_opts.h
@@ -39,6 +39,7 @@
#define OPT_INLINE_METHOD_CACHE 1
#define OPT_GLOBAL_METHOD_CACHE 1
#define OPT_BLOCKINLINING 0
+#define OPT_STR_LIT_UNDO 1
/* architecture independent, affects generated code */
#define OPT_OPERANDS_UNIFICATION 1
--
EW
next prev parent reply other threads:[~2014-11-15 22:16 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2014-11-15 22:16 [PATCH 1/2] compile.c: hide "literal" optimizations behind peephole optimize Eric Wong
2014-11-15 22:16 ` Eric Wong [this message]
2014-11-16 9:21 ` [PATCH 2/2 v2] opt_str_lit-v9 Eric Wong
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=opt_str_lit-v8@1 \
--to=e@80x24.org \
--cc=spew@80x24.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).