On Mon, Feb 9, 2026 at 4:45 PM Jonathan Wakely <[email protected]> wrote:

> On Mon, 02 Feb 2026 at 16:21 +0100, Tomasz Kamiński wrote:
> >Currently for the types T that contains padding bits, std::atomic<T>(T)
> >constructor was not usable at compile-time in C++14 or later modes. This
> >regression caused by use of __builtin_clear_padding introduced in
> >r13-2548-g157236dbd62164.
> >
> >This leads to two regressions when switching from C++11 to C++14
> >standard (or switching from GCC-12 to later version for C++14 standard),
> >where for type X that contains padding
> >* constexpr std::atomic<X> cx(X(...)) becomes ill-formed,
> >* std::atomic<X> gx(X(...)) with static storage duration, switch from
> >  static to dynamic initialization.
> >The latter breakage is silent and may introduced very hard to localize
> >order of initialization issues.
> >
> >This patch mitigates above issue by not invoking the
> __builtin_clear_padding,
> >during constant initialization (std::__is_constant_evaluated() is false).
> >This is considered to be safe, as:
> >* for objects with static storage duration, padding bits are already
> >  cleared by zero-initialization
> >* for constexpr objects with non-static storage duration, there is no
> >  API that would allow user to observe padding bits on const atomic
> objects
> >
> >To elaborate on the second point, values of padding bits in atomic can
> >be observed by:
> >* The compare_exchange_weak/compare_exchange_strong operations are
> mutating,
> >  so cannot be invoked on const objects.
> >* As atomic<X> is not required to store actual object of type X,
> >  observing its object representation does (via bitcast, memcpy), does
> >  not provide values of object representation of X. Furthermore, the
> >  operations are defined only for trivially_copyable types, and atomic
> >  specializations meets above requirement only due to bug in libstdc++
> >  (see PR67572).
> >
> >Note that above will no longer hold, and the solution will need to be
> >revisited during implementation of C++26 paper P3309R3: constexpr
> >atomic and atomic_ref (it will be possible to call compare_exchange
> >during constant evaluation).
> >
> >       PR libstdc++/123875
> >
> >libstdc++-v3/ChangeLog:
> >
> >       * include/bits/atomic_base.h (__atomic_impl::__clear_padding):
> >       Use if constexpr unconditionally.
> >       (__atomic_float<_Fp>::__atomic_float(_Fp)): Skip __clear_padding
> >       call for constant evaluation.
> >       * include/std/atomic (atomic<_Tp>::atomic(_Tp)): Likewise.
> >       * testsuite/29_atomics/atomic/cons/static_zero_padding.cc: New
> test.
> >---
> >v2 fixed typos in commit description identified by Patrick.
>
> OK, thanks
>
 I plan to wait a week or two, and then backport these changes to GCC up to
13.
For GCC 13, I will drop the replacement of if _GLIBCXX17_CONSTEXPR with if
constexpr,
as we started to use it as extension in GCC14 (the __compare_exchange
change is in for 14
branch).

>
> > libstdc++-v3/include/bits/atomic_base.h       |  13 ++-
> > libstdc++-v3/include/std/atomic               |   3 +-
> > .../atomic/cons/static_zero_padding.cc        | 106 ++++++++++++++++++
> > 3 files changed, 116 insertions(+), 6 deletions(-)
> > create mode 100644
> libstdc++-v3/testsuite/29_atomics/atomic/cons/static_zero_padding.cc
> >
> >diff --git a/libstdc++-v3/include/bits/atomic_base.h
> b/libstdc++-v3/include/bits/atomic_base.h
> >index bc4778f08cb..79c1b31ccc4 100644
> >--- a/libstdc++-v3/include/bits/atomic_base.h
> >+++ b/libstdc++-v3/include/bits/atomic_base.h
> >@@ -1003,21 +1003,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > #endif
> >       }
> >
> >+#pragma GCC diagnostic push
> >+#pragma GCC diagnostic ignored "-Wc++17-extensions"
> >+
> >     template<typename _Tp>
> >       _GLIBCXX_ALWAYS_INLINE _GLIBCXX14_CONSTEXPR _Tp*
> >       __clear_padding(_Tp& __val) noexcept
> >       {
> >       auto* __ptr = std::__addressof(__val);
> > #if __has_builtin(__builtin_clear_padding)
> >-      if _GLIBCXX17_CONSTEXPR (__atomic_impl::__maybe_has_padding<_Tp>())
> >+      if constexpr (__atomic_impl::__maybe_has_padding<_Tp>())

>         __builtin_clear_padding(__ptr);
> > #endif
> >       return __ptr;
> >       }
> >
> >-#pragma GCC diagnostic push
> >-#pragma GCC diagnostic ignored "-Wc++17-extensions"
> >-
> >     template<bool _AtomicRef = false, typename _Tp>
> >       _GLIBCXX_ALWAYS_INLINE bool
> >       __compare_exchange(_Tp& __val, _Val<_Tp>& __e, _Val<_Tp>& __i,
> >@@ -1392,7 +1392,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >
> >       constexpr
> >       __atomic_float(_Fp __t) : _M_fp(__t)
> >-      { __atomic_impl::__clear_padding(_M_fp); }
> >+      {
> >+      if (!std::__is_constant_evaluated())
> >+        __atomic_impl::__clear_padding(_M_fp);
> >+      }
> >
> >       __atomic_float(const __atomic_float&) = delete;
> >       __atomic_float& operator=(const __atomic_float&) = delete;
> >diff --git a/libstdc++-v3/include/std/atomic
> b/libstdc++-v3/include/std/atomic
> >index 46d2bbb509e..56dbe7bf5b9 100644
> >--- a/libstdc++-v3/include/std/atomic
> >+++ b/libstdc++-v3/include/std/atomic
> >@@ -251,7 +251,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >       {
> > #if __cplusplus >= 201402L && __has_builtin(__builtin_clear_padding)
> >       if _GLIBCXX17_CONSTEXPR (__atomic_impl::__maybe_has_padding<_Tp>())
> >-        __builtin_clear_padding(std::__addressof(_M_i));
> >+        if (!std::__is_constant_evaluated())
> >+          __builtin_clear_padding(std::__addressof(_M_i));
> > #endif
> >       }
> >
> >diff --git
> a/libstdc++-v3/testsuite/29_atomics/atomic/cons/static_zero_padding.cc
> b/libstdc++-v3/testsuite/29_atomics/atomic/cons/static_zero_padding.cc
> >new file mode 100644
> >index 00000000000..506513647c6
> >--- /dev/null
> >+++ b/libstdc++-v3/testsuite/29_atomics/atomic/cons/static_zero_padding.cc
> >@@ -0,0 +1,106 @@
> >+// { dg-do run { target c++11 } }
> >+// { dg-require-thread-fence "" }
> >+// { dg-add-options no_pch }
> >+
> >+#include <atomic>
> >+#include <cstring>
> >+#include <testsuite_hooks.h>
> >+
> >+struct TailPadding { int i; char c; };
> >+TailPadding ztail{1, 2}; // zeroed-padding
> >+constexpr std::atomic<TailPadding> ctail(TailPadding{1,2});
> >+std::atomic<TailPadding> gtail(TailPadding{1,2});
> >+
> >+struct MidPadding { char c; int x; };
> >+MidPadding zmid{1, 2}; // zeroed-padding
> >+constexpr std::atomic<MidPadding> cmid(MidPadding{1,2});
> >+std::atomic<MidPadding> gmid(MidPadding{1,2});
> >+
> >+struct BitPadding { int : 4; int i : 5; int : 4; int j : 5; int : 4;  };
> >+BitPadding zbit{1, 2}; // zeroed-padding
> >+constexpr std::atomic<BitPadding> cbit(BitPadding{1,2});
> >+std::atomic<BitPadding> gbit(BitPadding{1,2});
> >+
> >+struct Ctor
> >+{
> >+  Ctor() = default;
> >+
> >+  constexpr Ctor(char pc, char pi)
> >+    : c(pc), i(pi), d(pc)
> >+  {}
> >+
> >+  char c;
> >+  int i;
> >+  char d;
> >+};
> >+
> >+Ctor zctor{1, 2}; // zeroed-padding
> >+constexpr std::atomic<Ctor> cctor(Ctor{1,2});
> >+std::atomic<Ctor> gctor(Ctor{1,2});
> >+
> >+template<typename T>
> >+void test_struct(std::atomic<T>& g, const T& zp)
> >+{
> >+  T const d{3, 4};
> >+  T t;
> >+
> >+  std::memcpy(&t, &zp, sizeof(T));
> >+  VERIFY( g.compare_exchange_strong(t, d) );
> >+
> >+  static std::atomic<T> st(T{1, 2});
> >+  std::memcpy(&t, &zp, sizeof(T));
> >+  VERIFY( st.compare_exchange_strong(t, d) );
> >+
> >+  thread_local std::atomic<T> tl(T{1, 2});
> >+  std::memcpy(&t, &zp, sizeof(T));
> >+  VERIFY( tl.compare_exchange_strong(t, d) );
> >+
> >+  std::atomic<T> l(T{1, 2});
> >+  std::memcpy(&t, &zp, sizeof(T));
> >+#if __cplusplus >= 201402L // Remove once PR114865 is fixed
> >+  VERIFY( l.compare_exchange_strong(t, d) );
> >+#endif
> >+
> >+  constexpr std::atomic<T> cl(T{1, 2});
> >+}
> >+
> >+#if __cplusplus >= 202002L
> >+long double zld(10.5);
> >+constexpr std::atomic<long double> cld(10.5);
> >+std::atomic<long double> gld(10.5);
> >+
> >+template<typename T>
> >+void test_floating(std::atomic<T>& g, const T& zp)
> >+{
> >+  T const d = T(7.5);
> >+  T t;
> >+
> >+  std::memcpy(&t, &zp, sizeof(T));
> >+  VERIFY( g.compare_exchange_strong(t, d) );
> >+
> >+  static std::atomic<T> st(T(10.5));
> >+  std::memcpy(&t, &zp, sizeof(T));
> >+  VERIFY( st.compare_exchange_strong(t, d) );
> >+
> >+  thread_local std::atomic<T> tl(T(10.5));
> >+  std::memcpy(&t, &zp, sizeof(T));
> >+  VERIFY( tl.compare_exchange_strong(t, d) );
> >+
> >+  std::atomic<T> l(T(10.5));
> >+  std::memcpy(&t, &zp, sizeof(T));
> >+  VERIFY( l.compare_exchange_strong(t, d) );
> >+
> >+  constexpr std::atomic<T> cl(T(10.5));
> >+}
> >+#endif
> >+
> >+int main()
> >+{
> >+  test_struct(gtail, ztail);
> >+  test_struct(gmid, zmid);
> >+  test_struct(gbit, zbit);
> >+  test_struct(gctor, zctor);
> >+#if __cplusplus >= 202002L
> >+  test_floating(gld, zld);
> >+#endif
> >+}
> >--
> >2.52.0
> >
> >
>
>

Reply via email to