[PATCH] libstdc++: Allow constant initialization of std::atomic of types with padding [PR123875]
Tomasz Kaminski
tkaminsk@redhat.com
Fri Jan 30 14:44:33 GMT 2026
On Fri, Jan 30, 2026 at 3:39 PM Patrick Palka <ppalka@redhat.com> wrote:
> On Thu, Jan 29, 2026 at 3:05 PM Tomasz Kamiński <tkaminsk@redhat.com>
> 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 later 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 contexpr objects with 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 compapre_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 it's object representation does (via bitcast, memcpy), does
> > not provide values of object representation of X. Furthermore, the
> > operations are defined only for trivally_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
> > 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.
> > ---
> > Tested on x86_64-linux. OK for trunk?
> Then 13/14/15?
> >
> > 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..3866795a422
> > --- /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 >= 202002L // Remove once PR114865 is fixed
> > + VERIFY( l.compare_exchange_strong(t, d) );
> > +#endif
> > +
> > + constexpr std::atomic<T> cl(T{1, 2});
> > +}
> > +
> > +#if __cplusplus >= 202002L
>
> I forget, why is the floating point testing restricted to C++20?
>
Atomic support for floating point is C++20 feature, from version.def:
ftms = {
name = atomic_float;
values = {
v = 201711;
cxxmin = 20;
};
};
Which also means, I do not need to care about C++11 restrictions
for constructor bodies in __atomic_float.
> Besides that LGTM
>
>
> > +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
> >
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://gcc.gnu.org/pipermail/libstdc++/attachments/20260130/988a5243/attachment.htm>
More information about the Libstdc++
mailing list