On Fri, Jan 30, 2026 at 3:39 PM Patrick Palka <[email protected]> wrote:
> On Thu, Jan 29, 2026 at 3:05 PM 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 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 > > > >
