Skip to content

Commit 0b12f2e

Browse files
fix handling of recursion
1 parent 085c1d7 commit 0b12f2e

File tree

8 files changed

+58
-56
lines changed

8 files changed

+58
-56
lines changed

Include/internal/pycore_ceval.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -303,9 +303,9 @@ PyAPI_FUNC(PyObject *)_PyEval_MatchKeys(PyThreadState *tstate, PyObject *map, Py
303303
PyAPI_FUNC(void) _PyEval_MonitorRaise(PyThreadState *tstate, _PyInterpreterFrame *frame, _Py_CODEUNIT *instr);
304304
PyAPI_FUNC(int) _PyEval_UnpackIterableStackRef(PyThreadState *tstate, PyObject *v, int argcnt, int argcntafter, _PyStackRef *sp);
305305
PyAPI_FUNC(void) _PyEval_FrameClearAndPop(PyThreadState *tstate, _PyInterpreterFrame *frame);
306-
PyAPI_FUNC(PyObject **) _PyObjectArray_FromStackRefArray(_PyStackRef *restrict input, Py_ssize_t nargs, PyObject **restrict scratch);
306+
PyAPI_FUNC(PyObject **) _PyObjectArray_FromStackRefArray(_PyThreadStateImpl *_tstate, _PyStackRef *restrict input, Py_ssize_t nargs);
307307

308-
PyAPI_FUNC(void) _PyObjectArray_Free(PyObject **array, PyObject **scratch);
308+
PyAPI_FUNC(void) _PyObjectArray_Free(_PyThreadStateImpl *_tstate, PyObject **array, Py_ssize_t nargs, PyObject **temp_arr);
309309

310310
PyAPI_FUNC(PyObject *) _PyEval_GetANext(PyObject *aiter);
311311
PyAPI_FUNC(void) _PyEval_LoadGlobalStackRef(PyObject *globals, PyObject *builtins, PyObject *name, _PyStackRef *writeto);

Include/internal/pycore_tstate.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ struct _gc_thread_state {
2222
#endif
2323

2424
/* How much scratch space to give stackref to PyObject* conversion. */
25-
#define MAX_STACKREF_SCRATCH 10
25+
#define MAX_STACKREF_SCRATCH 1024
2626

2727
// Every PyThreadState is actually allocated as a _PyThreadStateImpl. The
2828
// PyThreadState fields are exposed as part of the C API, although most fields
@@ -50,7 +50,8 @@ typedef struct _PyThreadStateImpl {
5050
struct _qsbr_thread_state *qsbr; // only used by free-threaded build
5151
struct llist_node mem_free_queue; // delayed free queue
5252

53-
PyObject *stackref_scratch[MAX_STACKREF_SCRATCH + 1];
53+
PyObject *stackref_scratch[MAX_STACKREF_SCRATCH];
54+
int n_stackref_scratch_used;
5455
#ifdef Py_GIL_DISABLED
5556
// Stack references for the current thread that exist on the C stack
5657
struct _PyCStackRef *c_stack_refs;

Python/bytecodes.c

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1983,7 +1983,7 @@ dummy_func(
19831983
ERROR_IF(true);
19841984
}
19851985
PyObject *str_o = _PyUnicode_JoinArray(&_Py_STR(empty), pieces_o, oparg);
1986-
STACKREFS_TO_PYOBJECTS_CLEANUP(pieces_o);
1986+
STACKREFS_TO_PYOBJECTS_CLEANUP(pieces_o, oparg);
19871987
DECREF_INPUTS();
19881988
ERROR_IF(str_o == NULL);
19891989
str = PyStackRef_FromPyObjectSteal(str_o);
@@ -2108,7 +2108,7 @@ dummy_func(
21082108
values_o, 2,
21092109
values_o+1, 2,
21102110
oparg);
2111-
STACKREFS_TO_PYOBJECTS_CLEANUP(values_o);
2111+
STACKREFS_TO_PYOBJECTS_CLEANUP(values_o, oparg*2);
21122112
DECREF_INPUTS();
21132113
ERROR_IF(map_o == NULL);
21142114
map = PyStackRef_FromPyObjectStealMortal(map_o);
@@ -3755,7 +3755,7 @@ dummy_func(
37553755
callable_o, args_o,
37563756
total_args | PY_VECTORCALL_ARGUMENTS_OFFSET,
37573757
NULL);
3758-
STACKREFS_TO_PYOBJECTS_CLEANUP(args_o);
3758+
STACKREFS_TO_PYOBJECTS_CLEANUP(args_o, total_args);
37593759
if (opcode == INSTRUMENTED_CALL) {
37603760
PyObject *arg = total_args == 0 ?
37613761
&_PyInstrumentation_MISSING : PyStackRef_AsPyObjectBorrow(arguments[0]);
@@ -3910,7 +3910,7 @@ dummy_func(
39103910
callable_o, args_o,
39113911
total_args | PY_VECTORCALL_ARGUMENTS_OFFSET,
39123912
NULL);
3913-
STACKREFS_TO_PYOBJECTS_CLEANUP(args_o);
3913+
STACKREFS_TO_PYOBJECTS_CLEANUP(args_o, total_args);
39143914
assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
39153915
DECREF_INPUTS();
39163916
ERROR_IF(res_o == NULL);
@@ -4193,7 +4193,7 @@ dummy_func(
41934193
ERROR_IF(true);
41944194
}
41954195
PyObject *res_o = tp->tp_vectorcall((PyObject *)tp, args_o, total_args, NULL);
4196-
STACKREFS_TO_PYOBJECTS_CLEANUP(args_o);
4196+
STACKREFS_TO_PYOBJECTS_CLEANUP(args_o, total_args);
41974197
DECREF_INPUTS();
41984198
ERROR_IF(res_o == NULL);
41994199
res = PyStackRef_FromPyObjectSteal(res_o);
@@ -4264,7 +4264,7 @@ dummy_func(
42644264
PyCFunction_GET_SELF(callable_o),
42654265
args_o,
42664266
total_args);
4267-
STACKREFS_TO_PYOBJECTS_CLEANUP(args_o);
4267+
STACKREFS_TO_PYOBJECTS_CLEANUP(args_o, total_args);
42684268
assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
42694269
DECREF_INPUTS();
42704270
ERROR_IF(res_o == NULL);
@@ -4300,7 +4300,7 @@ dummy_func(
43004300
ERROR_IF(true);
43014301
}
43024302
PyObject *res_o = cfunc(PyCFunction_GET_SELF(callable_o), args_o, total_args, NULL);
4303-
STACKREFS_TO_PYOBJECTS_CLEANUP(args_o);
4303+
STACKREFS_TO_PYOBJECTS_CLEANUP(args_o, total_args);
43044304
assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
43054305
DECREF_INPUTS();
43064306
ERROR_IF(res_o == NULL);
@@ -4480,7 +4480,7 @@ dummy_func(
44804480
PyCFunctionFastWithKeywords cfunc =
44814481
_PyCFunctionFastWithKeywords_CAST(meth->ml_meth);
44824482
PyObject *res_o = cfunc(self, (args_o + 1), nargs, NULL);
4483-
STACKREFS_TO_PYOBJECTS_CLEANUP(args_o);
4483+
STACKREFS_TO_PYOBJECTS_CLEANUP(args_o, total_args);
44844484
assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
44854485
DECREF_INPUTS();
44864486
ERROR_IF(res_o == NULL);
@@ -4559,7 +4559,7 @@ dummy_func(
45594559
}
45604560
PyCFunctionFast cfunc = _PyCFunctionFast_CAST(meth->ml_meth);
45614561
PyObject *res_o = cfunc(self, (args_o + 1), nargs);
4562-
STACKREFS_TO_PYOBJECTS_CLEANUP(args_o);
4562+
STACKREFS_TO_PYOBJECTS_CLEANUP(args_o, total_args);
45634563
assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
45644564
DECREF_INPUTS();
45654565
ERROR_IF(res_o == NULL);
@@ -4658,7 +4658,7 @@ dummy_func(
46584658
callable_o, args_o,
46594659
positional_args | PY_VECTORCALL_ARGUMENTS_OFFSET,
46604660
kwnames_o);
4661-
STACKREFS_TO_PYOBJECTS_CLEANUP(args_o);
4661+
STACKREFS_TO_PYOBJECTS_CLEANUP(args_o, total_args);
46624662
if (opcode == INSTRUMENTED_CALL_KW) {
46634663
PyObject *arg = total_args == 0 ?
46644664
&_PyInstrumentation_MISSING : PyStackRef_AsPyObjectBorrow(arguments[0]);
@@ -4815,7 +4815,7 @@ dummy_func(
48154815
positional_args | PY_VECTORCALL_ARGUMENTS_OFFSET,
48164816
kwnames_o);
48174817
PyStackRef_CLOSE(kwnames);
4818-
STACKREFS_TO_PYOBJECTS_CLEANUP(args_o);
4818+
STACKREFS_TO_PYOBJECTS_CLEANUP(args_o, total_args);
48194819
assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
48204820
DECREF_INPUTS();
48214821
ERROR_IF(res_o == NULL);

Python/ceval.c

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -945,10 +945,12 @@ extern void _PyUOpPrint(const _PyUOpInstruction *uop);
945945

946946

947947
PyObject **
948-
_PyObjectArray_FromStackRefArray(_PyStackRef *input, Py_ssize_t nargs, PyObject **scratch)
948+
_PyObjectArray_FromStackRefArray(_PyThreadStateImpl *_tstate, _PyStackRef *input, Py_ssize_t nargs)
949949
{
950950
PyObject **result;
951-
if (nargs > MAX_STACKREF_SCRATCH) {
951+
/* +1 because vectorcall might use -1 to write self */
952+
PyObject **target = _tstate->stackref_scratch + _tstate->n_stackref_scratch_used + nargs + 1;
953+
if (target > _tstate->stackref_scratch + MAX_STACKREF_SCRATCH) {
952954
// +1 in case PY_VECTORCALL_ARGUMENTS_OFFSET is set.
953955
result = PyMem_Malloc((nargs + 1) * sizeof(PyObject *));
954956
if (result == NULL) {
@@ -957,7 +959,9 @@ _PyObjectArray_FromStackRefArray(_PyStackRef *input, Py_ssize_t nargs, PyObject
957959
result++;
958960
}
959961
else {
960-
result = scratch;
962+
result = _tstate->stackref_scratch + _tstate->n_stackref_scratch_used;
963+
_tstate->n_stackref_scratch_used += (int)nargs + 1;
964+
assert(_tstate->n_stackref_scratch_used < MAX_STACKREF_SCRATCH);
961965
}
962966
for (int i = 0; i < nargs; i++) {
963967
result[i] = PyStackRef_AsPyObjectBorrow(input[i]);
@@ -966,10 +970,15 @@ _PyObjectArray_FromStackRefArray(_PyStackRef *input, Py_ssize_t nargs, PyObject
966970
}
967971

968972
void
969-
_PyObjectArray_Free(PyObject **array, PyObject **scratch)
973+
_PyObjectArray_Free(_PyThreadStateImpl *_tstate, PyObject **array, Py_ssize_t nargs, PyObject **temp_arr)
970974
{
971-
if (array != scratch) {
972-
PyMem_Free(array);
975+
/* -1 because we +1 previously */
976+
if (array == temp_arr) {
977+
_tstate->n_stackref_scratch_used -= ((int)nargs + 1);
978+
assert(_tstate->n_stackref_scratch_used >= 0);
979+
}
980+
else {
981+
PyMem_Free(array-1);
973982
}
974983
}
975984

Python/ceval_macros.h

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -394,22 +394,13 @@ do { \
394394
/* Stackref macros */
395395

396396

397-
#if Py_TAIL_CALL_INTERP && defined(_MSC_VER)
398397
#define STACKREFS_TO_PYOBJECTS(ARGS, ARG_COUNT, NAME) \
399-
/* +1 because vectorcall might use -1 to write self */ \
400398
_PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate; \
401-
PyObject **NAME##_temp = _tstate->stackref_scratch; \
402-
PyObject **NAME = _PyObjectArray_FromStackRefArray(ARGS, ARG_COUNT, NAME##_temp + 1);
403-
#else
404-
#define STACKREFS_TO_PYOBJECTS(ARGS, ARG_COUNT, NAME) \
405-
/* +1 because vectorcall might use -1 to write self */ \
406-
PyObject *NAME##_temp[MAX_STACKREF_SCRATCH+1]; \
407-
PyObject **NAME = _PyObjectArray_FromStackRefArray(ARGS, ARG_COUNT, NAME##_temp + 1);
408-
#endif
399+
PyObject **NAME##_temp = _tstate->stackref_scratch + _tstate->n_stackref_scratch_used; \
400+
PyObject **NAME = _PyObjectArray_FromStackRefArray(_tstate, ARGS, ARG_COUNT);
409401

410-
#define STACKREFS_TO_PYOBJECTS_CLEANUP(NAME) \
411-
/* +1 because we +1 previously */ \
412-
_PyObjectArray_Free(NAME - 1, NAME##_temp);
402+
#define STACKREFS_TO_PYOBJECTS_CLEANUP(NAME, ARG_COUNT) \
403+
_PyObjectArray_Free(_tstate, NAME, ARG_COUNT, NAME##_temp);
413404

414405
#define CONVERSION_FAILED(NAME) ((NAME) == NULL)
415406

Python/executor_cases.c.h

Lines changed: 9 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)