From 5869fbafe785a5044faf14db8b61f81e88c97271 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Mon, 6 Feb 2023 13:50:15 -0800 Subject: [PATCH 1/7] Modernize FOR_ITER --- Python/bytecodes.c | 39 ++++++++++++++++++++++---------------- Python/generated_cases.c.h | 24 ++++++++++++----------- Python/opcode_metadata.h | 6 +++--- 3 files changed, 39 insertions(+), 30 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 8993567ac82206..d1f068491c2ef9 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2066,27 +2066,35 @@ dummy_func( PREDICT(LOAD_CONST); } - // stack effect: ( -- __0) - inst(FOR_ITER) { + // Most members of this family are "secretly" super-instructions. + // When the loop is exhausted, they jump, and the jump target is + // always END_FOR, which pops two values off the stack. + // This is optimized by skipping that instruction and combining + // its effect (popping 'iter' instead of pushing 'next'.) + + // family(for_iter, INLINE_CACHE_ENTRIES_FOR_ITER) = { + // FOR_ITER, + // FOR_ITER_LIST, + // FOR_ITER_TUPLE, + // FOR_ITER_RANGE, + // FOR_ITER_GEN, + // }; + + inst(FOR_ITER, (unused/1, iter -- iter, next)) { #if ENABLE_SPECIALIZATION _PyForIterCache *cache = (_PyForIterCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) { assert(cframe.use_tracing == 0); next_instr--; - _Py_Specialize_ForIter(TOP(), next_instr, oparg); + _Py_Specialize_ForIter(iter, next_instr, oparg); DISPATCH_SAME_OPARG(); } STAT_INC(FOR_ITER, deferred); DECREMENT_ADAPTIVE_COUNTER(cache->counter); #endif /* ENABLE_SPECIALIZATION */ - /* before: [iter]; after: [iter, iter()] *or* [] */ - PyObject *iter = TOP(); - PyObject *next = (*Py_TYPE(iter)->tp_iternext)(iter); - if (next != NULL) { - PUSH(next); - JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER); - } - else { + /* before: [iter]; after: [iter, iter()] *or* [] (and jump an extra instr.) */ + next = (*Py_TYPE(iter)->tp_iternext)(iter); + if (next == NULL) { if (_PyErr_Occurred(tstate)) { if (!_PyErr_ExceptionMatches(tstate, PyExc_StopIteration)) { goto error; @@ -2098,11 +2106,13 @@ dummy_func( } /* iterator ended normally */ assert(_Py_OPCODE(next_instr[INLINE_CACHE_ENTRIES_FOR_ITER + oparg]) == END_FOR); - STACK_SHRINK(1); Py_DECREF(iter); - /* Skip END_FOR */ + STACK_SHRINK(1); + /* Jump forward oparg, then skip following END_FOR instruction */ JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + oparg + 1); + DISPATCH(); } + // Common case: no jump, leave it to the code generator } // stack effect: ( -- __0) @@ -3173,9 +3183,6 @@ family(call, INLINE_CACHE_ENTRIES_CALL) = { CALL_NO_KW_LIST_APPEND, CALL_NO_KW_METHOD_DESCRIPTOR_FAST, CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS, CALL_NO_KW_METHOD_DESCRIPTOR_O, CALL_NO_KW_STR_1, CALL_NO_KW_TUPLE_1, CALL_NO_KW_TYPE_1 }; -family(for_iter, INLINE_CACHE_ENTRIES_FOR_ITER) = { - FOR_ITER, FOR_ITER_LIST, - FOR_ITER_RANGE }; family(store_fast) = { STORE_FAST, STORE_FAST__LOAD_FAST, STORE_FAST__STORE_FAST }; family(unpack_sequence, INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE) = { UNPACK_SEQUENCE, UNPACK_SEQUENCE_LIST, diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index e524bfcb99d470..6332d3c41dd3a4 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -2619,25 +2619,22 @@ TARGET(FOR_ITER) { PREDICTED(FOR_ITER); + PyObject *iter = PEEK(1); + PyObject *next; #if ENABLE_SPECIALIZATION _PyForIterCache *cache = (_PyForIterCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) { assert(cframe.use_tracing == 0); next_instr--; - _Py_Specialize_ForIter(TOP(), next_instr, oparg); + _Py_Specialize_ForIter(iter, next_instr, oparg); DISPATCH_SAME_OPARG(); } STAT_INC(FOR_ITER, deferred); DECREMENT_ADAPTIVE_COUNTER(cache->counter); #endif /* ENABLE_SPECIALIZATION */ - /* before: [iter]; after: [iter, iter()] *or* [] */ - PyObject *iter = TOP(); - PyObject *next = (*Py_TYPE(iter)->tp_iternext)(iter); - if (next != NULL) { - PUSH(next); - JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER); - } - else { + /* before: [iter]; after: [iter, iter()] *or* [] (and jump an extra instr.) */ + next = (*Py_TYPE(iter)->tp_iternext)(iter); + if (next == NULL) { if (_PyErr_Occurred(tstate)) { if (!_PyErr_ExceptionMatches(tstate, PyExc_StopIteration)) { goto error; @@ -2649,11 +2646,16 @@ } /* iterator ended normally */ assert(_Py_OPCODE(next_instr[INLINE_CACHE_ENTRIES_FOR_ITER + oparg]) == END_FOR); - STACK_SHRINK(1); Py_DECREF(iter); - /* Skip END_FOR */ + STACK_SHRINK(1); + /* Jump forward oparg, then skip following END_FOR instruction */ JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + oparg + 1); + DISPATCH(); } + // Common case: no jump, leave it to the code generator + STACK_GROW(1); + POKE(1, next); + JUMPBY(1); DISPATCH(); } diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index 857526c35aa5b6..bea95e3228da21 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -261,7 +261,7 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) { case GET_YIELD_FROM_ITER: return 1; case FOR_ITER: - return -1; + return 1; case FOR_ITER_LIST: return -1; case FOR_ITER_TUPLE: @@ -607,7 +607,7 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) { case GET_YIELD_FROM_ITER: return 1; case FOR_ITER: - return -1; + return 2; case FOR_ITER_LIST: return -1; case FOR_ITER_TUPLE: @@ -829,7 +829,7 @@ struct opcode_metadata { [MATCH_KEYS] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [GET_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [GET_YIELD_FROM_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, - [FOR_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [FOR_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, [FOR_ITER_LIST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [FOR_ITER_TUPLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [FOR_ITER_RANGE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, From bd65e1cd9473064f266b04223b5981a5f3bc7dd5 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Mon, 6 Feb 2023 14:50:03 -0800 Subject: [PATCH 2/7] Modernize FOR_ITER_LIST --- Python/bytecodes.c | 24 ++++++++++++------------ Python/generated_cases.c.h | 19 +++++++++++++------ Python/opcode_metadata.h | 6 +++--- 3 files changed, 28 insertions(+), 21 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index d1f068491c2ef9..eb2a2bc249daf4 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2072,13 +2072,13 @@ dummy_func( // This is optimized by skipping that instruction and combining // its effect (popping 'iter' instead of pushing 'next'.) - // family(for_iter, INLINE_CACHE_ENTRIES_FOR_ITER) = { - // FOR_ITER, - // FOR_ITER_LIST, + family(for_iter, INLINE_CACHE_ENTRIES_FOR_ITER) = { + FOR_ITER, + FOR_ITER_LIST, // FOR_ITER_TUPLE, // FOR_ITER_RANGE, // FOR_ITER_GEN, - // }; + }; inst(FOR_ITER, (unused/1, iter -- iter, next)) { #if ENABLE_SPECIALIZATION @@ -2115,27 +2115,27 @@ dummy_func( // Common case: no jump, leave it to the code generator } - // stack effect: ( -- __0) - inst(FOR_ITER_LIST) { + inst(FOR_ITER_LIST, (unused/1, iter -- iter, next)) { assert(cframe.use_tracing == 0); - _PyListIterObject *it = (_PyListIterObject *)TOP(); - DEOPT_IF(Py_TYPE(it) != &PyListIter_Type, FOR_ITER); + DEOPT_IF(Py_TYPE(iter) != &PyListIter_Type, FOR_ITER); + _PyListIterObject *it = (_PyListIterObject *)iter; STAT_INC(FOR_ITER, hit); PyListObject *seq = it->it_seq; if (seq) { if (it->it_index < PyList_GET_SIZE(seq)) { - PyObject *next = PyList_GET_ITEM(seq, it->it_index++); - PUSH(Py_NewRef(next)); - JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER); + next = Py_NewRef(PyList_GET_ITEM(seq, it->it_index++)); goto end_for_iter_list; // End of this instruction } it->it_seq = NULL; Py_DECREF(seq); } + Py_DECREF(iter); STACK_SHRINK(1); - Py_DECREF(it); + /* Jump forward oparg, then skip following END_FOR instruction */ JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + oparg + 1); + DISPATCH(); end_for_iter_list: + // Common case: no jump, leave it to the code generator } // stack effect: ( -- __0) diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 6332d3c41dd3a4..386d49a71f291e 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -2619,6 +2619,7 @@ TARGET(FOR_ITER) { PREDICTED(FOR_ITER); + static_assert(INLINE_CACHE_ENTRIES_FOR_ITER == 1, "incorrect cache size"); PyObject *iter = PEEK(1); PyObject *next; #if ENABLE_SPECIALIZATION @@ -2660,25 +2661,31 @@ } TARGET(FOR_ITER_LIST) { + PyObject *iter = PEEK(1); + PyObject *next; assert(cframe.use_tracing == 0); - _PyListIterObject *it = (_PyListIterObject *)TOP(); - DEOPT_IF(Py_TYPE(it) != &PyListIter_Type, FOR_ITER); + DEOPT_IF(Py_TYPE(iter) != &PyListIter_Type, FOR_ITER); + _PyListIterObject *it = (_PyListIterObject *)iter; STAT_INC(FOR_ITER, hit); PyListObject *seq = it->it_seq; if (seq) { if (it->it_index < PyList_GET_SIZE(seq)) { - PyObject *next = PyList_GET_ITEM(seq, it->it_index++); - PUSH(Py_NewRef(next)); - JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER); + next = Py_NewRef(PyList_GET_ITEM(seq, it->it_index++)); goto end_for_iter_list; // End of this instruction } it->it_seq = NULL; Py_DECREF(seq); } + Py_DECREF(iter); STACK_SHRINK(1); - Py_DECREF(it); + /* Jump forward oparg, then skip following END_FOR instruction */ JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + oparg + 1); + DISPATCH(); end_for_iter_list: + // Common case: no jump, leave it to the code generator + STACK_GROW(1); + POKE(1, next); + JUMPBY(1); DISPATCH(); } diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index bea95e3228da21..197085ac0213ab 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -263,7 +263,7 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) { case FOR_ITER: return 1; case FOR_ITER_LIST: - return -1; + return 1; case FOR_ITER_TUPLE: return -1; case FOR_ITER_RANGE: @@ -609,7 +609,7 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) { case FOR_ITER: return 2; case FOR_ITER_LIST: - return -1; + return 2; case FOR_ITER_TUPLE: return -1; case FOR_ITER_RANGE: @@ -830,7 +830,7 @@ struct opcode_metadata { [GET_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [GET_YIELD_FROM_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [FOR_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, - [FOR_ITER_LIST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [FOR_ITER_LIST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, [FOR_ITER_TUPLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [FOR_ITER_RANGE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [FOR_ITER_GEN] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, From 872e156ddad51c3ad529c49bcadf2ce6814d0d9d Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Mon, 6 Feb 2023 14:54:11 -0800 Subject: [PATCH 3/7] Modernize FOR_ITER_TUPLE --- Python/bytecodes.c | 16 ++++++++-------- Python/generated_cases.c.h | 16 +++++++++++----- Python/opcode_metadata.h | 6 +++--- 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index eb2a2bc249daf4..36ab46d80304cf 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2075,7 +2075,7 @@ dummy_func( family(for_iter, INLINE_CACHE_ENTRIES_FOR_ITER) = { FOR_ITER, FOR_ITER_LIST, - // FOR_ITER_TUPLE, + FOR_ITER_TUPLE, // FOR_ITER_RANGE, // FOR_ITER_GEN, }; @@ -2138,27 +2138,27 @@ dummy_func( // Common case: no jump, leave it to the code generator } - // stack effect: ( -- __0) - inst(FOR_ITER_TUPLE) { + inst(FOR_ITER_TUPLE, (unused/1, iter -- iter, next)) { assert(cframe.use_tracing == 0); - _PyTupleIterObject *it = (_PyTupleIterObject *)TOP(); + _PyTupleIterObject *it = (_PyTupleIterObject *)iter; DEOPT_IF(Py_TYPE(it) != &PyTupleIter_Type, FOR_ITER); STAT_INC(FOR_ITER, hit); PyTupleObject *seq = it->it_seq; if (seq) { if (it->it_index < PyTuple_GET_SIZE(seq)) { - PyObject *next = PyTuple_GET_ITEM(seq, it->it_index++); - PUSH(Py_NewRef(next)); - JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER); + next = Py_NewRef(PyTuple_GET_ITEM(seq, it->it_index++)); goto end_for_iter_tuple; // End of this instruction } it->it_seq = NULL; Py_DECREF(seq); } + Py_DECREF(iter); STACK_SHRINK(1); - Py_DECREF(it); + /* Jump forward oparg, then skip following END_FOR instruction */ JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + oparg + 1); + DISPATCH(); end_for_iter_tuple: + // Common case: no jump, leave it to the code generator } // stack effect: ( -- __0) diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 386d49a71f291e..392327d3249492 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -2690,25 +2690,31 @@ } TARGET(FOR_ITER_TUPLE) { + PyObject *iter = PEEK(1); + PyObject *next; assert(cframe.use_tracing == 0); - _PyTupleIterObject *it = (_PyTupleIterObject *)TOP(); + _PyTupleIterObject *it = (_PyTupleIterObject *)iter; DEOPT_IF(Py_TYPE(it) != &PyTupleIter_Type, FOR_ITER); STAT_INC(FOR_ITER, hit); PyTupleObject *seq = it->it_seq; if (seq) { if (it->it_index < PyTuple_GET_SIZE(seq)) { - PyObject *next = PyTuple_GET_ITEM(seq, it->it_index++); - PUSH(Py_NewRef(next)); - JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER); + next = Py_NewRef(PyTuple_GET_ITEM(seq, it->it_index++)); goto end_for_iter_tuple; // End of this instruction } it->it_seq = NULL; Py_DECREF(seq); } + Py_DECREF(iter); STACK_SHRINK(1); - Py_DECREF(it); + /* Jump forward oparg, then skip following END_FOR instruction */ JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + oparg + 1); + DISPATCH(); end_for_iter_tuple: + // Common case: no jump, leave it to the code generator + STACK_GROW(1); + POKE(1, next); + JUMPBY(1); DISPATCH(); } diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index 197085ac0213ab..4c12aff2fbb7cc 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -265,7 +265,7 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) { case FOR_ITER_LIST: return 1; case FOR_ITER_TUPLE: - return -1; + return 1; case FOR_ITER_RANGE: return -1; case FOR_ITER_GEN: @@ -611,7 +611,7 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) { case FOR_ITER_LIST: return 2; case FOR_ITER_TUPLE: - return -1; + return 2; case FOR_ITER_RANGE: return -1; case FOR_ITER_GEN: @@ -831,7 +831,7 @@ struct opcode_metadata { [GET_YIELD_FROM_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [FOR_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, [FOR_ITER_LIST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, - [FOR_ITER_TUPLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [FOR_ITER_TUPLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, [FOR_ITER_RANGE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [FOR_ITER_GEN] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [BEFORE_ASYNC_WITH] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, From a52a79d2fdd17333400b1b64f5586355c7375e5c Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Mon, 6 Feb 2023 16:06:06 -0800 Subject: [PATCH 4/7] Modernize FOR_ITER_RANGE (even weirder) --- Python/bytecodes.c | 11 +++++++---- Python/generated_cases.c.h | 4 +++- Python/opcode_metadata.h | 6 +++--- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 36ab46d80304cf..eafd99b75ac38c 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2076,7 +2076,7 @@ dummy_func( FOR_ITER, FOR_ITER_LIST, FOR_ITER_TUPLE, - // FOR_ITER_RANGE, + FOR_ITER_RANGE, // FOR_ITER_GEN, }; @@ -2161,10 +2161,11 @@ dummy_func( // Common case: no jump, leave it to the code generator } - // stack effect: ( -- __0) - inst(FOR_ITER_RANGE) { + // This is slightly different, when the loop isn't terminated we + // jump over the immediately following STORE_FAST instruction. + inst(FOR_ITER_RANGE, (unused/1, iter -- iter, unused)) { assert(cframe.use_tracing == 0); - _PyRangeIterObject *r = (_PyRangeIterObject *)TOP(); + _PyRangeIterObject *r = (_PyRangeIterObject *)iter; DEOPT_IF(Py_TYPE(r) != &PyRangeIter_Type, FOR_ITER); STAT_INC(FOR_ITER, hit); _Py_CODEUNIT next = next_instr[INLINE_CACHE_ENTRIES_FOR_ITER]; @@ -2172,6 +2173,7 @@ dummy_func( if (r->len <= 0) { STACK_SHRINK(1); Py_DECREF(r); + // Jump over END_FOR instruction. JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + oparg + 1); } else { @@ -2184,6 +2186,7 @@ dummy_func( // The STORE_FAST is already done. JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + 1); } + DISPATCH(); } inst(FOR_ITER_GEN) { diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 392327d3249492..546bf03fcfde57 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -2719,8 +2719,9 @@ } TARGET(FOR_ITER_RANGE) { + PyObject *iter = PEEK(1); assert(cframe.use_tracing == 0); - _PyRangeIterObject *r = (_PyRangeIterObject *)TOP(); + _PyRangeIterObject *r = (_PyRangeIterObject *)iter; DEOPT_IF(Py_TYPE(r) != &PyRangeIter_Type, FOR_ITER); STAT_INC(FOR_ITER, hit); _Py_CODEUNIT next = next_instr[INLINE_CACHE_ENTRIES_FOR_ITER]; @@ -2728,6 +2729,7 @@ if (r->len <= 0) { STACK_SHRINK(1); Py_DECREF(r); + // Jump over END_FOR instruction. JUMPBY(INLINE_CACHE_ENTRIES_FOR_ITER + oparg + 1); } else { diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index 4c12aff2fbb7cc..4a28b1149924f7 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -267,7 +267,7 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) { case FOR_ITER_TUPLE: return 1; case FOR_ITER_RANGE: - return -1; + return 1; case FOR_ITER_GEN: return -1; case BEFORE_ASYNC_WITH: @@ -613,7 +613,7 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) { case FOR_ITER_TUPLE: return 2; case FOR_ITER_RANGE: - return -1; + return 2; case FOR_ITER_GEN: return -1; case BEFORE_ASYNC_WITH: @@ -832,7 +832,7 @@ struct opcode_metadata { [FOR_ITER] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, [FOR_ITER_LIST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, [FOR_ITER_TUPLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, - [FOR_ITER_RANGE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [FOR_ITER_RANGE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, [FOR_ITER_GEN] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [BEFORE_ASYNC_WITH] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [BEFORE_WITH] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, From 53cfd22274b48d8ab1e81ee1fe19058e0d014261 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Mon, 6 Feb 2023 16:21:40 -0800 Subject: [PATCH 5/7] Modernize FOR_ITER_GEN --- Python/bytecodes.c | 7 ++++--- Python/generated_cases.c.h | 3 ++- Python/opcode_metadata.h | 6 +++--- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index eafd99b75ac38c..f1c6e2a818f44e 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2077,7 +2077,7 @@ dummy_func( FOR_ITER_LIST, FOR_ITER_TUPLE, FOR_ITER_RANGE, - // FOR_ITER_GEN, + FOR_ITER_GEN, }; inst(FOR_ITER, (unused/1, iter -- iter, next)) { @@ -2189,9 +2189,10 @@ dummy_func( DISPATCH(); } - inst(FOR_ITER_GEN) { + // This is *not* a super-instruction, unique in the family. + inst(FOR_ITER_GEN, (unused/1, iter -- iter, unused)) { assert(cframe.use_tracing == 0); - PyGenObject *gen = (PyGenObject *)TOP(); + PyGenObject *gen = (PyGenObject *)iter; DEOPT_IF(Py_TYPE(gen) != &PyGen_Type, FOR_ITER); DEOPT_IF(gen->gi_frame_state >= FRAME_EXECUTING, FOR_ITER); STAT_INC(FOR_ITER, hit); diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 546bf03fcfde57..bb05dc54bf0a90 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -2746,8 +2746,9 @@ } TARGET(FOR_ITER_GEN) { + PyObject *iter = PEEK(1); assert(cframe.use_tracing == 0); - PyGenObject *gen = (PyGenObject *)TOP(); + PyGenObject *gen = (PyGenObject *)iter; DEOPT_IF(Py_TYPE(gen) != &PyGen_Type, FOR_ITER); DEOPT_IF(gen->gi_frame_state >= FRAME_EXECUTING, FOR_ITER); STAT_INC(FOR_ITER, hit); diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index 4a28b1149924f7..9ccd79dffb40b4 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -269,7 +269,7 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) { case FOR_ITER_RANGE: return 1; case FOR_ITER_GEN: - return -1; + return 1; case BEFORE_ASYNC_WITH: return 1; case BEFORE_WITH: @@ -615,7 +615,7 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) { case FOR_ITER_RANGE: return 2; case FOR_ITER_GEN: - return -1; + return 2; case BEFORE_ASYNC_WITH: return 2; case BEFORE_WITH: @@ -833,7 +833,7 @@ struct opcode_metadata { [FOR_ITER_LIST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, [FOR_ITER_TUPLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, [FOR_ITER_RANGE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, - [FOR_ITER_GEN] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [FOR_ITER_GEN] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC }, [BEFORE_ASYNC_WITH] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [BEFORE_WITH] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [WITH_EXCEPT_START] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, From 76a4a7b26717f1a4fefcda2be835822d4de3c51f Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 7 Feb 2023 07:11:58 -0800 Subject: [PATCH 6/7] Accept comment suggestion Co-authored-by: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> --- Python/bytecodes.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index f1c6e2a818f44e..e27bd92eee63d9 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2092,7 +2092,7 @@ dummy_func( STAT_INC(FOR_ITER, deferred); DECREMENT_ADAPTIVE_COUNTER(cache->counter); #endif /* ENABLE_SPECIALIZATION */ - /* before: [iter]; after: [iter, iter()] *or* [] (and jump an extra instr.) */ + /* before: [iter]; after: [iter, iter()] *or* [] (and jump over END_FOR.) */ next = (*Py_TYPE(iter)->tp_iternext)(iter); if (next == NULL) { if (_PyErr_Occurred(tstate)) { From b39cf9041d30f56fb2fe1764fe0837d630cbebf8 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 7 Feb 2023 07:53:58 -0800 Subject: [PATCH 7/7] Update generated file --- Python/generated_cases.c.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index bb05dc54bf0a90..9feb14986e5b2a 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -2633,7 +2633,7 @@ STAT_INC(FOR_ITER, deferred); DECREMENT_ADAPTIVE_COUNTER(cache->counter); #endif /* ENABLE_SPECIALIZATION */ - /* before: [iter]; after: [iter, iter()] *or* [] (and jump an extra instr.) */ + /* before: [iter]; after: [iter, iter()] *or* [] (and jump over END_FOR.) */ next = (*Py_TYPE(iter)->tp_iternext)(iter); if (next == NULL) { if (_PyErr_Occurred(tstate)) {