Skip to content

Commit 169324c

Browse files
authored
GH-120024: Use pointer for stack pointer (GH-121923)
1 parent 24cf867 commit 169324c

File tree

5 files changed

+55
-43
lines changed

5 files changed

+55
-43
lines changed

Include/internal/pycore_frame.h

+25-20
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ typedef struct _PyInterpreterFrame {
6868
PyObject *f_locals; /* Strong reference, may be NULL. Only valid if not on C stack */
6969
PyFrameObject *frame_obj; /* Strong reference, may be NULL. Only valid if not on C stack */
7070
_Py_CODEUNIT *instr_ptr; /* Instruction currently executing (or about to begin) */
71-
int stacktop; /* Offset of TOS from localsplus */
71+
_PyStackRef *stackpointer;
7272
uint16_t return_offset; /* Only relevant during a function call */
7373
char owner;
7474
/* Locals and stack */
@@ -88,20 +88,20 @@ static inline _PyStackRef *_PyFrame_Stackbase(_PyInterpreterFrame *f) {
8888
}
8989

9090
static inline _PyStackRef _PyFrame_StackPeek(_PyInterpreterFrame *f) {
91-
assert(f->stacktop > _PyFrame_GetCode(f)->co_nlocalsplus);
92-
assert(!PyStackRef_IsNull(f->localsplus[f->stacktop-1]));
93-
return f->localsplus[f->stacktop-1];
91+
assert(f->stackpointer > f->localsplus + _PyFrame_GetCode(f)->co_nlocalsplus);
92+
assert(!PyStackRef_IsNull(f->stackpointer[-1]));
93+
return f->stackpointer[-1];
9494
}
9595

9696
static inline _PyStackRef _PyFrame_StackPop(_PyInterpreterFrame *f) {
97-
assert(f->stacktop > _PyFrame_GetCode(f)->co_nlocalsplus);
98-
f->stacktop--;
99-
return f->localsplus[f->stacktop];
97+
assert(f->stackpointer > f->localsplus + _PyFrame_GetCode(f)->co_nlocalsplus);
98+
f->stackpointer--;
99+
return *f->stackpointer;
100100
}
101101

102102
static inline void _PyFrame_StackPush(_PyInterpreterFrame *f, _PyStackRef value) {
103-
f->localsplus[f->stacktop] = value;
104-
f->stacktop++;
103+
*f->stackpointer = value;
104+
f->stackpointer++;
105105
}
106106

107107
#define FRAME_SPECIALS_SIZE ((int)((sizeof(_PyInterpreterFrame)-1)/sizeof(PyObject *)))
@@ -117,9 +117,12 @@ _PyFrame_NumSlotsForCodeObject(PyCodeObject *code)
117117

118118
static inline void _PyFrame_Copy(_PyInterpreterFrame *src, _PyInterpreterFrame *dest)
119119
{
120-
assert(src->stacktop >= _PyFrame_GetCode(src)->co_nlocalsplus);
121120
*dest = *src;
122-
for (int i = 1; i < src->stacktop; i++) {
121+
assert(src->stackpointer != NULL);
122+
int stacktop = (int)(src->stackpointer - src->localsplus);
123+
assert(stacktop >= _PyFrame_GetCode(src)->co_nlocalsplus);
124+
dest->stackpointer = dest->localsplus + stacktop;
125+
for (int i = 1; i < stacktop; i++) {
123126
dest->localsplus[i] = src->localsplus[i];
124127
}
125128
// Don't leave a dangling pointer to the old frame when creating generators
@@ -141,7 +144,7 @@ _PyFrame_Initialize(
141144
frame->f_builtins = func->func_builtins;
142145
frame->f_globals = func->func_globals;
143146
frame->f_locals = locals;
144-
frame->stacktop = code->co_nlocalsplus;
147+
frame->stackpointer = frame->localsplus + code->co_nlocalsplus;
145148
frame->frame_obj = NULL;
146149
frame->instr_ptr = _PyCode_CODE(code);
147150
frame->return_offset = 0;
@@ -161,22 +164,23 @@ _PyFrame_GetLocalsArray(_PyInterpreterFrame *frame)
161164
return frame->localsplus;
162165
}
163166

164-
/* Fetches the stack pointer, and sets stacktop to -1.
165-
Having stacktop <= 0 ensures that invalid
166-
values are not visible to the cycle GC.
167-
We choose -1 rather than 0 to assist debugging. */
167+
/* Fetches the stack pointer, and sets stackpointer to NULL.
168+
Having stackpointer == NULL ensures that invalid
169+
values are not visible to the cycle GC. */
168170
static inline _PyStackRef*
169171
_PyFrame_GetStackPointer(_PyInterpreterFrame *frame)
170172
{
171-
_PyStackRef *sp = frame->localsplus + frame->stacktop;
172-
frame->stacktop = -1;
173+
assert(frame->stackpointer != NULL);
174+
_PyStackRef *sp = frame->stackpointer;
175+
frame->stackpointer = NULL;
173176
return sp;
174177
}
175178

176179
static inline void
177180
_PyFrame_SetStackPointer(_PyInterpreterFrame *frame, _PyStackRef *stack_pointer)
178181
{
179-
frame->stacktop = (int)(stack_pointer - frame->localsplus);
182+
assert(frame->stackpointer == NULL);
183+
frame->stackpointer = stack_pointer;
180184
}
181185

182186
/* Determine whether a frame is incomplete.
@@ -304,7 +308,8 @@ _PyFrame_PushTrampolineUnchecked(PyThreadState *tstate, PyCodeObject *code, int
304308
frame->f_globals = NULL;
305309
#endif
306310
frame->f_locals = NULL;
307-
frame->stacktop = code->co_nlocalsplus + stackdepth;
311+
assert(stackdepth <= code->co_stacksize);
312+
frame->stackpointer = frame->localsplus + code->co_nlocalsplus + stackdepth;
308313
frame->frame_obj = NULL;
309314
frame->instr_ptr = _PyCode_CODE(code);
310315
frame->owner = FRAME_OWNED_BY_THREAD;

Lib/test/test_sys.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -1603,7 +1603,8 @@ class C(object): pass
16031603
def func():
16041604
return sys._getframe()
16051605
x = func()
1606-
check(x, size('3Pi2c2P7P2ic??2P'))
1606+
INTERPRETER_FRAME = '9PhcP'
1607+
check(x, size('3PiccPP' + INTERPRETER_FRAME + 'P'))
16071608
# function
16081609
def func(): pass
16091610
check(func, size('16Pi'))
@@ -1620,7 +1621,7 @@ def bar(cls):
16201621
check(bar, size('PP'))
16211622
# generator
16221623
def get_gen(): yield 1
1623-
check(get_gen(), size('PP4P4c7P2ic??2P'))
1624+
check(get_gen(), size('6P4c' + INTERPRETER_FRAME + 'P'))
16241625
# iterator
16251626
check(iter('abc'), size('lP'))
16261627
# callable-iterator

Objects/frameobject.c

+13-11
Original file line numberDiff line numberDiff line change
@@ -1620,8 +1620,10 @@ frame_dealloc(PyFrameObject *f)
16201620
Py_CLEAR(frame->f_funcobj);
16211621
Py_CLEAR(frame->f_locals);
16221622
_PyStackRef *locals = _PyFrame_GetLocalsArray(frame);
1623-
for (int i = 0; i < frame->stacktop; i++) {
1624-
PyStackRef_CLEAR(locals[i]);
1623+
_PyStackRef *sp = frame->stackpointer;
1624+
while (sp > locals) {
1625+
sp--;
1626+
PyStackRef_CLEAR(*sp);
16251627
}
16261628
}
16271629
Py_CLEAR(f->f_back);
@@ -1656,11 +1658,13 @@ frame_tp_clear(PyFrameObject *f)
16561658

16571659
/* locals and stack */
16581660
_PyStackRef *locals = _PyFrame_GetLocalsArray(f->f_frame);
1659-
assert(f->f_frame->stacktop >= 0);
1660-
for (int i = 0; i < f->f_frame->stacktop; i++) {
1661-
PyStackRef_CLEAR(locals[i]);
1661+
_PyStackRef *sp = f->f_frame->stackpointer;
1662+
assert(sp >= locals);
1663+
while (sp > locals) {
1664+
sp--;
1665+
PyStackRef_CLEAR(*sp);
16621666
}
1663-
f->f_frame->stacktop = 0;
1667+
f->f_frame->stackpointer = locals;
16641668
Py_CLEAR(f->f_frame->f_locals);
16651669
return 0;
16661670
}
@@ -1878,8 +1882,9 @@ frame_get_var(_PyInterpreterFrame *frame, PyCodeObject *co, int i,
18781882
return 0;
18791883
}
18801884

1881-
PyObject *value = PyStackRef_AsPyObjectBorrow(frame->localsplus[i]);
1882-
if (frame->stacktop) {
1885+
PyObject *value = NULL;
1886+
if (frame->stackpointer == NULL || frame->stackpointer > frame->localsplus + i) {
1887+
value = PyStackRef_AsPyObjectBorrow(frame->localsplus[i]);
18831888
if (kind & CO_FAST_FREE) {
18841889
// The cell was set by COPY_FREE_VARS.
18851890
assert(value != NULL && PyCell_Check(value));
@@ -1897,9 +1902,6 @@ frame_get_var(_PyInterpreterFrame *frame, PyCodeObject *co, int i,
18971902
}
18981903
}
18991904
}
1900-
else {
1901-
assert(value == NULL);
1902-
}
19031905
*pvalue = value;
19041906
return 1;
19051907
}

Python/ceval.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -764,7 +764,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
764764
#endif
765765
entry_frame.f_executable = Py_None;
766766
entry_frame.instr_ptr = (_Py_CODEUNIT *)_Py_INTERPRETER_TRAMPOLINE_INSTRUCTIONS + 1;
767-
entry_frame.stacktop = 0;
767+
entry_frame.stackpointer = entry_frame.localsplus;
768768
entry_frame.owner = FRAME_OWNED_BY_CSTACK;
769769
entry_frame.return_offset = 0;
770770
/* Push frame */

Python/frame.c

+13-9
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,11 @@ _PyFrame_Traverse(_PyInterpreterFrame *frame, visitproc visit, void *arg)
1717
Py_VISIT(_PyFrame_GetCode(frame));
1818
/* locals */
1919
_PyStackRef *locals = _PyFrame_GetLocalsArray(frame);
20-
int i = 0;
20+
_PyStackRef *sp = frame->stackpointer;
2121
/* locals and stack */
22-
for (; i <frame->stacktop; i++) {
23-
Py_VISIT(PyStackRef_AsPyObjectBorrow(locals[i]));
22+
while (sp > locals) {
23+
sp--;
24+
Py_VISIT(PyStackRef_AsPyObjectBorrow(*sp));
2425
}
2526
return 0;
2627
}
@@ -59,10 +60,11 @@ take_ownership(PyFrameObject *f, _PyInterpreterFrame *frame)
5960
assert(frame->owner != FRAME_OWNED_BY_CSTACK);
6061
assert(frame->owner != FRAME_OWNED_BY_FRAME_OBJECT);
6162
assert(frame->owner != FRAME_CLEARED);
62-
Py_ssize_t size = ((char*)&frame->localsplus[frame->stacktop]) - (char *)frame;
63+
Py_ssize_t size = ((char*)frame->stackpointer) - (char *)frame;
6364
Py_INCREF(_PyFrame_GetCode(frame));
6465
memcpy((_PyInterpreterFrame *)f->_f_frame_data, frame, size);
6566
frame = (_PyInterpreterFrame *)f->_f_frame_data;
67+
frame->stackpointer = (_PyStackRef *)(((char *)frame) + size);
6668
f->f_frame = frame;
6769
frame->owner = FRAME_OWNED_BY_FRAME_OBJECT;
6870
if (_PyFrame_IsIncomplete(frame)) {
@@ -97,11 +99,13 @@ take_ownership(PyFrameObject *f, _PyInterpreterFrame *frame)
9799
void
98100
_PyFrame_ClearLocals(_PyInterpreterFrame *frame)
99101
{
100-
assert(frame->stacktop >= 0);
101-
int stacktop = frame->stacktop;
102-
frame->stacktop = 0;
103-
for (int i = 0; i < stacktop; i++) {
104-
PyStackRef_XCLOSE(frame->localsplus[i]);
102+
assert(frame->stackpointer != NULL);
103+
_PyStackRef *sp = frame->stackpointer;
104+
_PyStackRef *locals = frame->localsplus;
105+
frame->stackpointer = locals;
106+
while (sp > locals) {
107+
sp--;
108+
PyStackRef_XCLOSE(*sp);
105109
}
106110
Py_CLEAR(frame->f_locals);
107111
}

0 commit comments

Comments
 (0)