Feature #10423 » opt_str_lit-v5.patch
| .gitignore | ||
|---|---|---|
| /newdate.rb | ||
| /newline.c | ||
| /newver.rb | ||
| /opt_method.h | ||
| /parse.c | ||
| /parse.h | ||
| /patches | ||
| benchmark/bm_vm2_array_delete_lit.rb | ||
|---|---|---|
| ary = [] | ||
| i = 0 | ||
| while i<6_000_000 # while loop 2 | ||
| i += 1 | ||
| ary.delete("foo") | ||
| end | ||
| benchmark/bm_vm2_array_include_lit.rb | ||
|---|---|---|
| ary = [] | ||
| i = 0 | ||
| while i<6_000_000 # while loop 2 | ||
| i += 1 | ||
| ary.include?("foo") | ||
| end | ||
| benchmark/bm_vm2_hash_aref_lit.rb | ||
|---|---|---|
| h = { "foo" => nil } | ||
| i = 0 | ||
| while i<6_000_000 # while loop 2 | ||
| i += 1 | ||
| h["foo"] | ||
| end | ||
| benchmark/bm_vm2_hash_aset_lit.rb | ||
|---|---|---|
| h = {} | ||
| i = 0 | ||
| while i<6_000_000 # while loop 2 | ||
| i += 1 | ||
| h["foo"] = nil | ||
| end | ||
| benchmark/bm_vm2_hash_delete_lit.rb | ||
|---|---|---|
| h = {} | ||
| i = 0 | ||
| while i<6_000_000 # while loop 2 | ||
| i += 1 | ||
| h.delete("foo") | ||
| end | ||
| benchmark/bm_vm2_set_include_lit.rb | ||
|---|---|---|
| require 'set' | ||
| set = Set.new | ||
| i = 0 | ||
| while i<6_000_000 # while loop 2 | ||
| i += 1 | ||
| set.include?("foo") | ||
| end | ||
| benchmark/bm_vm2_str_delete.rb | ||
|---|---|---|
| str = '' | ||
| i = 0 | ||
| while i<6_000_000 # while loop 2 | ||
| i += 1 | ||
| str.delete("foo") | ||
| end | ||
| benchmark/bm_vm2_str_eq1.rb | ||
|---|---|---|
| i = 0 | ||
| foo = "literal" | ||
| while i<6_000_000 # benchmark loop 2 | ||
| i += 1 | ||
| foo == "literal" | ||
| end | ||
| benchmark/bm_vm2_str_eq2.rb | ||
|---|---|---|
| i = 0 | ||
| foo = "literal" | ||
| while i<6_000_000 # benchmark loop 2 | ||
| i += 1 | ||
| "literal" == foo | ||
| end | ||
| benchmark/bm_vm2_str_eqq1.rb | ||
|---|---|---|
| i = 0 | ||
| foo = "literal" | ||
| while i<6_000_000 # benchmark loop 2 | ||
| i += 1 | ||
| foo === "literal" | ||
| end | ||
| benchmark/bm_vm2_str_eqq2.rb | ||
|---|---|---|
| i = 0 | ||
| foo = "literal" | ||
| while i<6_000_000 # benchmark loop 2 | ||
| i += 1 | ||
| "literal" === foo | ||
| end | ||
| benchmark/bm_vm2_str_fmt.rb | ||
|---|---|---|
| i = 0 | ||
| while i<6_000_000 # benchmark loop 2 | ||
| i += 1 | ||
| "%d" % i | ||
| end | ||
| benchmark/bm_vm2_str_gsub_bang_lit.rb | ||
|---|---|---|
| i = 0 | ||
| str = "" | ||
| while i<6_000_000 # benchmark loop 2 | ||
| i += 1 | ||
| str.gsub!("nomatch", "") | ||
| end | ||
| benchmark/bm_vm2_str_gsub_bang_re.rb | ||
|---|---|---|
| i = 0 | ||
| str = "" | ||
| while i<6_000_000 # benchmark loop 2 | ||
| i += 1 | ||
| str.gsub!(/a/, "") | ||
| end | ||
| benchmark/bm_vm2_str_gsub_re.rb | ||
|---|---|---|
| i = 0 | ||
| str = "" | ||
| while i<6_000_000 # benchmark loop 2 | ||
| i += 1 | ||
| str.gsub(/a/, "") | ||
| end | ||
| benchmark/bm_vm2_str_plus1.rb | ||
|---|---|---|
| i = 0 | ||
| foo = "a" | ||
| while i<6_000_000 # benchmark loop 2 | ||
| i += 1 | ||
| foo + "b" | ||
| end | ||
| benchmark/bm_vm2_str_plus2.rb | ||
|---|---|---|
| i = 0 | ||
| foo = "a" | ||
| while i<6_000_000 # benchmark loop 2 | ||
| i += 1 | ||
| "b" + foo | ||
| end | ||
| benchmark/bm_vm2_str_tr_bang.rb | ||
|---|---|---|
| i = 0 | ||
| str = "a" | ||
| while i<6_000_000 # benchmark loop 2 | ||
| i += 1 | ||
| str.tr!("a", "A") | ||
| str.tr!("A", "a") | ||
| end | ||
| benchmark/bm_vm2_strcat.rb | ||
|---|---|---|
| i = 0 | ||
| str = "" | ||
| while i<6_000_000 # benchmark loop 2 | ||
| i += 1 | ||
| str << "const" | ||
| str.clear | ||
| end | ||
| common.mk | ||
|---|---|---|
| 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 | ||
| ### | ||
| ... | ... | |
| $(VM_CORE_H_INCLUDES) {$(VPATH)}vm_method.c {$(VPATH)}vm_eval.c \ | ||
| {$(VPATH)}vm_insnhelper.c {$(VPATH)}vm_insnhelper.h {$(VPATH)}vm_exec.c \ | ||
| {$(VPATH)}vm_exec.h {$(VPATH)}insns.def {$(VPATH)}vmtc.inc \ | ||
| {$(VPATH)}vm.inc {$(VPATH)}insns.inc \ | ||
| {$(VPATH)}vm.inc {$(VPATH)}insns.inc {$(VPATH)}opt_method.inc \ | ||
| {$(VPATH)}internal.h {$(VPATH)}vm.h {$(VPATH)}constant.h \ | ||
| $(PROBES_H_INCLUDES) {$(VPATH)}probes_helper.h {$(VPATH)}vm_opts.h | ||
| vm_dump.$(OBJEXT): {$(VPATH)}vm_dump.c $(RUBY_H_INCLUDES) \ | ||
| ... | ... | |
| 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=$@ \ | ||
| compile.c | ||
|---|---|---|
| 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; | ||
| } | ||
| #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; | ||
| int data_p = 0; | ||
| switch (ci->mid) { | ||
| #define C(mid,klass) \ | ||
| case mid: \ | ||
| om = OM_##mid##__##klass; \ | ||
| tmask = OM_TMASK_##klass; \ | ||
| break | ||
| C(idAREF, Hash); | ||
| 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 | ||
| #define C(mid,klass) \ | ||
| case mid: \ | ||
| om = OM_##mid##__##klass; \ | ||
| tmask = OM_TMASK_##klass; \ | ||
| data_p = 1; \ | ||
| break | ||
| /* opt_str_lit_data oddities, maybe this is not worth supporting */ | ||
| C(idStrftime, Time); | ||
| #undef C | ||
| default: return; | ||
| } | ||
| list->insn_id = data_p ? BIN(opt_str_lit_data) : 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) | ||
| { | ||
| ... | ... | |
| } | ||
| } | ||
| } | ||
| /* string literal optimizations */ | ||
| if (iobj->insn_id == BIN(putstring)) { | ||
| INSN *niobj = (INSN *)get_next_insn((INSN *)list); | ||
| if (niobj && niobj->insn_id == BIN(send)) { | ||
| rb_call_info_t *ci = (rb_call_info_t *)niobj->operands[0]; | ||
| if (!ci->blockiseq && !(ci->flag & ~VM_CALL_ARGS_SIMPLE)) { | ||
| VALUE ri = Qfalse; | ||
| VALUE str = iobj->operands[0]; | ||
| switch (ci->orig_argc) { | ||
| case 0: | ||
| /* | ||
| * optimize: | ||
| * "literal".freeze | ||
| * "literal".size | ||
| * "literal".length | ||
| */ | ||
| switch (ci->mid) { | ||
| case idFreeze: | ||
| ri = new_recvinfo_for_recv(iseq, str, idFreeze, String); | ||
| break; | ||
| case idSize: | ||
| ri = new_recvinfo_for_recv(iseq, str, idSize, String); | ||
| break; | ||
| case idLength: | ||
| ri = new_recvinfo_for_recv(iseq, str, idLength, String); | ||
| break; | ||
| } | ||
| 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; | ||
| } | ||
| ... | ... | |
| 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 | ||
| ... | ... | |
| break; | ||
| } | ||
| case NODE_CALL: | ||
| /* optimization shortcut | ||
| * "literal".freeze -> opt_str_freeze("literal") | ||
| */ | ||
| if (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; | ||
| } | ||
| /* optimization shortcut | ||
| * obj["literal"] -> opt_aref_with(obj, "literal") | ||
| */ | ||
| if (node->nd_mid == idAREF && !private_recv_p(node) && node->nd_args && | ||
| nd_type(node->nd_args) == NODE_ARRAY && node->nd_args->nd_alen == 1 && | ||
| nd_type(node->nd_args->nd_head) == NODE_STR) | ||
| { | ||
| VALUE str = rb_fstring(node->nd_args->nd_head->nd_lit); | ||
| node->nd_args->nd_head->nd_lit = str; | ||
| COMPILE(ret, "recv", node->nd_recv); | ||
| ADD_INSN2(ret, line, opt_aref_with, | ||
| new_callinfo(iseq, idAREF, 1, 0, 0, NULL), str); | ||
| if (poped) { | ||
| ADD_INSN(ret, line, pop); | ||
| } | ||
| break; | ||
| } | ||
| case NODE_FCALL: | ||
| case NODE_VCALL:{ /* VCALL: variable or call */ | ||
| /* | ||
| ... | ... | |
| #endif | ||
| /* receiver */ | ||
| if (type == NODE_CALL) { | ||
| COMPILE(recv, "recv", node->nd_recv); | ||
| enum ruby_optimized_method om; | ||
| /* | ||
| * optimize: | ||
| * "string literal".method(...) | ||
| * "yoda" == other -> opt_str_lit("yoda").send(:==, other) | ||
| * "yoda" != other -> opt_str_lit("yoda").send(:!=, other) | ||
| * "str" + other -> opt_str_lit("str").send(:+, other) | ||
| * "str" * other -> opt_str_lit("str").send(:*, other) | ||
| * "fmt" % args -> opt_str_lit("str").send(:%, other) | ||
| * ... | ||
| */ | ||
| 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); | ||
| VALUE recv_info = new_recvinfo_for_recv_(iseq, yoda, om); | ||
| node->nd_recv->nd_lit = yoda; | ||
| ADD_INSN1(recv, line, opt_str_lit_recv, recv_info); | ||
| } else { | ||
| COMPILE(recv, "recv", node->nd_recv); | ||
| } | ||
| } | ||
| else if (type == NODE_FCALL || type == NODE_VCALL) { | ||
| ADD_CALL_RECEIVER(recv, line); | ||
| ... | ... | |
| int asgnflag; | ||
| /* optimization shortcut | ||
| * obj["literal"] = value -> opt_aset_with(obj, "literal", value) | ||
| * obj["literal"] = val -> send(obj, :[]=, opt_str_lit("lit"), val) | ||
| * TODO: ideally this should be done inside iseq_peephole_optimize, | ||
| * but that would require a lot of scanning as the `val' (2nd arg) | ||
| * is of variable distance between the :putstring and :send insns | ||
| */ | ||
| if (node->nd_mid == idASET && !private_recv_p(node) && node->nd_args && | ||
| if (iseq->compile_data->option->peephole_optimization && | ||
| node->nd_mid == idASET && !private_recv_p(node) && node->nd_args && | ||
| nd_type(node->nd_args) == NODE_ARRAY && node->nd_args->nd_alen == 2 && | ||
| nd_type(node->nd_args->nd_head) == NODE_STR) | ||
| { | ||
| VALUE str = rb_fstring(node->nd_args->nd_head->nd_lit); | ||
| VALUE recv_info = new_recvinfo_for_arg(iseq, str, idASET, Hash, 0); | ||
| node->nd_args->nd_head->nd_lit = str; | ||
| iseq_add_mark_object(iseq, str); | ||
| if (!poped) { | ||
| ADD_INSN(ret, line, putnil); | ||
| } | ||
| COMPILE(ret, "recv", node->nd_recv); | ||
| ADD_INSN1(ret, line, opt_str_lit_tmask, recv_info); | ||
| COMPILE(ret, "value", node->nd_args->nd_next->nd_head); | ||
| if (!poped) { | ||
| ADD_INSN(ret, line, swap); | ||
| ADD_INSN1(ret, line, topn, INT2FIX(1)); | ||
| ADD_INSN1(ret, line, setn, INT2FIX(3)); | ||
| } | ||
| ADD_INSN2(ret, line, opt_aset_with, | ||
| new_callinfo(iseq, idASET, 2, 0, 0, NULL), str); | ||
| flag = VM_CALL_ARGS_SIMPLE; | ||
| ADD_SEND_WITH_FLAG(ret, line, node->nd_mid, INT2FIX(2), | ||
| INT2FIX(flag)); | ||
| ADD_INSN(ret, line, pop); | ||
| break; | ||
| } | ||
| ... | ... | |
| { | ||
| return GET_THREAD()->parse_in_eval < 0; | ||
| } | ||
| /* | ||
| * 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_data)] == 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_data) == 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_data))); | ||
| 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 */ | ||
| } | ||
| defs/id.def | ||
|---|---|---|
| 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! | ||
| strftime | ||
| pack | ||
| unpack | ||
| split | ||
| join | ||
| encode | ||
| encode! | ||
| force_encoding | ||
| index | ||
| rindex | ||
| match | ||
| casecmp | ||
| insert | ||
| start_with? | ||
| end_with? | ||
| partition | ||
| rpartition | ||
| ] | ||
| class KeywordError < RuntimeError | ||
| ... | ... | |
| 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_") | ||
| defs/opt_method.def | ||
|---|---|---|
| # 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 Time), | ||
| %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(idStrftime Time), | ||
| %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 | ||
| IS_T_DATA = { | ||
| "Time" => true | ||
| } | ||
- « Previous
- 1
- 2
- Next »