mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
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 ``` git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@54100 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
a4e6f7d707
commit
ea5e885a95
4 changed files with 95 additions and 45 deletions
10
ChangeLog
10
ChangeLog
|
@ -1,3 +1,13 @@
|
|||
Mon Mar 14 16:53:37 2016 Nobuyoshi Nakada <nobu@ruby-lang.org>
|
||||
|
||||
* 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@ruby-lang.org>
|
||||
|
||||
* file.c (ruby_is_fd_loadable): now return -1 if loadable but
|
||||
|
|
63
compile.c
63
compile.c
|
@ -3648,6 +3648,65 @@ build_postexe_iseq(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE *body)
|
|||
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_ANCHOR *ret, NODE * node, int poped)
|
|||
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);
|
||||
}
|
||||
|
|
6
node.c
6
node.c
|
@ -583,8 +583,12 @@ dump_node(VALUE buf, VALUE indent, int comment, NODE *node)
|
|||
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:
|
||||
|
|
61
parse.y
61
parse.y
|
@ -502,8 +502,8 @@ static void reg_fragment_setenc_gen(struct parser_params*, VALUE, int);
|
|||
#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
|
|||
{
|
||||
/*%%%*/
|
||||
$$ = 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 {
|
|||
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 OnigUChar *name, const OnigUChar *name_end,
|
|||
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 OnigUChar *name, const OnigUChar *name_end,
|
|||
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
|
||||
|
|
Loading…
Reference in a new issue