Skip to content

Commit f6dd9c1

Browse files
stefanordanglin44
andauthored
pythonGH-139914: Handle stack growth direction on HPPA (pythonGH-140028)
Adapted from a patch for Python 3.14 submitted to the Debian BTS by John https://bugs.debian.org/1105111#20 Co-authored-by: John David Anglin <[email protected]>
1 parent 336366f commit f6dd9c1

File tree

10 files changed

+94
-5
lines changed

10 files changed

+94
-5
lines changed

Include/internal/pycore_ceval.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,11 @@ extern void _PyEval_DeactivateOpCache(void);
217217
static inline int _Py_MakeRecCheck(PyThreadState *tstate) {
218218
uintptr_t here_addr = _Py_get_machine_stack_pointer();
219219
_PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate;
220+
#if _Py_STACK_GROWS_DOWN
220221
return here_addr < _tstate->c_stack_soft_limit;
222+
#else
223+
return here_addr > _tstate->c_stack_soft_limit;
224+
#endif
221225
}
222226

223227
// Export for '_json' shared extension, used via _Py_EnterRecursiveCall()
@@ -249,7 +253,11 @@ static inline int _Py_ReachedRecursionLimit(PyThreadState *tstate) {
249253
uintptr_t here_addr = _Py_get_machine_stack_pointer();
250254
_PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate;
251255
assert(_tstate->c_stack_hard_limit != 0);
256+
#if _Py_STACK_GROWS_DOWN
252257
return here_addr <= _tstate->c_stack_soft_limit;
258+
#else
259+
return here_addr >= _tstate->c_stack_soft_limit;
260+
#endif
253261
}
254262

255263
static inline void _Py_LeaveRecursiveCall(void) {

Include/internal/pycore_pystate.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,11 @@ _Py_RecursionLimit_GetMargin(PyThreadState *tstate)
331331
_PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate;
332332
assert(_tstate->c_stack_hard_limit != 0);
333333
intptr_t here_addr = _Py_get_machine_stack_pointer();
334+
#if _Py_STACK_GROWS_DOWN
334335
return Py_ARITHMETIC_RIGHT_SHIFT(intptr_t, here_addr - (intptr_t)_tstate->c_stack_soft_limit, _PyOS_STACK_MARGIN_SHIFT);
336+
#else
337+
return Py_ARITHMETIC_RIGHT_SHIFT(intptr_t, (intptr_t)_tstate->c_stack_soft_limit - here_addr, _PyOS_STACK_MARGIN_SHIFT);
338+
#endif
335339
}
336340

337341
#ifdef __cplusplus

Include/pyport.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -677,4 +677,10 @@ extern "C" {
677677
#endif
678678

679679

680+
// Assume the stack grows down unless specified otherwise
681+
#ifndef _Py_STACK_GROWS_DOWN
682+
# define _Py_STACK_GROWS_DOWN 1
683+
#endif
684+
685+
680686
#endif /* Py_PYPORT_H */

Lib/test/test_call.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1048,9 +1048,14 @@ def get_sp():
10481048

10491049
this_sp = _testinternalcapi.get_stack_pointer()
10501050
lower_sp = _testcapi.pyobject_vectorcall(get_sp, (), ())
1051-
self.assertLess(lower_sp, this_sp)
1051+
if _testcapi._Py_STACK_GROWS_DOWN:
1052+
self.assertLess(lower_sp, this_sp)
1053+
safe_margin = this_sp - lower_sp
1054+
else:
1055+
self.assertGreater(lower_sp, this_sp)
1056+
safe_margin = lower_sp - this_sp
10521057
# Add an (arbitrary) extra 25% for safety
1053-
safe_margin = (this_sp - lower_sp) * 5 / 4
1058+
safe_margin = safe_margin * 5 / 4
10541059
self.assertLess(safe_margin, _testinternalcapi.get_stack_margin())
10551060

10561061
@skip_on_s390x
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Restore support for HP PA-RISC, which has an upwards-growing stack.

Modules/_testcapimodule.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3359,6 +3359,10 @@ _testcapi_exec(PyObject *m)
33593359
PyModule_AddObject(m, "INT64_MAX", PyLong_FromInt64(INT64_MAX));
33603360
PyModule_AddObject(m, "UINT64_MAX", PyLong_FromUInt64(UINT64_MAX));
33613361

3362+
if (PyModule_AddIntMacro(m, _Py_STACK_GROWS_DOWN)) {
3363+
return -1;
3364+
}
3365+
33623366
if (PyModule_AddIntMacro(m, Py_single_input)) {
33633367
return -1;
33643368
}

Python/ceval.c

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -351,21 +351,33 @@ _Py_ReachedRecursionLimitWithMargin(PyThreadState *tstate, int margin_count)
351351
{
352352
uintptr_t here_addr = _Py_get_machine_stack_pointer();
353353
_PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate;
354+
#if _Py_STACK_GROWS_DOWN
354355
if (here_addr > _tstate->c_stack_soft_limit + margin_count * _PyOS_STACK_MARGIN_BYTES) {
356+
#else
357+
if (here_addr <= _tstate->c_stack_soft_limit - margin_count * _PyOS_STACK_MARGIN_BYTES) {
358+
#endif
355359
return 0;
356360
}
357361
if (_tstate->c_stack_hard_limit == 0) {
358362
_Py_InitializeRecursionLimits(tstate);
359363
}
364+
#if _Py_STACK_GROWS_DOWN
360365
return here_addr <= _tstate->c_stack_soft_limit + margin_count * _PyOS_STACK_MARGIN_BYTES;
366+
#else
367+
return here_addr > _tstate->c_stack_soft_limit - margin_count * _PyOS_STACK_MARGIN_BYTES;
368+
#endif
361369
}
362370

363371
void
364372
_Py_EnterRecursiveCallUnchecked(PyThreadState *tstate)
365373
{
366374
uintptr_t here_addr = _Py_get_machine_stack_pointer();
367375
_PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate;
376+
#if _Py_STACK_GROWS_DOWN
368377
if (here_addr < _tstate->c_stack_hard_limit) {
378+
#else
379+
if (here_addr > _tstate->c_stack_hard_limit) {
380+
#endif
369381
Py_FatalError("Unchecked stack overflow.");
370382
}
371383
}
@@ -496,18 +508,33 @@ tstate_set_stack(PyThreadState *tstate,
496508
#ifdef _Py_THREAD_SANITIZER
497509
// Thread sanitizer crashes if we use more than half the stack.
498510
uintptr_t stacksize = top - base;
499-
base += stacksize / 2;
511+
# if _Py_STACK_GROWS_DOWN
512+
base += stacksize/2;
513+
# else
514+
top -= stacksize/2;
515+
# endif
500516
#endif
501517
_PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate;
518+
#if _Py_STACK_GROWS_DOWN
502519
_tstate->c_stack_top = top;
503520
_tstate->c_stack_hard_limit = base + _PyOS_STACK_MARGIN_BYTES;
504521
_tstate->c_stack_soft_limit = base + _PyOS_STACK_MARGIN_BYTES * 2;
505-
506-
#ifndef NDEBUG
522+
# ifndef NDEBUG
507523
// Sanity checks
508524
_PyThreadStateImpl *ts = (_PyThreadStateImpl *)tstate;
509525
assert(ts->c_stack_hard_limit <= ts->c_stack_soft_limit);
510526
assert(ts->c_stack_soft_limit < ts->c_stack_top);
527+
# endif
528+
#else
529+
_tstate->c_stack_top = base;
530+
_tstate->c_stack_hard_limit = top - _PyOS_STACK_MARGIN_BYTES;
531+
_tstate->c_stack_soft_limit = top - _PyOS_STACK_MARGIN_BYTES * 2;
532+
# ifndef NDEBUG
533+
// Sanity checks
534+
_PyThreadStateImpl *ts = (_PyThreadStateImpl *)tstate;
535+
assert(ts->c_stack_hard_limit >= ts->c_stack_soft_limit);
536+
assert(ts->c_stack_soft_limit > ts->c_stack_top);
537+
# endif
511538
#endif
512539
}
513540

@@ -568,9 +595,15 @@ _Py_CheckRecursiveCall(PyThreadState *tstate, const char *where)
568595
uintptr_t here_addr = _Py_get_machine_stack_pointer();
569596
assert(_tstate->c_stack_soft_limit != 0);
570597
assert(_tstate->c_stack_hard_limit != 0);
598+
#if _Py_STACK_GROWS_DOWN
571599
if (here_addr < _tstate->c_stack_hard_limit) {
572600
/* Overflowing while handling an overflow. Give up. */
573601
int kbytes_used = (int)(_tstate->c_stack_top - here_addr)/1024;
602+
#else
603+
if (here_addr > _tstate->c_stack_hard_limit) {
604+
/* Overflowing while handling an overflow. Give up. */
605+
int kbytes_used = (int)(here_addr - _tstate->c_stack_top)/1024;
606+
#endif
574607
char buffer[80];
575608
snprintf(buffer, 80, "Unrecoverable stack overflow (used %d kB)%s", kbytes_used, where);
576609
Py_FatalError(buffer);
@@ -579,7 +612,11 @@ _Py_CheckRecursiveCall(PyThreadState *tstate, const char *where)
579612
return 0;
580613
}
581614
else {
615+
#if _Py_STACK_GROWS_DOWN
582616
int kbytes_used = (int)(_tstate->c_stack_top - here_addr)/1024;
617+
#else
618+
int kbytes_used = (int)(here_addr - _tstate->c_stack_top)/1024;
619+
#endif
583620
tstate->recursion_headroom++;
584621
_PyErr_Format(tstate, PyExc_RecursionError,
585622
"Stack overflow (used %d kB)%s",

configure

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

configure.ac

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1202,6 +1202,14 @@ if test x$MULTIARCH != x; then
12021202
fi
12031203
AC_SUBST([MULTIARCH_CPPFLAGS])
12041204

1205+
# Guess C stack direction
1206+
AS_CASE([$host],
1207+
[hppa*], [_Py_STACK_GROWS_DOWN=0],
1208+
[_Py_STACK_GROWS_DOWN=1])
1209+
AC_DEFINE_UNQUOTED([_Py_STACK_GROWS_DOWN], [$_Py_STACK_GROWS_DOWN],
1210+
[Define to 1 if the machine stack grows down (default); 0 if it grows up.])
1211+
AC_SUBST([_Py_STACK_GROWS_DOWN])
1212+
12051213
dnl Support tiers according to https://peps.python.org/pep-0011/
12061214
dnl
12071215
dnl NOTE: Windows support tiers are defined in PC/pyconfig.h.

pyconfig.h.in

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2050,6 +2050,9 @@
20502050
/* HACL* library can compile SIMD256 implementations */
20512051
#undef _Py_HACL_CAN_COMPILE_VEC256
20522052

2053+
/* Define to 1 if the machine stack grows down (default); 0 if it grows up. */
2054+
#undef _Py_STACK_GROWS_DOWN
2055+
20532056
/* Define if you want to use tail-calling interpreters in CPython. */
20542057
#undef _Py_TAIL_CALL_INTERP
20552058

0 commit comments

Comments
 (0)