mirror of
https://github.com/python/cpython.git
synced 2025-07-07 19:35:27 +00:00
GH-132554: "Virtual" iterators (GH-132555)
* FOR_ITER now pushes either the iterator and NULL or leaves the iterable and pushes tagged zero * NEXT_ITER uses the tagged int as the index into the sequence or, if TOS is NULL, iterates as before.
This commit is contained in:
parent
9300a596d3
commit
f6f4e8a662
25 changed files with 713 additions and 618 deletions
|
@ -353,6 +353,8 @@ PyAPI_FUNC(_PyStackRef) _PyFloat_FromDouble_ConsumeInputs(_PyStackRef left, _PyS
|
|||
extern int _PyRunRemoteDebugger(PyThreadState *tstate);
|
||||
#endif
|
||||
|
||||
_PyStackRef _PyForIter_NextWithIndex(PyObject *seq, _PyStackRef index);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -313,7 +313,7 @@ extern void _Py_Specialize_CompareOp(_PyStackRef lhs, _PyStackRef rhs,
|
|||
_Py_CODEUNIT *instr, int oparg);
|
||||
extern void _Py_Specialize_UnpackSequence(_PyStackRef seq, _Py_CODEUNIT *instr,
|
||||
int oparg);
|
||||
extern void _Py_Specialize_ForIter(_PyStackRef iter, _Py_CODEUNIT *instr, int oparg);
|
||||
extern void _Py_Specialize_ForIter(_PyStackRef iter, _PyStackRef null_or_index, _Py_CODEUNIT *instr, int oparg);
|
||||
extern void _Py_Specialize_Send(_PyStackRef receiver, _Py_CODEUNIT *instr);
|
||||
extern void _Py_Specialize_ToBool(_PyStackRef value, _Py_CODEUNIT *instr);
|
||||
extern void _Py_Specialize_ContainsOp(_PyStackRef value, _Py_CODEUNIT *instr);
|
||||
|
|
|
@ -95,6 +95,7 @@ typedef enum {
|
|||
enum _PyCompile_FBlockType {
|
||||
COMPILE_FBLOCK_WHILE_LOOP,
|
||||
COMPILE_FBLOCK_FOR_LOOP,
|
||||
COMPILE_FBLOCK_ASYNC_FOR_LOOP,
|
||||
COMPILE_FBLOCK_TRY_EXCEPT,
|
||||
COMPILE_FBLOCK_FINALLY_TRY,
|
||||
COMPILE_FBLOCK_FINALLY_END,
|
||||
|
|
|
@ -279,6 +279,8 @@ Known values:
|
|||
Python 3.14b1 3624 (Don't optimize LOAD_FAST when local is killed by DELETE_FAST)
|
||||
Python 3.15a0 3650 (Initial version)
|
||||
Python 3.15a1 3651 (Simplify LOAD_CONST)
|
||||
Python 3.15a1 3652 (Virtual iterators)
|
||||
|
||||
|
||||
Python 3.16 will start with 3700
|
||||
|
||||
|
@ -291,7 +293,7 @@ PC/launcher.c must also be updated.
|
|||
|
||||
*/
|
||||
|
||||
#define PYC_MAGIC_NUMBER 3651
|
||||
#define PYC_MAGIC_NUMBER 3652
|
||||
/* This is equivalent to converting PYC_MAGIC_NUMBER to 2 bytes
|
||||
(little-endian) and then appending b'\r\n'. */
|
||||
#define PYC_MAGIC_NUMBER_TOKEN \
|
||||
|
|
40
Include/internal/pycore_opcode_metadata.h
generated
40
Include/internal/pycore_opcode_metadata.h
generated
|
@ -205,15 +205,15 @@ int _PyOpcode_num_popped(int opcode, int oparg) {
|
|||
case FORMAT_WITH_SPEC:
|
||||
return 2;
|
||||
case FOR_ITER:
|
||||
return 1;
|
||||
return 2;
|
||||
case FOR_ITER_GEN:
|
||||
return 1;
|
||||
return 2;
|
||||
case FOR_ITER_LIST:
|
||||
return 1;
|
||||
return 2;
|
||||
case FOR_ITER_RANGE:
|
||||
return 1;
|
||||
return 2;
|
||||
case FOR_ITER_TUPLE:
|
||||
return 1;
|
||||
return 2;
|
||||
case GET_AITER:
|
||||
return 1;
|
||||
case GET_ANEXT:
|
||||
|
@ -239,11 +239,11 @@ int _PyOpcode_num_popped(int opcode, int oparg) {
|
|||
case INSTRUMENTED_END_ASYNC_FOR:
|
||||
return 2;
|
||||
case INSTRUMENTED_END_FOR:
|
||||
return 2;
|
||||
return 3;
|
||||
case INSTRUMENTED_END_SEND:
|
||||
return 2;
|
||||
case INSTRUMENTED_FOR_ITER:
|
||||
return 1;
|
||||
return 2;
|
||||
case INSTRUMENTED_INSTRUCTION:
|
||||
return 0;
|
||||
case INSTRUMENTED_JUMP_BACKWARD:
|
||||
|
@ -257,7 +257,7 @@ int _PyOpcode_num_popped(int opcode, int oparg) {
|
|||
case INSTRUMENTED_NOT_TAKEN:
|
||||
return 0;
|
||||
case INSTRUMENTED_POP_ITER:
|
||||
return 1;
|
||||
return 2;
|
||||
case INSTRUMENTED_POP_JUMP_IF_FALSE:
|
||||
return 1;
|
||||
case INSTRUMENTED_POP_JUMP_IF_NONE:
|
||||
|
@ -395,7 +395,7 @@ int _PyOpcode_num_popped(int opcode, int oparg) {
|
|||
case POP_EXCEPT:
|
||||
return 1;
|
||||
case POP_ITER:
|
||||
return 1;
|
||||
return 2;
|
||||
case POP_JUMP_IF_FALSE:
|
||||
return 1;
|
||||
case POP_JUMP_IF_NONE:
|
||||
|
@ -688,15 +688,15 @@ int _PyOpcode_num_pushed(int opcode, int oparg) {
|
|||
case FORMAT_WITH_SPEC:
|
||||
return 1;
|
||||
case FOR_ITER:
|
||||
return 2;
|
||||
return 3;
|
||||
case FOR_ITER_GEN:
|
||||
return 1;
|
||||
return 2;
|
||||
case FOR_ITER_LIST:
|
||||
return 2;
|
||||
return 3;
|
||||
case FOR_ITER_RANGE:
|
||||
return 2;
|
||||
return 3;
|
||||
case FOR_ITER_TUPLE:
|
||||
return 2;
|
||||
return 3;
|
||||
case GET_AITER:
|
||||
return 1;
|
||||
case GET_ANEXT:
|
||||
|
@ -704,7 +704,7 @@ int _PyOpcode_num_pushed(int opcode, int oparg) {
|
|||
case GET_AWAITABLE:
|
||||
return 1;
|
||||
case GET_ITER:
|
||||
return 1;
|
||||
return 2;
|
||||
case GET_LEN:
|
||||
return 2;
|
||||
case GET_YIELD_FROM_ITER:
|
||||
|
@ -722,11 +722,11 @@ int _PyOpcode_num_pushed(int opcode, int oparg) {
|
|||
case INSTRUMENTED_END_ASYNC_FOR:
|
||||
return 0;
|
||||
case INSTRUMENTED_END_FOR:
|
||||
return 1;
|
||||
return 2;
|
||||
case INSTRUMENTED_END_SEND:
|
||||
return 1;
|
||||
case INSTRUMENTED_FOR_ITER:
|
||||
return 2;
|
||||
return 3;
|
||||
case INSTRUMENTED_INSTRUCTION:
|
||||
return 0;
|
||||
case INSTRUMENTED_JUMP_BACKWARD:
|
||||
|
@ -1157,7 +1157,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[267] = {
|
|||
[FOR_ITER_GEN] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_DEOPT_FLAG },
|
||||
[FOR_ITER_LIST] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG | HAS_ESCAPES_FLAG },
|
||||
[FOR_ITER_RANGE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG },
|
||||
[FOR_ITER_TUPLE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_EXIT_FLAG | HAS_ESCAPES_FLAG },
|
||||
[FOR_ITER_TUPLE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_EXIT_FLAG },
|
||||
[GET_AITER] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
|
||||
[GET_ANEXT] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG },
|
||||
[GET_AWAITABLE] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
|
||||
|
@ -1242,7 +1242,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[267] = {
|
|||
[NOP] = { true, INSTR_FMT_IX, HAS_PURE_FLAG },
|
||||
[NOT_TAKEN] = { true, INSTR_FMT_IX, HAS_PURE_FLAG },
|
||||
[POP_EXCEPT] = { true, INSTR_FMT_IX, HAS_ESCAPES_FLAG },
|
||||
[POP_ITER] = { true, INSTR_FMT_IX, HAS_ESCAPES_FLAG | HAS_PURE_FLAG },
|
||||
[POP_ITER] = { true, INSTR_FMT_IX, HAS_ESCAPES_FLAG },
|
||||
[POP_JUMP_IF_FALSE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG },
|
||||
[POP_JUMP_IF_NONE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_ESCAPES_FLAG },
|
||||
[POP_JUMP_IF_NOT_NONE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_ESCAPES_FLAG },
|
||||
|
@ -1453,7 +1453,7 @@ _PyOpcode_macro_expansion[256] = {
|
|||
[NOP] = { .nuops = 1, .uops = { { _NOP, OPARG_SIMPLE, 0 } } },
|
||||
[NOT_TAKEN] = { .nuops = 1, .uops = { { _NOP, OPARG_SIMPLE, 0 } } },
|
||||
[POP_EXCEPT] = { .nuops = 1, .uops = { { _POP_EXCEPT, OPARG_SIMPLE, 0 } } },
|
||||
[POP_ITER] = { .nuops = 1, .uops = { { _POP_TOP, OPARG_SIMPLE, 0 } } },
|
||||
[POP_ITER] = { .nuops = 1, .uops = { { _POP_ITER, OPARG_SIMPLE, 0 } } },
|
||||
[POP_JUMP_IF_FALSE] = { .nuops = 1, .uops = { { _POP_JUMP_IF_FALSE, OPARG_REPLACED, 1 } } },
|
||||
[POP_JUMP_IF_NONE] = { .nuops = 2, .uops = { { _IS_NONE, OPARG_SIMPLE, 1 }, { _POP_JUMP_IF_TRUE, OPARG_REPLACED, 1 } } },
|
||||
[POP_JUMP_IF_NOT_NONE] = { .nuops = 2, .uops = { { _IS_NONE, OPARG_SIMPLE, 1 }, { _POP_JUMP_IF_FALSE, OPARG_REPLACED, 1 } } },
|
||||
|
|
|
@ -232,6 +232,9 @@ extern intptr_t PyStackRef_UntagInt(_PyStackRef ref);
|
|||
|
||||
extern _PyStackRef PyStackRef_TagInt(intptr_t i);
|
||||
|
||||
/* Increments a tagged int, but does not check for overflow */
|
||||
extern _PyStackRef PyStackRef_IncrementTaggedIntNoOverflow(_PyStackRef ref);
|
||||
|
||||
extern bool
|
||||
PyStackRef_IsNullOrInt(_PyStackRef ref);
|
||||
|
||||
|
@ -239,11 +242,12 @@ PyStackRef_IsNullOrInt(_PyStackRef ref);
|
|||
|
||||
#define Py_INT_TAG 3
|
||||
#define Py_TAG_REFCNT 1
|
||||
#define Py_TAG_BITS 3
|
||||
|
||||
static inline bool
|
||||
PyStackRef_IsTaggedInt(_PyStackRef i)
|
||||
{
|
||||
return (i.bits & Py_INT_TAG) == Py_INT_TAG;
|
||||
return (i.bits & Py_TAG_BITS) == Py_INT_TAG;
|
||||
}
|
||||
|
||||
static inline _PyStackRef
|
||||
|
@ -262,12 +266,21 @@ PyStackRef_UntagInt(_PyStackRef i)
|
|||
}
|
||||
|
||||
|
||||
static inline _PyStackRef
|
||||
PyStackRef_IncrementTaggedIntNoOverflow(_PyStackRef ref)
|
||||
{
|
||||
assert((ref.bits & Py_TAG_BITS) == Py_INT_TAG); // Is tagged int
|
||||
assert((ref.bits & (~Py_TAG_BITS)) != (INT_MAX & (~Py_TAG_BITS))); // Isn't about to overflow
|
||||
return (_PyStackRef){ .bits = ref.bits + 4 };
|
||||
}
|
||||
|
||||
#define PyStackRef_IsDeferredOrTaggedInt(ref) (((ref).bits & Py_TAG_REFCNT) != 0)
|
||||
|
||||
#ifdef Py_GIL_DISABLED
|
||||
|
||||
#define Py_TAG_DEFERRED Py_TAG_REFCNT
|
||||
|
||||
#define Py_TAG_PTR ((uintptr_t)0)
|
||||
#define Py_TAG_BITS ((uintptr_t)1)
|
||||
|
||||
|
||||
static const _PyStackRef PyStackRef_NULL = { .bits = Py_TAG_DEFERRED};
|
||||
|
@ -379,7 +392,7 @@ PyStackRef_FromPyObjectBorrow(PyObject *obj)
|
|||
do { \
|
||||
_PyStackRef _close_tmp = (REF); \
|
||||
assert(!PyStackRef_IsNull(_close_tmp)); \
|
||||
if (!PyStackRef_IsDeferred(_close_tmp)) { \
|
||||
if (!PyStackRef_IsDeferredOrTaggedInt(_close_tmp)) { \
|
||||
Py_DECREF(PyStackRef_AsPyObjectBorrow(_close_tmp)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
@ -395,7 +408,7 @@ static inline _PyStackRef
|
|||
PyStackRef_DUP(_PyStackRef stackref)
|
||||
{
|
||||
assert(!PyStackRef_IsNull(stackref));
|
||||
if (PyStackRef_IsDeferred(stackref)) {
|
||||
if (PyStackRef_IsDeferredOrTaggedInt(stackref)) {
|
||||
return stackref;
|
||||
}
|
||||
Py_INCREF(PyStackRef_AsPyObjectBorrow(stackref));
|
||||
|
@ -442,7 +455,6 @@ PyStackRef_AsStrongReference(_PyStackRef stackref)
|
|||
/* References to immortal objects always have their tag bit set to Py_TAG_REFCNT
|
||||
* as they can (must) have their reclamation deferred */
|
||||
|
||||
#define Py_TAG_BITS 3
|
||||
#if _Py_IMMORTAL_FLAGS != Py_TAG_REFCNT
|
||||
# error "_Py_IMMORTAL_FLAGS != Py_TAG_REFCNT"
|
||||
#endif
|
||||
|
@ -678,7 +690,13 @@ PyStackRef_XCLOSE(_PyStackRef ref)
|
|||
|
||||
#endif // !defined(Py_GIL_DISABLED) && defined(Py_STACKREF_DEBUG)
|
||||
|
||||
#define PyStackRef_TYPE(stackref) Py_TYPE(PyStackRef_AsPyObjectBorrow(stackref))
|
||||
static inline PyTypeObject *
|
||||
PyStackRef_TYPE(_PyStackRef stackref) {
|
||||
if (PyStackRef_IsTaggedInt(stackref)) {
|
||||
return &PyLong_Type;
|
||||
}
|
||||
return Py_TYPE(PyStackRef_AsPyObjectBorrow(stackref));
|
||||
}
|
||||
|
||||
// Converts a PyStackRef back to a PyObject *, converting the
|
||||
// stackref to a new reference.
|
||||
|
@ -686,42 +704,30 @@ PyStackRef_XCLOSE(_PyStackRef ref)
|
|||
|
||||
// StackRef type checks
|
||||
|
||||
static inline bool
|
||||
PyStackRef_GenCheck(_PyStackRef stackref)
|
||||
{
|
||||
return PyGen_Check(PyStackRef_AsPyObjectBorrow(stackref));
|
||||
}
|
||||
#define STACKREF_CHECK_FUNC(T) \
|
||||
static inline bool \
|
||||
PyStackRef_ ## T ## Check(_PyStackRef stackref) { \
|
||||
if (PyStackRef_IsTaggedInt(stackref)) { \
|
||||
return false; \
|
||||
} \
|
||||
return Py ## T ## _Check(PyStackRef_AsPyObjectBorrow(stackref)); \
|
||||
}
|
||||
|
||||
static inline bool
|
||||
PyStackRef_BoolCheck(_PyStackRef stackref)
|
||||
{
|
||||
return PyBool_Check(PyStackRef_AsPyObjectBorrow(stackref));
|
||||
}
|
||||
STACKREF_CHECK_FUNC(Gen)
|
||||
STACKREF_CHECK_FUNC(Bool)
|
||||
STACKREF_CHECK_FUNC(ExceptionInstance)
|
||||
STACKREF_CHECK_FUNC(Code)
|
||||
STACKREF_CHECK_FUNC(Function)
|
||||
|
||||
static inline bool
|
||||
PyStackRef_LongCheck(_PyStackRef stackref)
|
||||
{
|
||||
if (PyStackRef_IsTaggedInt(stackref)) {
|
||||
return true;
|
||||
}
|
||||
return PyLong_Check(PyStackRef_AsPyObjectBorrow(stackref));
|
||||
}
|
||||
|
||||
static inline bool
|
||||
PyStackRef_ExceptionInstanceCheck(_PyStackRef stackref)
|
||||
{
|
||||
return PyExceptionInstance_Check(PyStackRef_AsPyObjectBorrow(stackref));
|
||||
}
|
||||
|
||||
static inline bool
|
||||
PyStackRef_CodeCheck(_PyStackRef stackref)
|
||||
{
|
||||
return PyCode_Check(PyStackRef_AsPyObjectBorrow(stackref));
|
||||
}
|
||||
|
||||
static inline bool
|
||||
PyStackRef_FunctionCheck(_PyStackRef stackref)
|
||||
{
|
||||
return PyFunction_Check(PyStackRef_AsPyObjectBorrow(stackref));
|
||||
}
|
||||
|
||||
static inline void
|
||||
_PyThreadState_PushCStackRef(PyThreadState *tstate, _PyCStackRef *ref)
|
||||
{
|
||||
|
|
1
Include/internal/pycore_uop_ids.h
generated
1
Include/internal/pycore_uop_ids.h
generated
|
@ -272,6 +272,7 @@ extern "C" {
|
|||
#define _POP_CALL_TWO 489
|
||||
#define _POP_CALL_TWO_LOAD_CONST_INLINE_BORROW 490
|
||||
#define _POP_EXCEPT POP_EXCEPT
|
||||
#define _POP_ITER POP_ITER
|
||||
#define _POP_JUMP_IF_FALSE 491
|
||||
#define _POP_JUMP_IF_TRUE 492
|
||||
#define _POP_TOP POP_TOP
|
||||
|
|
6
Include/internal/pycore_uop_metadata.h
generated
6
Include/internal/pycore_uop_metadata.h
generated
|
@ -66,6 +66,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = {
|
|||
[_POP_TWO] = HAS_ESCAPES_FLAG,
|
||||
[_PUSH_NULL] = HAS_PURE_FLAG,
|
||||
[_END_FOR] = HAS_ESCAPES_FLAG | HAS_NO_SAVE_IP_FLAG,
|
||||
[_POP_ITER] = HAS_ESCAPES_FLAG,
|
||||
[_END_SEND] = HAS_ESCAPES_FLAG | HAS_PURE_FLAG,
|
||||
[_UNARY_NEGATIVE] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG,
|
||||
[_UNARY_NOT] = HAS_PURE_FLAG,
|
||||
|
@ -205,7 +206,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = {
|
|||
[_FOR_ITER_TIER_TWO] = HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG,
|
||||
[_ITER_CHECK_LIST] = HAS_EXIT_FLAG,
|
||||
[_GUARD_NOT_EXHAUSTED_LIST] = HAS_EXIT_FLAG,
|
||||
[_ITER_NEXT_LIST_TIER_TWO] = HAS_EXIT_FLAG | HAS_ESCAPES_FLAG,
|
||||
[_ITER_NEXT_LIST_TIER_TWO] = HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG,
|
||||
[_ITER_CHECK_TUPLE] = HAS_EXIT_FLAG,
|
||||
[_GUARD_NOT_EXHAUSTED_TUPLE] = HAS_EXIT_FLAG,
|
||||
[_ITER_NEXT_TUPLE] = 0,
|
||||
|
@ -569,6 +570,7 @@ const char *const _PyOpcode_uop_name[MAX_UOP_ID+1] = {
|
|||
[_POP_CALL_TWO] = "_POP_CALL_TWO",
|
||||
[_POP_CALL_TWO_LOAD_CONST_INLINE_BORROW] = "_POP_CALL_TWO_LOAD_CONST_INLINE_BORROW",
|
||||
[_POP_EXCEPT] = "_POP_EXCEPT",
|
||||
[_POP_ITER] = "_POP_ITER",
|
||||
[_POP_TOP] = "_POP_TOP",
|
||||
[_POP_TOP_LOAD_CONST_INLINE] = "_POP_TOP_LOAD_CONST_INLINE",
|
||||
[_POP_TOP_LOAD_CONST_INLINE_BORROW] = "_POP_TOP_LOAD_CONST_INLINE_BORROW",
|
||||
|
@ -730,6 +732,8 @@ int _PyUop_num_popped(int opcode, int oparg)
|
|||
return 0;
|
||||
case _END_FOR:
|
||||
return 1;
|
||||
case _POP_ITER:
|
||||
return 2;
|
||||
case _END_SEND:
|
||||
return 2;
|
||||
case _UNARY_NEGATIVE:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue