summaryrefslogtreecommitdiffstats
path: root/Python
diff options
context:
space:
mode:
authorMark Shannon <mark@hotpy.org>2023-02-13 11:24:55 (GMT)
committerGitHub <noreply@github.com>2023-02-13 11:24:55 (GMT)
commit160f2fe2b90ed5ec7838cb4141dd35768422891f (patch)
tree2d9617e40227da45aa8e3c4452eaa0f389a0d879 /Python
parenta1f08f5f19753c7c9295f51b5ae1262c7a1c838f (diff)
downloadcpython-160f2fe2b90ed5ec7838cb4141dd35768422891f.zip
cpython-160f2fe2b90ed5ec7838cb4141dd35768422891f.tar.gz
cpython-160f2fe2b90ed5ec7838cb4141dd35768422891f.tar.bz2
GH-87849: Simplify stack effect of SEND and specialize it for generators and coroutines. (GH-101788)
Diffstat (limited to 'Python')
-rw-r--r--Python/bytecodes.c88
-rw-r--r--Python/compile.c2
-rw-r--r--Python/generated_cases.c.h89
-rw-r--r--Python/opcode_metadata.h11
-rw-r--r--Python/opcode_targets.h2
-rw-r--r--Python/specialize.c25
6 files changed, 139 insertions, 78 deletions
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 2b9f12f..429cd7f 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -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;
- }
+ 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 {
- gen_status = PYGEN_NEXT;
+ assert(retval == NULL);
+ goto error;
}
}
- 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;
- }
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));
diff --git a/Python/compile.c b/Python/compile.c
index a3c915c..b49eda3 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -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;
}
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index a224d4e..093ebff 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -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;
- }
+ 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 {
- gen_status = PYGEN_NEXT;
+ assert(retval == NULL);
+ goto error;
}
}
- 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;
- }
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();
}
diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h
index db1dfd3..d622eb1 100644
--- a/Python/opcode_metadata.h
+++ b/Python/opcode_metadata.h
@@ -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 },
diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h
index eceb246..301ec6e 100644
--- a/Python/opcode_targets.h
+++ b/Python/opcode_targets.h
@@ -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,
diff --git a/Python/specialize.c b/Python/specialize.c
index 908ad6d..4ede312 100644
--- a/Python/specialize.c
+++ b/Python/specialize.c
@@ -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();
+}