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.
This commit is contained in:
Brandt Bucher 2021-10-27 02:45:35 -07:00 committed by GitHub
parent 19a6c41e56
commit 82a662e521
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 130 additions and 116 deletions

View file

@ -4143,25 +4143,30 @@ check_eval_breaker:
}
TARGET(MATCH_CLASS) {
// Pop TOS. On success, set TOS to True and TOS1 to a tuple of
// attributes. On failure, set TOS to False.
// Pop TOS and TOS1. Set TOS to a tuple of attributes on success, or
// None on failure.
PyObject *names = POP();
PyObject *type = TOP();
PyObject *subject = SECOND();
PyObject *type = POP();
PyObject *subject = TOP();
assert(PyTuple_CheckExact(names));
PyObject *attrs = match_class(tstate, subject, type, oparg, names);
Py_DECREF(names);
Py_DECREF(type);
if (attrs) {
// Success!
assert(PyTuple_CheckExact(attrs));
Py_DECREF(subject);
SET_SECOND(attrs);
SET_TOP(attrs);
}
else if (_PyErr_Occurred(tstate)) {
// Error!
goto error;
}
Py_DECREF(type);
SET_TOP(PyBool_FromLong(!!attrs));
else {
// Failure!
Py_INCREF(Py_None);
SET_TOP(Py_None);
}
Py_DECREF(subject);
DISPATCH();
}
@ -4171,6 +4176,7 @@ check_eval_breaker:
PyObject *res = match ? Py_True : Py_False;
Py_INCREF(res);
PUSH(res);
PREDICT(POP_JUMP_IF_FALSE);
DISPATCH();
}
@ -4180,12 +4186,12 @@ check_eval_breaker:
PyObject *res = match ? Py_True : Py_False;
Py_INCREF(res);
PUSH(res);
PREDICT(POP_JUMP_IF_FALSE);
DISPATCH();
}
TARGET(MATCH_KEYS) {
// On successful match for all keys, PUSH(values) and PUSH(True).
// Otherwise, PUSH(None) and PUSH(False).
// On successful match, PUSH(values). Otherwise, PUSH(None).
PyObject *keys = TOP();
PyObject *subject = SECOND();
PyObject *values_or_none = match_keys(tstate, subject, keys);
@ -4193,40 +4199,6 @@ check_eval_breaker:
goto error;
}
PUSH(values_or_none);
if (Py_IsNone(values_or_none)) {
Py_INCREF(Py_False);
PUSH(Py_False);
DISPATCH();
}
assert(PyTuple_CheckExact(values_or_none));
Py_INCREF(Py_True);
PUSH(Py_True);
DISPATCH();
}
TARGET(COPY_DICT_WITHOUT_KEYS) {
// rest = dict(TOS1)
// for key in TOS:
// del rest[key]
// SET_TOP(rest)
PyObject *keys = TOP();
PyObject *subject = SECOND();
PyObject *rest = PyDict_New();
if (rest == NULL || PyDict_Update(rest, subject)) {
Py_XDECREF(rest);
goto error;
}
// This may seem a bit inefficient, but keys is rarely big enough to
// actually impact runtime.
assert(PyTuple_CheckExact(keys));
for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(keys); i++) {
if (PyDict_DelItem(rest, PyTuple_GET_ITEM(keys, i))) {
Py_DECREF(rest);
goto error;
}
}
Py_DECREF(keys);
SET_TOP(rest);
DISPATCH();
}
@ -5027,6 +4999,14 @@ check_eval_breaker:
DISPATCH();
}
TARGET(COPY) {
assert(oparg != 0);
PyObject *peek = PEEK(oparg);
Py_INCREF(peek);
PUSH(peek);
DISPATCH();
}
TARGET(EXTENDED_ARG) {
int oldoparg = oparg;
NEXTOPARG();

View file

@ -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));

View file

@ -33,11 +33,10 @@ static void *opcode_targets[256] = {
&&TARGET_MATCH_MAPPING,
&&TARGET_MATCH_SEQUENCE,
&&TARGET_MATCH_KEYS,
&&TARGET_COPY_DICT_WITHOUT_KEYS,
&&TARGET_PUSH_EXC_INFO,
&&TARGET_BINARY_MULTIPLY_INT,
&&TARGET_POP_EXCEPT_AND_RERAISE,
&&TARGET_PUSH_EXC_INFO,
&&TARGET_BINARY_MULTIPLY_FLOAT,
&&TARGET_POP_EXCEPT_AND_RERAISE,
&&TARGET_BINARY_SUBSCR_ADAPTIVE,
&&TARGET_BINARY_SUBSCR_LIST_INT,
&&TARGET_BINARY_SUBSCR_TUPLE_INT,
@ -48,6 +47,7 @@ static void *opcode_targets[256] = {
&&TARGET_CALL_FUNCTION_LEN,
&&TARGET_CALL_FUNCTION_ISINSTANCE,
&&TARGET_CALL_FUNCTION_PY_SIMPLE,
&&TARGET_JUMP_ABSOLUTE_QUICK,
&&TARGET_WITH_EXCEPT_START,
&&TARGET_GET_AITER,
&&TARGET_GET_ANEXT,
@ -57,7 +57,7 @@ static void *opcode_targets[256] = {
&&TARGET_INPLACE_ADD,
&&TARGET_INPLACE_SUBTRACT,
&&TARGET_INPLACE_MULTIPLY,
&&TARGET_JUMP_ABSOLUTE_QUICK,
&&TARGET_LOAD_ATTR_ADAPTIVE,
&&TARGET_INPLACE_MODULO,
&&TARGET_STORE_SUBSCR,
&&TARGET_DELETE_SUBSCR,
@ -79,15 +79,15 @@ static void *opcode_targets[256] = {
&&TARGET_INPLACE_AND,
&&TARGET_INPLACE_XOR,
&&TARGET_INPLACE_OR,
&&TARGET_LOAD_ATTR_ADAPTIVE,
&&TARGET_LOAD_ATTR_INSTANCE_VALUE,
&&TARGET_LOAD_ATTR_WITH_HINT,
&&TARGET_LIST_TO_TUPLE,
&&TARGET_RETURN_VALUE,
&&TARGET_IMPORT_STAR,
&&TARGET_SETUP_ANNOTATIONS,
&&TARGET_YIELD_VALUE,
&&TARGET_LOAD_ATTR_WITH_HINT,
&&TARGET_LOAD_ATTR_SLOT,
&&TARGET_LOAD_ATTR_MODULE,
&&TARGET_POP_EXCEPT,
&&TARGET_STORE_NAME,
&&TARGET_DELETE_NAME,
@ -119,7 +119,7 @@ static void *opcode_targets[256] = {
&&TARGET_IS_OP,
&&TARGET_CONTAINS_OP,
&&TARGET_RERAISE,
&&TARGET_LOAD_ATTR_MODULE,
&&TARGET_COPY,
&&TARGET_JUMP_IF_NOT_EXC_MATCH,
&&TARGET_LOAD_GLOBAL_ADAPTIVE,
&&TARGET_LOAD_GLOBAL_MODULE,