From 28551d58b4a7968675a73dd82f96e8dc363afff2 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Mon, 28 Apr 2025 15:36:31 +0200 Subject: [PATCH 1/3] gh-128972: Add `_Py_ALIGN_AS` and revert `PyASCIIObject` memory layout. Add `_Py_ALIGN_AS` as per C API WG vote: https://github.com/capi-workgroup/decisions/issues/61 This patch only adds it to free-threaded builds; the `#ifdef Py_GIL_DISABLED` can be removed in the future. Use this to revert `PyASCIIObject` memory layout for non-free-threaded builds. The long-term plan is to deprecate the entire struct; until that happens it's better to keep it unchanged, as courtesy to people that rely on it despite it not being stable ABI. --- Include/cpython/unicodeobject.h | 30 +++++++++----- Include/pymacro.h | 39 +++++++++++++++++++ ...-04-28-15-36-01.gh-issue-128972.8bZMIm.rst | 3 ++ Objects/unicodeobject.c | 6 +-- 4 files changed, 65 insertions(+), 13 deletions(-) create mode 100644 Misc/NEWS.d/next/C_API/2025-04-28-15-36-01.gh-issue-128972.8bZMIm.rst diff --git a/Include/cpython/unicodeobject.h b/Include/cpython/unicodeobject.h index e8b04d158b0805..136f5d5c5f8425 100644 --- a/Include/cpython/unicodeobject.h +++ b/Include/cpython/unicodeobject.h @@ -99,6 +99,11 @@ typedef struct { PyObject_HEAD Py_ssize_t length; /* Number of code points in the string */ Py_hash_t hash; /* Hash value; -1 if not set */ +#ifdef Py_GIL_DISABLED + /* Ensure 4 byte alignment for PyUnicode_DATA(), see gh-63736 on m68k. + In the non-free-threaded build, we'll use explicit padding instead */ + _Py_ALIGN_AS(4) +#endif struct { /* If interned is non-zero, the two references from the dictionary to this object are *not* counted in ob_refcnt. @@ -109,7 +114,12 @@ typedef struct { 3: Interned, Immortal, and Static This categorization allows the runtime to determine the right cleanup mechanism at runtime shutdown. */ - uint16_t interned; +#ifdef Py_GIL_DISABLED + // Needs to be accessed atomically, so can't be a bit field. + unsigned char interned; +#else + unsigned int interned:2; +#endif /* Character size: - PyUnicode_1BYTE_KIND (1): @@ -132,23 +142,23 @@ typedef struct { * all characters are in the range U+0000-U+10FFFF * at least one character is in the range U+10000-U+10FFFF */ - unsigned short kind:3; + unsigned int kind:3; /* Compact is with respect to the allocation scheme. Compact unicode objects only require one memory block while non-compact objects use one block for the PyUnicodeObject struct and another for its data buffer. */ - unsigned short compact:1; + unsigned int compact:1; /* The string only contains characters in the range U+0000-U+007F (ASCII) and the kind is PyUnicode_1BYTE_KIND. If ascii is set and compact is set, use the PyASCIIObject structure. */ - unsigned short ascii:1; + unsigned int ascii:1; /* The object is statically allocated. */ - unsigned short statically_allocated:1; + unsigned int statically_allocated:1; +#ifndef Py_GIL_DISABLED /* Padding to ensure that PyUnicode_DATA() is always aligned to - 4 bytes (see issue #19537 on m68k) and we use unsigned short to avoid - the extra four bytes on 32-bit Windows. This is restricted features - for specific compilers including GCC, MSVC, Clang and IBM's XL compiler. */ - unsigned short :10; + 4 bytes (see issue gh-63736 on m68k) */ + unsigned int :24; +#endif } state; } PyASCIIObject; @@ -198,7 +208,7 @@ typedef struct { /* Use only if you know it's a string */ static inline unsigned int PyUnicode_CHECK_INTERNED(PyObject *op) { #ifdef Py_GIL_DISABLED - return _Py_atomic_load_uint16_relaxed(&_PyASCIIObject_CAST(op)->state.interned); + return _Py_atomic_load_uint8_relaxed(&_PyASCIIObject_CAST(op)->state.interned); #else return _PyASCIIObject_CAST(op)->state.interned; #endif diff --git a/Include/pymacro.h b/Include/pymacro.h index a82f347866e8d0..76a55336629116 100644 --- a/Include/pymacro.h +++ b/Include/pymacro.h @@ -23,6 +23,45 @@ # define static_assert _Static_assert #endif + +// _Py_ALIGN_AS: this compiler's spelling of `alignas` keyword, +// We currently use alignas for free-threaded builds only; additional compat +// checking would be great before we add it to the default build. +// Standards/compiler support: +// - `alignas` is a keyword in C23 and C++11. +// - `_Alignas` is a keyword in C11 +// - GCC & clang has __attribute__((aligned)) +// (use that for older standards in pedantic mode) +// - MSVC has __declspec(align) +// - `_Alignas` is common C compiler extension +// Older compilers may name it differently; to allow compilation on such +// unsupported platforms, we don't redefine _Py_ALIGN_AS if it's already +// defined. Note that defining it wrong (including defining it to nothing) will +// cause ABI incompatibilities. +#ifdef Py_GIL_DISABLED +# ifndef _Py_ALIGN_AS +# ifdef __cplusplus +# if (__cplusplus < 201103L) \ + && (defined(__GNUC__) || defined(__clang__)) +# define _Py_ALIGN_AS(V) __attribute__((aligned(V))) +# else +# define _Py_ALIGN_AS(V) alignas(V) +# endif +# elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L +# define _Py_ALIGN_AS(V) alignas(V) +# elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L +# define _Py_ALIGN_AS(V) _Alignas(V) +# elif (defined(__GNUC__) || defined(__clang__)) \ + && defined(__STDC_VERSION__) && __STDC_VERSION__ < 201112L +# define _Py_ALIGN_AS(V) __attribute__((aligned(V))) +# elif defined(_MSC_VER) +# define _Py_ALIGN_AS(V) __declspec(align(V)) +# else +# define _Py_ALIGN_AS(V) _Alignas(V) +# endif +# endif +#endif + /* Minimum value between x and y */ #define Py_MIN(x, y) (((x) > (y)) ? (y) : (x)) diff --git a/Misc/NEWS.d/next/C_API/2025-04-28-15-36-01.gh-issue-128972.8bZMIm.rst b/Misc/NEWS.d/next/C_API/2025-04-28-15-36-01.gh-issue-128972.8bZMIm.rst new file mode 100644 index 00000000000000..4b6a6e3606ff16 --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2025-04-28-15-36-01.gh-issue-128972.8bZMIm.rst @@ -0,0 +1,3 @@ +For non-free-threaded builds, the memory layout of :c:struct:`PyASCIIObject` +is reverted to match Python 3.13. (Note that the structure is not part of +stable ABI and so its memory layout is *guaranteed* to remain stable.) diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 53c50734281db9..a83c3ba4439a3c 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -15904,7 +15904,7 @@ immortalize_interned(PyObject *s) _Py_DecRefTotal(_PyThreadState_GET()); } #endif - FT_ATOMIC_STORE_UINT16_RELAXED(_PyUnicode_STATE(s).interned, SSTATE_INTERNED_IMMORTAL); + FT_ATOMIC_STORE_UINT8_RELAXED(_PyUnicode_STATE(s).interned, SSTATE_INTERNED_IMMORTAL); _Py_SetImmortal(s); } @@ -16022,7 +16022,7 @@ intern_common(PyInterpreterState *interp, PyObject *s /* stolen */, Py_DECREF(s); Py_DECREF(s); } - FT_ATOMIC_STORE_UINT16_RELAXED(_PyUnicode_STATE(s).interned, SSTATE_INTERNED_MORTAL); + FT_ATOMIC_STORE_UINT8_RELAXED(_PyUnicode_STATE(s).interned, SSTATE_INTERNED_MORTAL); /* INTERNED_MORTAL -> INTERNED_IMMORTAL (if needed) */ @@ -16158,7 +16158,7 @@ _PyUnicode_ClearInterned(PyInterpreterState *interp) Py_UNREACHABLE(); } if (!shared) { - FT_ATOMIC_STORE_UINT16_RELAXED(_PyUnicode_STATE(s).interned, SSTATE_NOT_INTERNED); + FT_ATOMIC_STORE_UINT8_RELAXED(_PyUnicode_STATE(s).interned, SSTATE_NOT_INTERNED); } } #ifdef INTERNED_STATS From 9d4d01174e58367a4a1cbdaaf818d7d096408502 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Tue, 29 Apr 2025 13:41:39 +0200 Subject: [PATCH 2/3] Simplify the __cplusplus branch --- Include/pymacro.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Include/pymacro.h b/Include/pymacro.h index 76a55336629116..a66d63b6335a29 100644 --- a/Include/pymacro.h +++ b/Include/pymacro.h @@ -41,9 +41,12 @@ #ifdef Py_GIL_DISABLED # ifndef _Py_ALIGN_AS # ifdef __cplusplus -# if (__cplusplus < 201103L) \ - && (defined(__GNUC__) || defined(__clang__)) +# if __cplusplus >= 201103L +# define _Py_ALIGN_AS(V) alignas(V) +# elif defined(__GNUC__) || defined(__clang__) # define _Py_ALIGN_AS(V) __attribute__((aligned(V))) +# elif defined(_MSC_VER) +# define _Py_ALIGN_AS(V) __declspec(align(V)) # else # define _Py_ALIGN_AS(V) alignas(V) # endif From 95ffa4c14cb887fec9a9e1666334051f0f08a9b6 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Tue, 29 Apr 2025 13:45:13 +0200 Subject: [PATCH 3/3] Remove redundant __STDC_VERSION__ check --- Include/pymacro.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Include/pymacro.h b/Include/pymacro.h index a66d63b6335a29..218987a80b0d91 100644 --- a/Include/pymacro.h +++ b/Include/pymacro.h @@ -54,8 +54,7 @@ # define _Py_ALIGN_AS(V) alignas(V) # elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L # define _Py_ALIGN_AS(V) _Alignas(V) -# elif (defined(__GNUC__) || defined(__clang__)) \ - && defined(__STDC_VERSION__) && __STDC_VERSION__ < 201112L +# elif (defined(__GNUC__) || defined(__clang__)) # define _Py_ALIGN_AS(V) __attribute__((aligned(V))) # elif defined(_MSC_VER) # define _Py_ALIGN_AS(V) __declspec(align(V)) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy