[前][次][番号順一覧][スレッド一覧]

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/

[前][次][番号順一覧][スレッド一覧]