ruby-changes:42026
From: nobu <ko1@a...>
Date: Mon, 14 Mar 2016 16:53:45 +0900 (JST)
Subject: [ruby-changes:42026] nobu:r54100 (trunk): optimize named capture assignment
nobu 2016-03-14 16:53:39 +0900 (Mon, 14 Mar 2016) New Revision: 54100 https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=54100 Log: optimize named capture assignment * compile.c (compile_named_capture_assign): optimize named capture assignments, by replacing repeating global variable accesses with `dup`, and by returning the matched result instead of re-getting it from the MatchData. * parse.y (reg_named_capture_assign_gen): build just assignment nodes for the optimization. ex. `/(?<x>.)/ =~ "bar"` - old ``` 0000 putstring "bar" 0002 opt_regexpmatch1 /(?<x>.)/ 0004 pop 0005 getglobal $~ 0007 branchunless 25 0009 getglobal $~ 0011 putobject :x 0013 opt_aref <callinfo!mid:[], argc:1, ARGS_SIMPLE> 0016 setlocal_OP__WC__0 2 0018 getglobal $~ 0020 putobject_OP_INT2FIX_O_0_C_ 0021 opt_send_without_block <callinfo!mid:begin, argc:1, ARGS_SIMPLE> 0024 leave 0025 putobject nil 0027 setlocal_OP__WC__0 2 0029 putobject nil 0031 leave ``` - new ``` 0000 putstring "bar" 0002 opt_regexpmatch1 /(?<x>.)/ 0004 getglobal $~ 0006 dup 0007 branchunless 14 0009 putobject :x 0011 opt_aref <callinfo!mid:[], argc:1, ARGS_SIMPLE> 0014 setlocal_OP__WC__0 2 0016 leave ``` Modified files: trunk/ChangeLog trunk/compile.c trunk/node.c trunk/parse.y Index: node.c =================================================================== --- node.c (revision 54099) +++ node.c (revision 54100) @@ -583,8 +583,12 @@ dump_node(VALUE buf, VALUE indent, int c https://github.com/ruby/ruby/blob/trunk/node.c#L583 ANN("format: [nd_recv] =~ [nd_value]"); ANN("example: /foo/ =~ 'foo'"); F_NODE(nd_recv, "regexp (receiver)"); - LAST_NODE; + if (!node->nd_args) LAST_NODE; F_NODE(nd_value, "string (argument)"); + if (node->nd_args) { + LAST_NODE; + F_NODE(nd_args, "named captures"); + } break; case NODE_MATCH3: Index: parse.y =================================================================== --- parse.y (revision 54099) +++ parse.y (revision 54100) @@ -502,8 +502,8 @@ static void reg_fragment_setenc_gen(stru https://github.com/ruby/ruby/blob/trunk/parse.y#L502 #define reg_fragment_setenc(str,options) reg_fragment_setenc_gen(parser, (str), (options)) static int reg_fragment_check_gen(struct parser_params*, VALUE, int); #define reg_fragment_check(str,options) reg_fragment_check_gen(parser, (str), (options)) -static NODE *reg_named_capture_assign_gen(struct parser_params* parser, VALUE regexp, NODE *match); -#define reg_named_capture_assign(regexp,match) reg_named_capture_assign_gen(parser,(regexp),(match)) +static NODE *reg_named_capture_assign_gen(struct parser_params* parser, VALUE regexp); +#define reg_named_capture_assign(regexp) reg_named_capture_assign_gen(parser,(regexp)) static void parser_heredoc_dedent(struct parser_params*,NODE*); # define heredoc_dedent(str) parser_heredoc_dedent(parser, (str)) @@ -2327,9 +2327,12 @@ arg : lhs '=' arg https://github.com/ruby/ruby/blob/trunk/parse.y#L2327 { /*%%%*/ $$ = match_op($1, $3); - if (nd_type($1) == NODE_LIT && RB_TYPE_P($1->nd_lit, T_REGEXP)) { - $$ = reg_named_capture_assign($1->nd_lit, $$); - } + if (nd_type($1) == NODE_LIT) { + VALUE lit = $1->nd_lit; + if (RB_TYPE_P(lit, T_REGEXP)) { + $$->nd_args = reg_named_capture_assign(lit); + } + } /*% $$ = dispatch3(binary, $1, ID2SYM(idEqTilde), $3); %*/ @@ -10600,8 +10603,6 @@ typedef struct { https://github.com/ruby/ruby/blob/trunk/parse.y#L10603 struct parser_params* parser; rb_encoding *enc; NODE *succ_block; - NODE *fail_block; - int num; } reg_named_capture_assign_t; static int @@ -10614,13 +10615,7 @@ reg_named_capture_assign_iter(const Onig https://github.com/ruby/ruby/blob/trunk/parse.y#L10615 long len = name_end - name; const char *s = (const char *)name; ID var; - - arg->num++; - - if (arg->succ_block == 0) { - arg->succ_block = NEW_BEGIN(0); - arg->fail_block = NEW_BEGIN(0); - } + NODE *node, *succ; if (!len || (*name != '_' && ISASCII(*name) && !rb_enc_islower(*name, enc)) || (len < MAX_WORD_LENGTH && rb_reserved_word(s, (int)len)) || @@ -10632,48 +10627,26 @@ reg_named_capture_assign_iter(const Onig https://github.com/ruby/ruby/blob/trunk/parse.y#L10627 rb_warning1("named capture conflicts a local variable - %"PRIsWARN, rb_id2str(var)); } - arg->succ_block = block_append(arg->succ_block, - newline_node(node_assign(assignable(var,0), - NEW_CALL( - gettable(idBACKREF), - idAREF, - NEW_LIST(NEW_LIT(ID2SYM(var)))) - ))); - arg->fail_block = block_append(arg->fail_block, - newline_node(node_assign(assignable(var,0), NEW_LIT(Qnil)))); + node = newline_node(node_assign(assignable(var, 0), NEW_LIT(ID2SYM(var)))); + succ = arg->succ_block; + if (!succ) succ = NEW_BEGIN(0); + succ = block_append(succ, node); + arg->succ_block = succ; return ST_CONTINUE; } static NODE * -reg_named_capture_assign_gen(struct parser_params* parser, VALUE regexp, NODE *match) +reg_named_capture_assign_gen(struct parser_params* parser, VALUE regexp) { reg_named_capture_assign_t arg; arg.parser = parser; arg.enc = rb_enc_get(regexp); arg.succ_block = 0; - arg.fail_block = 0; - arg.num = 0; onig_foreach_name(RREGEXP_PTR(regexp), reg_named_capture_assign_iter, &arg); - if (arg.num == 0) - return match; - - return - block_append( - newline_node(match), - NEW_IF(gettable(idBACKREF), - block_append( - newline_node(arg.succ_block), - newline_node( - NEW_CALL( - gettable(idBACKREF), - rb_intern("begin"), - NEW_LIST(NEW_LIT(INT2FIX(0)))))), - block_append( - newline_node(arg.fail_block), - newline_node( - NEW_LIT(Qnil))))); + if (!arg.succ_block) return 0; + return arg.succ_block->nd_next; } static VALUE Index: ChangeLog =================================================================== --- ChangeLog (revision 54099) +++ ChangeLog (revision 54100) @@ -1,3 +1,13 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1 +Mon Mar 14 16:53:37 2016 Nobuyoshi Nakada <nobu@r...> + + * compile.c (compile_named_capture_assign): optimize named capture + assignments, by replacing repeating global variable accesses + with `dup`, and by returning the matched result instead of + re-getting it from the MatchData. + + * parse.y (reg_named_capture_assign_gen): build just assignment + nodes for the optimization. + Mon Mar 14 16:02:59 2016 Nobuyoshi Nakada <nobu@r...> * file.c (ruby_is_fd_loadable): now return -1 if loadable but Index: compile.c =================================================================== --- compile.c (revision 54099) +++ compile.c (revision 54100) @@ -3648,6 +3648,65 @@ build_postexe_iseq(rb_iseq_t *iseq, LINK https://github.com/ruby/ruby/blob/trunk/compile.c#L3648 return Qnil; } +static void +compile_named_capture_assign(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE *node) +{ + NODE *vars; + LINK_ELEMENT *last; + int line = nd_line(node); + LABEL *fail_label = NEW_LABEL(line), *end_label = NEW_LABEL(line); + +#if !(defined(NAMED_CAPTURE_BY_SVAR) && NAMED_CAPTURE_BY_SVAR-0) + ADD_INSN1(ret, line, getglobal, ((VALUE)rb_global_entry(idBACKREF) | 1)); +#else + ADD_INSN2(ret, line, getspecial, INT2FIX(1) /* '~' */, INT2FIX(0)); +#endif + ADD_INSN(ret, line, dup); + ADD_INSNL(ret, line, branchunless, fail_label); + + for (vars = node; vars; vars = vars->nd_next) { + INSN *cap; + if (vars->nd_next) { + ADD_INSN(ret, line, dup); + } + last = ret->last; + COMPILE_POPED(ret, "capture", vars->nd_head); + last = last->next; /* putobject :var */ + cap = new_insn_send(iseq, line, idAREF, INT2FIX(1), + NULL, INT2FIX(0), NULL); + INSERT_ELEM_PREV(last->next, (LINK_ELEMENT *)cap); +#if !defined(NAMED_CAPTURE_SINGLE_OPT) || NAMED_CAPTURE_SINGLE_OPT-0 + if (!vars->nd_next && vars == node) { + /* only one name */ + DECL_ANCHOR(nom); + + INIT_ANCHOR(nom); + ADD_INSNL(nom, line, jump, end_label); + ADD_LABEL(nom, fail_label); +# if 0 /* $~ must be MatchData or nil */ + ADD_INSN(nom, line, pop); + ADD_INSN(nom, line, putnil); +# endif + ADD_LABEL(nom, end_label); + (nom->last->next = cap->link.next)->prev = nom->last; + (cap->link.next = nom->anchor.next)->prev = &cap->link; + return; + } +#endif + } + ADD_INSNL(ret, line, jump, end_label); + ADD_LABEL(ret, fail_label); + ADD_INSN(ret, line, pop); + for (vars = node; vars; vars = vars->nd_next) { + last = ret->last; + COMPILE_POPED(ret, "capture", vars->nd_head); + last = last->next; /* putobject :var */ + ((INSN*)last)->insn_id = BIN(putnil); + ((INSN*)last)->operand_size = 0; + } + ADD_LABEL(ret, end_label); +} + /** compile each node @@ -5351,6 +5410,10 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ https://github.com/ruby/ruby/blob/trunk/compile.c#L5410 ADD_SEND(ret, line, idEqTilde, INT2FIX(1)); } + if (node->nd_args) { + compile_named_capture_assign(iseq, ret, node->nd_args); + } + if (poped) { ADD_INSN(ret, line, pop); } -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/