summaryrefslogtreecommitdiffstats
path: root/Python/compile.c
diff options
context:
space:
mode:
authorBrandt Bucher <brandt@python.org>2021-10-27 09:45:35 (GMT)
committerGitHub <noreply@github.com>2021-10-27 09:45:35 (GMT)
commit82a662e5216a9b3969054c540a759a9493468510 (patch)
tree51bf9b433828dfd6d58f172097cf06202b54df9a /Python/compile.c
parent19a6c41e56f129a67e2a3c96464ba893a97236f7 (diff)
downloadcpython-82a662e5216a9b3969054c540a759a9493468510.zip
cpython-82a662e5216a9b3969054c540a759a9493468510.tar.gz
cpython-82a662e5216a9b3969054c540a759a9493468510.tar.bz2
bpo-44511: Improve the bytecode for class and mapping patterns (GH-26922)
* Refactor mapping patterns and speed up class patterns. * Simplify MATCH_KEYS and MATCH_CLASS. * Add COPY opcode.
Diffstat (limited to 'Python/compile.c')
-rw-r--r--Python/compile.c73
1 files changed, 44 insertions, 29 deletions
diff --git a/Python/compile.c b/Python/compile.c
index 0c025ac..28b5c07 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -1248,18 +1248,17 @@ stack_effect(int opcode, int oparg, int jump)
case DICT_MERGE:
case DICT_UPDATE:
return -1;
- case COPY_DICT_WITHOUT_KEYS:
- return 0;
case MATCH_CLASS:
- return -1;
+ return -2;
case GET_LEN:
case MATCH_MAPPING:
case MATCH_SEQUENCE:
- return 1;
case MATCH_KEYS:
- return 2;
+ return 1;
case ROT_N:
return 0;
+ case COPY:
+ return 1;
default:
return PY_INVALID_STACK_EFFECT;
}
@@ -6118,10 +6117,16 @@ compiler_pattern_class(struct compiler *c, pattern_ty p, pattern_context *pc)
}
ADDOP_LOAD_CONST_NEW(c, attr_names);
ADDOP_I(c, MATCH_CLASS, nargs);
- // TOS is now a tuple of (nargs + nattrs) attributes. Preserve it:
+ ADDOP(c, DUP_TOP);
+ ADDOP_LOAD_CONST(c, Py_None);
+ ADDOP_I(c, IS_OP, 1);
+ // TOS is now a tuple of (nargs + nattrs) attributes (or None):
pc->on_top++;
RETURN_IF_FALSE(jump_to_fail_pop(c, pc, POP_JUMP_IF_FALSE));
+ ADDOP_I(c, UNPACK_SEQUENCE, nargs + nattrs);
+ pc->on_top += nargs + nattrs - 1;
for (i = 0; i < nargs + nattrs; i++) {
+ pc->on_top--;
pattern_ty pattern;
if (i < nargs) {
// Positional:
@@ -6132,17 +6137,12 @@ compiler_pattern_class(struct compiler *c, pattern_ty p, pattern_context *pc)
pattern = asdl_seq_GET(kwd_patterns, i - nargs);
}
if (WILDCARD_CHECK(pattern)) {
+ ADDOP(c, POP_TOP);
continue;
}
- // Get the i-th attribute, and match it against the i-th pattern:
- ADDOP(c, DUP_TOP);
- ADDOP_LOAD_CONST_NEW(c, PyLong_FromSsize_t(i));
- ADDOP(c, BINARY_SUBSCR);
RETURN_IF_FALSE(compiler_pattern_subpattern(c, pattern, pc));
}
// Success! Pop the tuple of attributes:
- pc->on_top--;
- ADDOP(c, POP_TOP);
return 1;
}
@@ -6183,7 +6183,7 @@ compiler_pattern_mapping(struct compiler *c, pattern_ty p, pattern_context *pc)
return compiler_error(c, "too many sub-patterns in mapping pattern");
}
// Collect all of the keys into a tuple for MATCH_KEYS and
- // COPY_DICT_WITHOUT_KEYS. They can either be dotted names or literals:
+ // **rest. They can either be dotted names or literals:
// Maintaining a set of Constant_kind kind keys allows us to raise a
// SyntaxError in the case of duplicates.
@@ -6235,35 +6235,45 @@ compiler_pattern_mapping(struct compiler *c, pattern_ty p, pattern_context *pc)
ADDOP(c, MATCH_KEYS);
// There's now a tuple of keys and a tuple of values on top of the subject:
pc->on_top += 2;
+ ADDOP(c, DUP_TOP);
+ ADDOP_LOAD_CONST(c, Py_None);
+ ADDOP_I(c, IS_OP, 1);
RETURN_IF_FALSE(jump_to_fail_pop(c, pc, POP_JUMP_IF_FALSE));
// So far so good. Use that tuple of values on the stack to match
// sub-patterns against:
+ ADDOP_I(c, UNPACK_SEQUENCE, size);
+ pc->on_top += size - 1;
for (Py_ssize_t i = 0; i < size; i++) {
+ pc->on_top--;
pattern_ty pattern = asdl_seq_GET(patterns, i);
- if (WILDCARD_CHECK(pattern)) {
- continue;
- }
- ADDOP(c, DUP_TOP);
- ADDOP_LOAD_CONST_NEW(c, PyLong_FromSsize_t(i));
- ADDOP(c, BINARY_SUBSCR);
RETURN_IF_FALSE(compiler_pattern_subpattern(c, pattern, pc));
}
- // If we get this far, it's a match! We're done with the tuple of values,
- // and whatever happens next should consume the tuple of keys underneath it:
+ // If we get this far, it's a match! Whatever happens next should consume
+ // the tuple of keys and the subject:
pc->on_top -= 2;
- ADDOP(c, POP_TOP);
if (star_target) {
- // If we have a starred name, bind a dict of remaining items to it:
- ADDOP(c, COPY_DICT_WITHOUT_KEYS);
+ // If we have a starred name, bind a dict of remaining items to it (this may
+ // seem a bit inefficient, but keys is rarely big enough to actually impact
+ // runtime):
+ // rest = dict(TOS1)
+ // for key in TOS:
+ // del rest[key]
+ ADDOP_I(c, BUILD_MAP, 0); // [subject, keys, empty]
+ ADDOP(c, ROT_THREE); // [empty, subject, keys]
+ ADDOP(c, ROT_TWO); // [empty, keys, subject]
+ ADDOP_I(c, DICT_UPDATE, 2); // [copy, keys]
+ ADDOP_I(c, UNPACK_SEQUENCE, size); // [copy, keys...]
+ while (size) {
+ ADDOP_I(c, COPY, 1 + size--); // [copy, keys..., copy]
+ ADDOP(c, ROT_TWO); // [copy, keys..., copy, key]
+ ADDOP(c, DELETE_SUBSCR); // [copy, keys...]
+ }
RETURN_IF_FALSE(pattern_helper_store_name(c, star_target, pc));
}
else {
- // Otherwise, we don't care about this tuple of keys anymore:
- ADDOP(c, POP_TOP);
+ ADDOP(c, POP_TOP); // Tuple of keys.
+ ADDOP(c, POP_TOP); // Subject.
}
- // Pop the subject:
- pc->on_top--;
- ADDOP(c, POP_TOP);
return 1;
error:
@@ -8362,6 +8372,11 @@ optimize_basic_block(struct compiler *c, basicblock *bb, PyObject *consts)
fold_rotations(inst - oparg + 1, oparg);
}
break;
+ case COPY:
+ if (oparg == 1) {
+ inst->i_opcode = DUP_TOP;
+ }
+ break;
default:
/* All HAS_CONST opcodes should be handled with LOAD_CONST */
assert (!HAS_CONST(inst->i_opcode));