diff --git a/Include/cpython/funcobject.h b/Include/cpython/funcobject.h index 9e1599a7648564..e8a985ca37a35f 100644 --- a/Include/cpython/funcobject.h +++ b/Include/cpython/funcobject.h @@ -43,6 +43,7 @@ typedef struct { PyObject *func_annotations; /* Annotations, a dict or NULL */ PyObject *func_annotate; /* Callable to fill the annotations dictionary */ PyObject *func_typeparams; /* Tuple of active type variables or NULL */ + PyObject *func_old_codes; /* List of past code objects or NULL */ vectorcallfunc vectorcall; /* Version number for use by specializer. * Can set to non-zero when we want to specialize. diff --git a/Include/internal/pycore_interpframe.h b/Include/internal/pycore_interpframe.h index 9809cd292995f0..178e83ffdec02f 100644 --- a/Include/internal/pycore_interpframe.h +++ b/Include/internal/pycore_interpframe.h @@ -18,10 +18,9 @@ extern "C" { ((int)((IF)->instr_ptr - _PyFrame_GetBytecode((IF)))) static inline PyCodeObject *_PyFrame_GetCode(_PyInterpreterFrame *f) { - assert(!PyStackRef_IsNull(f->f_executable)); - PyObject *executable = PyStackRef_AsPyObjectBorrow(f->f_executable); - assert(PyCode_Check(executable)); - return (PyCodeObject *)executable; + assert(f->f_executable != NULL); + assert(PyCode_Check(f->f_executable)); + return (PyCodeObject *)f->f_executable; } // Similar to _PyFrame_GetCode(), but return NULL if the frame is invalid or @@ -36,7 +35,7 @@ _PyFrame_SafeGetCode(_PyInterpreterFrame *f) return NULL; } - if (PyStackRef_IsNull(f->f_executable)) { + if (f->f_executable == NULL) { return NULL; } void *ptr; @@ -44,7 +43,7 @@ _PyFrame_SafeGetCode(_PyInterpreterFrame *f) if (_PyMem_IsPtrFreed(ptr)) { return NULL; } - PyObject *executable = PyStackRef_AsPyObjectBorrow(f->f_executable); + PyObject *executable = f->f_executable; if (_PyObject_IsFreed(executable)) { return NULL; } @@ -132,7 +131,7 @@ _PyFrame_NumSlotsForCodeObject(PyCodeObject *code) static inline void _PyFrame_Copy(_PyInterpreterFrame *src, _PyInterpreterFrame *dest) { - dest->f_executable = PyStackRef_MakeHeapSafe(src->f_executable); + dest->f_executable = src->f_executable; // Don't leave a dangling pointer to the old frame when creating generators // and coroutines: dest->previous = NULL; @@ -160,6 +159,15 @@ static inline void _PyFrame_Copy(_PyInterpreterFrame *src, _PyInterpreterFrame * } } +/* Generator frames need a strong reference to the code object */ +static inline void +_PyFrame_CopyForGenerators(_PyInterpreterFrame *old_frame, _PyInterpreterFrame *gen_frame) +{ + _PyFrame_Copy(old_frame, gen_frame); + gen_frame->owner = FRAME_OWNED_BY_GENERATOR; + Py_INCREF(gen_frame->f_executable); +} + #ifdef Py_GIL_DISABLED static inline void _PyFrame_InitializeTLBC(PyThreadState *tstate, _PyInterpreterFrame *frame, @@ -191,7 +199,7 @@ _PyFrame_Initialize( { frame->previous = previous; frame->f_funcobj = func; - frame->f_executable = PyStackRef_FromPyObjectNew(code); + frame->f_executable = (PyObject *)code; PyFunctionObject *func_obj = (PyFunctionObject *)PyStackRef_AsPyObjectBorrow(func); frame->f_builtins = func_obj->func_builtins; frame->f_globals = func_obj->func_globals; @@ -424,7 +432,7 @@ _PyFrame_PushTrampolineUnchecked(PyThreadState *tstate, PyCodeObject *code, int assert(tstate->datastack_top < tstate->datastack_limit); frame->previous = previous; frame->f_funcobj = PyStackRef_None; - frame->f_executable = PyStackRef_FromPyObjectNew(code); + frame->f_executable = (PyObject *)code; #ifdef Py_DEBUG frame->f_builtins = NULL; frame->f_globals = NULL; diff --git a/Include/internal/pycore_interpframe_structs.h b/Include/internal/pycore_interpframe_structs.h index 4d267e35504b94..9854141a8ad3ef 100644 --- a/Include/internal/pycore_interpframe_structs.h +++ b/Include/internal/pycore_interpframe_structs.h @@ -27,7 +27,9 @@ enum _frameowner { }; struct _PyInterpreterFrame { - _PyStackRef f_executable; /* Deferred or strong reference (code object or None) */ + /* Borrowed reference (code object or None) */ + /* Strong reference for generators */ + PyObject *f_executable; struct _PyInterpreterFrame *previous; _PyStackRef f_funcobj; /* Deferred or strong reference. Only valid if not on C stack */ PyObject *f_globals; /* Borrowed reference. Only valid if not on C stack */ diff --git a/Include/internal/pycore_stackref.h b/Include/internal/pycore_stackref.h index 9495ccc8ac3889..e1a78368ce32f4 100644 --- a/Include/internal/pycore_stackref.h +++ b/Include/internal/pycore_stackref.h @@ -770,7 +770,6 @@ PyStackRef_TYPE(_PyStackRef stackref) { STACKREF_CHECK_FUNC(Gen) STACKREF_CHECK_FUNC(Bool) STACKREF_CHECK_FUNC(ExceptionInstance) -STACKREF_CHECK_FUNC(Code) STACKREF_CHECK_FUNC(Function) static inline bool diff --git a/Lib/test/test_capi/test_function.py b/Lib/test/test_capi/test_function.py index c1a278e5d4da91..eb976a97c81dc3 100644 --- a/Lib/test/test_capi/test_function.py +++ b/Lib/test/test_capi/test_function.py @@ -325,6 +325,28 @@ def annofn(arg: int) -> str: with self.assertRaises(SystemError): _testcapi.function_get_annotations(None) + def test_function_old_codes(self): + def f(): + pass + + def g(): + pass + + def h(): + pass + + old_codes = _testcapi.function_get_old_codes(f) + self.assertIsNone(old_codes) + + f.__code__ = g.__code__ + old_codes = _testcapi.function_get_old_codes(f) + self.assertIsInstance(old_codes, list) + self.assertEqual(len(old_codes), 1) + + f.__code__ = h.__code__ + old_codes = _testcapi.function_get_old_codes(f) + self.assertEqual(len(old_codes), 2) + # TODO: test PyFunction_New() # TODO: test PyFunction_NewWithQualName() # TODO: test PyFunction_SetVectorcall() diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index 1773633730ea00..fd1fa40b73e214 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -1703,7 +1703,7 @@ def func(): check(x, size('3PiccPPP' + INTERPRETER_FRAME + 'P')) # function def func(): pass - check(func, size('16Pi')) + check(func, size('17Pi')) class c(): @staticmethod def foo(): diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-07-04-17-04-03.gh-issue-152666.Vo5wJv.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-07-04-17-04-03.gh-issue-152666.Vo5wJv.rst new file mode 100644 index 00000000000000..299f4961d2c13b --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-07-04-17-04-03.gh-issue-152666.Vo5wJv.rst @@ -0,0 +1,3 @@ +Avoid reference counting of :class:`code` objects when creating and destroying +frames by having functions retain a list of all past :class:`code` objects. + diff --git a/Modules/_testcapi/function.c b/Modules/_testcapi/function.c index 40767adbd3f14a..bc790a63254b43 100644 --- a/Modules/_testcapi/function.c +++ b/Modules/_testcapi/function.c @@ -130,6 +130,19 @@ function_get_annotations(PyObject *self, PyObject *func) } +static PyObject * +function_get_old_codes(PyObject *self, PyObject *func) +{ + PyFunctionObject *func_o = (PyFunctionObject *) func; + PyObject *old_codes = func_o->func_old_codes; + if (old_codes == NULL) { + Py_RETURN_NONE; + } + + return Py_NewRef(old_codes); +} + + static PyMethodDef test_methods[] = { {"function_get_code", function_get_code, METH_O, NULL}, {"function_get_globals", function_get_globals, METH_O, NULL}, @@ -141,6 +154,7 @@ static PyMethodDef test_methods[] = { {"function_get_closure", function_get_closure, METH_O, NULL}, {"function_set_closure", function_set_closure, METH_VARARGS, NULL}, {"function_get_annotations", function_get_annotations, METH_O, NULL}, + {"function_get_old_codes", function_get_old_codes, METH_O, NULL}, {NULL}, }; diff --git a/Modules/_testinternalcapi/interpreter.c b/Modules/_testinternalcapi/interpreter.c index 237635c5f2bcdb..1c8cb0c8613ed8 100644 --- a/Modules/_testinternalcapi/interpreter.c +++ b/Modules/_testinternalcapi/interpreter.c @@ -84,7 +84,7 @@ Test_EvalFrame(PyThreadState *tstate, _PyInterpreterFrame *frame, int throwflag) entry.frame.f_globals = (PyObject*)0xaaa3; entry.frame.f_builtins = (PyObject*)0xaaa4; #endif - entry.frame.f_executable = PyStackRef_None; + entry.frame.f_executable = NULL; entry.frame.instr_ptr = (_Py_CODEUNIT *)_Py_INTERPRETER_TRAMPOLINE_INSTRUCTIONS_PTR + 1; entry.frame.stackpointer = entry.stack; entry.frame.owner = FRAME_OWNED_BY_INTERPRETER; diff --git a/Modules/_testinternalcapi/test_cases.c.h b/Modules/_testinternalcapi/test_cases.c.h index 5f2b1ae5d978aa..a3f3a1badf5c75 100644 --- a/Modules/_testinternalcapi/test_cases.c.h +++ b/Modules/_testinternalcapi/test_cases.c.h @@ -8340,7 +8340,7 @@ FT_ATOMIC_STORE_INT8_RELEASE(gen->gi_frame_state, FRAME_SUSPENDED + oparg); assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER); #if TIER_ONE && defined(Py_DEBUG) - if (!PyStackRef_IsNone(frame->f_executable)) { + if (frame->f_executable != NULL) { Py_ssize_t i = frame->instr_ptr - _PyFrame_GetBytecode(frame); assert(i >= 0 && i <= INT_MAX); int opcode = _Py_GetBaseCodeUnit(_PyFrame_GetCode(frame), (int)i).op.code; @@ -11555,10 +11555,9 @@ _PyFrame_StackPointerValidate(frame); _PyInterpreterFrame *gen_frame = &gen->gi_iframe; frame->instr_ptr++; - _PyFrame_Copy(frame, gen_frame); + _PyFrame_CopyForGenerators(frame, gen_frame); assert(frame->frame_obj == NULL); gen->gi_frame_state = FRAME_CREATED; - gen_frame->owner = FRAME_OWNED_BY_GENERATOR; _Py_LeaveRecursiveCallPy(tstate); _PyInterpreterFrame *prev = frame->previous; _PyThreadState_UpdateLastProfiledFrame(tstate, frame, prev); @@ -13245,7 +13244,7 @@ } tracer->prev_state.recorded_count = 0; tracer->prev_state.instr = next_instr; - PyObject *prev_code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + PyObject *prev_code = frame->f_executable; if (tracer->prev_state.instr_code != (PyCodeObject *)prev_code) { assert(stack_pointer == _PyFrame_GetStackPointer(frame)); _PyFrame_StackPointerValidate(frame); @@ -13254,7 +13253,7 @@ } tracer->prev_state.instr_frame = frame; tracer->prev_state.instr_oparg = oparg; - tracer->prev_state.instr_stacklevel = PyStackRef_IsNone(frame->f_executable) ? 2 : STACK_LEVEL(); + tracer->prev_state.instr_stacklevel = frame->f_executable == NULL ? 2 : STACK_LEVEL(); if (_PyOpcode_Caches[_PyOpcode_Deopt[opcode]] // Branch opcodes use the cache for branch history, not // specialization counters. Don't reset it. @@ -13707,7 +13706,7 @@ FT_ATOMIC_STORE_INT8_RELEASE(gen->gi_frame_state, FRAME_SUSPENDED + oparg); assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER); #if TIER_ONE && defined(Py_DEBUG) - if (!PyStackRef_IsNone(frame->f_executable)) { + if (frame->f_executable != NULL) { Py_ssize_t i = frame->instr_ptr - _PyFrame_GetBytecode(frame); assert(i >= 0 && i <= INT_MAX); int opcode = _Py_GetBaseCodeUnit(_PyFrame_GetCode(frame), (int)i).op.code; diff --git a/Objects/frameobject.c b/Objects/frameobject.c index e7ac59379dcfbc..36f64818a1de94 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -1939,7 +1939,7 @@ frame_dealloc(PyObject *op) /* Kill all local variables including specials, if we own them */ if (f->f_frame == frame && frame->owner == FRAME_OWNED_BY_FRAME_OBJECT) { - PyStackRef_CLEAR(frame->f_executable); + frame->f_executable = NULL; PyStackRef_CLEAR(frame->f_funcobj); Py_CLEAR(frame->f_locals); _PyStackRef *locals = _PyFrame_GetLocalsArray(frame); diff --git a/Objects/funcobject.c b/Objects/funcobject.c index 0fffd36ad462da..1d898ba71f4268 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -145,6 +145,7 @@ _PyFunction_FromConstructor(PyFrameConstructor *constr) op->func_typeparams = NULL; op->vectorcall = _PyFunction_Vectorcall; op->func_version = FUNC_VERSION_UNSET; + op->func_old_codes = NULL; // NOTE: functions created via FrameConstructor do not use deferred // reference counting because they are typically not part of cycles // nor accessed by multiple threads. @@ -223,6 +224,7 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname op->func_typeparams = NULL; op->vectorcall = _PyFunction_Vectorcall; op->func_version = FUNC_VERSION_UNSET; + op->func_old_codes = NULL; if (((code_obj->co_flags & CO_NESTED) == 0) || (code_obj->co_flags & CO_METHOD)) { // Use deferred reference counting for top-level functions, but not @@ -686,6 +688,17 @@ func_set_code(PyObject *self, PyObject *value, void *Py_UNUSED(ignored)) handle_func_event(PyFunction_EVENT_MODIFY_CODE, op, value); _PyFunction_ClearVersion(op); + if (op->func_old_codes == NULL) { + op->func_old_codes = PyList_New(0); + if (op->func_old_codes == NULL) { + return -1; + } + } + + if (PyList_Append(op->func_old_codes, op->func_code) < 0) { + return -1; + } + Py_XSETREF(op->func_code, Py_NewRef(value)); return 0; } @@ -1114,6 +1127,7 @@ func_clear(PyObject *self) Py_CLEAR(op->func_annotations); Py_CLEAR(op->func_annotate); Py_CLEAR(op->func_typeparams); + Py_CLEAR(op->func_old_codes); // Don't Py_CLEAR(op->func_code), since code is always required // to be non-NULL. Similarly, name and qualname shouldn't be NULL. // However, name and qualname could be str subclasses, so they @@ -1169,6 +1183,7 @@ func_traverse(PyObject *self, visitproc visit, void *arg) Py_VISIT(f->func_annotate); Py_VISIT(f->func_typeparams); Py_VISIT(f->func_qualname); + Py_VISIT(f->func_old_codes); return 0; } diff --git a/Objects/genobject.c b/Objects/genobject.c index 3cdc06733363d3..79d8c661f84a1d 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -96,7 +96,7 @@ gen_traverse(PyObject *self, visitproc visit, void *arg) else { // We still need to visit the code object when the frame is cleared to // ensure that it's kept alive if the reference is deferred. - _Py_VISIT_STACKREF(gen->gi_iframe.f_executable); + Py_VISIT(gen->gi_iframe.f_executable); } /* No need to visit cr_origin, because it's just tuples/str/int, so can't participate in a reference cycle. */ @@ -231,7 +231,7 @@ gen_dealloc(PyObject *self) gen_clear_frame(gen); } assert(gen->gi_exc_state.exc_value == NULL); - PyStackRef_CLEAR(gen->gi_iframe.f_executable); + Py_CLEAR(gen->gi_iframe.f_executable); Py_CLEAR(gen->gi_name); Py_CLEAR(gen->gi_qualname); @@ -1105,7 +1105,7 @@ make_gen(PyTypeObject *type, PyFunctionObject *func) gen->gi_weakreflist = NULL; gen->gi_exc_state.exc_value = NULL; gen->gi_exc_state.previous_item = NULL; - gen->gi_iframe.f_executable = PyStackRef_None; + gen->gi_iframe.f_executable = NULL; assert(func->func_name != NULL); gen->gi_name = Py_NewRef(func->func_name); assert(func->func_qualname != NULL); @@ -1176,11 +1176,10 @@ gen_new_with_qualname(PyTypeObject *type, PyFrameObject *f, assert(f->f_frame->frame_obj == NULL); assert(f->f_frame->owner == FRAME_OWNED_BY_FRAME_OBJECT); _PyInterpreterFrame *frame = &gen->gi_iframe; - _PyFrame_Copy((_PyInterpreterFrame *)f->_f_frame_data, frame); + _PyFrame_CopyForGenerators((_PyInterpreterFrame *)f->_f_frame_data, frame); gen->gi_frame_state = FRAME_CREATED; assert(frame->frame_obj == f); f->f_frame = frame; - frame->owner = FRAME_OWNED_BY_GENERATOR; assert(PyObject_GC_IsTracked((PyObject *)f)); Py_DECREF(f); gen->gi_weakreflist = NULL; diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 8ac397081e2738..0165e0feb3edfb 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1870,7 +1870,7 @@ dummy_func( /* We don't know which of these is relevant here, so keep them equal */ assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER); #if TIER_ONE && defined(Py_DEBUG) - if (!PyStackRef_IsNone(frame->f_executable)) { + if (frame->f_executable != NULL) { Py_ssize_t i = frame->instr_ptr - _PyFrame_GetBytecode(frame); assert(i >= 0 && i <= INT_MAX); int opcode = _Py_GetBaseCodeUnit(_PyFrame_GetCode(frame), (int)i).op.code; @@ -5854,10 +5854,9 @@ dummy_func( SAVE_STACK(); _PyInterpreterFrame *gen_frame = &gen->gi_iframe; frame->instr_ptr++; - _PyFrame_Copy(frame, gen_frame); + _PyFrame_CopyForGenerators(frame, gen_frame); assert(frame->frame_obj == NULL); gen->gi_frame_state = FRAME_CREATED; - gen_frame->owner = FRAME_OWNED_BY_GENERATOR; _Py_LeaveRecursiveCallPy(tstate); _PyInterpreterFrame *prev = frame->previous; _PyThreadState_UpdateLastProfiledFrame(tstate, frame, prev); @@ -6318,7 +6317,7 @@ dummy_func( } tier2 op(_GUARD_CODE_VERSION__PUSH_FRAME, (version/2 -- )) { - PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + PyObject *code = frame->f_executable; assert(PyCode_Check(code)); if (((PyCodeObject *)code)->co_version != version) { EXIT_IF(true); @@ -6326,7 +6325,7 @@ dummy_func( } tier2 op(_GUARD_CODE_VERSION_YIELD_VALUE, (version/2 -- )) { - PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + PyObject *code = frame->f_executable; assert(PyCode_Check(code)); if (((PyCodeObject *)code)->co_version != version) { frame->instr_ptr += 1 + INLINE_CACHE_ENTRIES_SEND; @@ -6335,7 +6334,7 @@ dummy_func( } tier2 op(_GUARD_CODE_VERSION_RETURN_VALUE, (version/2 -- )) { - PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + PyObject *code = frame->f_executable; assert(PyCode_Check(code)); if (((PyCodeObject *)code)->co_version != version) { frame->instr_ptr += frame->return_offset; @@ -6344,7 +6343,7 @@ dummy_func( } tier2 op(_GUARD_CODE_VERSION_RETURN_GENERATOR, (version/2 -- )) { - PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + PyObject *code = frame->f_executable; assert(PyCode_Check(code)); if (((PyCodeObject *)code)->co_version != version) { frame->instr_ptr += frame->return_offset; @@ -6638,14 +6637,14 @@ dummy_func( } tracer->prev_state.recorded_count = 0; tracer->prev_state.instr = next_instr; - PyObject *prev_code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + PyObject *prev_code = frame->f_executable; if (tracer->prev_state.instr_code != (PyCodeObject *)prev_code) { Py_SETREF(tracer->prev_state.instr_code, (PyCodeObject*)Py_NewRef((prev_code))); } tracer->prev_state.instr_frame = frame; tracer->prev_state.instr_oparg = oparg; - tracer->prev_state.instr_stacklevel = PyStackRef_IsNone(frame->f_executable) ? 2 : STACK_LEVEL(); + tracer->prev_state.instr_stacklevel = frame->f_executable == NULL ? 2 : STACK_LEVEL(); if (_PyOpcode_Caches[_PyOpcode_Deopt[opcode]] // Branch opcodes use the cache for branch history, not // specialization counters. Don't reset it. diff --git a/Python/ceval.c b/Python/ceval.c index f3f03b28112137..a3b376c0a3e9f8 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1272,7 +1272,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int entry.frame.f_globals = (PyObject*)0xaaa3; entry.frame.f_builtins = (PyObject*)0xaaa4; #endif - entry.frame.f_executable = PyStackRef_None; + entry.frame.f_executable = NULL; entry.frame.instr_ptr = (_Py_CODEUNIT *)_Py_INTERPRETER_TRAMPOLINE_INSTRUCTIONS + 1; entry.frame.stackpointer = entry.stack; entry.frame.owner = FRAME_OWNED_BY_INTERPRETER; @@ -1982,7 +1982,7 @@ clear_thread_frame(PyThreadState *tstate, _PyInterpreterFrame * frame) tstate->datastack_top); assert(frame->frame_obj == NULL || frame->frame_obj->f_frame == frame); _PyFrame_ClearExceptCode(frame); - PyStackRef_CLEAR(frame->f_executable); + frame->f_executable = NULL; _PyThreadState_PopFrame(tstate, frame); } diff --git a/Python/ceval.h b/Python/ceval.h index 0437ab85c5a668..74d7b456c375f6 100644 --- a/Python/ceval.h +++ b/Python/ceval.h @@ -248,7 +248,7 @@ static void lltrace_resume_frame(_PyInterpreterFrame *frame) { PyObject *fobj = PyStackRef_AsPyObjectBorrow(frame->f_funcobj); - if (!PyStackRef_CodeCheck(frame->f_executable) || + if (!PyCode_Check(frame->f_executable) || fobj == NULL || !PyFunction_Check(fobj) ) { diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 96dea2d2a6d5bc..2573898e9f9f69 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -9436,7 +9436,7 @@ FT_ATOMIC_STORE_INT8_RELEASE(gen->gi_frame_state, FRAME_SUSPENDED + oparg); assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER); #if TIER_ONE && defined(Py_DEBUG) - if (!PyStackRef_IsNone(frame->f_executable)) { + if (frame->f_executable != NULL) { Py_ssize_t i = frame->instr_ptr - _PyFrame_GetBytecode(frame); assert(i >= 0 && i <= INT_MAX); int opcode = _Py_GetBaseCodeUnit(_PyFrame_GetCode(frame), (int)i).op.code; @@ -20714,10 +20714,9 @@ _PyFrame_StackPointerValidate(frame); _PyInterpreterFrame *gen_frame = &gen->gi_iframe; frame->instr_ptr++; - _PyFrame_Copy(frame, gen_frame); + _PyFrame_CopyForGenerators(frame, gen_frame); assert(frame->frame_obj == NULL); gen->gi_frame_state = FRAME_CREATED; - gen_frame->owner = FRAME_OWNED_BY_GENERATOR; _Py_LeaveRecursiveCallPy(tstate); _PyInterpreterFrame *prev = frame->previous; _PyThreadState_UpdateLastProfiledFrame(tstate, frame, prev); @@ -24053,7 +24052,7 @@ CHECK_CURRENT_CACHED_VALUES(0); ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); uint32_t version = (uint32_t)CURRENT_OPERAND0_32(); - PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + PyObject *code = frame->f_executable; assert(PyCode_Check(code)); if (((PyCodeObject *)code)->co_version != version) { if (true) { @@ -24072,7 +24071,7 @@ ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef _stack_item_0 = _tos_cache0; uint32_t version = (uint32_t)CURRENT_OPERAND0_32(); - PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + PyObject *code = frame->f_executable; assert(PyCode_Check(code)); if (((PyCodeObject *)code)->co_version != version) { if (true) { @@ -24094,7 +24093,7 @@ _PyStackRef _stack_item_0 = _tos_cache0; _PyStackRef _stack_item_1 = _tos_cache1; uint32_t version = (uint32_t)CURRENT_OPERAND0_32(); - PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + PyObject *code = frame->f_executable; assert(PyCode_Check(code)); if (((PyCodeObject *)code)->co_version != version) { if (true) { @@ -24119,7 +24118,7 @@ _PyStackRef _stack_item_1 = _tos_cache1; _PyStackRef _stack_item_2 = _tos_cache2; uint32_t version = (uint32_t)CURRENT_OPERAND0_32(); - PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + PyObject *code = frame->f_executable; assert(PyCode_Check(code)); if (((PyCodeObject *)code)->co_version != version) { if (true) { @@ -24143,7 +24142,7 @@ CHECK_CURRENT_CACHED_VALUES(0); ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); uint32_t version = (uint32_t)CURRENT_OPERAND0_32(); - PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + PyObject *code = frame->f_executable; assert(PyCode_Check(code)); if (((PyCodeObject *)code)->co_version != version) { frame->instr_ptr += 1 + INLINE_CACHE_ENTRIES_SEND; @@ -24163,7 +24162,7 @@ ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef _stack_item_0 = _tos_cache0; uint32_t version = (uint32_t)CURRENT_OPERAND0_32(); - PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + PyObject *code = frame->f_executable; assert(PyCode_Check(code)); if (((PyCodeObject *)code)->co_version != version) { frame->instr_ptr += 1 + INLINE_CACHE_ENTRIES_SEND; @@ -24186,7 +24185,7 @@ _PyStackRef _stack_item_0 = _tos_cache0; _PyStackRef _stack_item_1 = _tos_cache1; uint32_t version = (uint32_t)CURRENT_OPERAND0_32(); - PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + PyObject *code = frame->f_executable; assert(PyCode_Check(code)); if (((PyCodeObject *)code)->co_version != version) { frame->instr_ptr += 1 + INLINE_CACHE_ENTRIES_SEND; @@ -24212,7 +24211,7 @@ _PyStackRef _stack_item_1 = _tos_cache1; _PyStackRef _stack_item_2 = _tos_cache2; uint32_t version = (uint32_t)CURRENT_OPERAND0_32(); - PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + PyObject *code = frame->f_executable; assert(PyCode_Check(code)); if (((PyCodeObject *)code)->co_version != version) { frame->instr_ptr += 1 + INLINE_CACHE_ENTRIES_SEND; @@ -24237,7 +24236,7 @@ CHECK_CURRENT_CACHED_VALUES(0); ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); uint32_t version = (uint32_t)CURRENT_OPERAND0_32(); - PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + PyObject *code = frame->f_executable; assert(PyCode_Check(code)); if (((PyCodeObject *)code)->co_version != version) { frame->instr_ptr += frame->return_offset; @@ -24257,7 +24256,7 @@ ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef _stack_item_0 = _tos_cache0; uint32_t version = (uint32_t)CURRENT_OPERAND0_32(); - PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + PyObject *code = frame->f_executable; assert(PyCode_Check(code)); if (((PyCodeObject *)code)->co_version != version) { frame->instr_ptr += frame->return_offset; @@ -24280,7 +24279,7 @@ _PyStackRef _stack_item_0 = _tos_cache0; _PyStackRef _stack_item_1 = _tos_cache1; uint32_t version = (uint32_t)CURRENT_OPERAND0_32(); - PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + PyObject *code = frame->f_executable; assert(PyCode_Check(code)); if (((PyCodeObject *)code)->co_version != version) { frame->instr_ptr += frame->return_offset; @@ -24306,7 +24305,7 @@ _PyStackRef _stack_item_1 = _tos_cache1; _PyStackRef _stack_item_2 = _tos_cache2; uint32_t version = (uint32_t)CURRENT_OPERAND0_32(); - PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + PyObject *code = frame->f_executable; assert(PyCode_Check(code)); if (((PyCodeObject *)code)->co_version != version) { frame->instr_ptr += frame->return_offset; @@ -24331,7 +24330,7 @@ CHECK_CURRENT_CACHED_VALUES(0); ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); uint32_t version = (uint32_t)CURRENT_OPERAND0_32(); - PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + PyObject *code = frame->f_executable; assert(PyCode_Check(code)); if (((PyCodeObject *)code)->co_version != version) { frame->instr_ptr += frame->return_offset; @@ -24351,7 +24350,7 @@ ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef _stack_item_0 = _tos_cache0; uint32_t version = (uint32_t)CURRENT_OPERAND0_32(); - PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + PyObject *code = frame->f_executable; assert(PyCode_Check(code)); if (((PyCodeObject *)code)->co_version != version) { frame->instr_ptr += frame->return_offset; @@ -24374,7 +24373,7 @@ _PyStackRef _stack_item_0 = _tos_cache0; _PyStackRef _stack_item_1 = _tos_cache1; uint32_t version = (uint32_t)CURRENT_OPERAND0_32(); - PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + PyObject *code = frame->f_executable; assert(PyCode_Check(code)); if (((PyCodeObject *)code)->co_version != version) { frame->instr_ptr += frame->return_offset; @@ -24400,7 +24399,7 @@ _PyStackRef _stack_item_1 = _tos_cache1; _PyStackRef _stack_item_2 = _tos_cache2; uint32_t version = (uint32_t)CURRENT_OPERAND0_32(); - PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + PyObject *code = frame->f_executable; assert(PyCode_Check(code)); if (((PyCodeObject *)code)->co_version != version) { frame->instr_ptr += frame->return_offset; diff --git a/Python/frame.c b/Python/frame.c index ff81eb0b3020c7..de177ddceb5799 100644 --- a/Python/frame.c +++ b/Python/frame.c @@ -13,7 +13,6 @@ _PyFrame_Traverse(_PyInterpreterFrame *frame, visitproc visit, void *arg) Py_VISIT(frame->frame_obj); Py_VISIT(frame->f_locals); _Py_VISIT_STACKREF(frame->f_funcobj); - _Py_VISIT_STACKREF(frame->f_executable); return _PyGC_VisitFrameStack(frame, visit, arg); } @@ -54,7 +53,6 @@ take_ownership(PyFrameObject *f, _PyInterpreterFrame *frame) _PyFrame_Copy(frame, new_frame); // _PyFrame_Copy takes the reference to the executable, // so we need to restore it. - new_frame->f_executable = PyStackRef_DUP(new_frame->f_executable); f->f_frame = new_frame; new_frame->owner = FRAME_OWNED_BY_FRAME_OBJECT; if (_PyFrame_IsIncomplete(new_frame)) { @@ -132,7 +130,7 @@ _PyFrame_ClearExceptCode(_PyInterpreterFrame *frame) PyObject * PyUnstable_InterpreterFrame_GetCode(struct _PyInterpreterFrame *frame) { - return PyStackRef_AsPyObjectNew(frame->f_executable); + return Py_NewRef(frame->f_executable); } // NOTE: We allow racy accesses to the instruction pointer from other threads diff --git a/Python/gc_free_threading.c b/Python/gc_free_threading.c index 4e36189580bbf8..f7d2c47bdd952d 100644 --- a/Python/gc_free_threading.c +++ b/Python/gc_free_threading.c @@ -253,8 +253,6 @@ frame_disable_deferred_refcounting(_PyInterpreterFrame *frame) assert(frame->owner == FRAME_OWNED_BY_FRAME_OBJECT || frame->owner == FRAME_OWNED_BY_GENERATOR); - frame->f_executable = PyStackRef_AsStrongReference(frame->f_executable); - if (frame->owner == FRAME_OWNED_BY_GENERATOR) { PyGenObject *gen = _PyGen_GetGeneratorFromFrame(frame); if (gen->gi_frame_state == FRAME_CLEARED) { @@ -467,7 +465,6 @@ gc_visit_thread_stacks(PyInterpreterState *interp, struct collection_state *stat continue; } - gc_visit_stackref(f->f_executable); while (top != f->localsplus) { --top; gc_visit_stackref(*top); @@ -863,7 +860,7 @@ gc_visit_thread_stacks_mark_alive(PyInterpreterState *interp, gc_mark_args_t *ar } _PyStackRef *top = f->stackpointer; - if (gc_visit_stackref_mark_alive(args, f->f_executable) < 0) { + if (gc_mark_enqueue(f->f_executable, args) < 0) { err = -1; goto exit; } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index cc95179ccaab17..64b0cbe0f57d60 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -8339,7 +8339,7 @@ FT_ATOMIC_STORE_INT8_RELEASE(gen->gi_frame_state, FRAME_SUSPENDED + oparg); assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER); #if TIER_ONE && defined(Py_DEBUG) - if (!PyStackRef_IsNone(frame->f_executable)) { + if (frame->f_executable != NULL) { Py_ssize_t i = frame->instr_ptr - _PyFrame_GetBytecode(frame); assert(i >= 0 && i <= INT_MAX); int opcode = _Py_GetBaseCodeUnit(_PyFrame_GetCode(frame), (int)i).op.code; @@ -11552,10 +11552,9 @@ _PyFrame_StackPointerValidate(frame); _PyInterpreterFrame *gen_frame = &gen->gi_iframe; frame->instr_ptr++; - _PyFrame_Copy(frame, gen_frame); + _PyFrame_CopyForGenerators(frame, gen_frame); assert(frame->frame_obj == NULL); gen->gi_frame_state = FRAME_CREATED; - gen_frame->owner = FRAME_OWNED_BY_GENERATOR; _Py_LeaveRecursiveCallPy(tstate); _PyInterpreterFrame *prev = frame->previous; _PyThreadState_UpdateLastProfiledFrame(tstate, frame, prev); @@ -13242,7 +13241,7 @@ } tracer->prev_state.recorded_count = 0; tracer->prev_state.instr = next_instr; - PyObject *prev_code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + PyObject *prev_code = frame->f_executable; if (tracer->prev_state.instr_code != (PyCodeObject *)prev_code) { assert(stack_pointer == _PyFrame_GetStackPointer(frame)); _PyFrame_StackPointerValidate(frame); @@ -13251,7 +13250,7 @@ } tracer->prev_state.instr_frame = frame; tracer->prev_state.instr_oparg = oparg; - tracer->prev_state.instr_stacklevel = PyStackRef_IsNone(frame->f_executable) ? 2 : STACK_LEVEL(); + tracer->prev_state.instr_stacklevel = frame->f_executable == NULL ? 2 : STACK_LEVEL(); if (_PyOpcode_Caches[_PyOpcode_Deopt[opcode]] // Branch opcodes use the cache for branch history, not // specialization counters. Don't reset it. @@ -13704,7 +13703,7 @@ FT_ATOMIC_STORE_INT8_RELEASE(gen->gi_frame_state, FRAME_SUSPENDED + oparg); assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER); #if TIER_ONE && defined(Py_DEBUG) - if (!PyStackRef_IsNone(frame->f_executable)) { + if (frame->f_executable != NULL) { Py_ssize_t i = frame->instr_ptr - _PyFrame_GetBytecode(frame); assert(i >= 0 && i <= INT_MAX); int opcode = _Py_GetBaseCodeUnit(_PyFrame_GetCode(frame), (int)i).op.code; diff --git a/Python/optimizer.c b/Python/optimizer.c index c9f6ebdb62f07b..d8caeb0a31b210 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -1072,8 +1072,7 @@ _PyJit_translate_single_bytecode_to_trace( DPRINTF(1, "Unknown uop needing guard ip %s\n", _PyOpcode_uop_name[last_opcode]); Py_UNREACHABLE(); } - PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); - Py_INCREF(code); + PyObject *code = Py_NewRef(frame->f_executable); ADD_TO_TRACE(_RECORD_CODE, 0, (uintptr_t)code, 0); ADD_TO_TRACE(guard_ip, 0, (uintptr_t)next_instr, 0); if (PyCode_Check(code)) { diff --git a/Python/pystate.c b/Python/pystate.c index e90642fa882db7..8e84983b955438 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -1588,7 +1588,7 @@ init_threadstate(_PyThreadStateImpl *_tstate, // Initialize the embedded base frame - sentinel at the bottom of the frame stack _tstate->base_frame.previous = NULL; - _tstate->base_frame.f_executable = PyStackRef_None; + _tstate->base_frame.f_executable = NULL; _tstate->base_frame.f_funcobj = PyStackRef_NULL; _tstate->base_frame.f_globals = NULL; _tstate->base_frame.f_builtins = NULL; diff --git a/Python/specialize.c b/Python/specialize.c index 6aaae6f383d22f..ea256165ec9db4 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -2946,7 +2946,7 @@ _Py_Specialize_Resume(_Py_CODEUNIT *instr, PyThreadState *tstate, _PyInterpreter { if (tstate->tracing == 0 && instr->op.code == RESUME) { if (tstate->interp->jit) { - PyCodeObject *co = (PyCodeObject *)PyStackRef_AsPyObjectBorrow(frame->f_executable); + PyCodeObject *co = (PyCodeObject *)frame->f_executable; if (co != NULL && PyCode_Check(co) && (co->co_flags & (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR)) == 0) { diff --git a/Python/tracemalloc.c b/Python/tracemalloc.c index 0afc84e021817c..012a73d1906744 100644 --- a/Python/tracemalloc.c +++ b/Python/tracemalloc.c @@ -221,7 +221,7 @@ hashtable_compare_traceback(const void *key1, const void *key2) static void tracemalloc_get_frame(_PyInterpreterFrame *pyframe, frame_t *frame) { - assert(PyStackRef_CodeCheck(pyframe->f_executable)); + assert(PyCode_Check(pyframe->f_executable)); frame->filename = &_Py_STR(anon_unknown); int lineno = -1;