mirror of
https://github.com/python/cpython.git
synced 2025-07-19 17:25:54 +00:00
GH-87849: Simplify stack effect of SEND and specialize it for generators and coroutines. (GH-101788)
This commit is contained in:
parent
a1f08f5f19
commit
160f2fe2b9
15 changed files with 191 additions and 105 deletions
|
@ -680,51 +680,66 @@ dummy_func(
|
|||
PREDICT(LOAD_CONST);
|
||||
}
|
||||
|
||||
inst(SEND, (receiver, v -- receiver if (!jump), retval)) {
|
||||
family(for_iter, INLINE_CACHE_ENTRIES_FOR_ITER) = {
|
||||
SEND,
|
||||
SEND_GEN,
|
||||
};
|
||||
|
||||
inst(SEND, (unused/1, receiver, v -- receiver, retval)) {
|
||||
#if ENABLE_SPECIALIZATION
|
||||
_PySendCache *cache = (_PySendCache *)next_instr;
|
||||
if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) {
|
||||
assert(cframe.use_tracing == 0);
|
||||
next_instr--;
|
||||
_Py_Specialize_Send(receiver, next_instr);
|
||||
DISPATCH_SAME_OPARG();
|
||||
}
|
||||
STAT_INC(SEND, deferred);
|
||||
DECREMENT_ADAPTIVE_COUNTER(cache->counter);
|
||||
#endif /* ENABLE_SPECIALIZATION */
|
||||
assert(frame != &entry_frame);
|
||||
bool jump = false;
|
||||
PySendResult gen_status;
|
||||
if (tstate->c_tracefunc == NULL) {
|
||||
gen_status = PyIter_Send(receiver, v, &retval);
|
||||
} else {
|
||||
if (Py_IsNone(v) && PyIter_Check(receiver)) {
|
||||
retval = Py_TYPE(receiver)->tp_iternext(receiver);
|
||||
}
|
||||
else {
|
||||
retval = PyObject_CallMethodOneArg(receiver, &_Py_ID(send), v);
|
||||
}
|
||||
if (retval == NULL) {
|
||||
if (tstate->c_tracefunc != NULL
|
||||
&& _PyErr_ExceptionMatches(tstate, PyExc_StopIteration))
|
||||
call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj, tstate, frame);
|
||||
if (_PyGen_FetchStopIterationValue(&retval) == 0) {
|
||||
gen_status = PYGEN_RETURN;
|
||||
}
|
||||
else {
|
||||
gen_status = PYGEN_ERROR;
|
||||
}
|
||||
}
|
||||
else {
|
||||
gen_status = PYGEN_NEXT;
|
||||
}
|
||||
}
|
||||
if (gen_status == PYGEN_ERROR) {
|
||||
assert(retval == NULL);
|
||||
goto error;
|
||||
}
|
||||
Py_DECREF(v);
|
||||
if (gen_status == PYGEN_RETURN) {
|
||||
assert(retval != NULL);
|
||||
Py_DECREF(receiver);
|
||||
JUMPBY(oparg);
|
||||
jump = true;
|
||||
if (Py_IsNone(v) && PyIter_Check(receiver)) {
|
||||
retval = Py_TYPE(receiver)->tp_iternext(receiver);
|
||||
}
|
||||
else {
|
||||
retval = PyObject_CallMethodOneArg(receiver, &_Py_ID(send), v);
|
||||
}
|
||||
if (retval == NULL) {
|
||||
if (tstate->c_tracefunc != NULL
|
||||
&& _PyErr_ExceptionMatches(tstate, PyExc_StopIteration))
|
||||
call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj, tstate, frame);
|
||||
if (_PyGen_FetchStopIterationValue(&retval) == 0) {
|
||||
assert(retval != NULL);
|
||||
JUMPBY(oparg);
|
||||
}
|
||||
else {
|
||||
assert(retval == NULL);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
else {
|
||||
assert(gen_status == PYGEN_NEXT);
|
||||
assert(retval != NULL);
|
||||
}
|
||||
}
|
||||
|
||||
inst(SEND_GEN, (unused/1, receiver, v -- receiver)) {
|
||||
assert(cframe.use_tracing == 0);
|
||||
PyGenObject *gen = (PyGenObject *)receiver;
|
||||
DEOPT_IF(Py_TYPE(gen) != &PyGen_Type &&
|
||||
Py_TYPE(gen) != &PyCoro_Type, SEND);
|
||||
DEOPT_IF(gen->gi_frame_state >= FRAME_EXECUTING, SEND);
|
||||
STAT_INC(SEND, hit);
|
||||
_PyInterpreterFrame *gen_frame = (_PyInterpreterFrame *)gen->gi_iframe;
|
||||
frame->yield_offset = oparg;
|
||||
STACK_SHRINK(1);
|
||||
_PyFrame_StackPush(gen_frame, v);
|
||||
gen->gi_frame_state = FRAME_EXECUTING;
|
||||
gen->gi_exc_state.previous_item = tstate->exc_info;
|
||||
tstate->exc_info = &gen->gi_exc_state;
|
||||
JUMPBY(INLINE_CACHE_ENTRIES_SEND + oparg);
|
||||
DISPATCH_INLINED(gen_frame);
|
||||
}
|
||||
|
||||
inst(YIELD_VALUE, (retval -- unused)) {
|
||||
// NOTE: It's important that YIELD_VALUE never raises an exception!
|
||||
// The compiler treats any exception raised here as a failed close()
|
||||
|
@ -796,12 +811,13 @@ dummy_func(
|
|||
}
|
||||
}
|
||||
|
||||
inst(CLEANUP_THROW, (sub_iter, last_sent_val, exc_value -- value)) {
|
||||
inst(CLEANUP_THROW, (sub_iter, last_sent_val, exc_value -- none, value)) {
|
||||
assert(throwflag);
|
||||
assert(exc_value && PyExceptionInstance_Check(exc_value));
|
||||
if (PyErr_GivenExceptionMatches(exc_value, PyExc_StopIteration)) {
|
||||
value = Py_NewRef(((PyStopIterationObject *)exc_value)->value);
|
||||
DECREF_INPUTS();
|
||||
none = Py_NewRef(Py_None);
|
||||
}
|
||||
else {
|
||||
_PyErr_SetRaisedException(tstate, Py_NewRef(exc_value));
|
||||
|
|
|
@ -1789,6 +1789,8 @@ compiler_add_yield_from(struct compiler *c, location loc, int await)
|
|||
ADDOP(c, loc, CLEANUP_THROW);
|
||||
|
||||
USE_LABEL(c, exit);
|
||||
ADDOP_I(c, loc, SWAP, 2);
|
||||
ADDOP(c, loc, POP_TOP);
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
|
|
95
Python/generated_cases.c.h
generated
95
Python/generated_cases.c.h
generated
|
@ -882,57 +882,69 @@
|
|||
}
|
||||
|
||||
TARGET(SEND) {
|
||||
PREDICTED(SEND);
|
||||
PyObject *v = PEEK(1);
|
||||
PyObject *receiver = PEEK(2);
|
||||
PyObject *retval;
|
||||
#if ENABLE_SPECIALIZATION
|
||||
_PySendCache *cache = (_PySendCache *)next_instr;
|
||||
if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) {
|
||||
assert(cframe.use_tracing == 0);
|
||||
next_instr--;
|
||||
_Py_Specialize_Send(receiver, next_instr);
|
||||
DISPATCH_SAME_OPARG();
|
||||
}
|
||||
STAT_INC(SEND, deferred);
|
||||
DECREMENT_ADAPTIVE_COUNTER(cache->counter);
|
||||
#endif /* ENABLE_SPECIALIZATION */
|
||||
assert(frame != &entry_frame);
|
||||
bool jump = false;
|
||||
PySendResult gen_status;
|
||||
if (tstate->c_tracefunc == NULL) {
|
||||
gen_status = PyIter_Send(receiver, v, &retval);
|
||||
} else {
|
||||
if (Py_IsNone(v) && PyIter_Check(receiver)) {
|
||||
retval = Py_TYPE(receiver)->tp_iternext(receiver);
|
||||
}
|
||||
else {
|
||||
retval = PyObject_CallMethodOneArg(receiver, &_Py_ID(send), v);
|
||||
}
|
||||
if (retval == NULL) {
|
||||
if (tstate->c_tracefunc != NULL
|
||||
&& _PyErr_ExceptionMatches(tstate, PyExc_StopIteration))
|
||||
call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj, tstate, frame);
|
||||
if (_PyGen_FetchStopIterationValue(&retval) == 0) {
|
||||
gen_status = PYGEN_RETURN;
|
||||
}
|
||||
else {
|
||||
gen_status = PYGEN_ERROR;
|
||||
}
|
||||
}
|
||||
else {
|
||||
gen_status = PYGEN_NEXT;
|
||||
}
|
||||
}
|
||||
if (gen_status == PYGEN_ERROR) {
|
||||
assert(retval == NULL);
|
||||
goto error;
|
||||
}
|
||||
Py_DECREF(v);
|
||||
if (gen_status == PYGEN_RETURN) {
|
||||
assert(retval != NULL);
|
||||
Py_DECREF(receiver);
|
||||
JUMPBY(oparg);
|
||||
jump = true;
|
||||
if (Py_IsNone(v) && PyIter_Check(receiver)) {
|
||||
retval = Py_TYPE(receiver)->tp_iternext(receiver);
|
||||
}
|
||||
else {
|
||||
retval = PyObject_CallMethodOneArg(receiver, &_Py_ID(send), v);
|
||||
}
|
||||
if (retval == NULL) {
|
||||
if (tstate->c_tracefunc != NULL
|
||||
&& _PyErr_ExceptionMatches(tstate, PyExc_StopIteration))
|
||||
call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj, tstate, frame);
|
||||
if (_PyGen_FetchStopIterationValue(&retval) == 0) {
|
||||
assert(retval != NULL);
|
||||
JUMPBY(oparg);
|
||||
}
|
||||
else {
|
||||
assert(retval == NULL);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
else {
|
||||
assert(gen_status == PYGEN_NEXT);
|
||||
assert(retval != NULL);
|
||||
}
|
||||
STACK_SHRINK(1);
|
||||
STACK_GROW(((!jump) ? 1 : 0));
|
||||
POKE(1, retval);
|
||||
JUMPBY(1);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
TARGET(SEND_GEN) {
|
||||
PyObject *v = PEEK(1);
|
||||
PyObject *receiver = PEEK(2);
|
||||
assert(cframe.use_tracing == 0);
|
||||
PyGenObject *gen = (PyGenObject *)receiver;
|
||||
DEOPT_IF(Py_TYPE(gen) != &PyGen_Type &&
|
||||
Py_TYPE(gen) != &PyCoro_Type, SEND);
|
||||
DEOPT_IF(gen->gi_frame_state >= FRAME_EXECUTING, SEND);
|
||||
STAT_INC(SEND, hit);
|
||||
_PyInterpreterFrame *gen_frame = (_PyInterpreterFrame *)gen->gi_iframe;
|
||||
frame->yield_offset = oparg;
|
||||
STACK_SHRINK(1);
|
||||
_PyFrame_StackPush(gen_frame, v);
|
||||
gen->gi_frame_state = FRAME_EXECUTING;
|
||||
gen->gi_exc_state.previous_item = tstate->exc_info;
|
||||
tstate->exc_info = &gen->gi_exc_state;
|
||||
JUMPBY(INLINE_CACHE_ENTRIES_SEND + oparg);
|
||||
DISPATCH_INLINED(gen_frame);
|
||||
}
|
||||
|
||||
TARGET(YIELD_VALUE) {
|
||||
PyObject *retval = PEEK(1);
|
||||
// NOTE: It's important that YIELD_VALUE never raises an exception!
|
||||
|
@ -1026,6 +1038,7 @@
|
|||
PyObject *exc_value = PEEK(1);
|
||||
PyObject *last_sent_val = PEEK(2);
|
||||
PyObject *sub_iter = PEEK(3);
|
||||
PyObject *none;
|
||||
PyObject *value;
|
||||
assert(throwflag);
|
||||
assert(exc_value && PyExceptionInstance_Check(exc_value));
|
||||
|
@ -1034,13 +1047,15 @@
|
|||
Py_DECREF(sub_iter);
|
||||
Py_DECREF(last_sent_val);
|
||||
Py_DECREF(exc_value);
|
||||
none = Py_NewRef(Py_None);
|
||||
}
|
||||
else {
|
||||
_PyErr_SetRaisedException(tstate, Py_NewRef(exc_value));
|
||||
goto exception_unwind;
|
||||
}
|
||||
STACK_SHRINK(2);
|
||||
STACK_SHRINK(1);
|
||||
POKE(1, value);
|
||||
POKE(2, none);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
|
|
|
@ -104,6 +104,8 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) {
|
|||
return 1;
|
||||
case SEND:
|
||||
return 2;
|
||||
case SEND_GEN:
|
||||
return 2;
|
||||
case YIELD_VALUE:
|
||||
return 1;
|
||||
case POP_EXCEPT:
|
||||
|
@ -453,7 +455,9 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) {
|
|||
case GET_AWAITABLE:
|
||||
return 1;
|
||||
case SEND:
|
||||
return ((!jump) ? 1 : 0) + 1;
|
||||
return 2;
|
||||
case SEND_GEN:
|
||||
return 1;
|
||||
case YIELD_VALUE:
|
||||
return 1;
|
||||
case POP_EXCEPT:
|
||||
|
@ -465,7 +469,7 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) {
|
|||
case END_ASYNC_FOR:
|
||||
return 0;
|
||||
case CLEANUP_THROW:
|
||||
return 1;
|
||||
return 2;
|
||||
case LOAD_ASSERTION_ERROR:
|
||||
return 1;
|
||||
case LOAD_BUILD_CLASS:
|
||||
|
@ -763,7 +767,8 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[256] = {
|
|||
[GET_AITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
|
||||
[GET_ANEXT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
|
||||
[GET_AWAITABLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
|
||||
[SEND] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
|
||||
[SEND] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC },
|
||||
[SEND_GEN] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC },
|
||||
[YIELD_VALUE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
|
||||
[POP_EXCEPT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
|
||||
[RERAISE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
|
||||
|
|
2
Python/opcode_targets.h
generated
2
Python/opcode_targets.h
generated
|
@ -165,7 +165,7 @@ static void *opcode_targets[256] = {
|
|||
&&TARGET_SET_UPDATE,
|
||||
&&TARGET_DICT_MERGE,
|
||||
&&TARGET_DICT_UPDATE,
|
||||
&&_unknown_opcode,
|
||||
&&TARGET_SEND_GEN,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
&&_unknown_opcode,
|
||||
|
|
|
@ -128,6 +128,7 @@ print_spec_stats(FILE *out, OpcodeStats *stats)
|
|||
fprintf(out, "opcode[%d].specializable : 1\n", BINARY_SLICE);
|
||||
fprintf(out, "opcode[%d].specializable : 1\n", COMPARE_OP);
|
||||
fprintf(out, "opcode[%d].specializable : 1\n", STORE_SLICE);
|
||||
fprintf(out, "opcode[%d].specializable : 1\n", SEND);
|
||||
for (int i = 0; i < 256; i++) {
|
||||
if (_PyOpcode_Caches[i]) {
|
||||
fprintf(out, "opcode[%d].specializable : 1\n", i);
|
||||
|
@ -1084,7 +1085,7 @@ PyObject *descr, DescriptorClassification kind)
|
|||
if (dict) {
|
||||
SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_NOT_MANAGED_DICT);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
assert(owner_cls->tp_dictoffset > 0);
|
||||
assert(owner_cls->tp_dictoffset <= INT16_MAX);
|
||||
_py_set_opcode(instr, LOAD_ATTR_METHOD_LAZY_DICT);
|
||||
|
@ -2183,3 +2184,25 @@ success:
|
|||
STAT_INC(FOR_ITER, success);
|
||||
cache->counter = adaptive_counter_cooldown();
|
||||
}
|
||||
|
||||
void
|
||||
_Py_Specialize_Send(PyObject *receiver, _Py_CODEUNIT *instr)
|
||||
{
|
||||
assert(ENABLE_SPECIALIZATION);
|
||||
assert(_PyOpcode_Caches[SEND] == INLINE_CACHE_ENTRIES_SEND);
|
||||
_PySendCache *cache = (_PySendCache *)(instr + 1);
|
||||
PyTypeObject *tp = Py_TYPE(receiver);
|
||||
if (tp == &PyGen_Type || tp == &PyCoro_Type) {
|
||||
_py_set_opcode(instr, SEND_GEN);
|
||||
goto success;
|
||||
}
|
||||
SPECIALIZATION_FAIL(SEND,
|
||||
_PySpecialization_ClassifyIterator(receiver));
|
||||
STAT_INC(SEND, failure);
|
||||
_py_set_opcode(instr, SEND);
|
||||
cache->counter = adaptive_counter_backoff(cache->counter);
|
||||
return;
|
||||
success:
|
||||
STAT_INC(SEND, success);
|
||||
cache->counter = adaptive_counter_cooldown();
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue