https://github.com/python/cpython/commit/f6dd9c12a8ba391cbbcc793411ac7dcfa6e01028
commit: f6dd9c12a8ba391cbbcc793411ac7dcfa6e01028
branch: main
author: Stefano Rivera <[email protected]>
committer: encukou <[email protected]>
date: 2025-11-17T14:41:22+01:00
summary:

GH-139914: Handle stack growth direction on HPPA (GH-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]>

files:
A 
Misc/NEWS.d/next/Core_and_Builtins/2025-10-13-13-54-19.gh-issue-139914.M-y_3E.rst
M Include/internal/pycore_ceval.h
M Include/internal/pycore_pystate.h
M Include/pyport.h
M Lib/test/test_call.py
M Modules/_testcapimodule.c
M Python/ceval.c
M configure
M configure.ac
M pyconfig.h.in

diff --git a/Include/internal/pycore_ceval.h b/Include/internal/pycore_ceval.h
index 33b9fd053f70cb..47c42fccdc2376 100644
--- a/Include/internal/pycore_ceval.h
+++ b/Include/internal/pycore_ceval.h
@@ -217,7 +217,11 @@ extern void _PyEval_DeactivateOpCache(void);
 static inline int _Py_MakeRecCheck(PyThreadState *tstate)  {
     uintptr_t here_addr = _Py_get_machine_stack_pointer();
     _PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate;
+#if _Py_STACK_GROWS_DOWN
     return here_addr < _tstate->c_stack_soft_limit;
+#else
+    return here_addr > _tstate->c_stack_soft_limit;
+#endif
 }
 
 // Export for '_json' shared extension, used via _Py_EnterRecursiveCall()
@@ -249,7 +253,11 @@ static inline int _Py_ReachedRecursionLimit(PyThreadState 
*tstate)  {
     uintptr_t here_addr = _Py_get_machine_stack_pointer();
     _PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate;
     assert(_tstate->c_stack_hard_limit != 0);
+#if _Py_STACK_GROWS_DOWN
     return here_addr <= _tstate->c_stack_soft_limit;
+#else
+    return here_addr >= _tstate->c_stack_soft_limit;
+#endif
 }
 
 static inline void _Py_LeaveRecursiveCall(void)  {
diff --git a/Include/internal/pycore_pystate.h 
b/Include/internal/pycore_pystate.h
index cab458f84028e2..189a8dde9f09ed 100644
--- a/Include/internal/pycore_pystate.h
+++ b/Include/internal/pycore_pystate.h
@@ -331,7 +331,11 @@ _Py_RecursionLimit_GetMargin(PyThreadState *tstate)
     _PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate;
     assert(_tstate->c_stack_hard_limit != 0);
     intptr_t here_addr = _Py_get_machine_stack_pointer();
+#if _Py_STACK_GROWS_DOWN
     return Py_ARITHMETIC_RIGHT_SHIFT(intptr_t, here_addr - 
(intptr_t)_tstate->c_stack_soft_limit, _PyOS_STACK_MARGIN_SHIFT);
+#else
+    return Py_ARITHMETIC_RIGHT_SHIFT(intptr_t, 
(intptr_t)_tstate->c_stack_soft_limit - here_addr, _PyOS_STACK_MARGIN_SHIFT);
+#endif
 }
 
 #ifdef __cplusplus
diff --git a/Include/pyport.h b/Include/pyport.h
index e77b39026a59c1..b250f9e308f2dd 100644
--- a/Include/pyport.h
+++ b/Include/pyport.h
@@ -677,4 +677,10 @@ extern "C" {
 #endif
 
 
+// Assume the stack grows down unless specified otherwise
+#ifndef _Py_STACK_GROWS_DOWN
+#  define _Py_STACK_GROWS_DOWN 1
+#endif
+
+
 #endif /* Py_PYPORT_H */
diff --git a/Lib/test/test_call.py b/Lib/test/test_call.py
index 31e58e825be422..f42526aee19417 100644
--- a/Lib/test/test_call.py
+++ b/Lib/test/test_call.py
@@ -1048,9 +1048,14 @@ def get_sp():
 
         this_sp = _testinternalcapi.get_stack_pointer()
         lower_sp = _testcapi.pyobject_vectorcall(get_sp, (), ())
-        self.assertLess(lower_sp, this_sp)
+        if _testcapi._Py_STACK_GROWS_DOWN:
+            self.assertLess(lower_sp, this_sp)
+            safe_margin = this_sp - lower_sp
+        else:
+            self.assertGreater(lower_sp, this_sp)
+            safe_margin = lower_sp - this_sp
         # Add an (arbitrary) extra 25% for safety
-        safe_margin = (this_sp - lower_sp) * 5 / 4
+        safe_margin = safe_margin * 5 / 4
         self.assertLess(safe_margin, _testinternalcapi.get_stack_margin())
 
     @skip_on_s390x
diff --git 
a/Misc/NEWS.d/next/Core_and_Builtins/2025-10-13-13-54-19.gh-issue-139914.M-y_3E.rst
 
b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-13-13-54-19.gh-issue-139914.M-y_3E.rst
new file mode 100644
index 00000000000000..7529108d5d4772
--- /dev/null
+++ 
b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-13-13-54-19.gh-issue-139914.M-y_3E.rst
@@ -0,0 +1 @@
+Restore support for HP PA-RISC, which has an upwards-growing stack.
diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c
index 22cd731d410082..c14f925b4e7632 100644
--- a/Modules/_testcapimodule.c
+++ b/Modules/_testcapimodule.c
@@ -3359,6 +3359,10 @@ _testcapi_exec(PyObject *m)
     PyModule_AddObject(m, "INT64_MAX", PyLong_FromInt64(INT64_MAX));
     PyModule_AddObject(m, "UINT64_MAX", PyLong_FromUInt64(UINT64_MAX));
 
+    if (PyModule_AddIntMacro(m, _Py_STACK_GROWS_DOWN)) {
+        return -1;
+    }
+
     if (PyModule_AddIntMacro(m, Py_single_input)) {
         return -1;
     }
diff --git a/Python/ceval.c b/Python/ceval.c
index 31b81a37464718..25294ebd993f6c 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -351,13 +351,21 @@ _Py_ReachedRecursionLimitWithMargin(PyThreadState 
*tstate, int margin_count)
 {
     uintptr_t here_addr = _Py_get_machine_stack_pointer();
     _PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate;
+#if _Py_STACK_GROWS_DOWN
     if (here_addr > _tstate->c_stack_soft_limit + margin_count * 
_PyOS_STACK_MARGIN_BYTES) {
+#else
+    if (here_addr <= _tstate->c_stack_soft_limit - margin_count * 
_PyOS_STACK_MARGIN_BYTES) {
+#endif
         return 0;
     }
     if (_tstate->c_stack_hard_limit == 0) {
         _Py_InitializeRecursionLimits(tstate);
     }
+#if _Py_STACK_GROWS_DOWN
     return here_addr <= _tstate->c_stack_soft_limit + margin_count * 
_PyOS_STACK_MARGIN_BYTES;
+#else
+    return here_addr > _tstate->c_stack_soft_limit - margin_count * 
_PyOS_STACK_MARGIN_BYTES;
+#endif
 }
 
 void
@@ -365,7 +373,11 @@ _Py_EnterRecursiveCallUnchecked(PyThreadState *tstate)
 {
     uintptr_t here_addr = _Py_get_machine_stack_pointer();
     _PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate;
+#if _Py_STACK_GROWS_DOWN
     if (here_addr < _tstate->c_stack_hard_limit) {
+#else
+    if (here_addr > _tstate->c_stack_hard_limit) {
+#endif
         Py_FatalError("Unchecked stack overflow.");
     }
 }
@@ -496,18 +508,33 @@ tstate_set_stack(PyThreadState *tstate,
 #ifdef _Py_THREAD_SANITIZER
     // Thread sanitizer crashes if we use more than half the stack.
     uintptr_t stacksize = top - base;
-    base += stacksize / 2;
+#  if _Py_STACK_GROWS_DOWN
+    base += stacksize/2;
+#  else
+    top -= stacksize/2;
+#  endif
 #endif
     _PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate;
+#if _Py_STACK_GROWS_DOWN
     _tstate->c_stack_top = top;
     _tstate->c_stack_hard_limit = base + _PyOS_STACK_MARGIN_BYTES;
     _tstate->c_stack_soft_limit = base + _PyOS_STACK_MARGIN_BYTES * 2;
-
-#ifndef NDEBUG
+#  ifndef NDEBUG
     // Sanity checks
     _PyThreadStateImpl *ts = (_PyThreadStateImpl *)tstate;
     assert(ts->c_stack_hard_limit <= ts->c_stack_soft_limit);
     assert(ts->c_stack_soft_limit < ts->c_stack_top);
+#  endif
+#else
+    _tstate->c_stack_top = base;
+    _tstate->c_stack_hard_limit = top - _PyOS_STACK_MARGIN_BYTES;
+    _tstate->c_stack_soft_limit = top - _PyOS_STACK_MARGIN_BYTES * 2;
+#  ifndef NDEBUG
+    // Sanity checks
+    _PyThreadStateImpl *ts = (_PyThreadStateImpl *)tstate;
+    assert(ts->c_stack_hard_limit >= ts->c_stack_soft_limit);
+    assert(ts->c_stack_soft_limit > ts->c_stack_top);
+#  endif
 #endif
 }
 
@@ -568,9 +595,15 @@ _Py_CheckRecursiveCall(PyThreadState *tstate, const char 
*where)
     uintptr_t here_addr = _Py_get_machine_stack_pointer();
     assert(_tstate->c_stack_soft_limit != 0);
     assert(_tstate->c_stack_hard_limit != 0);
+#if _Py_STACK_GROWS_DOWN
     if (here_addr < _tstate->c_stack_hard_limit) {
         /* Overflowing while handling an overflow. Give up. */
         int kbytes_used = (int)(_tstate->c_stack_top - here_addr)/1024;
+#else
+    if (here_addr > _tstate->c_stack_hard_limit) {
+        /* Overflowing while handling an overflow. Give up. */
+        int kbytes_used = (int)(here_addr - _tstate->c_stack_top)/1024;
+#endif
         char buffer[80];
         snprintf(buffer, 80, "Unrecoverable stack overflow (used %d kB)%s", 
kbytes_used, where);
         Py_FatalError(buffer);
@@ -579,7 +612,11 @@ _Py_CheckRecursiveCall(PyThreadState *tstate, const char 
*where)
         return 0;
     }
     else {
+#if _Py_STACK_GROWS_DOWN
         int kbytes_used = (int)(_tstate->c_stack_top - here_addr)/1024;
+#else
+        int kbytes_used = (int)(here_addr - _tstate->c_stack_top)/1024;
+#endif
         tstate->recursion_headroom++;
         _PyErr_Format(tstate, PyExc_RecursionError,
                     "Stack overflow (used %d kB)%s",
diff --git a/configure b/configure
index eeb24c1d844e86..a4514f80c3af37 100755
--- a/configure
+++ b/configure
@@ -967,6 +967,7 @@ LDLIBRARY
 LIBRARY
 BUILDEXEEXT
 NO_AS_NEEDED
+_Py_STACK_GROWS_DOWN
 MULTIARCH_CPPFLAGS
 PLATFORM_TRIPLET
 MULTIARCH
@@ -7213,6 +7214,18 @@ if test x$MULTIARCH != x; then
 fi
 
 
+# Guess C stack direction
+case $host in #(
+  hppa*) :
+    _Py_STACK_GROWS_DOWN=0 ;; #(
+  *) :
+    _Py_STACK_GROWS_DOWN=1 ;;
+esac
+
+printf "%s\n" "#define _Py_STACK_GROWS_DOWN $_Py_STACK_GROWS_DOWN" >>confdefs.h
+
+
+
 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for PEP 11 support 
tier" >&5
 printf %s "checking for PEP 11 support tier... " >&6; }
 case $host/$ac_cv_cc_name in #(
diff --git a/configure.ac b/configure.ac
index 92adc44da0d6fe..a059a07bec2fe4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1202,6 +1202,14 @@ if test x$MULTIARCH != x; then
 fi
 AC_SUBST([MULTIARCH_CPPFLAGS])
 
+# Guess C stack direction
+AS_CASE([$host],
+  [hppa*], [_Py_STACK_GROWS_DOWN=0],
+  [_Py_STACK_GROWS_DOWN=1])
+AC_DEFINE_UNQUOTED([_Py_STACK_GROWS_DOWN], [$_Py_STACK_GROWS_DOWN],
+  [Define to 1 if the machine stack grows down (default); 0 if it grows up.])
+AC_SUBST([_Py_STACK_GROWS_DOWN])
+
 dnl Support tiers according to https://peps.python.org/pep-0011/
 dnl
 dnl NOTE: Windows support tiers are defined in PC/pyconfig.h.
diff --git a/pyconfig.h.in b/pyconfig.h.in
index fb12079bafa95e..8a9f5ca8ec826d 100644
--- a/pyconfig.h.in
+++ b/pyconfig.h.in
@@ -2050,6 +2050,9 @@
 /* HACL* library can compile SIMD256 implementations */
 #undef _Py_HACL_CAN_COMPILE_VEC256
 
+/* Define to 1 if the machine stack grows down (default); 0 if it grows up. */
+#undef _Py_STACK_GROWS_DOWN
+
 /* Define if you want to use tail-calling interpreters in CPython. */
 #undef _Py_TAIL_CALL_INTERP
 

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to