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
+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