Skip to content

Commit 3514aa1

Browse files
committed
Speedup recursion checks
1 parent 8aa20f2 commit 3514aa1

File tree

6 files changed

+49
-38
lines changed

6 files changed

+49
-38
lines changed

Include/internal/pycore_ceval.h

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -196,25 +196,6 @@ extern void _PyEval_DeactivateOpCache(void);
196196

197197
/* --- _Py_EnterRecursiveCall() ----------------------------------------- */
198198

199-
#if !_Py__has_builtin(__builtin_frame_address) && !defined(_MSC_VER)
200-
static uintptr_t return_pointer_as_int(char* p) {
201-
return (uintptr_t)p;
202-
}
203-
#endif
204-
205-
static inline uintptr_t
206-
_Py_get_machine_stack_pointer(void) {
207-
#if _Py__has_builtin(__builtin_frame_address)
208-
return (uintptr_t)__builtin_frame_address(0);
209-
#elif defined(_MSC_VER)
210-
return (uintptr_t)_AddressOfReturnAddress();
211-
#else
212-
char here;
213-
/* Avoid compiler warning about returning stack address */
214-
return return_pointer_as_int(&here);
215-
#endif
216-
}
217-
218199
static inline int _Py_MakeRecCheck(PyThreadState *tstate) {
219200
uintptr_t here_addr = _Py_get_machine_stack_pointer();
220201
_PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate;
@@ -249,12 +230,7 @@ PyAPI_FUNC(void) _Py_InitializeRecursionLimits(PyThreadState *tstate);
249230
static inline int _Py_ReachedRecursionLimit(PyThreadState *tstate) {
250231
uintptr_t here_addr = _Py_get_machine_stack_pointer();
251232
_PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate;
252-
if (here_addr > _tstate->c_stack_soft_limit) {
253-
return 0;
254-
}
255-
if (_tstate->c_stack_hard_limit == 0) {
256-
_Py_InitializeRecursionLimits(tstate);
257-
}
233+
assert(_tstate->c_stack_hard_limit != 0);
258234
return here_addr <= _tstate->c_stack_soft_limit;
259235
}
260236

Include/internal/pycore_pystate.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ extern "C" {
99
#endif
1010

1111
#include "pycore_typedefs.h" // _PyRuntimeState
12+
#include "pycore_tstate.h"
1213

1314

1415
// Values for PyThreadState.state. A thread must be in the "attached" state
@@ -296,6 +297,34 @@ _Py_AssertHoldsTstateFunc(const char *func)
296297
#define _Py_AssertHoldsTstate()
297298
#endif
298299

300+
#if !_Py__has_builtin(__builtin_frame_address) && !defined(_MSC_VER)
301+
static uintptr_t return_pointer_as_int(char* p) {
302+
return (uintptr_t)p;
303+
}
304+
#endif
305+
306+
static inline uintptr_t
307+
_Py_get_machine_stack_pointer(void) {
308+
#if _Py__has_builtin(__builtin_frame_address)
309+
return (uintptr_t)__builtin_frame_address(0);
310+
#elif defined(_MSC_VER)
311+
return (uintptr_t)_AddressOfReturnAddress();
312+
#else
313+
char here;
314+
/* Avoid compiler warning about returning stack address */
315+
return return_pointer_as_int(&here);
316+
#endif
317+
}
318+
319+
static inline intptr_t
320+
_Py_RecursionLimit_GetMargin(PyThreadState *tstate)
321+
{
322+
_PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate;
323+
assert(_tstate->c_stack_hard_limit != 0);
324+
intptr_t here_addr = _Py_get_machine_stack_pointer();
325+
return Py_ARITHMETIC_RIGHT_SHIFT(intptr_t, here_addr - (intptr_t)_tstate->c_stack_soft_limit, PYOS_STACK_MARGIN_SHIFT);
326+
}
327+
299328
#ifdef __cplusplus
300329
}
301330
#endif

Include/pythonrun.h

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,25 @@ PyAPI_DATA(int) (*PyOS_InputHook)(void);
2626
* apart. In practice, that means it must be larger than the C
2727
* stack consumption of PyEval_EvalDefault */
2828
#if defined(_Py_ADDRESS_SANITIZER) || defined(_Py_THREAD_SANITIZER)
29-
# define PYOS_STACK_MARGIN 4096
29+
# define PYOS_LOG_STACK_MARGIN 12
3030
#elif defined(Py_DEBUG) && defined(WIN32)
31-
# define PYOS_STACK_MARGIN 4096
31+
# define PYOS_LOG_STACK_MARGIN 12
3232
#elif defined(__wasi__)
3333
/* Web assembly has two stacks, so this isn't really a size */
34-
# define PYOS_STACK_MARGIN 500
34+
# define PYOS_LOG_STACK_MARGIN 9
3535
#else
36-
# define PYOS_STACK_MARGIN 2048
36+
# define PYOS_LOG_STACK_MARGIN 11
3737
#endif
38+
#define PYOS_STACK_MARGIN (1 << PYOS_LOG_STACK_MARGIN)
3839
#define PYOS_STACK_MARGIN_BYTES (PYOS_STACK_MARGIN * sizeof(void *))
3940

41+
#if SIZEOF_VOID_P == 8
42+
#define PYOS_STACK_MARGIN_SHIFT (PYOS_LOG_STACK_MARGIN + 3)
43+
#else
44+
#define PYOS_STACK_MARGIN_SHIFT (PYOS_LOG_STACK_MARGIN + 2)
45+
#endif
46+
47+
4048
#if defined(WIN32)
4149
#define USE_STACKCHECK
4250
#endif

Objects/object.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3015,7 +3015,8 @@ _Py_Dealloc(PyObject *op)
30153015
PyTypeObject *type = Py_TYPE(op);
30163016
destructor dealloc = type->tp_dealloc;
30173017
PyThreadState *tstate = _PyThreadState_GET();
3018-
if (_Py_ReachedRecursionLimitWithMargin(tstate, 2)) {
3018+
intptr_t margin = _Py_RecursionLimit_GetMargin(tstate);
3019+
if (margin < 2) {
30193020
_PyTrash_thread_deposit_object(tstate, (PyObject *)op);
30203021
return;
30213022
}
@@ -3061,7 +3062,7 @@ _Py_Dealloc(PyObject *op)
30613062
Py_XDECREF(old_exc);
30623063
Py_DECREF(type);
30633064
#endif
3064-
if (tstate->delete_later && !_Py_ReachedRecursionLimitWithMargin(tstate, 4)) {
3065+
if (tstate->delete_later && margin >= 4) {
30653066
_PyTrash_thread_destroy_chain(tstate);
30663067
}
30673068
}

Python/ceval.c

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -476,12 +476,6 @@ _Py_CheckRecursiveCall(PyThreadState *tstate, const char *where)
476476
_PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate;
477477
uintptr_t here_addr = _Py_get_machine_stack_pointer();
478478
assert(_tstate->c_stack_soft_limit != 0);
479-
if (_tstate->c_stack_hard_limit == 0) {
480-
_Py_InitializeRecursionLimits(tstate);
481-
}
482-
if (here_addr >= _tstate->c_stack_soft_limit) {
483-
return 0;
484-
}
485479
assert(_tstate->c_stack_hard_limit != 0);
486480
if (here_addr < _tstate->c_stack_hard_limit) {
487481
/* Overflowing while handling an overflow. Give up. */

Python/pystate.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2132,7 +2132,10 @@ _PyThreadState_Attach(PyThreadState *tstate)
21322132
if (current_fast_get() != NULL) {
21332133
Py_FatalError("non-NULL old thread state");
21342134
}
2135-
2135+
_PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate;
2136+
if (_tstate->c_stack_hard_limit == 0) {
2137+
_Py_InitializeRecursionLimits(tstate);
2138+
}
21362139

21372140
while (1) {
21382141
_PyEval_AcquireLock(tstate);

0 commit comments

Comments
 (0)