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
This commit is contained in:
Mark Shannon 2024-06-18 12:17:46 +01:00 committed by GitHub
parent 73dc1c678e
commit 9cefcc0ee7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 663 additions and 651 deletions

View file

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