Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Reduce C stack usage in the Python interpreter on recent versions of Clang.
10 changes: 9 additions & 1 deletion Modules/_testcapi/vectorcall.c
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,15 @@ _testcapi_pyobject_vectorcall_impl(PyObject *module, PyObject *func,
PyErr_SetString(PyExc_TypeError, "kwnames must be None or a tuple");
return NULL;
}
return PyObject_Vectorcall(func, stack, nargs, kwnames);
PyObject *res;
// The CPython interpreter does not guarantee that vectorcalls are
// checked for recursion limit. It's thus up to the C extension themselves to check.
if (Py_EnterRecursiveCall("in _testcapi.pyobject_vectorcall")) {
return NULL;
}
res = PyObject_Vectorcall(func, stack, nargs, kwnames);
Py_LeaveRecursiveCall();
return res;
}

static PyObject *
Expand Down
7 changes: 7 additions & 0 deletions Python/ceval.c
Original file line number Diff line number Diff line change
Expand Up @@ -1168,6 +1168,13 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
return NULL;
}

/* +1 because vectorcall might use -1 to write self */
/* gh-138115: This must not be in individual cases for
non-tail-call interpreters, as it results in excessive
stack usage in some compilers.
*/
PyObject *STACKREF_SCRATCH[MAX_STACKREF_SCRATCH+1];

/* Local "register" variables.
* These are cached values from the frame and code object. */
_Py_CODEUNIT *next_instr;
Expand Down
6 changes: 6 additions & 0 deletions Python/ceval_macros.h
Original file line number Diff line number Diff line change
Expand Up @@ -427,10 +427,16 @@ do { \
/* How much scratch space to give stackref to PyObject* conversion. */
#define MAX_STACKREF_SCRATCH 10

#if Py_TAIL_CALL_INTERP
#define STACKREFS_TO_PYOBJECTS(ARGS, ARG_COUNT, NAME) \
/* +1 because vectorcall might use -1 to write self */ \
PyObject *NAME##_temp[MAX_STACKREF_SCRATCH+1]; \
PyObject **NAME = _PyObjectArray_FromStackRefArray(ARGS, ARG_COUNT, NAME##_temp + 1);
#else
#define STACKREFS_TO_PYOBJECTS(ARGS, ARG_COUNT, NAME) \
PyObject **NAME##_temp = (PyObject **)&STACKREF_SCRATCH; \
PyObject **NAME = _PyObjectArray_FromStackRefArray(ARGS, ARG_COUNT, NAME##_temp + 1);
#endif

#define STACKREFS_TO_PYOBJECTS_CLEANUP(NAME) \
/* +1 because we +1 previously */ \
Expand Down
Loading