mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
Add #deconstruct cache to find pattern
This commit is contained in:
parent
5320375732
commit
c9ee34a18b
Notes:
git
2020-06-27 13:51:28 +09:00
2 changed files with 74 additions and 59 deletions
124
compile.c
124
compile.c
|
@ -5611,6 +5611,8 @@ compile_case2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_no
|
|||
|
||||
static int iseq_compile_pattern_match(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, LABEL *unmatched, int in_alt_pattern, int deconstructed_pos);
|
||||
|
||||
static int iseq_compile_array_deconstruct(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, LABEL *deconstruct, LABEL *deconstructed, LABEL *match_failed, LABEL *type_error, int deconstructed_pos);
|
||||
|
||||
static int
|
||||
iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, LABEL *matched, LABEL *unmatched, int in_alt_pattern, int deconstructed_pos)
|
||||
{
|
||||
|
@ -5701,52 +5703,8 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c
|
|||
ADD_INSNL(ret, line, branchunless, match_failed);
|
||||
}
|
||||
|
||||
// NOTE: this optimization allows us to re-use the #deconstruct value
|
||||
// (or its absence).
|
||||
// `deconstructed_pos` contains the distance to the stack relative location
|
||||
// where the value is stored.
|
||||
if (deconstructed_pos) {
|
||||
// If value is nil then we haven't tried to deconstruct
|
||||
ADD_INSN1(ret, line, topn, INT2FIX(deconstructed_pos));
|
||||
ADD_INSNL(ret, line, branchnil, deconstruct);
|
||||
CHECK(iseq_compile_array_deconstruct(iseq, ret, node, deconstruct, deconstructed, match_failed, type_error, deconstructed_pos));
|
||||
|
||||
// If false then the value is not deconstructable
|
||||
ADD_INSN1(ret, line, topn, INT2FIX(deconstructed_pos));
|
||||
ADD_INSNL(ret, line, branchunless, match_failed);
|
||||
|
||||
// Drop value, add deconstructed to the stack and jump
|
||||
ADD_INSN(ret, line, pop);
|
||||
ADD_INSN1(ret, line, topn, INT2FIX(deconstructed_pos - 1));
|
||||
ADD_INSNL(ret, line, jump, deconstructed);
|
||||
} else {
|
||||
ADD_INSNL(ret, line, jump, deconstruct);
|
||||
}
|
||||
|
||||
ADD_LABEL(ret, deconstruct);
|
||||
ADD_INSN(ret, line, dup);
|
||||
ADD_INSN1(ret, line, putobject, ID2SYM(rb_intern("deconstruct")));
|
||||
ADD_SEND(ret, line, idRespond_to, INT2FIX(1));
|
||||
|
||||
// Cache the result of respond_to? (in case it's false is stays there, if true — it's overwritten after #deconstruct)
|
||||
if (deconstructed_pos) {
|
||||
ADD_INSN1(ret, line, setn, INT2FIX(deconstructed_pos + 1));
|
||||
}
|
||||
|
||||
ADD_INSNL(ret, line, branchunless, match_failed);
|
||||
|
||||
ADD_SEND(ret, line, rb_intern("deconstruct"), INT2FIX(0));
|
||||
|
||||
// Cache the result (if it's cacheable — currently, only top-level array patterns)
|
||||
if (deconstructed_pos) {
|
||||
ADD_INSN1(ret, line, setn, INT2FIX(deconstructed_pos));
|
||||
}
|
||||
|
||||
ADD_INSN(ret, line, dup);
|
||||
ADD_INSN1(ret, line, checktype, INT2FIX(T_ARRAY));
|
||||
ADD_INSNL(ret, line, branchunless, type_error);
|
||||
ADD_INSNL(ret, line, jump, deconstructed);
|
||||
|
||||
ADD_LABEL(ret, deconstructed);
|
||||
ADD_INSN(ret, line, dup);
|
||||
ADD_SEND(ret, line, idLength, INT2FIX(0));
|
||||
ADD_INSN1(ret, line, putobject, INT2FIX(min_argc));
|
||||
|
@ -5873,9 +5831,11 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c
|
|||
const NODE *args = fpinfo->args;
|
||||
const int args_num = fpinfo->args ? rb_long2int(fpinfo->args->nd_alen) : 0;
|
||||
|
||||
LABEL *match_failed, *type_error;
|
||||
LABEL *match_failed, *type_error, *deconstruct, *deconstructed;
|
||||
match_failed = NEW_LABEL(line);
|
||||
type_error = NEW_LABEL(line);
|
||||
deconstruct = NEW_LABEL(line);
|
||||
deconstructed = NEW_LABEL(line);
|
||||
|
||||
if (node->nd_pconst) {
|
||||
ADD_INSN(ret, line, dup);
|
||||
|
@ -5884,16 +5844,7 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c
|
|||
ADD_INSNL(ret, line, branchunless, match_failed);
|
||||
}
|
||||
|
||||
ADD_INSN(ret, line, dup);
|
||||
ADD_INSN1(ret, line, putobject, ID2SYM(rb_intern("deconstruct")));
|
||||
ADD_SEND(ret, line, idRespond_to, INT2FIX(1));
|
||||
ADD_INSNL(ret, line, branchunless, match_failed);
|
||||
|
||||
ADD_SEND(ret, line, rb_intern("deconstruct"), INT2FIX(0));
|
||||
|
||||
ADD_INSN(ret, line, dup);
|
||||
ADD_INSN1(ret, line, checktype, INT2FIX(T_ARRAY));
|
||||
ADD_INSNL(ret, line, branchunless, type_error);
|
||||
CHECK(iseq_compile_array_deconstruct(iseq, ret, node, deconstruct, deconstructed, match_failed, type_error, deconstructed_pos));
|
||||
|
||||
ADD_INSN(ret, line, dup);
|
||||
ADD_SEND(ret, line, idLength, INT2FIX(0));
|
||||
|
@ -5933,7 +5884,7 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c
|
|||
}
|
||||
ADD_SEND(ret, line, idAREF, INT2FIX(1));
|
||||
|
||||
CHECK(iseq_compile_pattern_match(iseq, ret, args->nd_head, next_loop, in_alt_pattern));
|
||||
CHECK(iseq_compile_pattern_match(iseq, ret, args->nd_head, next_loop, in_alt_pattern, FALSE));
|
||||
args = args->nd_next;
|
||||
}
|
||||
|
||||
|
@ -5942,7 +5893,7 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c
|
|||
ADD_INSN1(ret, line, putobject, INT2FIX(0));
|
||||
ADD_INSN1(ret, line, topn, INT2FIX(2));
|
||||
ADD_SEND(ret, line, idAREF, INT2FIX(2));
|
||||
CHECK(iseq_compile_pattern_match(iseq, ret, fpinfo->pre_rest_arg, find_failed, in_alt_pattern));
|
||||
CHECK(iseq_compile_pattern_match(iseq, ret, fpinfo->pre_rest_arg, find_failed, in_alt_pattern, FALSE));
|
||||
}
|
||||
if (NODE_NAMED_REST_P(fpinfo->post_rest_arg)) {
|
||||
ADD_INSN1(ret, line, topn, INT2FIX(3));
|
||||
|
@ -5951,7 +5902,7 @@ iseq_compile_pattern_each(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *c
|
|||
ADD_SEND(ret, line, idPLUS, INT2FIX(1));
|
||||
ADD_INSN1(ret, line, topn, INT2FIX(3));
|
||||
ADD_SEND(ret, line, idAREF, INT2FIX(2));
|
||||
CHECK(iseq_compile_pattern_match(iseq, ret, fpinfo->post_rest_arg, find_failed, in_alt_pattern));
|
||||
CHECK(iseq_compile_pattern_match(iseq, ret, fpinfo->post_rest_arg, find_failed, in_alt_pattern, FALSE));
|
||||
}
|
||||
ADD_INSNL(ret, line, jump, find_succeeded);
|
||||
|
||||
|
@ -6288,6 +6239,61 @@ iseq_compile_pattern_match(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *
|
|||
return COMPILE_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
iseq_compile_array_deconstruct(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, LABEL *deconstruct, LABEL *deconstructed, LABEL *match_failed, LABEL *type_error, int deconstructed_pos)
|
||||
{
|
||||
const int line = nd_line(node);
|
||||
|
||||
// NOTE: this optimization allows us to re-use the #deconstruct value
|
||||
// (or its absence).
|
||||
// `deconstructed_pos` contains the distance to the stack relative location
|
||||
// where the value is stored.
|
||||
if (deconstructed_pos) {
|
||||
// If value is nil then we haven't tried to deconstruct
|
||||
ADD_INSN1(ret, line, topn, INT2FIX(deconstructed_pos));
|
||||
ADD_INSNL(ret, line, branchnil, deconstruct);
|
||||
|
||||
// If false then the value is not deconstructable
|
||||
ADD_INSN1(ret, line, topn, INT2FIX(deconstructed_pos));
|
||||
ADD_INSNL(ret, line, branchunless, match_failed);
|
||||
|
||||
// Drop value, add deconstructed to the stack and jump
|
||||
ADD_INSN(ret, line, pop);
|
||||
ADD_INSN1(ret, line, topn, INT2FIX(deconstructed_pos - 1));
|
||||
ADD_INSNL(ret, line, jump, deconstructed);
|
||||
} else {
|
||||
ADD_INSNL(ret, line, jump, deconstruct);
|
||||
}
|
||||
|
||||
ADD_LABEL(ret, deconstruct);
|
||||
ADD_INSN(ret, line, dup);
|
||||
ADD_INSN1(ret, line, putobject, ID2SYM(rb_intern("deconstruct")));
|
||||
ADD_SEND(ret, line, idRespond_to, INT2FIX(1));
|
||||
|
||||
// Cache the result of respond_to? (in case it's false is stays there, if true — it's overwritten after #deconstruct)
|
||||
if (deconstructed_pos) {
|
||||
ADD_INSN1(ret, line, setn, INT2FIX(deconstructed_pos + 1));
|
||||
}
|
||||
|
||||
ADD_INSNL(ret, line, branchunless, match_failed);
|
||||
|
||||
ADD_SEND(ret, line, rb_intern("deconstruct"), INT2FIX(0));
|
||||
|
||||
// Cache the result (if it's cacheable — currently, only top-level array patterns)
|
||||
if (deconstructed_pos) {
|
||||
ADD_INSN1(ret, line, setn, INT2FIX(deconstructed_pos));
|
||||
}
|
||||
|
||||
ADD_INSN(ret, line, dup);
|
||||
ADD_INSN1(ret, line, checktype, INT2FIX(T_ARRAY));
|
||||
ADD_INSNL(ret, line, branchunless, type_error);
|
||||
ADD_INSNL(ret, line, jump, deconstructed);
|
||||
|
||||
ADD_LABEL(ret, deconstructed);
|
||||
|
||||
return COMPILE_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
compile_case3(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_node, int popped)
|
||||
{
|
||||
|
|
|
@ -1340,6 +1340,15 @@ END
|
|||
true
|
||||
end
|
||||
end
|
||||
|
||||
assert_block do
|
||||
case CDeconstructCache.new([[0, :a, 1]])
|
||||
in [*, String => x, *]
|
||||
false
|
||||
in [*, Symbol => x, *]
|
||||
true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
################################################################
|
||||
|
|
Loading…
Reference in a new issue