diff options
author | Mark Shannon <mark@hotpy.org> | 2024-06-18 12:17:46 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-06-18 12:17:46 +0100 |
commit | 9cefcc0ee781a1bef9e0685c2271237005fb488b (patch) | |
tree | 7c02067f4e021d90ec4fd850d9a06315b8fc39ab /Python | |
parent | 73dc1c678eb720c2ced94d2f435a908bb6d18566 (diff) | |
download | cpython-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.c | 81 | ||||
-rw-r--r-- | Python/ceval.c | 23 | ||||
-rw-r--r-- | Python/compile.c | 18 | ||||
-rw-r--r-- | Python/executor_cases.c.h | 39 | ||||
-rw-r--r-- | Python/generated_cases.c.h | 128 | ||||
-rw-r--r-- | Python/opcode_targets.h | 8 | ||||
-rw-r--r-- | Python/optimizer_bytecodes.c | 7 | ||||
-rw-r--r-- | Python/optimizer_cases.c.h | 16 |
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; |