On Mon, Sep 7, 2026 at 6:08 PM Tomasz Kamiński <[email protected]> wrote:
> Since the implementation of P0528R3, "The Curious Case of Padding Bits,
> Featuring Atomic Compare-and-Exchange", in r13-2548-g157236dbd62164, the
> atomic<T> clears the padding bits of T (if any) in construction and each
> store operation. The atomic<T>::compare_exchange_(weak_strong) (and
> __atomic_impl::__compare_exchange<false>) implementation assumes above,
> to provide shorter (in terms of instructions) implementation.
>
> However, as indicated in the issues, this assumption may not hold when
> the store operation is performed in the TU compiled with GCC 12 or eariel.
> In consequence program that worked correctly when compiled with GCC 12
> (while not required by standard to do so), may trigger infinite loop when
> some of TUs are recompiled with GCC 13.
>
> This patch introduces and _GLIBCXX_FORCE_ATOMIC_CMPXCHG_COMPAT and
> corresponding --enable-atomic-cmpxchg-compat configuration option, that
> forces atomic<T> to use implementation that handles non-zero padding
> bits (already used for atomic_ref).
>
> libstdc++-v3/ChangeLog:
>
> libstdc++/PR126230
> * acinclude.m4 (GLIBCXX_FORCE_ATOMIC_CMPXCHG_COMPAT): Define.
> * config.h.in: Regenerate.
> * configure: Regenerate.
> * configure.ac: Call GLIBCXX_FORCE_ATOMIC_CMPXCHG_COMPAT.
> * doc/html/manual/using_macros.html: Regenerate.
> * doc/xml/manual/using.xml
> (_GLIBCXX_FORCE_ATOMIC_CMPXCHG_COMPAT): Add entry.
> * include/bits/atomic_base.h (__atomic_impl::__compare_exchange):
> Rename _AtomicRef template parameter to _NonZeroPadding. And
> default it to true if _GLIBCXX_FORCE_ATOMIC_CMPXCHG_COMPAT is
> defined.
> * testsuite/29_atomics/atomic/126230.cc: New test.
> * testsuite/29_atomics/atomic/126230_compat.cc: Test above
> with _GLIBCXX_FORCE_ATOMIC_CMPXCHG_COMPAT defined.
> ---
> In the end I haven't followed Jakub's suggestion to mark the
> __atomic_compare_exchange path returning true as likely, in this patch,
> as I would like to do separate measurments on it (we would impact
> the pre-existing uses of atomic_ref).
>
> For the test, I am reinterpret casting the atomic, and wrritting
> over it object reprsentation. While not portable, I haven't have a
> better idea how to test this.
>
> Tested on x86_64-linux locally. Also checked the
> --enable-atomic-cmpxchg-compat=yes.
> Testing full build and boostrap powerpc64-linux.
>
Boostrap was sucessfull and all test passed.
> OK for trunk? I assume we want to backport this up to 14?
>
> libstdc++-v3/acinclude.m4 | 19 ++++++++++
> libstdc++-v3/config.h.in | 3 ++
> libstdc++-v3/configure | 38 ++++++++++++++++---
> libstdc++-v3/configure.ac | 3 ++
> .../doc/html/manual/using_macros.html | 9 +++++
> libstdc++-v3/doc/xml/manual/using.xml | 13 +++++++
> libstdc++-v3/include/bits/atomic_base.h | 8 +++-
> .../testsuite/29_atomics/atomic/126230.cc | 36 ++++++++++++++++++
> .../29_atomics/atomic/126230_compat.cc | 5 +++
> 9 files changed, 126 insertions(+), 8 deletions(-)
> create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/126230.cc
> create mode 100644
> libstdc++-v3/testsuite/29_atomics/atomic/126230_compat.cc
>
> diff --git a/libstdc++-v3/acinclude.m4 b/libstdc++-v3/acinclude.m4
> index 8c44e618cb8..5b36939a2f8 100644
> --- a/libstdc++-v3/acinclude.m4
> +++ b/libstdc++-v3/acinclude.m4
> @@ -5888,6 +5888,25 @@ AC_DEFUN([GLIBCXX_CHECK_STDIO_LOCKING], [
> AC_LANG_POP([C++])
> ])
>
> +dnl
> +dnl Forces atomic::compare_exchange_weak and
> atomic::compare_exchange_strong
> +dnl to use slower implementation that handles non-zero padding bits inside
> +dnl the stored object, that may be produced by TUs compiled by GCC 12 or
> eariel.
> +dnl Set to yes, for programs linked against binaries compiled with such
> GCC
> +dnl version, that use atomic with types containing padding bits.
> +dnl
> +dnl --enable-atomic-cmpxchg-compat=yes turns compatibility mode and
> defines
> +dnl _GLIBCXX_FORCE_ATOMIC_CMPXCHG_COMPAT.
> +dnl Default is no.
> +dnl
> +AC_DEFUN([GLIBCXX_FORCE_ATOMIC_CMPXCHG_COMPAT], [
> + GLIBCXX_ENABLE(atomic-cmpxchg-compat,no,,
> + [enable compatibility mode for atomic::compare_exchange], [permit
> yes|no])
> + if test $enable_atomic_cmpxchg_compat = yes; then
> + AC_DEFINE(_GLIBCXX_FORCE_ATOMIC_CMPXCHG_COMPAT, 1,
> + [Define to enable compatibility mode for atomic::compare_exchange])
> + fi
> +])
>
> # Macros from the top-level gcc directory.
> m4_include([../config/gc++filt.m4])
> diff --git a/libstdc++-v3/config.h.in b/libstdc++-v3/config.h.in
> index 4cfb9ba26be..a08c42d19e8 100644
> --- a/libstdc++-v3/config.h.in
> +++ b/libstdc++-v3/config.h.in
> @@ -658,6 +658,9 @@
> /* Define to use concept checking code from the boost libraries. */
> #undef _GLIBCXX_CONCEPT_CHECKS
>
> +/* Define to enable compatibility mode for atomic::compare_exchange */
> +#undef _GLIBCXX_FORCE_ATOMIC_CMPXCHG_COMPAT
> +
> /* Define to 1 if a fully dynamic basic_string is wanted, 0 to disable,
> undefined for platform defaults */
> #undef _GLIBCXX_FULLY_DYNAMIC_STRING
> diff --git a/libstdc++-v3/configure b/libstdc++-v3/configure
> index 577640fcb75..84adff7bb9e 100755
> --- a/libstdc++-v3/configure
> +++ b/libstdc++-v3/configure
> @@ -971,6 +971,7 @@ enable_libstdcxx_backtrace
> enable_libstdcxx_static_eh_pool
> with_libstdcxx_eh_pool_obj_count
> with_libstdcxx_zoneinfo
> +enable_atomic_cmpxchg_compat
> enable_cet
> with_gxx_include_dir
> enable_version_specific_runtime_libs
> @@ -1687,6 +1688,9 @@ Optional Features:
> --enable-libstdcxx-static-eh-pool
> use a fixed-size static buffer for allocating
> exceptions if malloc fails
> + --enable-atomic-cmpxchg-compat
> + enable compatibility mode for
> + atomic::compare_exchange [default=no]
> --enable-cet enable Intel CET in target libraries
> [default=auto]
> --enable-version-specific-runtime-libs
> Specify that runtime libraries should be
> installed
> @@ -12679,7 +12683,7 @@ else
> lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
> lt_status=$lt_dlunknown
> cat > conftest.$ac_ext <<_LT_EOF
> -#line 12682 "configure"
> +#line 12686 "configure"
> #include "confdefs.h"
>
> #if HAVE_DLFCN_H
> @@ -12785,7 +12789,7 @@ else
> lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
> lt_status=$lt_dlunknown
> cat > conftest.$ac_ext <<_LT_EOF
> -#line 12788 "configure"
> +#line 12792 "configure"
> #include "confdefs.h"
>
> #if HAVE_DLFCN_H
> @@ -16475,7 +16479,7 @@ $as_echo "$glibcxx_cv_atomic_word" >&6; }
> # Fake what AC_COMPILE_IFELSE does.
>
> cat > conftest.$ac_ext << EOF
> -#line 16478 "configure"
> +#line 16482 "configure"
> #include "atomic_word.h"
> int main()
> {
> @@ -16622,7 +16626,7 @@ $as_echo "mutex" >&6; }
> # unnecessary for this test.
>
> cat > conftest.$ac_ext << EOF
> -#line 16625 "configure"
> +#line 16629 "configure"
> int main()
> {
> _Decimal32 d1;
> @@ -16663,7 +16667,7 @@ ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
> # unnecessary for this test.
>
> cat > conftest.$ac_ext << EOF
> -#line 16666 "configure"
> +#line 16670 "configure"
> template<typename T1, typename T2>
> struct same
> { typedef T2 type; };
> @@ -53737,7 +53741,7 @@ $as_echo "$glibcxx_cv_libbacktrace_atomics" >&6; }
> CXXFLAGS='-O0 -S'
>
> cat > conftest.$ac_ext << EOF
> -#line 53740 "configure"
> +#line 53744 "configure"
> #include <stddef.h>
> int main()
> {
> @@ -54909,6 +54913,28 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
>
>
>
> +# For atomic::compare_exchange
> +
> + # Check whether --enable-atomic-cmpxchg-compat was given.
> +if test "${enable_atomic_cmpxchg_compat+set}" = set; then :
> + enableval=$enable_atomic_cmpxchg_compat;
> + case "$enableval" in
> + yes|no) ;;
> + *) as_fn_error $? "Unknown argument to enable/disable
> atomic-cmpxchg-compat" "$LINENO" 5 ;;
> + esac
> +
> +else
> + enable_atomic_cmpxchg_compat=no
> +fi
> +
> +
> + if test $enable_atomic_cmpxchg_compat = yes; then
> +
> +$as_echo "#define _GLIBCXX_FORCE_ATOMIC_CMPXCHG_COMPAT 1" >>confdefs.h
> +
> + fi
> +
> +
> # Define documentation rules conditionally.
>
> # See if makeinfo has been installed and is modern enough
> diff --git a/libstdc++-v3/configure.ac b/libstdc++-v3/configure.ac
> index db08de7735e..af8efd22eef 100644
> --- a/libstdc++-v3/configure.ac
> +++ b/libstdc++-v3/configure.ac
> @@ -666,6 +666,9 @@ GLIBCXX_CHECK_DEBUGGING
> # For std::print
> GLIBCXX_CHECK_STDIO_LOCKING
>
> +# For atomic::compare_exchange
> +GLIBCXX_FORCE_ATOMIC_CMPXCHG_COMPAT
> +
> # Define documentation rules conditionally.
>
> # See if makeinfo has been installed and is modern enough
> diff --git a/libstdc++-v3/doc/html/manual/using_macros.html
> b/libstdc++-v3/doc/html/manual/using_macros.html
> index 6cb467471c8..4cba4015305 100644
> --- a/libstdc++-v3/doc/html/manual/using_macros.html
> +++ b/libstdc++-v3/doc/html/manual/using_macros.html
> @@ -177,4 +177,13 @@
> <code class="code">result_type</code> values (instead of <code
> class="code">double</code>).
> This is required for standard conformance, but is incompatible with
> the default implementation of those class templates.
> + </p></dd><dt><span class="term"><code
> class="code">_GLIBCXX_FORCE_ATOMIC_CMPXCHG_COMPAT</code></span></dt><dd><p>
> + Undefined by default. Configurable via
> + <code class="code">--enable-atomic-cmpxchg-compat</code>. When
> defined,
> + Forces atomic::compare_exchange_weak and
> + atomic::compare_exchange_strong to use slower implementation that
> + handles non-zero padding bits inside the stored object, that may be
> + produced by TUs compiled by GCC 12 or eariel.
> + Use for programs linked against binaries compiled with such GCC
> + version, that use atomic with types containing padding bits.
> </p></dd></dl></div></div><div class="navfooter"><hr /><table
> width="100%" summary="Navigation footer"><tr><td width="40%"
> align="left"><a accesskey="p" href="using_headers.html">Prev</a> </td><td
> width="20%" align="center"><a accesskey="u"
> href="using.html">Up</a></td><td width="40%" align="right"> <a
> accesskey="n" href="using_dual_abi.html">Next</a></td></tr><tr><td
> width="40%" align="left" valign="top">Headers </td><td width="20%"
> align="center"><a accesskey="h" href="../index.html">Home</a></td><td
> width="40%" align="right" valign="top"> Dual
> ABI</td></tr></table></div></body></html>
> \ No newline at end of file
> diff --git a/libstdc++-v3/doc/xml/manual/using.xml
> b/libstdc++-v3/doc/xml/manual/using.xml
> index da710be408f..5849f75205d 100644
> --- a/libstdc++-v3/doc/xml/manual/using.xml
> +++ b/libstdc++-v3/doc/xml/manual/using.xml
> @@ -1412,6 +1412,19 @@ g++ -Winvalid-pch -I. -include stdc++.h -H -g -O2
> hello.cc -o test.exe
> the default implementation of those class templates.
> </para>
> </listitem></varlistentry>
> +
> <varlistentry><term><code>_GLIBCXX_FORCE_ATOMIC_CMPXCHG_COMPAT</code></term>
> + <listitem>
> + <para>
> + Undefined by default. Configurable via
> + <code>--enable-atomic-cmpxchg-compat</code>. When defined,
> + Forces atomic::compare_exchange_weak and
> + atomic::compare_exchange_strong to use slower implementation that
> + handles non-zero padding bits inside the stored object, that may be
> + produced by TUs compiled by GCC 12 or eariel.
> + Use for programs linked against binaries compiled with such GCC
> + version, that use atomic with types containing padding bits.
> + </para>
> + </listitem></varlistentry>
> </variablelist>
>
> </section>
> diff --git a/libstdc++-v3/include/bits/atomic_base.h
> b/libstdc++-v3/include/bits/atomic_base.h
> index 3a8e9a7b4c1..9ac955be633 100644
> --- a/libstdc++-v3/include/bits/atomic_base.h
> +++ b/libstdc++-v3/include/bits/atomic_base.h
> @@ -1055,7 +1055,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> return __ptr;
> }
>
> - template<bool _AtomicRef = false, typename _Tp>
> +#ifdef _GLIBCXX_FORCE_ATOMIC_CMPXCHG_COMPAT
> + template<bool _NonZeroPadding = true, typename _Tp>
> +#else
> + template<bool _NonZeroPadding = false, typename _Tp>
> +#endif
> _GLIBCXX_ALWAYS_INLINE bool
> __compare_exchange(_Tp& __val, _Val<_Tp>& __e, _Val<_Tp>& __i,
> bool __is_weak,
> @@ -1072,7 +1076,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> std::__addressof(__i),
> __is_weak,
> int(__s), int(__f));
> }
> - else if constexpr (!_AtomicRef) // std::atomic<T>
> + else if constexpr (!_NonZeroPadding) // std::atomic<T> compiled
> with GCC>=13
> {
> // Clear padding of the value we want to set:
> _Vp* const __pi = __atomic_impl::__clear_padding(__i);
> diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/126230.cc
> b/libstdc++-v3/testsuite/29_atomics/atomic/126230.cc
> new file mode 100644
> index 00000000000..b6b0d1e383d
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/29_atomics/atomic/126230.cc
> @@ -0,0 +1,36 @@
> +// { dg-do run { target c++11 } }
> +// { dg-require-atomic-builtins "" }
> +
> +#include <atomic>
> +#include <cstring>
> +#include <testsuite_hooks.h>
> +
> +// libstdc++/60658
> +
> +struct alignas(4) Foo {
> + char buf;
> +};
> +
> +int
> +main ()
> +{
> + std::atomic<Foo> f(Foo{'z'});
> + // Simulate atomic that contains non-zero padding bits,
> + // that can be produced by TU compiled with GCC<=12.
> + std::memset(reinterpret_cast<char*>(&f) + 1, 'a', sizeof(Foo) - 1);
> + VERIFY(f.load().buf == 'z');
> +
> + Foo expected{'z'};
> +#ifdef _GLIBCXX_FORCE_ATOMIC_CMPXCHG_COMPAT
> + // compare_exchange handles non_zero padding bits, and if
> + // comparision of object representation fails, then clears
> + // padding bits for loaded value
> + VERIFY( f.compare_exchange_strong(expected, {}) );
> +#else
> + // compare_exchange assumes that padding bits are cleared
> + // and unconditionally clears them inside expected before
> + // object representation, never finishes if they are empty
> + for (int i = 0; i < 100; ++i)
> + VERIFY( !f.compare_exchange_strong(expected, {}) );
> +#endif
> +}
> diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/126230_compat.cc
> b/libstdc++-v3/testsuite/29_atomics/atomic/126230_compat.cc
> new file mode 100644
> index 00000000000..44a71fc7e9a
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/29_atomics/atomic/126230_compat.cc
> @@ -0,0 +1,5 @@
> +// { dg-options "-D_GLIBCXX_FORCE_ATOMIC_CMPXCHG_COMPAT" }
> +// { dg-do run { target c++11 } }
> +// { dg-require-atomic-builtins "" }
> +#include "126230.cc"
> +
> --
> 2.55.0
>
>