[libcxx-commits] [libcxx] [libc++] Implement P0528R3 `std::atomic` CAS for types with paddings (PR #76180)
via libcxx-commits
libcxx-commits at lists.llvm.org
Sat Jul 4 07:31:38 PDT 2026
https://github.com/huixie90 updated https://github.com/llvm/llvm-project/pull/76180
>From d93e557c304cea49a5cf654b3bf1710f5cee6855 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie0621 at gmail.com>
Date: Thu, 21 Dec 2023 20:31:29 +0000
Subject: [PATCH 1/8] [libc++] make std::atomic works with types with paddings
---
libcxx/include/CMakeLists.txt | 1 +
libcxx/include/__atomic/support/c11.h | 83 ++++--
libcxx/include/__atomic/support/common.h | 75 ++++++
.../atomics.types.generic/padding.pass.cpp | 241 ++++++++++++++++++
4 files changed, 375 insertions(+), 25 deletions(-)
create mode 100644 libcxx/include/__atomic/support/common.h
create mode 100644 libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 10dfb4b4d58cb..5e1a7afea06a2 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -226,6 +226,7 @@ set(files
__atomic/kill_dependency.h
__atomic/memory_order.h
__atomic/support.h
+ __atomic/support/common.h
__atomic/support/c11.h
__atomic/support/gcc.h
__atomic/to_gcc_order.h
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 1ad299882a12a..f83572e6c9f49 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -10,6 +10,7 @@
#define _LIBCPP___ATOMIC_SUPPORT_C11_H
#include <__atomic/memory_order.h>
+#include <__atomic/support/common.h>
#include <__config>
#include <__cstddef/ptrdiff_t.h>
#include <__memory/addressof.h>
@@ -30,11 +31,24 @@ struct __cxx_atomic_base_impl {
_LIBCPP_HIDE_FROM_ABI
#ifndef _LIBCPP_CXX03_LANG
__cxx_atomic_base_impl() _NOEXCEPT = default;
+
+# if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+ __cxx_atomic_base_impl() noexcept
+ requires __needs_clear_padding<_Tp>::value
+ : __a_value() {
+ if (!__builtin_is_constant_evaluated()) {
+ __builtin_clear_padding(__a_value);
+ }
+ }
+# endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
#else
__cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
}
#endif // _LIBCPP_CXX03_LANG
- _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {}
+ _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+ std::__clear_padding_if_needed(__a_value);
+ }
_Atomic(_Tp) __a_value;
};
@@ -50,21 +64,25 @@ _LIBCPP_HIDE_FROM_ABI inline void __cxx_atomic_signal_fence(memory_order __order
template <class _Tp>
_LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val) _NOEXCEPT {
+ std::__clear_padding_if_needed(__val);
__c11_atomic_init(std::addressof(__a->__a_value), __val);
}
template <class _Tp>
_LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val) _NOEXCEPT {
+ std::__clear_padding_if_needed(__val);
__c11_atomic_init(std::addressof(__a->__a_value), __val);
}
template <class _Tp>
_LIBCPP_HIDE_FROM_ABI void
__cxx_atomic_store(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+ std::__clear_padding_if_needed(__val);
__c11_atomic_store(std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
}
template <class _Tp>
_LIBCPP_HIDE_FROM_ABI void
__cxx_atomic_store(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+ std::__clear_padding_if_needed(__val);
__c11_atomic_store(std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
}
@@ -100,12 +118,14 @@ __cxx_atomic_load_inplace(__cxx_atomic_base_impl<_Tp> const* __a, _Tp* __dst, me
template <class _Tp>
_LIBCPP_HIDE_FROM_ABI _Tp
__cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __value, memory_order __order) _NOEXCEPT {
+ std::__clear_padding_if_needed(__value);
return __c11_atomic_exchange(
std::addressof(__a->__a_value), __value, static_cast<__memory_order_underlying_t>(__order));
}
template <class _Tp>
_LIBCPP_HIDE_FROM_ABI _Tp
__cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) _NOEXCEPT {
+ std::__clear_padding_if_needed(__value);
return __c11_atomic_exchange(
std::addressof(__a->__a_value), __value, static_cast<__memory_order_underlying_t>(__order));
}
@@ -124,23 +144,29 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
_Tp __value,
memory_order __success,
memory_order __failure) _NOEXCEPT {
- return __c11_atomic_compare_exchange_strong(
- std::addressof(__a->__a_value),
- __expected,
- __value,
- static_cast<__memory_order_underlying_t>(__success),
- static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+ return __atomic_cas_with_clear_padding(
+ __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+ return __c11_atomic_compare_exchange_strong(
+ std::addressof(__a->__a_value),
+ __expected_or_copy,
+ __value_maybe_padding_cleared,
+ static_cast<__memory_order_underlying_t>(__success),
+ static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+ });
}
template <class _Tp>
_LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
__cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
_NOEXCEPT {
- return __c11_atomic_compare_exchange_strong(
- std::addressof(__a->__a_value),
- __expected,
- __value,
- static_cast<__memory_order_underlying_t>(__success),
- static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+ return __atomic_cas_with_clear_padding(
+ __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+ return __c11_atomic_compare_exchange_strong(
+ std::addressof(__a->__a_value),
+ __expected_or_copy,
+ __value_maybe_padding_cleared,
+ static_cast<__memory_order_underlying_t>(__success),
+ static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+ });
}
template <class _Tp>
@@ -150,23 +176,30 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
_Tp __value,
memory_order __success,
memory_order __failure) _NOEXCEPT {
- return __c11_atomic_compare_exchange_weak(
- std::addressof(__a->__a_value),
- __expected,
- __value,
- static_cast<__memory_order_underlying_t>(__success),
- static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+ return __atomic_cas_with_clear_padding(
+ __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+ return __c11_atomic_compare_exchange_weak(
+ std::addressof(__a->__a_value),
+ __expected_or_copy,
+ __value_maybe_padding_cleared,
+ static_cast<__memory_order_underlying_t>(__success),
+ static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+ });
}
+
template <class _Tp>
_LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
__cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
_NOEXCEPT {
- return __c11_atomic_compare_exchange_weak(
- std::addressof(__a->__a_value),
- __expected,
- __value,
- static_cast<__memory_order_underlying_t>(__success),
- static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+ return __atomic_cas_with_clear_padding(
+ __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+ return __c11_atomic_compare_exchange_weak(
+ std::addressof(__a->__a_value),
+ __expected_or_copy,
+ __value_maybe_padding_cleared,
+ static_cast<__memory_order_underlying_t>(__success),
+ static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+ });
}
template <class _Tp>
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
new file mode 100644
index 0000000000000..fe02e3736f88c
--- /dev/null
+++ b/libcxx/include/__atomic/support/common.h
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+#define _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+
+#include <__config>
+#include <__memory/addressof.h>
+#include <__type_traits/conjunction.h>
+#include <__type_traits/has_unique_object_representation.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/negation.h>
+#include <__type_traits/remove_cv.h>
+#include <__type_traits/remove_cvref.h>
+#include <__utility/forward.h>
+#include <cstring>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+template <class _Tp>
+struct __needs_clear_padding
+ : _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noexcept {
+ if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
+ if (!__builtin_is_constant_evaluated()) {
+ __builtin_clear_padding(std::addressof(__obj));
+ }
+ }
+}
+
+template <class _Tp, class _Up, class _CasFunc>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+ if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
+ return __cas_func(__expected, std::forward<_Up>(__value));
+ } else {
+ std::__clear_padding_if_needed(__value);
+ remove_cv_t<_Tp> __expected_copy = *__expected;
+ std::__clear_padding_if_needed(__expected_copy);
+ if (__cas_func(std::addressof(__expected_copy), std::forward<_Up>(__value))) {
+ return true;
+ } else {
+ std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
+ return false;
+ }
+ }
+}
+
+#else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) noexcept {}
+
+template <class _Tp, class _Up, class _CasFunc>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+ return __cas_func(__expected, std::forward<_Up>(__value));
+}
+
+#endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___ATOMIC_SUPPORT_COMMON_H
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
new file mode 100644
index 0000000000000..e8ea1ecbddd81
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -0,0 +1,241 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// XFAIL: !has-64-bit-atomics
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+#include <atomic>
+#include <cassert>
+#include <cstring>
+#include <type_traits>
+
+struct Foo {
+ int i;
+ char c;
+};
+
+static_assert(!std::has_unique_object_representations_v<Foo>);
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+ Foo f;
+ std::memset(&f, pad_byte, sizeof(Foo));
+ f.i = i;
+ f.c = c;
+ return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+ alignas(Foo) unsigned char buf[sizeof(Foo)];
+ std::memset(buf, pad_byte, sizeof(Foo));
+ Foo& reference = *reinterpret_cast<Foo*>(buf);
+ reference.i = f.i;
+ reference.c = f.c;
+ assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+#if __has_builtin(__builtin_clear_padding)
+
+void test_default_constructor() {
+ std::atomic<Foo> a;
+ Foo loaded = a.load();
+ assert(loaded.i == 0);
+ assert(loaded.c == '\0');
+ assert_foo_padding(loaded, 0);
+}
+
+void test_value_constructor() {
+ Foo init = make_foo(10, 'a', 0xBB);
+ assert_foo_padding(init, 0xBB);
+ std::atomic<Foo> a(init);
+ Foo loaded = a.load();
+ assert(loaded.i == 10);
+ assert(loaded.c == 'a');
+ assert_foo_padding(loaded, 0);
+}
+
+void test_store() {
+ std::atomic<Foo> a;
+ Foo value = make_foo(5, 'x', 0xAB);
+ assert_foo_padding(value, 0xAB);
+ a.store(value);
+ Foo loaded = a.load();
+ assert(loaded.i == 5);
+ assert(loaded.c == 'x');
+ assert_foo_padding(loaded, 0);
+}
+
+void test_exchange() {
+ Foo initial = make_foo(1, 'a', 0x00);
+ assert_foo_padding(initial, 0x00);
+ std::atomic<Foo> a(initial);
+ Foo new_val = make_foo(2, 'b', 0xCD);
+ assert_foo_padding(new_val, 0xCD);
+ Foo old = a.exchange(new_val);
+ assert(old.i == 1);
+ assert(old.c == 'a');
+ assert_foo_padding(old, 0);
+ Foo loaded = a.load();
+ assert(loaded.i == 2);
+ assert(loaded.c == 'b');
+ assert_foo_padding(loaded, 0);
+}
+
+void test_atomic_init() {
+ std::atomic<Foo> a;
+ Foo init = make_foo(7, 'z', 0xEF);
+ assert_foo_padding(init, 0xEF);
+ std::atomic_init(&a, init);
+ Foo loaded = a.load();
+ assert(loaded.i == 7);
+ assert(loaded.c == 'z');
+ assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_success_padding_only() {
+ // CAS should succeed when only padding differs in expected; expected is unchanged.
+ std::atomic<Foo> a;
+
+ Foo init = make_foo(10, 'a', 0xBB);
+ assert_foo_padding(init, 0xBB);
+ a.store(init);
+
+ Foo expected = make_foo(10, 'a', 0xAA);
+ assert_foo_padding(expected, 0xAA);
+
+ alignas(Foo) char original_expected[sizeof(Foo)];
+ std::memcpy(original_expected, &expected, sizeof(Foo));
+
+ Foo new_value = make_foo(42, 'b', 0xCC);
+ assert_foo_padding(new_value, 0xCC);
+
+ bool r = a.compare_exchange_strong(expected, new_value);
+
+ assert(r);
+ assert(std::memcmp(&expected, original_expected, sizeof(Foo)) == 0);
+ Foo loaded = a.load();
+ assert(loaded.i == 42);
+ assert(loaded.c == 'b');
+ assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_strong_failure() {
+ std::atomic<Foo> a;
+ Foo stored = make_foo(10, 'a', 0xBB);
+ assert_foo_padding(stored, 0xBB);
+ a.store(stored);
+
+ Foo expected = make_foo(99, 'a', 0xAA);
+ assert_foo_padding(expected, 0xAA);
+ Foo new_value = make_foo(42, 'b', 0xCC);
+ assert_foo_padding(new_value, 0xCC);
+
+ bool r = a.compare_exchange_strong(expected, new_value);
+
+ assert(!r);
+ assert(expected.i == 10);
+ assert(expected.c == 'a');
+ assert_foo_padding(expected, 0);
+ Foo loaded = a.load();
+ assert(loaded.i == 10);
+ assert(loaded.c == 'a');
+ assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_weak_success_padding_only() {
+ std::atomic<Foo> a;
+ Foo stored = make_foo(10, 'a', 0xBB);
+ assert_foo_padding(stored, 0xBB);
+ a.store(stored);
+
+ Foo new_value = make_foo(42, 'b', 0xCC);
+ assert_foo_padding(new_value, 0xCC);
+
+ Foo original_expected = make_foo(10, 'a', 0xAA);
+ assert_foo_padding(original_expected, 0xAA);
+
+ bool r = false;
+ while (!r) {
+ Foo expected = make_foo(10, 'a', 0xAA);
+ assert_foo_padding(expected, 0xAA);
+ r = a.compare_exchange_weak(expected, new_value);
+ if (r) {
+ assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+ } else {
+ // Spurious failure: expected is updated to the current atomic value.
+ assert(expected.i == 10);
+ assert(expected.c == 'a');
+ assert_foo_padding(expected, 0);
+ }
+ }
+
+ Foo loaded = a.load();
+ assert(loaded.i == 42);
+ assert(loaded.c == 'b');
+ assert_foo_padding(loaded, 0);
+}
+
+void test_compare_exchange_weak_failure() {
+ std::atomic<Foo> a;
+ Foo stored = make_foo(10, 'a', 0xBB);
+ assert_foo_padding(stored, 0xBB);
+ a.store(stored);
+
+ Foo expected = make_foo(99, 'a', 0xAA);
+ assert_foo_padding(expected, 0xAA);
+ Foo new_value = make_foo(42, 'b', 0xCC);
+ assert_foo_padding(new_value, 0xCC);
+
+ bool r = a.compare_exchange_weak(expected, new_value);
+
+ assert(!r);
+ assert(expected.i == 10);
+ assert(expected.c == 'a');
+ assert_foo_padding(expected, 0);
+ Foo loaded = a.load();
+ assert(loaded.i == 10);
+ assert(loaded.c == 'a');
+ assert_foo_padding(loaded, 0);
+}
+
+void test_no_padding_type() {
+ // Types with unique object representations skip the padding-clearing path.
+ std::atomic<int> a(1);
+ int expected = 1;
+ assert(a.compare_exchange_strong(expected, 2));
+ assert(expected == 1);
+ assert(a.load() == 2);
+
+ expected = 3;
+ assert(!a.compare_exchange_strong(expected, 4));
+ assert(expected == 2);
+ assert(a.load() == 2);
+}
+
+int main(int, char**) {
+ test_default_constructor();
+ test_value_constructor();
+ test_store();
+ test_exchange();
+ test_atomic_init();
+ test_compare_exchange_strong_success_padding_only();
+ test_compare_exchange_strong_failure();
+ test_compare_exchange_weak_success_padding_only();
+ test_compare_exchange_weak_failure();
+ test_no_padding_type();
+
+ return 0;
+}
+
+#else // !__has_builtin(__builtin_clear_padding)
+
+int main(int, char**) { return 0; }
+
+#endif // __has_builtin(__builtin_clear_padding)
>From f17e51a372a7a1372fa63a572871939eec329145 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 7 Jun 2026 22:08:54 +0100
Subject: [PATCH 2/8] gcc
---
libcxx/include/__atomic/support/gcc.h | 89 +++++++++++++------
.../atomics.types.generic/padding.pass.cpp | 5 +-
2 files changed, 64 insertions(+), 30 deletions(-)
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 73c1b1c8070a4..74696878a6789 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -10,6 +10,7 @@
#define _LIBCPP___ATOMIC_SUPPORT_GCC_H
#include <__atomic/memory_order.h>
+#include <__atomic/support/common.h>
#include <__atomic/to_gcc_order.h>
#include <__config>
#include <__memory/addressof.h>
@@ -48,21 +49,35 @@ struct __cxx_atomic_base_impl {
_LIBCPP_HIDE_FROM_ABI
#ifndef _LIBCPP_CXX03_LANG
__cxx_atomic_base_impl() _NOEXCEPT = default;
+
+# if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+ __cxx_atomic_base_impl() noexcept
+ requires __needs_clear_padding<_Tp>::value
+ : __a_value() {
+ if (!__builtin_is_constant_evaluated()) {
+ __builtin_clear_padding(__a_value);
+ }
+ }
+# endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
#else
__cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
}
#endif // _LIBCPP_CXX03_LANG
- _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {}
+ _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {
+ std::__clear_padding_if_needed(__a_value);
+ }
_Tp __a_value;
};
template <typename _Tp>
_LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val) {
+ std::__clear_padding_if_needed(__val);
__cxx_atomic_assign_volatile(__a->__a_value, __val);
}
template <typename _Tp>
_LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val) {
+ std::__clear_padding_if_needed(__val);
__a->__a_value = __val;
}
@@ -77,11 +92,13 @@ _LIBCPP_HIDE_FROM_ABI inline void __cxx_atomic_signal_fence(memory_order __order
template <typename _Tp>
_LIBCPP_HIDE_FROM_ABI void
__cxx_atomic_store(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
+ std::__clear_padding_if_needed(__val);
__atomic_store(std::addressof(__a->__a_value), std::addressof(__val), __to_gcc_order(__order));
}
template <typename _Tp>
_LIBCPP_HIDE_FROM_ABI void __cxx_atomic_store(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) {
+ std::__clear_padding_if_needed(__val);
__atomic_store(std::addressof(__a->__a_value), std::addressof(__val), __to_gcc_order(__order));
}
@@ -115,6 +132,7 @@ template <typename _Tp>
_LIBCPP_HIDE_FROM_ABI _Tp
__cxx_atomic_exchange(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) {
_Tp __ret;
+ std::__clear_padding_if_needed(__value);
__atomic_exchange(
std::addressof(__a->__a_value), std::addressof(__value), std::addressof(__ret), __to_gcc_order(__order));
return __ret;
@@ -123,6 +141,7 @@ __cxx_atomic_exchange(volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp __value, me
template <typename _Tp>
_LIBCPP_HIDE_FROM_ABI _Tp __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_order __order) {
_Tp __ret;
+ std::__clear_padding_if_needed(__value);
__atomic_exchange(
std::addressof(__a->__a_value), std::addressof(__value), std::addressof(__ret), __to_gcc_order(__order));
return __ret;
@@ -135,25 +154,31 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
_Tp __value,
memory_order __success,
memory_order __failure) {
- return __atomic_compare_exchange(
- std::addressof(__a->__a_value),
- __expected,
- std::addressof(__value),
- false,
- __to_gcc_order(__success),
- __to_gcc_failure_order(__failure));
+ return __atomic_cas_with_clear_padding(
+ __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+ return __atomic_compare_exchange(
+ std::addressof(__a->__a_value),
+ __expected_or_copy,
+ std::addressof(__value_maybe_padding_cleared),
+ false,
+ __to_gcc_order(__success),
+ __to_gcc_failure_order(__failure));
+ });
}
template <typename _Tp>
_LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
__cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
- return __atomic_compare_exchange(
- std::addressof(__a->__a_value),
- __expected,
- std::addressof(__value),
- false,
- __to_gcc_order(__success),
- __to_gcc_failure_order(__failure));
+ return __atomic_cas_with_clear_padding(
+ __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+ return __atomic_compare_exchange(
+ std::addressof(__a->__a_value),
+ __expected_or_copy,
+ std::addressof(__value_maybe_padding_cleared),
+ false,
+ __to_gcc_order(__success),
+ __to_gcc_failure_order(__failure));
+ });
}
template <typename _Tp>
@@ -163,25 +188,31 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
_Tp __value,
memory_order __success,
memory_order __failure) {
- return __atomic_compare_exchange(
- std::addressof(__a->__a_value),
- __expected,
- std::addressof(__value),
- true,
- __to_gcc_order(__success),
- __to_gcc_failure_order(__failure));
+ return __atomic_cas_with_clear_padding(
+ __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+ return __atomic_compare_exchange(
+ std::addressof(__a->__a_value),
+ __expected_or_copy,
+ std::addressof(__value_maybe_padding_cleared),
+ true,
+ __to_gcc_order(__success),
+ __to_gcc_failure_order(__failure));
+ });
}
template <typename _Tp>
_LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
__cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
- return __atomic_compare_exchange(
- std::addressof(__a->__a_value),
- __expected,
- std::addressof(__value),
- true,
- __to_gcc_order(__success),
- __to_gcc_failure_order(__failure));
+ return __atomic_cas_with_clear_padding(
+ __expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
+ return __atomic_compare_exchange(
+ std::addressof(__a->__a_value),
+ __expected_or_copy,
+ std::addressof(__value_maybe_padding_cleared),
+ true,
+ __to_gcc_order(__success),
+ __to_gcc_failure_order(__failure));
+ });
}
template <typename _Tp>
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e8ea1ecbddd81..a66fd3e74bfc6 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -13,6 +13,7 @@
#include <atomic>
#include <cassert>
+#include <chrono>
#include <cstring>
#include <type_traits>
@@ -161,8 +162,10 @@ void test_compare_exchange_weak_success_padding_only() {
Foo original_expected = make_foo(10, 'a', 0xAA);
assert_foo_padding(original_expected, 0xAA);
- bool r = false;
+ bool r = false;
+ const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(3);
while (!r) {
+ assert(std::chrono::steady_clock::now() < deadline && "compare_exchange_weak did not succeed within 3 seconds");
Foo expected = make_foo(10, 'a', 0xAA);
assert_foo_padding(expected, 0xAA);
r = a.compare_exchange_weak(expected, new_value);
>From ac972d414dd906c0e73a3e8b70697abfc537dafa Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 13 Jun 2026 09:42:18 +0100
Subject: [PATCH 3/8] lint
---
libcxx/include/CMakeLists.txt | 2 +-
libcxx/include/__atomic/support/common.h | 2 +-
libcxx/include/__atomic/support/gcc.h | 5 ++++-
libcxx/include/module.modulemap.in | 1 +
.../test/std/atomics/atomics.types.generic/padding.pass.cpp | 1 -
5 files changed, 7 insertions(+), 4 deletions(-)
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 5e1a7afea06a2..4477c821235de 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -226,8 +226,8 @@ set(files
__atomic/kill_dependency.h
__atomic/memory_order.h
__atomic/support.h
- __atomic/support/common.h
__atomic/support/c11.h
+ __atomic/support/common.h
__atomic/support/gcc.h
__atomic/to_gcc_order.h
__bit/bit_cast.h
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index fe02e3736f88c..c8a1a3a0f6876 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -61,7 +61,7 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&
#else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) noexcept {}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {}
template <class _Tp, class _Up, class _CasFunc>
_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 74696878a6789..b20b5e25fc9cd 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -63,8 +63,11 @@ struct __cxx_atomic_base_impl {
__cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
}
#endif // _LIBCPP_CXX03_LANG
- _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp value) _NOEXCEPT : __a_value(value) {
+ _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+ // gcc c++11 mode constexpr constructor requires empty body
+#if _LIBCPP_STD_VER >= 14
std::__clear_padding_if_needed(__a_value);
+#endif
}
_Tp __a_value;
};
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 29f3818c39671..b0f40e5a261ed 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -896,6 +896,7 @@ module std [system] {
module support {
header "__atomic/support.h"
textual header "__atomic/support/c11.h"
+ textual header "__atomic/support/common.h"
textual header "__atomic/support/gcc.h"
}
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index a66fd3e74bfc6..e759c20214241 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -6,7 +6,6 @@
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
-// XFAIL: !has-64-bit-atomics
// atomic_init is deprecated
// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
>From 33732fee8003a503f62524e1208aef02094b0bc6 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sun, 21 Jun 2026 10:51:35 +0100
Subject: [PATCH 4/8] gcc c++11 ci
---
libcxx/include/__atomic/support/c11.h | 2 ++
libcxx/include/__atomic/support/common.h | 8 +++++++-
2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index f83572e6c9f49..7cae19d716f4b 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -47,7 +47,9 @@ struct __cxx_atomic_base_impl {
}
#endif // _LIBCPP_CXX03_LANG
_LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
std::__clear_padding_if_needed(__a_value);
+#endif
}
_Atomic(_Tp) __a_value;
};
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index c8a1a3a0f6876..672ff0a6b2683 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -61,7 +61,13 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&
#else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {}
+_LIBCPP_HIDE_FROM_ABI
+# if _LIBCPP_STD_VER >= 14
+// c++11 does not allow constexpr functions to return void
+constexpr
+# endif
+ void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {
+}
template <class _Tp, class _Up, class _CasFunc>
_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
>From dc33657dd85cca1c1511d5cd7605bbcca05b35f8 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:47:30 +0100
Subject: [PATCH 5/8] Update
libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
.../test/std/atomics/atomics.types.generic/padding.pass.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index e759c20214241..38b4428f3a271 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -110,8 +110,8 @@ void test_compare_exchange_strong_success_padding_only() {
Foo expected = make_foo(10, 'a', 0xAA);
assert_foo_padding(expected, 0xAA);
- alignas(Foo) char original_expected[sizeof(Foo)];
- std::memcpy(original_expected, &expected, sizeof(Foo));
+Foo original_expected; // make a copy including padding bits
+std::memcpy(&original_expected, expected, sizeof(Foo));
Foo new_value = make_foo(42, 'b', 0xCC);
assert_foo_padding(new_value, 0xCC);
>From f133b64bf0da078d6ef4659603f8f92f12e7ec8c Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:47:57 +0100
Subject: [PATCH 6/8] Update libcxx/include/__atomic/support/c11.h
Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
libcxx/include/__atomic/support/c11.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 7cae19d716f4b..56cbfdaa5c0ff 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -146,7 +146,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
_Tp __value,
memory_order __success,
memory_order __failure) _NOEXCEPT {
- return __atomic_cas_with_clear_padding(
+ return std::__atomic_cas_with_clear_padding(
__expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
return __c11_atomic_compare_exchange_strong(
std::addressof(__a->__a_value),
>From e943f4968a86c2c73214cdb22ccdf77b122428e5 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 09:48:20 +0100
Subject: [PATCH 7/8] Update libcxx/include/__atomic/support/common.h
Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
---
libcxx/include/__atomic/support/common.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index 672ff0a6b2683..16bb21e46f88e 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -42,7 +42,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noex
}
template <class _Tp, class _Up, class _CasFunc>
-_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
return __cas_func(__expected, std::forward<_Up>(__value));
} else {
>From 6070b97b3d62691fabebe6dbd7b79a3fd5a58c4d Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Sat, 4 Jul 2026 15:31:21 +0100
Subject: [PATCH 8/8] review comments batch 1
---
libcxx/include/__atomic/support/c11.h | 6 +-
libcxx/include/__atomic/support/common.h | 18 +-
libcxx/include/__atomic/support/gcc.h | 13 +-
.../atomics.types.generic/padding.pass.cpp | 114 +++++++
.../atomics.types.generic/padding.pass.cpp | 316 ++++++++----------
5 files changed, 266 insertions(+), 201 deletions(-)
create mode 100644 libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 56cbfdaa5c0ff..8ccbcf9d8d227 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -160,7 +160,7 @@ template <class _Tp>
_LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
__cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
_NOEXCEPT {
- return __atomic_cas_with_clear_padding(
+ return std::__atomic_cas_with_clear_padding(
__expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
return __c11_atomic_compare_exchange_strong(
std::addressof(__a->__a_value),
@@ -178,7 +178,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
_Tp __value,
memory_order __success,
memory_order __failure) _NOEXCEPT {
- return __atomic_cas_with_clear_padding(
+ return std::__atomic_cas_with_clear_padding(
__expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
return __c11_atomic_compare_exchange_weak(
std::addressof(__a->__a_value),
@@ -193,7 +193,7 @@ template <class _Tp>
_LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
__cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure)
_NOEXCEPT {
- return __atomic_cas_with_clear_padding(
+ return std::__atomic_cas_with_clear_padding(
__expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
return __c11_atomic_compare_exchange_weak(
std::addressof(__a->__a_value),
diff --git a/libcxx/include/__atomic/support/common.h b/libcxx/include/__atomic/support/common.h
index 16bb21e46f88e..308302f8754a5 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/support/common.h
@@ -33,7 +33,7 @@ struct __needs_clear_padding
: _And<_Not<has_unique_object_representations<_Tp>>, _Not<is_same<_Tp, float>>, _Not<is_same<_Tp, double>>> {};
template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noexcept {
+_LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexcept {
if constexpr (__needs_clear_padding<remove_cvref_t<_Tp>>::value) {
if (!__builtin_is_constant_evaluated()) {
__builtin_clear_padding(std::addressof(__obj));
@@ -44,12 +44,12 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp&& __obj) noex
template <class _Tp, class _Up, class _CasFunc>
_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
if constexpr (!__needs_clear_padding<remove_cv_t<_Tp>>::value) {
- return __cas_func(__expected, std::forward<_Up>(__value));
+ return __cas_func(__expected, __value);
} else {
std::__clear_padding_if_needed(__value);
remove_cv_t<_Tp> __expected_copy = *__expected;
std::__clear_padding_if_needed(__expected_copy);
- if (__cas_func(std::addressof(__expected_copy), std::forward<_Up>(__value))) {
+ if (__cas_func(std::addressof(__expected_copy), __value)) {
return true;
} else {
std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
@@ -61,17 +61,11 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
#else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI
-# if _LIBCPP_STD_VER >= 14
-// c++11 does not allow constexpr functions to return void
-constexpr
-# endif
- void __clear_padding_if_needed(_Tp&&) _NOEXCEPT {
-}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
template <class _Tp, class _Up, class _CasFunc>
-_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up&& __value, _CasFunc&& __cas_func) {
- return __cas_func(__expected, std::forward<_Up>(__value));
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
+ return __cas_func(__expected, __value);
}
#endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index b20b5e25fc9cd..04e12afd21689 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -63,11 +63,8 @@ struct __cxx_atomic_base_impl {
__cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
}
#endif // _LIBCPP_CXX03_LANG
- _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
- // gcc c++11 mode constexpr constructor requires empty body
-#if _LIBCPP_STD_VER >= 14
+ _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
std::__clear_padding_if_needed(__a_value);
-#endif
}
_Tp __a_value;
};
@@ -157,7 +154,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
_Tp __value,
memory_order __success,
memory_order __failure) {
- return __atomic_cas_with_clear_padding(
+ return std::__atomic_cas_with_clear_padding(
__expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
return __atomic_compare_exchange(
std::addressof(__a->__a_value),
@@ -172,7 +169,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
template <typename _Tp>
_LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
__cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
- return __atomic_cas_with_clear_padding(
+ return std::__atomic_cas_with_clear_padding(
__expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
return __atomic_compare_exchange(
std::addressof(__a->__a_value),
@@ -191,7 +188,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
_Tp __value,
memory_order __success,
memory_order __failure) {
- return __atomic_cas_with_clear_padding(
+ return std::__atomic_cas_with_clear_padding(
__expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
return __atomic_compare_exchange(
std::addressof(__a->__a_value),
@@ -206,7 +203,7 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
template <typename _Tp>
_LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
__cxx_atomic_base_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) {
- return __atomic_cas_with_clear_padding(
+ return std::__atomic_cas_with_clear_padding(
__expected, __value, [__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) {
return __atomic_compare_exchange(
std::addressof(__a->__a_value),
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
new file mode 100644
index 0000000000000..78674a3f85204
--- /dev/null
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -0,0 +1,114 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// atomic_init is deprecated
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// libc++ maintains the invariant of the atomic to have zero for all padding bits
+
+#include <atomic>
+#include <cassert>
+#include <chrono>
+#include <cstring>
+#include <type_traits>
+
+struct Foo {
+ int i;
+ char c;
+};
+
+static_assert(!std::has_unique_object_representations_v<Foo>);
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+
+Foo make_foo(int i, char c, unsigned char pad_byte) {
+ Foo f;
+ std::memset(&f, pad_byte, sizeof(Foo));
+ f.i = i;
+ f.c = c;
+ return f;
+}
+
+void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+ alignas(Foo) unsigned char buf[sizeof(Foo)];
+ std::memset(buf, pad_byte, sizeof(Foo));
+ Foo& reference = *reinterpret_cast<Foo*>(buf);
+ reference.i = f.i;
+ reference.c = f.c;
+ assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
+}
+
+void test() {
+ {
+ // atomic();
+ std::atomic<Foo> a;
+ Foo loaded = a.load();
+ assert(loaded.i == 0);
+ assert(loaded.c == '\0');
+ assert_foo_padding(loaded, 0);
+ }
+
+ {
+ // atomic(T);
+ Foo init = make_foo(10, 'a', 0xBB);
+ assert_foo_padding(init, 0xBB);
+ std::atomic<Foo> a(init);
+ Foo loaded = a.load();
+ assert(loaded.i == 10);
+ assert(loaded.c == 'a');
+ assert_foo_padding(loaded, 0);
+ }
+ {
+ // atomic::store
+ std::atomic<Foo> a;
+ Foo value = make_foo(5, 'x', 0xAB);
+ assert_foo_padding(value, 0xAB);
+ a.store(value);
+ Foo loaded = a.load();
+ assert(loaded.i == 5);
+ assert(loaded.c == 'x');
+ assert_foo_padding(loaded, 0);
+ }
+ {
+ // atomic::exchange
+ Foo initial = make_foo(1, 'a', 0x00);
+ assert_foo_padding(initial, 0x00);
+ std::atomic<Foo> a(initial);
+ Foo new_val = make_foo(2, 'b', 0xCD);
+ assert_foo_padding(new_val, 0xCD);
+ Foo old = a.exchange(new_val);
+ assert(old.i == 1);
+ assert(old.c == 'a');
+ assert_foo_padding(old, 0);
+ Foo loaded = a.load();
+ assert(loaded.i == 2);
+ assert(loaded.c == 'b');
+ assert_foo_padding(loaded, 0);
+ }
+ {
+ // atomic_init
+ std::atomic<Foo> a;
+ Foo init = make_foo(7, 'z', 0xEF);
+ assert_foo_padding(init, 0xEF);
+ std::atomic_init(&a, init);
+ Foo loaded = a.load();
+ assert(loaded.i == 7);
+ assert(loaded.c == 'z');
+ assert_foo_padding(loaded, 0);
+ }
+}
+
+int main(int, char**) {
+// TODO(LLVM-23): Switch to XFAIL: clang-22
+#if __has_builtin(__builtin_clear_padding)
+ test();
+#endif
+ return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
index 38b4428f3a271..e5fa3b96fc1d0 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -10,9 +10,12 @@
// atomic_init is deprecated
// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+// atomic<T>::compare_exchange_weak
+// atomic<T>::compare_exchange_strong
+// CAS should work on types with padding bits
+
#include <atomic>
#include <cassert>
-#include <chrono>
#include <cstring>
#include <type_traits>
@@ -41,203 +44,160 @@ void assert_foo_padding(const Foo& f, unsigned char pad_byte) {
assert(std::memcmp(&f, &reference, sizeof(Foo)) == 0);
}
-#if __has_builtin(__builtin_clear_padding)
-
-void test_default_constructor() {
- std::atomic<Foo> a;
- Foo loaded = a.load();
- assert(loaded.i == 0);
- assert(loaded.c == '\0');
- assert_foo_padding(loaded, 0);
-}
-
-void test_value_constructor() {
- Foo init = make_foo(10, 'a', 0xBB);
- assert_foo_padding(init, 0xBB);
- std::atomic<Foo> a(init);
- Foo loaded = a.load();
- assert(loaded.i == 10);
- assert(loaded.c == 'a');
- assert_foo_padding(loaded, 0);
-}
-
-void test_store() {
- std::atomic<Foo> a;
- Foo value = make_foo(5, 'x', 0xAB);
- assert_foo_padding(value, 0xAB);
- a.store(value);
- Foo loaded = a.load();
- assert(loaded.i == 5);
- assert(loaded.c == 'x');
- assert_foo_padding(loaded, 0);
-}
-
-void test_exchange() {
- Foo initial = make_foo(1, 'a', 0x00);
- assert_foo_padding(initial, 0x00);
- std::atomic<Foo> a(initial);
- Foo new_val = make_foo(2, 'b', 0xCD);
- assert_foo_padding(new_val, 0xCD);
- Foo old = a.exchange(new_val);
- assert(old.i == 1);
- assert(old.c == 'a');
- assert_foo_padding(old, 0);
- Foo loaded = a.load();
- assert(loaded.i == 2);
- assert(loaded.c == 'b');
- assert_foo_padding(loaded, 0);
+void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+#ifdef _LIBCPP_VERSION
+ assert_foo_padding(f, pad_byte);
+#else
+ (void)f;
+ (void)pad_byte;
+#endif
}
-void test_atomic_init() {
- std::atomic<Foo> a;
- Foo init = make_foo(7, 'z', 0xEF);
- assert_foo_padding(init, 0xEF);
- std::atomic_init(&a, init);
- Foo loaded = a.load();
- assert(loaded.i == 7);
- assert(loaded.c == 'z');
- assert_foo_padding(loaded, 0);
-}
+void test() {
+ {
+ // compare_exchange_strong
+ // CAS should succeed when only padding differs in expected; expected is unchanged.
+ std::atomic<Foo> a;
-void test_compare_exchange_strong_success_padding_only() {
- // CAS should succeed when only padding differs in expected; expected is unchanged.
- std::atomic<Foo> a;
+ Foo init = make_foo(10, 'a', 0xBB);
+ assert_foo_padding(init, 0xBB);
+ a.store(init);
- Foo init = make_foo(10, 'a', 0xBB);
- assert_foo_padding(init, 0xBB);
- a.store(init);
+ Foo expected = make_foo(10, 'a', 0xAA);
+ assert_foo_padding(expected, 0xAA);
- Foo expected = make_foo(10, 'a', 0xAA);
- assert_foo_padding(expected, 0xAA);
+ Foo original_expected; // make a copy including padding bits
+ std::memcpy(&original_expected, &expected, sizeof(Foo));
-Foo original_expected; // make a copy including padding bits
-std::memcpy(&original_expected, expected, sizeof(Foo));
+ Foo new_value = make_foo(42, 'b', 0xCC);
+ assert_foo_padding(new_value, 0xCC);
- Foo new_value = make_foo(42, 'b', 0xCC);
- assert_foo_padding(new_value, 0xCC);
+ bool r = a.compare_exchange_strong(expected, new_value);
- bool r = a.compare_exchange_strong(expected, new_value);
+ assert(r);
+ assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+ Foo loaded = a.load();
+ assert(loaded.i == 42);
+ assert(loaded.c == 'b');
+ // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+ libcpp_assert_foo_padding(loaded, 0);
+ }
- assert(r);
- assert(std::memcmp(&expected, original_expected, sizeof(Foo)) == 0);
- Foo loaded = a.load();
- assert(loaded.i == 42);
- assert(loaded.c == 'b');
- assert_foo_padding(loaded, 0);
-}
+ {
+ // compare_exchange_strong
+ // atomic and expected values are different; failure
+ std::atomic<Foo> a;
+ Foo stored = make_foo(10, 'a', 0xBB);
+ assert_foo_padding(stored, 0xBB);
+ a.store(stored);
-void test_compare_exchange_strong_failure() {
- std::atomic<Foo> a;
- Foo stored = make_foo(10, 'a', 0xBB);
- assert_foo_padding(stored, 0xBB);
- a.store(stored);
-
- Foo expected = make_foo(99, 'a', 0xAA);
- assert_foo_padding(expected, 0xAA);
- Foo new_value = make_foo(42, 'b', 0xCC);
- assert_foo_padding(new_value, 0xCC);
-
- bool r = a.compare_exchange_strong(expected, new_value);
-
- assert(!r);
- assert(expected.i == 10);
- assert(expected.c == 'a');
- assert_foo_padding(expected, 0);
- Foo loaded = a.load();
- assert(loaded.i == 10);
- assert(loaded.c == 'a');
- assert_foo_padding(loaded, 0);
-}
+ Foo expected = make_foo(99, 'a', 0xAA);
+ assert_foo_padding(expected, 0xAA);
+ Foo new_value = make_foo(42, 'b', 0xCC);
+ assert_foo_padding(new_value, 0xCC);
+
+ bool r = a.compare_exchange_strong(expected, new_value);
+
+ assert(!r);
+ assert(expected.i == 10);
+ assert(expected.c == 'a');
+ // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+ libcpp_assert_foo_padding(expected, 0);
+ Foo loaded = a.load();
+ assert(loaded.i == 10);
+ assert(loaded.c == 'a');
+ // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+ libcpp_assert_foo_padding(loaded, 0);
+ }
-void test_compare_exchange_weak_success_padding_only() {
- std::atomic<Foo> a;
- Foo stored = make_foo(10, 'a', 0xBB);
- assert_foo_padding(stored, 0xBB);
- a.store(stored);
+ {
+ // compare_exchange_weak
+ // atomic and expected only differs in padding bits. It should either succeed or spuriously fail
+ std::atomic<Foo> a;
+ Foo stored = make_foo(10, 'a', 0xBB);
+ assert_foo_padding(stored, 0xBB);
+ a.store(stored);
+
+ Foo new_value = make_foo(42, 'b', 0xCC);
+ assert_foo_padding(new_value, 0xCC);
+
+ Foo original_expected = make_foo(10, 'a', 0xAA);
+ assert_foo_padding(original_expected, 0xAA);
+
+ bool r = false;
+ const auto max_attempts = 100;
+ auto current_attempt = 0;
+ while (!r) {
+ ++current_attempt;
+ assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
+ Foo expected = make_foo(10, 'a', 0xAA);
+ assert_foo_padding(expected, 0xAA);
+ r = a.compare_exchange_weak(expected, new_value);
+ if (r) {
+ assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+ } else {
+ // Spurious failure: expected is updated to the current atomic value.
+ assert(expected.i == 10);
+ assert(expected.c == 'a');
+ // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+ libcpp_assert_foo_padding(expected, 0);
+ }
+ }
- Foo new_value = make_foo(42, 'b', 0xCC);
- assert_foo_padding(new_value, 0xCC);
+ Foo loaded = a.load();
+ assert(loaded.i == 42);
+ assert(loaded.c == 'b');
+ // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+ libcpp_assert_foo_padding(loaded, 0);
+ }
- Foo original_expected = make_foo(10, 'a', 0xAA);
- assert_foo_padding(original_expected, 0xAA);
+ {
+ // compare_exchange_strong
+ // atomic and expected values are different; failure
+ std::atomic<Foo> a;
+ Foo stored = make_foo(10, 'a', 0xBB);
+ assert_foo_padding(stored, 0xBB);
+ a.store(stored);
- bool r = false;
- const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(3);
- while (!r) {
- assert(std::chrono::steady_clock::now() < deadline && "compare_exchange_weak did not succeed within 3 seconds");
- Foo expected = make_foo(10, 'a', 0xAA);
+ Foo expected = make_foo(99, 'a', 0xAA);
assert_foo_padding(expected, 0xAA);
- r = a.compare_exchange_weak(expected, new_value);
- if (r) {
- assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
- } else {
- // Spurious failure: expected is updated to the current atomic value.
- assert(expected.i == 10);
- assert(expected.c == 'a');
- assert_foo_padding(expected, 0);
- }
+ Foo new_value = make_foo(42, 'b', 0xCC);
+ assert_foo_padding(new_value, 0xCC);
+
+ bool r = a.compare_exchange_weak(expected, new_value);
+
+ assert(!r);
+ assert(expected.i == 10);
+ assert(expected.c == 'a');
+ // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+ libcpp_assert_foo_padding(expected, 0);
+ Foo loaded = a.load();
+ assert(loaded.i == 10);
+ assert(loaded.c == 'a');
+ // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+ libcpp_assert_foo_padding(loaded, 0);
}
- Foo loaded = a.load();
- assert(loaded.i == 42);
- assert(loaded.c == 'b');
- assert_foo_padding(loaded, 0);
-}
-
-void test_compare_exchange_weak_failure() {
- std::atomic<Foo> a;
- Foo stored = make_foo(10, 'a', 0xBB);
- assert_foo_padding(stored, 0xBB);
- a.store(stored);
-
- Foo expected = make_foo(99, 'a', 0xAA);
- assert_foo_padding(expected, 0xAA);
- Foo new_value = make_foo(42, 'b', 0xCC);
- assert_foo_padding(new_value, 0xCC);
-
- bool r = a.compare_exchange_weak(expected, new_value);
-
- assert(!r);
- assert(expected.i == 10);
- assert(expected.c == 'a');
- assert_foo_padding(expected, 0);
- Foo loaded = a.load();
- assert(loaded.i == 10);
- assert(loaded.c == 'a');
- assert_foo_padding(loaded, 0);
-}
-
-void test_no_padding_type() {
- // Types with unique object representations skip the padding-clearing path.
- std::atomic<int> a(1);
- int expected = 1;
- assert(a.compare_exchange_strong(expected, 2));
- assert(expected == 1);
- assert(a.load() == 2);
-
- expected = 3;
- assert(!a.compare_exchange_strong(expected, 4));
- assert(expected == 2);
- assert(a.load() == 2);
+ {
+ // Types with unique object representations skip the padding-clearing path.
+ std::atomic<int> a(1);
+ int expected = 1;
+ assert(a.compare_exchange_strong(expected, 2));
+ assert(expected == 1);
+ assert(a.load() == 2);
+
+ expected = 3;
+ assert(!a.compare_exchange_strong(expected, 4));
+ assert(expected == 2);
+ assert(a.load() == 2);
+ }
}
int main(int, char**) {
- test_default_constructor();
- test_value_constructor();
- test_store();
- test_exchange();
- test_atomic_init();
- test_compare_exchange_strong_success_padding_only();
- test_compare_exchange_strong_failure();
- test_compare_exchange_weak_success_padding_only();
- test_compare_exchange_weak_failure();
- test_no_padding_type();
+// TODO(LLVM-23): Switch to XFAIL: clang-22
+#if __has_builtin(__builtin_clear_padding)
+ test();
+#endif // __has_builtin(__builtin_clear_padding)
return 0;
}
-
-#else // !__has_builtin(__builtin_clear_padding)
-
-int main(int, char**) { return 0; }
-
-#endif // __has_builtin(__builtin_clear_padding)
More information about the libcxx-commits
mailing list