From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.3.2 (2011-06-06) on dcvr.yhbt.net X-Spam-Level: X-Spam-ASN: X-Spam-Status: No, score=-2.0 required=3.0 tests=ALL_TRUSTED,AWL,BAYES_00, BODY_8BITS,URIBL_BLOCKED shortcircuit=no autolearn=unavailable version=3.3.2 X-Original-To: spew@80x24.org Received: from localhost (dcvr.yhbt.net [127.0.0.1]) by dcvr.yhbt.net (Postfix) with ESMTP id DCDFE1F708; Sun, 16 Nov 2014 09:21:35 +0000 (UTC) Date: Sun, 16 Nov 2014 09:21:35 +0000 From: Eric Wong To: spew@80x24.org Subject: [PATCH 2/2 v2] opt_str_lit-v9 Message-ID: References: MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Disposition: inline Content-Transfer-Encoding: 8bit In-Reply-To: List-Id: Changes since -v8: * rebased on r48458 and updated deps 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 | 38 +++++ compile.c | 313 ++++++++++++++++++++++++++++++++-- defs/id.def | 37 ++++ defs/opt_method.def | 85 +++++++++ ext/coverage/depend | 1 + ext/objspace/depend | 1 + 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 + 34 files changed, 929 insertions(+), 195 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 44be870..50d76c0 100644 --- a/common.mk +++ b/common.mk @@ -741,6 +741,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=$@ \ @@ -1920,6 +1934,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 @@ -2108,4 +2123,27 @@ vm_dump.$(OBJEXT): $(hdrdir)/ruby.h vm_dump.$(OBJEXT): {$(VPATH)}encoding.h vm_dump.$(OBJEXT): {$(VPATH)}oniguruma.h vm_trace.$(OBJEXT): $(hdrdir)/ruby.h +class.$(OBJEXT): {$(VPATH)}opt_method.h +compile.$(OBJEXT): {$(VPATH)}opt_method.h +cont.$(OBJEXT): {$(VPATH)}opt_method.h +debug.$(OBJEXT): {$(VPATH)}opt_method.h +error.$(OBJEXT): {$(VPATH)}opt_method.h +eval.$(OBJEXT): {$(VPATH)}opt_method.h +gc.$(OBJEXT): {$(VPATH)}opt_method.h +golf_prelude.$(OBJEXT): {$(VPATH)}opt_method.h +iseq.$(OBJEXT): {$(VPATH)}opt_method.h +load.$(OBJEXT): {$(VPATH)}opt_method.h +miniprelude.$(OBJEXT): {$(VPATH)}opt_method.h +node.$(OBJEXT): {$(VPATH)}opt_method.h +prelude.$(OBJEXT): {$(VPATH)}opt_method.h +proc.$(OBJEXT): {$(VPATH)}opt_method.h +process.$(OBJEXT): {$(VPATH)}opt_method.h +ruby.$(OBJEXT): {$(VPATH)}opt_method.h +safe.$(OBJEXT): {$(VPATH)}opt_method.h +signal.$(OBJEXT): {$(VPATH)}opt_method.h +thread.$(OBJEXT): {$(VPATH)}opt_method.h +vm.$(OBJEXT): {$(VPATH)}opt_method.h +vm_backtrace.$(OBJEXT): {$(VPATH)}opt_method.h +vm_dump.$(OBJEXT): {$(VPATH)}opt_method.h +vm_trace.$(OBJEXT): {$(VPATH)}opt_method.h # AUTOGENERATED DEPENDENCIES END 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 + * + * opt_str_lit_yoda([str, om, off]) + * send mid ... + * + * result B: + * opt_str_lit_recv([str, om]) => str + * + * 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 d4cd5e7..c56d03a 100644 --- a/defs/id.def +++ b/defs/id.def @@ -60,6 +60,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 @@ -87,6 +123,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/ext/coverage/depend b/ext/coverage/depend index 6a14155..7bb124c 100644 --- a/ext/coverage/depend +++ b/ext/coverage/depend @@ -17,4 +17,5 @@ coverage.o: $(top_srcdir)/ccan/list/list.h coverage.o: $(top_srcdir)/ccan/str/str.h coverage.o: $(hdrdir)/ruby/encoding.h coverage.o: $(hdrdir)/ruby/oniguruma.h +coverage.o: $(top_srcdir)/opt_method.h # AUTOGENERATED DEPENDENCIES END diff --git a/ext/objspace/depend b/ext/objspace/depend index 297e3bd..8ce93ee 100644 --- a/ext/objspace/depend +++ b/ext/objspace/depend @@ -56,4 +56,5 @@ object_tracing.o: $(hdrdir)/ruby/encoding.h object_tracing.o: $(hdrdir)/ruby/oniguruma.h objspace.o: $(top_srcdir)/include/ruby.h objspace_dump.o: $(top_srcdir)/include/ruby.h +objspace_dump.o: $(top_srcdir)/opt_method.h # AUTOGENERATED DEPENDENCIES 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