On Fri, Feb 27, 2026 at 10:41 AM Jonathan Wakely <[email protected]> wrote:

> On Fri, 27 Feb 2026 at 08:58, Tomasz Kamiński <[email protected]> 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/std/atomic (atomic<_Tp>::atomic(_Tp)): Skip
> __clear_padding
> >         call for constant evaluation.
> >         * testsuite/29_atomics/atomic/cons/zero_padding.cc: New test.
> >
> > Reviewed-by: Patrick Palka  <[email protected]>
> > Reviewed-by: Jonathan Wakely <[email protected]>
> > Signed-off-by: Tomasz Kamiński <[email protected]>
> >         (cherry picked with modifications from commits
> >         6b550d69fe7cb62ea6e240ce7a4ba29ce33aa1b1
> >         682c95b808724e6f876ea709b873ac6771704d7b
> >         060d7c2a9c1fe16d23d98a74287fdb7c73ddb784
> >         f3ba5ad088cebe117f857329a35b95d18d97a808)
> > ---
> > The GCC-13 version drops chages to atomic_base altogether:
> > * we haven't used if constexpr as extension in eariel modes, so
> >   __clear_padding still uses if __GLIBCXX17_CONSTEXPR
>
> That's just because we didn't realise that we can use pragmas to make
> it usable back then. The first commit to do that was
> r15-3132-g8cf51d7516b92b and that got backported to gcc-14.
>
> In principle there's no reason we couldn't use 'if constexpr' on the
> gcc-13 branch, we just haven't backported anything that does that.
>
My concern here was clang support, i.e. if this backport might break using
libstdc++ v13 with some clang version that does not support if constexpr
as an extension (this would be first use). As for v14 and later this change
is a cleanup that extended a pre-existing block of if constexpr usage,
I do not think the change was worth the risk of being the first `constexpr`
extension usage.

>
>
> > * __atomic_float didn't clear the padding bits at that point
> > The atomic_float/zero_padding.cc test is also dropped for above reason.
> >
> > Tested on x86_64-linux. OK for v13?
>
> OK thanks.
>
> > Or should I backport clering padding for atomic_float instead?
> > It's C++20, and we haven't backported real C++20 fixes to v13,
> > so I think this approach is more conservative.
>
> Agreed.
>
> >
> >  libstdc++-v3/include/std/atomic               |  3 +-
> >  .../29_atomics/atomic/cons/zero_padding.cc    | 79 +++++++++++++++++++
> >  2 files changed, 81 insertions(+), 1 deletion(-)
> >  create mode 100644
> libstdc++-v3/testsuite/29_atomics/atomic/cons/zero_padding.cc
> >
> > diff --git a/libstdc++-v3/include/std/atomic
> b/libstdc++-v3/include/std/atomic
> > index 396e29f3b80..0b05e054720 100644
> > --- a/libstdc++-v3/include/std/atomic
> > +++ b/libstdc++-v3/include/std/atomic
> > @@ -234,7 +234,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/zero_padding.cc
> b/libstdc++-v3/testsuite/29_atomics/atomic/cons/zero_padding.cc
> > new file mode 100644
> > index 00000000000..d57846d6020
> > --- /dev/null
> > +++ b/libstdc++-v3/testsuite/29_atomics/atomic/cons/zero_padding.cc
> > @@ -0,0 +1,79 @@
> > +// { dg-do run { target c++11 } }
> > +// { dg-require-atomic-cmpxchg-word "" }
> > +// { dg-add-options libatomic }
> > +
> > +#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)
> > +  {}
> > +
> > +  char c;
> > +  int i;
> > +};
> > +
> > +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
> > +
> > +  std::atomic<T>* h = new std::atomic<T>(T{1, 2});
> > +  std::memcpy(&t, &zp, sizeof(T));
> > +#if __cplusplus >= 201402L // Remove once PR114865 is fixed
> > +  VERIFY( h->compare_exchange_strong(t, d) );
> > +#endif
> > +  delete h;
> > +
> > +  constexpr std::atomic<T> cl(T{1, 2});
> > +}
> > +
> > +int main()
> > +{
> > +  test_struct(gtail, ztail);
> > +  test_struct(gmid, zmid);
> > +  test_struct(gbit, zbit);
> > +  test_struct(gctor, zctor);
> > +}
> > --
> > 2.53.0
> >
>
>

Reply via email to