Skip to content

Commit b65e6ef

Browse files
committed
Make frames a union of interpreter frames and builtin frames
1 parent 77d2fd4 commit b65e6ef

12 files changed

Lines changed: 167 additions & 116 deletions

File tree

Include/cpython/pystate.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ struct _ts {
124124
int what_event; /* The event currently being monitored, if any. */
125125

126126
/* Pointer to currently executing frame. */
127-
struct _PyInterpreterFrame *current_frame;
127+
union _PyVMFrame *current_frame;
128128

129129
Py_tracefunc c_profilefunc;
130130
Py_tracefunc c_tracefunc;

Include/internal/pycore_frame.h

Lines changed: 45 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -61,32 +61,44 @@ enum _frameowner {
6161
FRAME_OWNED_BY_CSTACK = 4,
6262
};
6363

64+
struct _PyFrameCore {
65+
_PyStackRef f_executable; /* Deferred or strong reference */
66+
union _PyVMFrame *previous;
67+
char owner;
68+
};
69+
6470
typedef struct _PyInterpreterFrame {
6571
_PyStackRef f_executable; /* Deferred or strong reference (code object or None) */
66-
struct _PyInterpreterFrame *previous;
67-
_PyStackRef f_funcobj; /* Deferred or strong reference. Only valid if not on C stack */
68-
PyObject *f_globals; /* Borrowed reference. Only valid if not on C stack */
69-
PyObject *f_builtins; /* Borrowed reference. Only valid if not on C stack */
70-
PyObject *f_locals; /* Strong reference, may be NULL. Only valid if not on C stack */
71-
PyFrameObject *frame_obj; /* Strong reference, may be NULL. Only valid if not on C stack */
72-
_Py_CODEUNIT *instr_ptr; /* Instruction currently executing (or about to begin) */
73-
_PyStackRef *stackpointer;
74-
#ifdef Py_GIL_DISABLED
75-
/* Index of thread-local bytecode containing instr_ptr. */
76-
int32_t tlbc_index;
77-
#endif
78-
uint16_t return_offset; /* Only relevant during a function call */
72+
union _PyVMFrame *previous;
7973
char owner;
8074
#ifdef Py_DEBUG
8175
uint8_t visited:1;
8276
uint8_t lltrace:7;
8377
#else
8478
uint8_t visited;
8579
#endif
80+
uint16_t return_offset; /* Only relevant during a function call */
81+
#ifdef Py_GIL_DISABLED
82+
/* Index of thread-local bytecode containing instr_ptr. */
83+
int32_t tlbc_index;
84+
#endif
85+
_PyStackRef f_funcobj; /* Deferred or strong reference. Only valid if not on C stack */
86+
PyObject *f_globals; /* Borrowed reference. Only valid if not on C stack */
87+
PyObject *f_builtins; /* Borrowed reference. Only valid if not on C stack */
88+
PyObject *f_locals; /* Strong reference, may be NULL. Only valid if not on C stack */
89+
PyFrameObject *frame_obj; /* Strong reference, may be NULL. Only valid if not on C stack */
90+
_Py_CODEUNIT *instr_ptr; /* Instruction currently executing (or about to begin) */
91+
_PyStackRef *stackpointer;
8692
/* Locals and stack */
8793
_PyStackRef localsplus[1];
8894
} _PyInterpreterFrame;
8995

96+
97+
typedef union _PyVMFrame {
98+
struct _PyFrameCore core;
99+
_PyInterpreterFrame iframe;
100+
} _PyVMFrame;
101+
90102
#define _PyInterpreterFrame_LASTI(IF) \
91103
((int)((IF)->instr_ptr - _PyFrame_GetBytecode((IF))))
92104

@@ -200,7 +212,7 @@ _PyFrame_Initialize(
200212
PyThreadState *tstate, _PyInterpreterFrame *frame, _PyStackRef func,
201213
PyObject *locals, PyCodeObject *code, int null_locals_from, _PyInterpreterFrame *previous)
202214
{
203-
frame->previous = previous;
215+
frame->previous = (_PyVMFrame *)previous;
204216
frame->f_funcobj = func;
205217
frame->f_executable = PyStackRef_FromPyObjectNew(code);
206218
PyFunctionObject *func_obj = (PyFunctionObject *)PyStackRef_AsPyObjectBorrow(func);
@@ -264,23 +276,32 @@ _PyFrame_SetStackPointer(_PyInterpreterFrame *frame, _PyStackRef *stack_pointer)
264276
* Frames owned by a generator are always complete.
265277
*/
266278
static inline bool
267-
_PyFrame_IsIncomplete(_PyInterpreterFrame *frame)
279+
_PyVMFrame_IsIncomplete(_PyVMFrame *frame)
268280
{
269-
if (frame->owner >= FRAME_OWNED_BY_INTERPRETER) {
281+
if (frame->core.owner >= FRAME_OWNED_BY_INTERPRETER) {
270282
return true;
271283
}
272-
return frame->owner != FRAME_OWNED_BY_GENERATOR &&
273-
frame->instr_ptr < _PyFrame_GetBytecode(frame) +
274-
_PyFrame_GetCode(frame)->_co_firsttraceable;
284+
if (frame->core.owner == FRAME_OWNED_BY_GENERATOR) {
285+
return false;
286+
}
287+
_PyInterpreterFrame *iframe = &frame->iframe;
288+
return iframe->instr_ptr < _PyFrame_GetBytecode(iframe) +
289+
_PyFrame_GetCode(iframe)->_co_firsttraceable;
290+
}
291+
292+
static inline bool
293+
_PyFrame_IsIncomplete(_PyInterpreterFrame *frame)
294+
{
295+
return _PyVMFrame_IsIncomplete((_PyVMFrame *)frame);
275296
}
276297

277298
static inline _PyInterpreterFrame *
278-
_PyFrame_GetFirstComplete(_PyInterpreterFrame *frame)
299+
_PyFrame_GetFirstComplete(_PyVMFrame *frame)
279300
{
280-
while (frame && _PyFrame_IsIncomplete(frame)) {
281-
frame = frame->previous;
301+
while (frame && _PyVMFrame_IsIncomplete(frame)) {
302+
frame = frame->core.previous;
282303
}
283-
return frame;
304+
return (_PyInterpreterFrame *)frame;
284305
}
285306

286307
static inline _PyInterpreterFrame *
@@ -376,7 +397,7 @@ _PyFrame_PushTrampolineUnchecked(PyThreadState *tstate, PyCodeObject *code, int
376397
_PyInterpreterFrame *frame = (_PyInterpreterFrame *)tstate->datastack_top;
377398
tstate->datastack_top += code->co_framesize;
378399
assert(tstate->datastack_top < tstate->datastack_limit);
379-
frame->previous = previous;
400+
frame->previous = (_PyVMFrame *)previous;
380401
frame->f_funcobj = PyStackRef_None;
381402
frame->f_executable = PyStackRef_FromPyObjectNew(code);
382403
#ifdef Py_DEBUG

Objects/frameobject.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2221,7 +2221,7 @@ _PyFrame_IsEntryFrame(PyFrameObject *frame)
22212221
assert(frame != NULL);
22222222
_PyInterpreterFrame *f = frame->f_frame;
22232223
assert(!_PyFrame_IsIncomplete(f));
2224-
return f->previous && f->previous->owner == FRAME_OWNED_BY_INTERPRETER;
2224+
return f->previous && f->previous->core.owner == FRAME_OWNED_BY_INTERPRETER;
22252225
}
22262226

22272227
PyCodeObject *
@@ -2242,10 +2242,10 @@ PyFrame_GetBack(PyFrameObject *frame)
22422242
assert(!_PyFrame_IsIncomplete(frame->f_frame));
22432243
PyFrameObject *back = frame->f_back;
22442244
if (back == NULL) {
2245-
_PyInterpreterFrame *prev = frame->f_frame->previous;
2246-
prev = _PyFrame_GetFirstComplete(prev);
2247-
if (prev) {
2248-
back = _PyFrame_GetFrameObject(prev);
2245+
_PyVMFrame *prev = frame->f_frame->previous;
2246+
_PyInterpreterFrame *iframe = _PyFrame_GetFirstComplete(prev);
2247+
if (iframe) {
2248+
back = _PyFrame_GetFrameObject(iframe);
22492249
}
22502250
}
22512251
return (PyFrameObject*)Py_XNewRef(back);

Objects/genobject.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -490,9 +490,9 @@ _gen_throw(PyGenObject *gen, int close_on_genexit,
490490
/* Link frame into the stack to enable complete backtraces. */
491491
/* XXX We should probably be updating the current frame somewhere in
492492
ceval.c. */
493-
_PyInterpreterFrame *prev = tstate->current_frame;
493+
_PyVMFrame *prev = tstate->current_frame;
494494
frame->previous = prev;
495-
tstate->current_frame = frame;
495+
tstate->current_frame = (_PyVMFrame *)frame;
496496
/* Close the generator that we are currently iterating with
497497
'yield from' or awaiting on with 'await'. */
498498
PyFrameState state = gen->gi_frame_state;
@@ -514,9 +514,9 @@ _gen_throw(PyGenObject *gen, int close_on_genexit,
514514
goto throw_here;
515515
}
516516

517-
_PyInterpreterFrame *prev = tstate->current_frame;
517+
_PyVMFrame *prev = tstate->current_frame;
518518
frame->previous = prev;
519-
tstate->current_frame = frame;
519+
tstate->current_frame = (_PyVMFrame *)frame;
520520
PyFrameState state = gen->gi_frame_state;
521521
gen->gi_frame_state = FRAME_EXECUTING;
522522
ret = PyObject_CallFunctionObjArgs(meth, typ, val, tb, NULL);

Objects/object.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2991,7 +2991,8 @@ _Py_Dealloc(PyObject *op)
29912991
#if !defined(Py_GIL_DISABLED) && !defined(Py_STACKREF_DEBUG)
29922992
/* This assertion doesn't hold for the free-threading build, as
29932993
* PyStackRef_CLOSE_SPECIALIZED is not implemented */
2994-
assert(tstate->current_frame == NULL || tstate->current_frame->stackpointer != NULL);
2994+
assert(_PyFrame_GetFirstComplete(tstate->current_frame) == NULL ||
2995+
_PyFrame_GetFirstComplete(tstate->current_frame)->stackpointer != NULL);
29952996
#endif
29962997
PyObject *old_exc = tstate != NULL ? tstate->current_exception : NULL;
29972998
// Keep the old exception type alive to prevent undefined behavior

Python/bytecodes.c

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1113,7 +1113,8 @@ dummy_func(
11131113
_Py_LeaveRecursiveCallPy(tstate);
11141114
// GH-99729: We need to unlink the frame *before* clearing it:
11151115
_PyInterpreterFrame *dying = frame;
1116-
frame = tstate->current_frame = dying->previous;
1116+
frame = (_PyInterpreterFrame *)frame->previous;
1117+
tstate->current_frame = (_PyVMFrame *)frame;
11171118
_PyEval_FrameClearAndPop(tstate, dying);
11181119
RELOAD_STACK();
11191120
LOAD_IP(frame->return_offset);
@@ -1218,7 +1219,7 @@ dummy_func(
12181219
assert(INSTRUCTION_SIZE + oparg <= UINT16_MAX);
12191220
frame->return_offset = (uint16_t)(INSTRUCTION_SIZE + oparg);
12201221
assert(gen_frame->previous == NULL);
1221-
gen_frame->previous = frame;
1222+
gen_frame->previous = (_PyVMFrame *)frame;
12221223
DISPATCH_INLINED(gen_frame);
12231224
}
12241225
if (PyStackRef_IsNone(v) && PyIter_Check(receiver_o)) {
@@ -1263,7 +1264,7 @@ dummy_func(
12631264
tstate->exc_info = &gen->gi_exc_state;
12641265
assert(INSTRUCTION_SIZE + oparg <= UINT16_MAX);
12651266
frame->return_offset = (uint16_t)(INSTRUCTION_SIZE + oparg);
1266-
gen_frame->previous = frame;
1267+
gen_frame->previous = (_PyVMFrame *)frame;
12671268
}
12681269

12691270
macro(SEND_GEN) =
@@ -1289,7 +1290,8 @@ dummy_func(
12891290
gen->gi_exc_state.previous_item = NULL;
12901291
_Py_LeaveRecursiveCallPy(tstate);
12911292
_PyInterpreterFrame *gen_frame = frame;
1292-
frame = tstate->current_frame = frame->previous;
1293+
frame = (_PyInterpreterFrame *)frame->previous;
1294+
tstate->current_frame = (_PyVMFrame *)frame;
12931295
gen_frame->previous = NULL;
12941296
/* We don't know which of these is relevant here, so keep them equal */
12951297
assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER);
@@ -3335,7 +3337,7 @@ dummy_func(
33353337
gen->gi_frame_state = FRAME_EXECUTING;
33363338
gen->gi_exc_state.previous_item = tstate->exc_info;
33373339
tstate->exc_info = &gen->gi_exc_state;
3338-
gen_frame->previous = frame;
3340+
gen_frame->previous = (_PyVMFrame *)frame;
33393341
// oparg is the return offset from the next instruction.
33403342
frame->return_offset = (uint16_t)(INSTRUCTION_SIZE + oparg);
33413343
}
@@ -3853,9 +3855,10 @@ dummy_func(
38533855
DEAD(new_frame);
38543856
SYNC_SP();
38553857
_PyFrame_SetStackPointer(frame, stack_pointer);
3856-
assert(new_frame->previous == frame || new_frame->previous->previous == frame);
3858+
assert(new_frame->previous == (_PyVMFrame *)frame || new_frame->previous->core.previous == (_PyVMFrame *)frame);
38573859
CALL_STAT_INC(inlined_py_calls);
3858-
frame = tstate->current_frame = temp;
3860+
tstate->current_frame = (_PyVMFrame *)temp;
3861+
frame = temp;
38593862
tstate->py_recursion_remaining--;
38603863
LOAD_SP();
38613864
LOAD_IP(0);
@@ -4794,9 +4797,10 @@ dummy_func(
47944797
gen->gi_frame_state = FRAME_CREATED;
47954798
gen_frame->owner = FRAME_OWNED_BY_GENERATOR;
47964799
_Py_LeaveRecursiveCallPy(tstate);
4797-
_PyInterpreterFrame *prev = frame->previous;
4800+
_PyVMFrame *prev = frame->previous;
47984801
_PyThreadState_PopFrame(tstate, frame);
4799-
frame = tstate->current_frame = prev;
4802+
frame = (_PyInterpreterFrame *)prev;
4803+
tstate->current_frame = prev;
48004804
LOAD_IP(frame->return_offset);
48014805
RELOAD_STACK();
48024806
res = PyStackRef_FromPyObjectStealMortal((PyObject *)gen);
@@ -5296,7 +5300,8 @@ dummy_func(
52965300
assert(frame->owner != FRAME_OWNED_BY_INTERPRETER);
52975301
// GH-99729: We need to unlink the frame *before* clearing it:
52985302
_PyInterpreterFrame *dying = frame;
5299-
frame = tstate->current_frame = dying->previous;
5303+
frame = (_PyInterpreterFrame *)frame->previous;
5304+
tstate->current_frame = (_PyVMFrame *)frame;
53005305
_PyEval_FrameClearAndPop(tstate, dying);
53015306
frame->return_offset = 0;
53025307
if (frame->owner == FRAME_OWNED_BY_INTERPRETER) {

Python/ceval.c

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -967,7 +967,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
967967
#if !Py_TAIL_CALL_INTERP
968968
uint8_t opcode; /* Current opcode */
969969
int oparg; /* Current opcode argument, if any */
970-
assert(tstate->current_frame == NULL || tstate->current_frame->stackpointer != NULL);
970+
assert(_PyFrame_GetFirstComplete(tstate->current_frame) == NULL ||
971+
_PyFrame_GetFirstComplete(tstate->current_frame)->stackpointer != NULL);
971972
#endif
972973
_PyInterpreterFrame entry_frame;
973974

@@ -1001,8 +1002,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
10011002
#endif
10021003
/* Push frame */
10031004
entry_frame.previous = tstate->current_frame;
1004-
frame->previous = &entry_frame;
1005-
tstate->current_frame = frame;
1005+
frame->previous = (_PyVMFrame *)&entry_frame;
1006+
tstate->current_frame = (_PyVMFrame *)frame;
10061007

10071008
/* support for generator.throw() */
10081009
if (throwflag) {
@@ -1156,12 +1157,13 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
11561157
assert(frame->owner != FRAME_OWNED_BY_INTERPRETER);
11571158
// GH-99729: We need to unlink the frame *before* clearing it:
11581159
_PyInterpreterFrame *dying = frame;
1159-
frame = tstate->current_frame = dying->previous;
1160+
_PyVMFrame *prev = dying->previous;
1161+
tstate->current_frame = prev;
11601162
_PyEval_FrameClearAndPop(tstate, dying);
1161-
frame->return_offset = 0;
1162-
assert(frame->owner == FRAME_OWNED_BY_INTERPRETER);
1163+
assert(prev->core.owner == FRAME_OWNED_BY_INTERPRETER);
1164+
prev->iframe.return_offset = 0;
11631165
/* Restore previous frame and exit */
1164-
tstate->current_frame = frame->previous;
1166+
tstate->current_frame = prev->core.previous;
11651167
return NULL;
11661168
}
11671169

@@ -2711,11 +2713,11 @@ int
27112713
PyEval_MergeCompilerFlags(PyCompilerFlags *cf)
27122714
{
27132715
PyThreadState *tstate = _PyThreadState_GET();
2714-
_PyInterpreterFrame *current_frame = tstate->current_frame;
2716+
_PyVMFrame *current_frame = tstate->current_frame;
27152717
int result = cf->cf_flags != 0;
27162718

2717-
if (current_frame != NULL) {
2718-
const int codeflags = _PyFrame_GetCode(current_frame)->co_flags;
2719+
if (current_frame != NULL && !_PyVMFrame_IsIncomplete(current_frame)) {
2720+
const int codeflags = _PyFrame_GetCode((_PyInterpreterFrame *)current_frame)->co_flags;
27192721
const int compilerflags = codeflags & PyCF_MASK;
27202722
if (compilerflags) {
27212723
result = 1;

Python/ceval_macros.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -157,10 +157,11 @@ do { \
157157
do { \
158158
assert(tstate->interp->eval_frame == NULL); \
159159
_PyFrame_SetStackPointer(frame, stack_pointer); \
160-
assert((NEW_FRAME)->previous == frame); \
161-
frame = tstate->current_frame = (NEW_FRAME); \
160+
assert((NEW_FRAME)->previous == (_PyVMFrame *)frame); \
161+
frame = (NEW_FRAME); \
162+
tstate->current_frame = (_PyVMFrame *)frame; \
162163
CALL_STAT_INC(inlined_py_calls); \
163-
JUMP_TO_LABEL(start_frame); \
164+
JUMP_TO_LABEL(start_frame); \
164165
} while (0)
165166

166167
/* Tuple access macros */

0 commit comments

Comments
 (0)