aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Python
diff options
context:
space:
mode:
authorMark Shannon <mark@hotpy.org>2024-06-18 12:17:46 +0100
committerGitHub <noreply@github.com>2024-06-18 12:17:46 +0100
commit9cefcc0ee781a1bef9e0685c2271237005fb488b (patch)
tree7c02067f4e021d90ec4fd850d9a06315b8fc39ab /Python
parent73dc1c678eb720c2ced94d2f435a908bb6d18566 (diff)
downloadcpython-9cefcc0ee781a1bef9e0685c2271237005fb488b.tar.gz
cpython-9cefcc0ee781a1bef9e0685c2271237005fb488b.zip
GH-120507: Lower the `BEFORE_WITH` and `BEFORE_ASYNC_WITH` instructions. (#120640)
* Remove BEFORE_WITH and BEFORE_ASYNC_WITH instructions. * Add LOAD_SPECIAL instruction * Reimplement `with` and `async with` statements using LOAD_SPECIAL
Diffstat (limited to 'Python')
-rw-r--r--Python/bytecodes.c81
-rw-r--r--Python/ceval.c23
-rw-r--r--Python/compile.c18
-rw-r--r--Python/executor_cases.c.h39
-rw-r--r--Python/generated_cases.c.h128
-rw-r--r--Python/opcode_targets.h8
-rw-r--r--Python/optimizer_bytecodes.c7
-rw-r--r--Python/optimizer_cases.c.h16
8 files changed, 141 insertions, 179 deletions
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 31db2842600..50444bcc0d2 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -2814,79 +2814,27 @@ dummy_func(
_FOR_ITER_GEN_FRAME +
_PUSH_FRAME;
- inst(BEFORE_ASYNC_WITH, (mgr -- exit, res)) {
- PyObject *enter = _PyObject_LookupSpecial(mgr, &_Py_ID(__aenter__));
- if (enter == NULL) {
- if (!_PyErr_Occurred(tstate)) {
- _PyErr_Format(tstate, PyExc_TypeError,
- "'%.200s' object does not support the "
- "asynchronous context manager protocol",
- Py_TYPE(mgr)->tp_name);
- }
- ERROR_NO_POP();
- }
- exit = _PyObject_LookupSpecial(mgr, &_Py_ID(__aexit__));
- if (exit == NULL) {
- if (!_PyErr_Occurred(tstate)) {
- _PyErr_Format(tstate, PyExc_TypeError,
- "'%.200s' object does not support the "
- "asynchronous context manager protocol "
- "(missed __aexit__ method)",
- Py_TYPE(mgr)->tp_name);
- }
- Py_DECREF(enter);
- ERROR_NO_POP();
- }
- DECREF_INPUTS();
- res = PyObject_CallNoArgs(enter);
- Py_DECREF(enter);
- if (res == NULL) {
- Py_DECREF(exit);
- ERROR_IF(true, error);
- }
- }
-
- inst(BEFORE_WITH, (mgr -- exit, res)) {
- /* pop the context manager, push its __exit__ and the
- * value returned from calling its __enter__
- */
- PyObject *enter = _PyObject_LookupSpecial(mgr, &_Py_ID(__enter__));
- if (enter == NULL) {
- if (!_PyErr_Occurred(tstate)) {
- _PyErr_Format(tstate, PyExc_TypeError,
- "'%.200s' object does not support the "
- "context manager protocol",
- Py_TYPE(mgr)->tp_name);
- }
- ERROR_NO_POP();
- }
- exit = _PyObject_LookupSpecial(mgr, &_Py_ID(__exit__));
- if (exit == NULL) {
+ inst(LOAD_SPECIAL, (owner -- attr, self_or_null)) {
+ assert(oparg <= SPECIAL_MAX);
+ PyObject *name = _Py_SpecialMethods[oparg].name;
+ attr = _PyObject_LookupSpecialMethod(owner, name, &self_or_null);
+ if (attr == NULL) {
if (!_PyErr_Occurred(tstate)) {
_PyErr_Format(tstate, PyExc_TypeError,
- "'%.200s' object does not support the "
- "context manager protocol "
- "(missed __exit__ method)",
- Py_TYPE(mgr)->tp_name);
+ _Py_SpecialMethods[oparg].error,
+ Py_TYPE(owner)->tp_name);
}
- Py_DECREF(enter);
- ERROR_NO_POP();
- }
- DECREF_INPUTS();
- res = PyObject_CallNoArgs(enter);
- Py_DECREF(enter);
- if (res == NULL) {
- Py_DECREF(exit);
- ERROR_IF(true, error);
}
+ ERROR_IF(attr == NULL, error);
}
- inst(WITH_EXCEPT_START, (exit_func, lasti, unused, val -- exit_func, lasti, unused, val, res)) {
+ inst(WITH_EXCEPT_START, (exit_func, exit_self, lasti, unused, val -- exit_func, exit_self, lasti, unused, val, res)) {
/* At the top of the stack are 4 values:
- val: TOP = exc_info()
- unused: SECOND = previous exception
- lasti: THIRD = lasti of exception in exc_info()
- - exit_func: FOURTH = the context.__exit__ bound method
+ - exit_self: FOURTH = the context or NULL
+ - exit_func: FIFTH = the context.__exit__ function or context.__exit__ bound method
We call FOURTH(type(TOP), TOP, GetTraceback(TOP)).
Then we push the __exit__ return value.
*/
@@ -2903,9 +2851,10 @@ dummy_func(
}
assert(PyLong_Check(lasti));
(void)lasti; // Shut up compiler warning if asserts are off
- PyObject *stack[4] = {NULL, exc, val, tb};
- res = PyObject_Vectorcall(exit_func, stack + 1,
- 3 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL);
+ PyObject *stack[5] = {NULL, exit_self, exc, val, tb};
+ int has_self = (exit_self != NULL);
+ res = PyObject_Vectorcall(exit_func, stack + 2 - has_self,
+ (3 + has_self) | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL);
ERROR_IF(res == NULL, error);
}
diff --git a/Python/ceval.c b/Python/ceval.c
index e3968b07486..a8df045799d 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -343,6 +343,29 @@ const conversion_func _PyEval_ConversionFuncs[4] = {
[FVC_ASCII] = PyObject_ASCII
};
+const _Py_SpecialMethod _Py_SpecialMethods[] = {
+ [SPECIAL___ENTER__] = {
+ .name = &_Py_ID(__enter__),
+ .error = "'%.200s' object does not support the "
+ "context manager protocol (missed __enter__ method)",
+ },
+ [SPECIAL___EXIT__] = {
+ .name = &_Py_ID(__exit__),
+ .error = "'%.200s' object does not support the "
+ "context manager protocol (missed __exit__ method)",
+ },
+ [SPECIAL___AENTER__] = {
+ .name = &_Py_ID(__aenter__),
+ .error = "'%.200s' object does not support the asynchronous "
+ "context manager protocol (missed __aenter__ method)",
+ },
+ [SPECIAL___AEXIT__] = {
+ .name = &_Py_ID(__aexit__),
+ .error = "'%.200s' object does not support the asynchronous "
+ "context manager protocol (missed __aexit__ method)",
+ }
+};
+
// PEP 634: Structural Pattern Matching
diff --git a/Python/compile.c b/Python/compile.c
index 749b69f5911..53fe6db5ed2 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -1260,7 +1260,7 @@ compiler_call_exit_with_nones(struct compiler *c, location loc)
ADDOP_LOAD_CONST(c, loc, Py_None);
ADDOP_LOAD_CONST(c, loc, Py_None);
ADDOP_LOAD_CONST(c, loc, Py_None);
- ADDOP_I(c, loc, CALL, 2);
+ ADDOP_I(c, loc, CALL, 3);
return SUCCESS;
}
@@ -1369,6 +1369,7 @@ compiler_unwind_fblock(struct compiler *c, location *ploc,
*ploc = LOC((stmt_ty)info->fb_datum);
ADDOP(c, *ploc, POP_BLOCK);
if (preserve_tos) {
+ ADDOP_I(c, *ploc, SWAP, 3);
ADDOP_I(c, *ploc, SWAP, 2);
}
RETURN_IF_ERROR(compiler_call_exit_with_nones(c, *ploc));
@@ -5897,6 +5898,7 @@ compiler_with_except_finish(struct compiler *c, jump_target_label cleanup) {
ADDOP(c, NO_LOCATION, POP_EXCEPT);
ADDOP(c, NO_LOCATION, POP_TOP);
ADDOP(c, NO_LOCATION, POP_TOP);
+ ADDOP(c, NO_LOCATION, POP_TOP);
NEW_JUMP_TARGET_LABEL(c, exit);
ADDOP_JUMP(c, NO_LOCATION, JUMP_NO_INTERRUPT, exit);
@@ -5952,7 +5954,12 @@ compiler_async_with(struct compiler *c, stmt_ty s, int pos)
/* Evaluate EXPR */
VISIT(c, expr, item->context_expr);
loc = LOC(item->context_expr);
- ADDOP(c, loc, BEFORE_ASYNC_WITH);
+ ADDOP_I(c, loc, COPY, 1);
+ ADDOP_I(c, loc, LOAD_SPECIAL, SPECIAL___AEXIT__);
+ ADDOP_I(c, loc, SWAP, 2);
+ ADDOP_I(c, loc, SWAP, 3);
+ ADDOP_I(c, loc, LOAD_SPECIAL, SPECIAL___AENTER__);
+ ADDOP_I(c, loc, CALL, 0);
ADDOP_I(c, loc, GET_AWAITABLE, 1);
ADDOP_LOAD_CONST(c, loc, Py_None);
ADD_YIELD_FROM(c, loc, 1);
@@ -6050,7 +6057,12 @@ compiler_with(struct compiler *c, stmt_ty s, int pos)
VISIT(c, expr, item->context_expr);
/* Will push bound __exit__ */
location loc = LOC(item->context_expr);
- ADDOP(c, loc, BEFORE_WITH);
+ ADDOP_I(c, loc, COPY, 1);
+ ADDOP_I(c, loc, LOAD_SPECIAL, SPECIAL___EXIT__);
+ ADDOP_I(c, loc, SWAP, 2);
+ ADDOP_I(c, loc, SWAP, 3);
+ ADDOP_I(c, loc, LOAD_SPECIAL, SPECIAL___ENTER__);
+ ADDOP_I(c, loc, CALL, 0);
ADDOP_JUMP(c, loc, SETUP_WITH, final);
/* SETUP_WITH pushes a finally block. */
diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h
index d390c9fc2f6..b4e5261f57a 100644
--- a/Python/executor_cases.c.h
+++ b/Python/executor_cases.c.h
@@ -2883,23 +2883,45 @@
break;
}
- /* _BEFORE_ASYNC_WITH is not a viable micro-op for tier 2 because it has both popping and not-popping errors */
-
- /* _BEFORE_WITH is not a viable micro-op for tier 2 because it has both popping and not-popping errors */
+ case _LOAD_SPECIAL: {
+ PyObject *owner;
+ PyObject *attr;
+ PyObject *self_or_null;
+ oparg = CURRENT_OPARG();
+ owner = stack_pointer[-1];
+ assert(oparg <= SPECIAL_MAX);
+ PyObject *name = _Py_SpecialMethods[oparg].name;
+ attr = _PyObject_LookupSpecialMethod(owner, name, &self_or_null);
+ if (attr == NULL) {
+ if (!_PyErr_Occurred(tstate)) {
+ _PyErr_Format(tstate, PyExc_TypeError,
+ _Py_SpecialMethods[oparg].error,
+ Py_TYPE(owner)->tp_name);
+ }
+ }
+ if (attr == NULL) JUMP_TO_ERROR();
+ stack_pointer[-1] = attr;
+ stack_pointer[0] = self_or_null;
+ stack_pointer += 1;
+ break;
+ }
case _WITH_EXCEPT_START: {
PyObject *val;
PyObject *lasti;
+ PyObject *exit_self;
PyObject *exit_func;
PyObject *res;
val = stack_pointer[-1];
lasti = stack_pointer[-3];
- exit_func = stack_pointer[-4];
+ exit_self = stack_pointer[-4];
+ exit_func = stack_pointer[-5];
/* At the top of the stack are 4 values:
- val: TOP = exc_info()
- unused: SECOND = previous exception
- lasti: THIRD = lasti of exception in exc_info()
- - exit_func: FOURTH = the context.__exit__ bound method
+ - exit_self: FOURTH = the context or NULL
+ - exit_func: FIFTH = the context.__exit__ function or context.__exit__ bound method
We call FOURTH(type(TOP), TOP, GetTraceback(TOP)).
Then we push the __exit__ return value.
*/
@@ -2915,9 +2937,10 @@
}
assert(PyLong_Check(lasti));
(void)lasti; // Shut up compiler warning if asserts are off
- PyObject *stack[4] = {NULL, exc, val, tb};
- res = PyObject_Vectorcall(exit_func, stack + 1,
- 3 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL);
+ PyObject *stack[5] = {NULL, exit_self, exc, val, tb};
+ int has_self = (exit_self != NULL);
+ res = PyObject_Vectorcall(exit_func, stack + 2 - has_self,
+ (3 + has_self) | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL);
if (res == NULL) JUMP_TO_ERROR();
stack_pointer[0] = res;
stack_pointer += 1;
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index 8a6f5ff784f..3980f9852e6 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -9,95 +9,6 @@
#define TIER_ONE 1
- TARGET(BEFORE_ASYNC_WITH) {
- frame->instr_ptr = next_instr;
- next_instr += 1;
- INSTRUCTION_STATS(BEFORE_ASYNC_WITH);
- PyObject *mgr;
- PyObject *exit;
- PyObject *res;
- mgr = stack_pointer[-1];
- PyObject *enter = _PyObject_LookupSpecial(mgr, &_Py_ID(__aenter__));
- if (enter == NULL) {
- if (!_PyErr_Occurred(tstate)) {
- _PyErr_Format(tstate, PyExc_TypeError,
- "'%.200s' object does not support the "
- "asynchronous context manager protocol",
- Py_TYPE(mgr)->tp_name);
- }
- goto error;
- }
- exit = _PyObject_LookupSpecial(mgr, &_Py_ID(__aexit__));
- if (exit == NULL) {
- if (!_PyErr_Occurred(tstate)) {
- _PyErr_Format(tstate, PyExc_TypeError,
- "'%.200s' object does not support the "
- "asynchronous context manager protocol "
- "(missed __aexit__ method)",
- Py_TYPE(mgr)->tp_name);
- }
- Py_DECREF(enter);
- goto error;
- }
- Py_DECREF(mgr);
- res = PyObject_CallNoArgs(enter);
- Py_DECREF(enter);
- if (res == NULL) {
- Py_DECREF(exit);
- if (true) goto pop_1_error;
- }
- stack_pointer[-1] = exit;
- stack_pointer[0] = res;
- stack_pointer += 1;
- DISPATCH();
- }
-
- TARGET(BEFORE_WITH) {
- frame->instr_ptr = next_instr;
- next_instr += 1;
- INSTRUCTION_STATS(BEFORE_WITH);
- PyObject *mgr;
- PyObject *exit;
- PyObject *res;
- mgr = stack_pointer[-1];
- /* pop the context manager, push its __exit__ and the
- * value returned from calling its __enter__
- */
- PyObject *enter = _PyObject_LookupSpecial(mgr, &_Py_ID(__enter__));
- if (enter == NULL) {
- if (!_PyErr_Occurred(tstate)) {
- _PyErr_Format(tstate, PyExc_TypeError,
- "'%.200s' object does not support the "
- "context manager protocol",
- Py_TYPE(mgr)->tp_name);
- }
- goto error;
- }
- exit = _PyObject_LookupSpecial(mgr, &_Py_ID(__exit__));
- if (exit == NULL) {
- if (!_PyErr_Occurred(tstate)) {
- _PyErr_Format(tstate, PyExc_TypeError,
- "'%.200s' object does not support the "
- "context manager protocol "
- "(missed __exit__ method)",
- Py_TYPE(mgr)->tp_name);
- }
- Py_DECREF(enter);
- goto error;
- }
- Py_DECREF(mgr);
- res = PyObject_CallNoArgs(enter);
- Py_DECREF(enter);
- if (res == NULL) {
- Py_DECREF(exit);
- if (true) goto pop_1_error;
- }
- stack_pointer[-1] = exit;
- stack_pointer[0] = res;
- stack_pointer += 1;
- DISPATCH();
- }
-
TARGET(BINARY_OP) {
frame->instr_ptr = next_instr;
next_instr += 2;
@@ -4635,6 +4546,31 @@
DISPATCH();
}
+ TARGET(LOAD_SPECIAL) {
+ frame->instr_ptr = next_instr;
+ next_instr += 1;
+ INSTRUCTION_STATS(LOAD_SPECIAL);
+ PyObject *owner;
+ PyObject *attr;
+ PyObject *self_or_null;
+ owner = stack_pointer[-1];
+ assert(oparg <= SPECIAL_MAX);
+ PyObject *name = _Py_SpecialMethods[oparg].name;
+ attr = _PyObject_LookupSpecialMethod(owner, name, &self_or_null);
+ if (attr == NULL) {
+ if (!_PyErr_Occurred(tstate)) {
+ _PyErr_Format(tstate, PyExc_TypeError,
+ _Py_SpecialMethods[oparg].error,
+ Py_TYPE(owner)->tp_name);
+ }
+ }
+ if (attr == NULL) goto pop_1_error;
+ stack_pointer[-1] = attr;
+ stack_pointer[0] = self_or_null;
+ stack_pointer += 1;
+ DISPATCH();
+ }
+
TARGET(LOAD_SUPER_ATTR) {
frame->instr_ptr = next_instr;
next_instr += 2;
@@ -6210,16 +6146,19 @@
INSTRUCTION_STATS(WITH_EXCEPT_START);
PyObject *val;
PyObject *lasti;
+ PyObject *exit_self;
PyObject *exit_func;
PyObject *res;
val = stack_pointer[-1];
lasti = stack_pointer[-3];
- exit_func = stack_pointer[-4];
+ exit_self = stack_pointer[-4];
+ exit_func = stack_pointer[-5];
/* At the top of the stack are 4 values:
- val: TOP = exc_info()
- unused: SECOND = previous exception
- lasti: THIRD = lasti of exception in exc_info()
- - exit_func: FOURTH = the context.__exit__ bound method
+ - exit_self: FOURTH = the context or NULL
+ - exit_func: FIFTH = the context.__exit__ function or context.__exit__ bound method
We call FOURTH(type(TOP), TOP, GetTraceback(TOP)).
Then we push the __exit__ return value.
*/
@@ -6235,9 +6174,10 @@
}
assert(PyLong_Check(lasti));
(void)lasti; // Shut up compiler warning if asserts are off
- PyObject *stack[4] = {NULL, exc, val, tb};
- res = PyObject_Vectorcall(exit_func, stack + 1,
- 3 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL);
+ PyObject *stack[5] = {NULL, exit_self, exc, val, tb};
+ int has_self = (exit_self != NULL);
+ res = PyObject_Vectorcall(exit_func, stack + 2 - has_self,
+ (3 + has_self) | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL);
if (res == NULL) goto error;
stack_pointer[0] = res;
stack_pointer += 1;
diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h
index 322483fefec..6097b249c0a 100644
--- a/Python/opcode_targets.h
+++ b/Python/opcode_targets.h
@@ -1,10 +1,8 @@
static void *opcode_targets[256] = {
&&TARGET_CACHE,
- &&TARGET_BEFORE_ASYNC_WITH,
- &&TARGET_BEFORE_WITH,
- &&TARGET_BINARY_OP_INPLACE_ADD_UNICODE,
&&TARGET_BINARY_SLICE,
&&TARGET_BINARY_SUBSCR,
+ &&TARGET_BINARY_OP_INPLACE_ADD_UNICODE,
&&TARGET_CHECK_EG_MATCH,
&&TARGET_CHECK_EXC_MATCH,
&&TARGET_CLEANUP_THROW,
@@ -16,9 +14,9 @@ static void *opcode_targets[256] = {
&&TARGET_FORMAT_SIMPLE,
&&TARGET_FORMAT_WITH_SPEC,
&&TARGET_GET_AITER,
- &&TARGET_RESERVED,
&&TARGET_GET_ANEXT,
&&TARGET_GET_ITER,
+ &&TARGET_RESERVED,
&&TARGET_GET_LEN,
&&TARGET_GET_YIELD_FROM_ITER,
&&TARGET_INTERPRETER_EXIT,
@@ -92,6 +90,7 @@ static void *opcode_targets[256] = {
&&TARGET_LOAD_FROM_DICT_OR_GLOBALS,
&&TARGET_LOAD_GLOBAL,
&&TARGET_LOAD_NAME,
+ &&TARGET_LOAD_SPECIAL,
&&TARGET_LOAD_SUPER_ATTR,
&&TARGET_MAKE_CELL,
&&TARGET_MAP_ADD,
@@ -148,6 +147,7 @@ static void *opcode_targets[256] = {
&&_unknown_opcode,
&&_unknown_opcode,
&&_unknown_opcode,
+ &&_unknown_opcode,
&&TARGET_RESUME,
&&TARGET_BINARY_OP_ADD_FLOAT,
&&TARGET_BINARY_OP_ADD_INT,
diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c
index 121ca928fed..35463f25246 100644
--- a/Python/optimizer_bytecodes.c
+++ b/Python/optimizer_bytecodes.c
@@ -774,6 +774,12 @@ dummy_func(void) {
}
}
+ op(_LOAD_SPECIAL, (owner -- attr, self_or_null)) {
+ (void)owner;
+ attr = sym_new_not_null(ctx);
+ self_or_null = sym_new_unknown(ctx);
+ }
+
op(_JUMP_TO_TOP, (--)) {
ctx->done = true;
}
@@ -782,7 +788,6 @@ dummy_func(void) {
ctx->done = true;
}
-
// END BYTECODES //
}
diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h
index 53959a39b0b..46501862ff2 100644
--- a/Python/optimizer_cases.c.h
+++ b/Python/optimizer_cases.c.h
@@ -1368,9 +1368,19 @@
break;
}
- /* _BEFORE_ASYNC_WITH is not a viable micro-op for tier 2 */
-
- /* _BEFORE_WITH is not a viable micro-op for tier 2 */
+ case _LOAD_SPECIAL: {
+ _Py_UopsSymbol *owner;
+ _Py_UopsSymbol *attr;
+ _Py_UopsSymbol *self_or_null;
+ owner = stack_pointer[-1];
+ (void)owner;
+ attr = sym_new_not_null(ctx);
+ self_or_null = sym_new_unknown(ctx);
+ stack_pointer[-1] = attr;
+ stack_pointer[0] = self_or_null;
+ stack_pointer += 1;
+ break;
+ }
case _WITH_EXCEPT_START: {
_Py_UopsSymbol *res;