Skip to content

Commit 8b7160f

Browse files
markshannonseehwan80
authored andcommitted
pythonGH-131498: Cases generator: manage stacks automatically (pythonGH-132074)
1 parent 9af0605 commit 8b7160f

15 files changed

+215
-250
lines changed

Include/internal/pycore_opcode_metadata.h

+6-6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_stackref.h

-5
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,6 @@
44
extern "C" {
55
#endif
66

7-
// Define this to get precise tracking of closed stackrefs.
8-
// This will use unbounded memory, as it can only grow.
9-
// Use this to track double closes in short-lived programs
10-
// #define Py_STACKREF_CLOSE_DEBUG 1
11-
127
#ifndef Py_BUILD_CORE
138
# error "this header requires Py_BUILD_CORE define"
149
#endif

Include/internal/pycore_structs.h

+6
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,12 @@ typedef struct {
5757
// Define this to get precise tracking of stackrefs.
5858
// #define Py_STACKREF_DEBUG 1
5959

60+
// Define this to get precise tracking of closed stackrefs.
61+
// This will use unbounded memory, as it can only grow.
62+
// Use this to track double closes in short-lived programs
63+
// #define Py_STACKREF_CLOSE_DEBUG 1
64+
65+
6066
typedef union _PyStackRef {
6167
#if !defined(Py_GIL_DISABLED) && defined(Py_STACKREF_DEBUG)
6268
uint64_t index;

Python/bytecodes.c

+37-30
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ dummy_func(
204204
ptrdiff_t off = this_instr - _PyFrame_GetBytecode(frame);
205205
frame->tlbc_index = ((_PyThreadStateImpl *)tstate)->tlbc_index;
206206
frame->instr_ptr = bytecode + off;
207-
// Make sure this_instr gets reset correctley for any uops that
207+
// Make sure this_instr gets reset correctly for any uops that
208208
// follow
209209
next_instr = frame->instr_ptr;
210210
DISPATCH();
@@ -1111,7 +1111,7 @@ dummy_func(
11111111
tstate->current_frame = frame->previous;
11121112
assert(!_PyErr_Occurred(tstate));
11131113
PyObject *result = PyStackRef_AsPyObjectSteal(retval);
1114-
SYNC_SP(); /* Not strictly necessary, but prevents warnings */
1114+
LLTRACE_RESUME_FRAME();
11151115
return result;
11161116
}
11171117

@@ -1123,7 +1123,7 @@ dummy_func(
11231123
_PyStackRef temp = PyStackRef_MakeHeapSafe(retval);
11241124
DEAD(retval);
11251125
SAVE_STACK();
1126-
assert(EMPTY());
1126+
assert(STACK_LEVEL() == 0);
11271127
_Py_LeaveRecursiveCallPy(tstate);
11281128
// GH-99729: We need to unlink the frame *before* clearing it:
11291129
_PyInterpreterFrame *dying = frame;
@@ -1223,8 +1223,9 @@ dummy_func(
12231223
{
12241224
PyGenObject *gen = (PyGenObject *)receiver_o;
12251225
_PyInterpreterFrame *gen_frame = &gen->gi_iframe;
1226-
STACK_SHRINK(1);
12271226
_PyFrame_StackPush(gen_frame, PyStackRef_MakeHeapSafe(v));
1227+
DEAD(v);
1228+
SYNC_SP();
12281229
gen->gi_frame_state = FRAME_EXECUTING;
12291230
gen->gi_exc_state.previous_item = tstate->exc_info;
12301231
tstate->exc_info = &gen->gi_exc_state;
@@ -2436,10 +2437,10 @@ dummy_func(
24362437
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 1);
24372438
_PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(
24382439
tstate, PyStackRef_FromPyObjectNew(f), 2, frame);
2439-
// Manipulate stack directly because we exit with DISPATCH_INLINED().
2440-
STACK_SHRINK(1);
24412440
new_frame->localsplus[0] = owner;
24422441
DEAD(owner);
2442+
// Manipulate stack directly because we exit with DISPATCH_INLINED().
2443+
SYNC_SP();
24432444
new_frame->localsplus[1] = PyStackRef_FromPyObjectNew(name);
24442445
frame->return_offset = INSTRUCTION_SIZE;
24452446
DISPATCH_INLINED(new_frame);
@@ -3083,12 +3084,11 @@ dummy_func(
30833084
macro(FOR_ITER) = _SPECIALIZE_FOR_ITER + _FOR_ITER;
30843085

30853086

3086-
inst(INSTRUMENTED_FOR_ITER, (unused/1 -- )) {
3087-
_PyStackRef iter_stackref = TOP();
3088-
PyObject *iter = PyStackRef_AsPyObjectBorrow(iter_stackref);
3089-
PyObject *next = (*Py_TYPE(iter)->tp_iternext)(iter);
3090-
if (next != NULL) {
3091-
PUSH(PyStackRef_FromPyObjectSteal(next));
3087+
inst(INSTRUMENTED_FOR_ITER, (unused/1, iter -- iter, next)) {
3088+
PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter);
3089+
PyObject *next_o = (*Py_TYPE(iter_o)->tp_iternext)(iter_o);
3090+
if (next_o != NULL) {
3091+
next = PyStackRef_FromPyObjectSteal(next_o);
30923092
INSTRUMENTED_JUMP(this_instr, next_instr, PY_MONITORING_EVENT_BRANCH_LEFT);
30933093
}
30943094
else {
@@ -3105,6 +3105,7 @@ dummy_func(
31053105
next_instr[oparg].op.code == INSTRUMENTED_END_FOR);
31063106
/* Skip END_FOR */
31073107
JUMPBY(oparg + 1);
3108+
DISPATCH();
31083109
}
31093110
}
31103111

@@ -4022,7 +4023,6 @@ dummy_func(
40224023
_PUSH_FRAME;
40234024

40244025
inst(EXIT_INIT_CHECK, (should_be_none -- )) {
4025-
assert(STACK_LEVEL() == 2);
40264026
if (!PyStackRef_IsNone(should_be_none)) {
40274027
PyErr_Format(PyExc_TypeError,
40284028
"__init__() should return None, not '%.200s'",
@@ -4813,7 +4813,7 @@ dummy_func(
48134813
PyFunctionObject *func = (PyFunctionObject *)PyStackRef_AsPyObjectBorrow(frame->f_funcobj);
48144814
PyGenObject *gen = (PyGenObject *)_Py_MakeCoro(func);
48154815
ERROR_IF(gen == NULL, error);
4816-
assert(EMPTY());
4816+
assert(STACK_LEVEL() == 0);
48174817
SAVE_STACK();
48184818
_PyInterpreterFrame *gen_frame = &gen->gi_iframe;
48194819
frame->instr_ptr++;
@@ -4932,6 +4932,7 @@ dummy_func(
49324932
}
49334933
next_instr = frame->instr_ptr;
49344934
if (next_instr != this_instr) {
4935+
SYNC_SP();
49354936
DISPATCH();
49364937
}
49374938
}
@@ -4976,46 +4977,48 @@ dummy_func(
49764977
_CHECK_PERIODIC +
49774978
_MONITOR_JUMP_BACKWARD;
49784979

4979-
inst(INSTRUMENTED_POP_JUMP_IF_TRUE, (unused/1 -- )) {
4980-
_PyStackRef cond = POP();
4980+
inst(INSTRUMENTED_POP_JUMP_IF_TRUE, (unused/1, cond -- )) {
49814981
assert(PyStackRef_BoolCheck(cond));
49824982
int jump = PyStackRef_IsTrue(cond);
4983+
DEAD(cond);
49834984
RECORD_BRANCH_TAKEN(this_instr[1].cache, jump);
49844985
if (jump) {
49854986
INSTRUMENTED_JUMP(this_instr, next_instr + oparg, PY_MONITORING_EVENT_BRANCH_RIGHT);
49864987
}
49874988
}
49884989

4989-
inst(INSTRUMENTED_POP_JUMP_IF_FALSE, (unused/1 -- )) {
4990-
_PyStackRef cond = POP();
4990+
inst(INSTRUMENTED_POP_JUMP_IF_FALSE, (unused/1, cond -- )) {
49914991
assert(PyStackRef_BoolCheck(cond));
49924992
int jump = PyStackRef_IsFalse(cond);
4993+
DEAD(cond);
49934994
RECORD_BRANCH_TAKEN(this_instr[1].cache, jump);
49944995
if (jump) {
49954996
INSTRUMENTED_JUMP(this_instr, next_instr + oparg, PY_MONITORING_EVENT_BRANCH_RIGHT);
49964997
}
49974998
}
49984999

4999-
inst(INSTRUMENTED_POP_JUMP_IF_NONE, (unused/1 -- )) {
5000-
_PyStackRef value_stackref = POP();
5001-
int jump = PyStackRef_IsNone(value_stackref);
5000+
inst(INSTRUMENTED_POP_JUMP_IF_NONE, (unused/1, value -- )) {
5001+
int jump = PyStackRef_IsNone(value);
50025002
RECORD_BRANCH_TAKEN(this_instr[1].cache, jump);
50035003
if (jump) {
5004+
DEAD(value);
50045005
INSTRUMENTED_JUMP(this_instr, next_instr + oparg, PY_MONITORING_EVENT_BRANCH_RIGHT);
50055006
}
50065007
else {
5007-
PyStackRef_CLOSE(value_stackref);
5008+
PyStackRef_CLOSE(value);
50085009
}
50095010
}
50105011

5011-
inst(INSTRUMENTED_POP_JUMP_IF_NOT_NONE, (unused/1 -- )) {
5012-
_PyStackRef value_stackref = POP();
5013-
int jump = !PyStackRef_IsNone(value_stackref);
5012+
inst(INSTRUMENTED_POP_JUMP_IF_NOT_NONE, (unused/1, value -- )) {
5013+
int jump = !PyStackRef_IsNone(value);
50145014
RECORD_BRANCH_TAKEN(this_instr[1].cache, jump);
50155015
if (jump) {
5016-
PyStackRef_CLOSE(value_stackref);
5016+
PyStackRef_CLOSE(value);
50175017
INSTRUMENTED_JUMP(this_instr, next_instr + oparg, PY_MONITORING_EVENT_BRANCH_RIGHT);
50185018
}
5019+
else {
5020+
DEAD(value);
5021+
}
50195022
}
50205023

50215024
tier1 inst(EXTENDED_ARG, ( -- )) {
@@ -5219,22 +5222,26 @@ dummy_func(
52195222
}
52205223

52215224
label(pop_4_error) {
5222-
STACK_SHRINK(4);
5225+
stack_pointer -= 4;
5226+
assert(WITHIN_STACK_BOUNDS());
52235227
goto error;
52245228
}
52255229

52265230
label(pop_3_error) {
5227-
STACK_SHRINK(3);
5231+
stack_pointer -= 3;
5232+
assert(WITHIN_STACK_BOUNDS());
52285233
goto error;
52295234
}
52305235

52315236
label(pop_2_error) {
5232-
STACK_SHRINK(2);
5237+
stack_pointer -= 2;
5238+
assert(WITHIN_STACK_BOUNDS());
52335239
goto error;
52345240
}
52355241

52365242
label(pop_1_error) {
5237-
STACK_SHRINK(1);
5243+
stack_pointer -= 1;
5244+
assert(WITHIN_STACK_BOUNDS());
52385245
goto error;
52395246
}
52405247

Python/ceval.c

+19-25
Original file line numberDiff line numberDiff line change
@@ -151,18 +151,6 @@ dump_item(_PyStackRef item)
151151
printf("<nil>");
152152
return;
153153
}
154-
if (
155-
obj == Py_None
156-
|| PyBool_Check(obj)
157-
|| PyLong_CheckExact(obj)
158-
|| PyFloat_CheckExact(obj)
159-
|| PyUnicode_CheckExact(obj)
160-
) {
161-
if (PyObject_Print(obj, stdout, 0) == 0) {
162-
return;
163-
}
164-
PyErr_Clear();
165-
}
166154
// Don't call __repr__(), it might recurse into the interpreter.
167155
printf("<%s at %p>", Py_TYPE(obj)->tp_name, (void *)obj);
168156
}
@@ -182,14 +170,19 @@ dump_stack(_PyInterpreterFrame *frame, _PyStackRef *stack_pointer)
182170
dump_item(*ptr);
183171
}
184172
printf("]\n");
185-
printf(" stack=[");
186-
for (_PyStackRef *ptr = stack_base; ptr < stack_pointer; ptr++) {
187-
if (ptr != stack_base) {
188-
printf(", ");
173+
if (stack_pointer < stack_base) {
174+
printf(" stack=%d\n", (int)(stack_pointer-stack_base));
175+
}
176+
else {
177+
printf(" stack=[");
178+
for (_PyStackRef *ptr = stack_base; ptr < stack_pointer; ptr++) {
179+
if (ptr != stack_base) {
180+
printf(", ");
181+
}
182+
dump_item(*ptr);
189183
}
190-
dump_item(*ptr);
184+
printf("]\n");
191185
}
192-
printf("]\n");
193186
fflush(stdout);
194187
PyErr_SetRaisedException(exc);
195188
_PyFrame_GetStackPointer(frame);
@@ -202,13 +195,13 @@ lltrace_instruction(_PyInterpreterFrame *frame,
202195
int opcode,
203196
int oparg)
204197
{
205-
if (frame->owner >= FRAME_OWNED_BY_INTERPRETER) {
206-
return;
198+
int offset = 0;
199+
if (frame->owner < FRAME_OWNED_BY_INTERPRETER) {
200+
dump_stack(frame, stack_pointer);
201+
offset = (int)(next_instr - _PyFrame_GetBytecode(frame));
207202
}
208-
dump_stack(frame, stack_pointer);
209203
const char *opname = _PyOpcode_OpName[opcode];
210204
assert(opname != NULL);
211-
int offset = (int)(next_instr - _PyFrame_GetBytecode(frame));
212205
if (OPCODE_HAS_ARG((int)_PyOpcode_Deopt[opcode])) {
213206
printf("%d: %s %d\n", offset * 2, opname, oparg);
214207
}
@@ -986,8 +979,10 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
986979
* These are cached values from the frame and code object. */
987980
_Py_CODEUNIT *next_instr;
988981
_PyStackRef *stack_pointer;
989-
990-
#if defined(Py_DEBUG) && !defined(Py_STACKREF_DEBUG)
982+
entry_frame.localsplus[0] = PyStackRef_NULL;
983+
#ifdef Py_STACKREF_DEBUG
984+
entry_frame.f_funcobj = PyStackRef_None;
985+
#elif defined(Py_DEBUG)
991986
/* Set these to invalid but identifiable values for debugging. */
992987
entry_frame.f_funcobj = (_PyStackRef){.bits = 0xaaa0};
993988
entry_frame.f_locals = (PyObject*)0xaaa1;
@@ -1044,7 +1039,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
10441039
_PyExecutorObject *current_executor = NULL;
10451040
const _PyUOpInstruction *next_uop = NULL;
10461041
#endif
1047-
10481042
#if Py_TAIL_CALL_INTERP
10491043
return _TAIL_CALL_start_frame(frame, NULL, tstate, NULL, 0);
10501044
#else

Python/ceval_macros.h

-40
Original file line numberDiff line numberDiff line change
@@ -194,48 +194,8 @@ GETITEM(PyObject *v, Py_ssize_t i) {
194194
#define JUMPBY(x) (next_instr += (x))
195195
#define SKIP_OVER(x) (next_instr += (x))
196196

197-
198-
/* Stack manipulation macros */
199-
200-
/* The stack can grow at most MAXINT deep, as co_nlocals and
201-
co_stacksize are ints. */
202197
#define STACK_LEVEL() ((int)(stack_pointer - _PyFrame_Stackbase(frame)))
203198
#define STACK_SIZE() (_PyFrame_GetCode(frame)->co_stacksize)
204-
#define EMPTY() (STACK_LEVEL() == 0)
205-
#define TOP() (stack_pointer[-1])
206-
#define SECOND() (stack_pointer[-2])
207-
#define THIRD() (stack_pointer[-3])
208-
#define FOURTH() (stack_pointer[-4])
209-
#define PEEK(n) (stack_pointer[-(n)])
210-
#define POKE(n, v) (stack_pointer[-(n)] = (v))
211-
#define SET_TOP(v) (stack_pointer[-1] = (v))
212-
#define SET_SECOND(v) (stack_pointer[-2] = (v))
213-
#define BASIC_STACKADJ(n) (stack_pointer += n)
214-
#define BASIC_PUSH(v) (*stack_pointer++ = (v))
215-
#define BASIC_POP() (*--stack_pointer)
216-
217-
#ifdef Py_DEBUG
218-
#define PUSH(v) do { \
219-
BASIC_PUSH(v); \
220-
assert(STACK_LEVEL() <= STACK_SIZE()); \
221-
} while (0)
222-
#define POP() (assert(STACK_LEVEL() > 0), BASIC_POP())
223-
#define STACK_GROW(n) do { \
224-
assert(n >= 0); \
225-
BASIC_STACKADJ(n); \
226-
assert(STACK_LEVEL() <= STACK_SIZE()); \
227-
} while (0)
228-
#define STACK_SHRINK(n) do { \
229-
assert(n >= 0); \
230-
assert(STACK_LEVEL() >= n); \
231-
BASIC_STACKADJ(-(n)); \
232-
} while (0)
233-
#else
234-
#define PUSH(v) BASIC_PUSH(v)
235-
#define POP() BASIC_POP()
236-
#define STACK_GROW(n) BASIC_STACKADJ(n)
237-
#define STACK_SHRINK(n) BASIC_STACKADJ(-(n))
238-
#endif
239199

240200
#define WITHIN_STACK_BOUNDS() \
241201
(frame->owner == FRAME_OWNED_BY_INTERPRETER || (STACK_LEVEL() >= 0 && STACK_LEVEL() <= STACK_SIZE()))

0 commit comments

Comments
 (0)