[libcxx-commits] [libcxx] [libc++] Implement P0528R3 `std::atomic` CAS for types with paddings (PR #76180)
via libcxx-commits
libcxx-commits at lists.llvm.org
Wed Jul 22 07:49:55 PDT 2026
https://github.com/huixie90 updated https://github.com/llvm/llvm-project/pull/76180
>From 145920aa6c68ee172dc65111a6a0cfb99d4c9069 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 01/30] [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 b40f586161e62..11a0b42ea54f5 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 899502422570a..4ae0f6a28637a 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>
@@ -32,11 +33,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;
};
@@ -52,21 +66,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));
}
@@ -102,12 +120,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));
}
@@ -126,23 +146,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>
@@ -152,23 +178,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 90d997369a9f358f579ae61963226b1f30c81d5a 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 02/30] 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 37c38d25012c6..78e27d9ef9781 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 7a63b7897f421f0d367057c933db66b3ea0f21e1 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 03/30] 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 11a0b42ea54f5..caa7f9d1a2535 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 78e27d9ef9781..640f6e5e916ca 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 39b4e0bb986c6..8bdde44c1cb8c 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -895,6 +895,7 @@ module std {
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 e6a881f9199428718c9b93a92dcea056d7e84356 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 04/30] 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 4ae0f6a28637a..c496ecf5ac742 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -49,7 +49,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 13589231818d564e6ba520c405eae011f72f10fe 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 05/30] 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 d3c6b5e3b74e59c2beaf5b3158db90faef36600c 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 06/30] 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 c496ecf5ac742..c55de19cd4b59 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -148,7 +148,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 11cedf33d3fe73d5bad5930a8c4f12df33e0eac8 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 07/30] 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 7e6410501ecdccbd78ce814b0e4906a60f379df6 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 08/30] 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 c55de19cd4b59..0cf50d7ebd1ec 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -162,7 +162,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),
@@ -180,7 +180,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),
@@ -195,7 +195,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 640f6e5e916ca..44f1229f859b3 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)
>From f666483df06fffa6f94a403ba051621cf0432415 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 6 Jul 2026 15:26:28 +0100
Subject: [PATCH 09/30] batch2
---
libcxx/include/CMakeLists.txt | 2 +-
.../{support/common.h => clear_padding.h} | 27 ++++++++++---------
libcxx/include/__atomic/support/c11.h | 25 ++++-------------
libcxx/include/__atomic/support/gcc.h | 20 +++-----------
libcxx/include/module.modulemap.in | 2 +-
.../atomics.types.generic/padding.pass.cpp | 7 ++---
.../atomics.types.generic/padding.pass.cpp | 5 ++--
7 files changed, 29 insertions(+), 59 deletions(-)
rename libcxx/include/__atomic/{support/common.h => clear_padding.h} (69%)
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index caa7f9d1a2535..b6650a314bd40 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -219,6 +219,7 @@ set(files
__atomic/atomic_sync_timed.h
__atomic/atomic_waitable_traits.h
__atomic/check_memory_order.h
+ __atomic/clear_padding.h
__atomic/contention_t.h
__atomic/fence.h
__atomic/floating_point_helper.h
@@ -227,7 +228,6 @@ set(files
__atomic/memory_order.h
__atomic/support.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/clear_padding.h
similarity index 69%
rename from libcxx/include/__atomic/support/common.h
rename to libcxx/include/__atomic/clear_padding.h
index 308302f8754a5..ac83939d6ec16 100644
--- a/libcxx/include/__atomic/support/common.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -6,13 +6,14 @@
//
//===----------------------------------------------------------------------===//
-#ifndef _LIBCPP___ATOMIC_SUPPORT_COMMON_H
-#define _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+#ifndef _LIBCPP___ATOMIC_CLEAR_PADDING_H
+#define _LIBCPP___ATOMIC_CLEAR_PADDING_H
#include <__config>
#include <__memory/addressof.h>
#include <__type_traits/conjunction.h>
#include <__type_traits/has_unique_object_representation.h>
+#include <__type_traits/integral_constant.h>
#include <__type_traits/is_same.h>
#include <__type_traits/negation.h>
#include <__type_traits/remove_cv.h>
@@ -26,15 +27,17 @@
_LIBCPP_BEGIN_NAMESPACE_STD
-#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#if __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>>> {};
+ : _And<_Not<integral_constant<bool, __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) {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 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));
}
@@ -43,22 +46,22 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __clear_padding_if_needed(_Tp& __obj) noexc
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) {
+ if constexpr (!__needs_clear_padding<__remove_cvref_t<_Tp>>::value) {
return __cas_func(__expected, __value);
} else {
std::__clear_padding_if_needed(__value);
- remove_cv_t<_Tp> __expected_copy = *__expected;
+ __remove_cvref_t<_Tp> __expected_copy = *__expected;
std::__clear_padding_if_needed(__expected_copy);
if (__cas_func(std::addressof(__expected_copy), __value)) {
return true;
} else {
- std::memcpy(__expected, std::addressof(__expected_copy), sizeof(remove_cv_t<_Tp>));
+ std::memcpy(__expected, std::addressof(__expected_copy), sizeof(__remove_cvref_t<_Tp>));
return false;
}
}
}
-#else // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#else // __has_builtin(__builtin_clear_padding)
template <class _Tp>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
@@ -68,8 +71,8 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
return __cas_func(__expected, __value);
}
-#endif // _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+#endif // __has_builtin(__builtin_clear_padding)
_LIBCPP_END_NAMESPACE_STD
-#endif // _LIBCPP___ATOMIC_SUPPORT_COMMON_H
+#endif // _LIBCPP___ATOMIC_CLEAR_PADDING_H
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 0cf50d7ebd1ec..05b22d04962fa 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -9,8 +9,8 @@
#ifndef _LIBCPP___ATOMIC_SUPPORT_C11_H
#define _LIBCPP___ATOMIC_SUPPORT_C11_H
+#include <__atomic/clear_padding.h>
#include <__atomic/memory_order.h>
-#include <__atomic/support/common.h>
#include <__config>
#include <__cstddef/ptrdiff_t.h>
#include <__memory/addressof.h>
@@ -30,29 +30,14 @@ _LIBCPP_BEGIN_NAMESPACE_STD
template <typename _Tp>
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);
- }
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
+ std::__clear_padding_if_needed(__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) {
-#if _LIBCPP_STD_VER >= 20 && __has_builtin(__builtin_clear_padding)
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
std::__clear_padding_if_needed(__a_value);
-#endif
}
+
_Atomic(_Tp) __a_value;
};
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 44f1229f859b3..0a6e3d54b5911 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -9,8 +9,8 @@
#ifndef _LIBCPP___ATOMIC_SUPPORT_GCC_H
#define _LIBCPP___ATOMIC_SUPPORT_GCC_H
+#include <__atomic/clear_padding.h>
#include <__atomic/memory_order.h>
-#include <__atomic/support/common.h>
#include <__atomic/to_gcc_order.h>
#include <__config>
#include <__memory/addressof.h>
@@ -46,23 +46,9 @@ _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_assign_volatile(_Tp volatile& __a_value,
template <typename _Tp>
struct __cxx_atomic_base_impl {
+ _LIBCPP_HIDE_FROM_ABI __cxx_atomic_base_impl() _NOEXCEPT : __a_value() { std::__clear_padding_if_needed(__a_value); }
+
_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_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
std::__clear_padding_if_needed(__a_value);
}
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 8bdde44c1cb8c..f038c9d39a27b 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -884,6 +884,7 @@ module std {
export std.atomic.atomic_base // most of std::atomic methods are defined there
}
module check_memory_order { header "__atomic/check_memory_order.h" }
+ module clear_padding { header "__atomic/clear_padding.h" }
module contention_t { header "__atomic/contention_t.h" }
module fence { header "__atomic/fence.h" }
module floating_point_helper { header "__atomic/floating_point_helper.h" }
@@ -895,7 +896,6 @@ module std {
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/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 78674a3f85204..e422e1c7e3e83 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -5,7 +5,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: c++03
// atomic_init is deprecated
// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
@@ -16,17 +16,14 @@
#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));
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
Foo make_foo(int i, char c, unsigned char pad_byte) {
Foo f;
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 e5fa3b96fc1d0..c09745a3d149f 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -5,7 +5,7 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: c++03
// atomic_init is deprecated
// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
@@ -24,8 +24,7 @@ struct Foo {
char c;
};
-static_assert(!std::has_unique_object_representations_v<Foo>);
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char));
+static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
Foo make_foo(int i, char c, unsigned char pad_byte) {
Foo f;
>From f622cc3f082c34d6f0c5cd6e47177664eed48938 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 10:06:00 +0100
Subject: [PATCH 10/30] CI
---
libcxx/include/__atomic/clear_padding.h | 9 +++++++--
.../std/atomics/atomics.types.generic/padding.pass.cpp | 2 +-
2 files changed, 8 insertions(+), 3 deletions(-)
diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index ac83939d6ec16..5bc77ff4401ab 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -36,12 +36,14 @@ struct __needs_clear_padding
_Not<is_same<_Tp, double>>> {};
template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp& __obj) noexcept {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __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));
}
}
+ // return __obj as c++11 does not constexpr function to return void
+ return __obj;
}
template <class _Tp, class _Up, class _CasFunc>
@@ -64,7 +66,10 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
#else // __has_builtin(__builtin_clear_padding)
template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __clear_padding_if_needed(_Tp&) _NOEXCEPT {}
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) _NOEXCEPT {
+ // return __obj as c++11 does not constexpr function to return void
+ return __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) {
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 c09745a3d149f..dbd93ea9f2cfb 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -196,7 +196,7 @@ int main(int, char**) {
// TODO(LLVM-23): Switch to XFAIL: clang-22
#if __has_builtin(__builtin_clear_padding)
test();
-#endif // __has_builtin(__builtin_clear_padding)
+#endif
return 0;
}
>From 1e55b9997f985525c33ac424b94de00d91b36bba Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 16:44:53 +0100
Subject: [PATCH 11/30] review comment
---
libcxx/include/__atomic/support/gcc.h | 7 +-
.../atomics.types.generic/padding.pass.cpp | 112 ++++---
.../atomics.types.generic/padding.pass.cpp | 153 ++++++----
.../atomics.types.generic/padding2.pass.cpp | 276 ++++++++++++++++++
4 files changed, 439 insertions(+), 109 deletions(-)
create mode 100644 libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 0a6e3d54b5911..2f6e0d5dbd20a 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -46,10 +46,11 @@ _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_assign_volatile(_Tp volatile& __a_value,
template <typename _Tp>
struct __cxx_atomic_base_impl {
- _LIBCPP_HIDE_FROM_ABI __cxx_atomic_base_impl() _NOEXCEPT : __a_value() { std::__clear_padding_if_needed(__a_value); }
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
+ std::__clear_padding_if_needed(__a_value);
+ }
- _LIBCPP_HIDE_FROM_ABI
- _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
std::__clear_padding_if_needed(__a_value);
}
_Tp __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
index e422e1c7e3e83..c8176b1940843 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -18,94 +18,120 @@
#include <cassert>
#include <cstring>
-struct Foo {
+struct WithTailPadding {
int i;
char c;
};
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
+static_assert(sizeof(WithTailPadding) > 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;
+struct WithInternalPadding {
+ char c;
+ int i;
+};
+
+static_assert(sizeof(WithInternalPadding) > sizeof(int) + sizeof(char), "");
+
+struct WithInternalAndTailPadding {
+ char c;
+ int i;
+ char c2;
+};
+
+static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
+
+template <class T>
+T make(int i, char c, unsigned char pad_byte) {
+ T obj;
+ std::memset(&obj, pad_byte, sizeof(T));
+ obj.i = i;
+ obj.c = c;
+ if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+ obj.c2 = c;
+ }
+ return obj;
}
-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);
+template <class T>
+void assert_padding(const T& obj, unsigned char pad_byte) {
+ alignas(T) unsigned char buf[sizeof(T)];
+ std::memset(buf, pad_byte, sizeof(T));
+ T& reference = *reinterpret_cast<T*>(buf);
+ reference.i = obj.i;
+ reference.c = obj.c;
+ if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+ reference.c2 = obj.c2;
+ }
+ assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
}
+template <class T>
void test() {
{
// atomic();
- std::atomic<Foo> a;
- Foo loaded = a.load();
+ std::atomic<T> a;
+ T loaded = a.load();
assert(loaded.i == 0);
assert(loaded.c == '\0');
- assert_foo_padding(loaded, 0);
+ assert_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();
+ T init = make<T>(10, 'a', 0xBB);
+ assert_padding(init, 0xBB);
+ std::atomic<T> a(init);
+ T loaded = a.load();
assert(loaded.i == 10);
assert(loaded.c == 'a');
- assert_foo_padding(loaded, 0);
+ assert_padding(loaded, 0);
}
{
// atomic::store
- std::atomic<Foo> a;
- Foo value = make_foo(5, 'x', 0xAB);
- assert_foo_padding(value, 0xAB);
+ std::atomic<T> a;
+ T value = make<T>(5, 'x', 0xAB);
+ assert_padding(value, 0xAB);
a.store(value);
- Foo loaded = a.load();
+ T loaded = a.load();
assert(loaded.i == 5);
assert(loaded.c == 'x');
- assert_foo_padding(loaded, 0);
+ assert_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);
+ T initial = make<T>(1, 'a', 0x00);
+ assert_padding(initial, 0x00);
+ std::atomic<T> a(initial);
+ T new_val = make<T>(2, 'b', 0xCD);
+ assert_padding(new_val, 0xCD);
+ T old = a.exchange(new_val);
assert(old.i == 1);
assert(old.c == 'a');
- assert_foo_padding(old, 0);
- Foo loaded = a.load();
+ assert_padding(old, 0);
+ T loaded = a.load();
assert(loaded.i == 2);
assert(loaded.c == 'b');
- assert_foo_padding(loaded, 0);
+ assert_padding(loaded, 0);
}
{
// atomic_init
- std::atomic<Foo> a;
- Foo init = make_foo(7, 'z', 0xEF);
- assert_foo_padding(init, 0xEF);
+ std::atomic<T> a;
+ T init = make<T>(7, 'z', 0xEF);
+ assert_padding(init, 0xEF);
std::atomic_init(&a, init);
- Foo loaded = a.load();
+ T loaded = a.load();
assert(loaded.i == 7);
assert(loaded.c == 'z');
- assert_foo_padding(loaded, 0);
+ assert_padding(loaded, 0);
}
}
int main(int, char**) {
// TODO(LLVM-23): Switch to XFAIL: clang-22
#if __has_builtin(__builtin_clear_padding)
- test();
+ test<WithTailPadding>();
+ test<WithInternalPadding>();
+ test<WithInternalAndTailPadding>();
#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 dbd93ea9f2cfb..d7889f2a1ed4d 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -19,81 +19,106 @@
#include <cstring>
#include <type_traits>
-struct Foo {
+struct WithTailPadding {
int i;
char c;
};
-static_assert(sizeof(Foo) > sizeof(int) + sizeof(char), "");
+static_assert(sizeof(WithTailPadding) > 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;
+struct WithInternalPadding {
+ char c;
+ int i;
+};
+
+static_assert(sizeof(WithInternalPadding) > sizeof(int) + sizeof(char), "");
+
+struct WithInternalAndTailPadding {
+ char c;
+ int i;
+ char c2;
+};
+
+static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
+
+template <class T>
+T make(int i, char c, unsigned char pad_byte) {
+ T obj;
+ std::memset(&obj, pad_byte, sizeof(T));
+ obj.i = i;
+ obj.c = c;
+ if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+ obj.c2 = c;
+ }
+ return obj;
}
-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);
+template <class T>
+void assert_padding(const T& obj, unsigned char pad_byte) {
+ alignas(T) unsigned char buf[sizeof(T)];
+ std::memset(buf, pad_byte, sizeof(T));
+ T& reference = *reinterpret_cast<T*>(buf);
+ reference.i = obj.i;
+ reference.c = obj.c;
+ if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+ reference.c2 = obj.c2;
+ }
+ assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
}
-void libcpp_assert_foo_padding(const Foo& f, unsigned char pad_byte) {
+template <class T>
+void libcpp_assert_padding(const T& obj, unsigned char pad_byte) {
#ifdef _LIBCPP_VERSION
- assert_foo_padding(f, pad_byte);
+ assert_padding(obj, pad_byte);
#else
(void)f;
(void)pad_byte;
#endif
}
+template <class T>
void test() {
{
// compare_exchange_strong
// CAS should succeed when only padding differs in expected; expected is unchanged.
- std::atomic<Foo> a;
+ std::atomic<T> a;
- Foo init = make_foo(10, 'a', 0xBB);
- assert_foo_padding(init, 0xBB);
+ T init = make<T>(10, 'a', 0xBB);
+ assert_padding(init, 0xBB);
a.store(init);
- Foo expected = make_foo(10, 'a', 0xAA);
- assert_foo_padding(expected, 0xAA);
+ T expected = make<T>(10, 'a', 0xAA);
+ assert_padding(expected, 0xAA);
- Foo original_expected; // make a copy including padding bits
- std::memcpy(&original_expected, &expected, sizeof(Foo));
+ T original_expected; // make a copy including padding bits
+ std::memcpy(&original_expected, &expected, sizeof(T));
- Foo new_value = make_foo(42, 'b', 0xCC);
- assert_foo_padding(new_value, 0xCC);
+ T new_value = make<T>(42, 'b', 0xCC);
+ assert_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(std::memcmp(&expected, &original_expected, sizeof(T)) == 0);
+ T 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);
+ libcpp_assert_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);
+ std::atomic<T> a;
+ T stored = make<T>(10, 'a', 0xBB);
+ assert_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);
+ T expected = make<T>(99, 'a', 0xAA);
+ assert_padding(expected, 0xAA);
+ T new_value = make<T>(42, 'b', 0xCC);
+ assert_padding(new_value, 0xCC);
bool r = a.compare_exchange_strong(expected, new_value);
@@ -101,27 +126,27 @@ void test() {
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();
+ libcpp_assert_padding(expected, 0);
+ T 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);
+ libcpp_assert_padding(loaded, 0);
}
{
// 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);
+ std::atomic<T> a;
+ T stored = make<T>(10, 'a', 0xBB);
+ assert_padding(stored, 0xBB);
a.store(stored);
- Foo new_value = make_foo(42, 'b', 0xCC);
- assert_foo_padding(new_value, 0xCC);
+ T new_value = make<T>(42, 'b', 0xCC);
+ assert_padding(new_value, 0xCC);
- Foo original_expected = make_foo(10, 'a', 0xAA);
- assert_foo_padding(original_expected, 0xAA);
+ T original_expected = make<T>(10, 'a', 0xAA);
+ assert_padding(original_expected, 0xAA);
bool r = false;
const auto max_attempts = 100;
@@ -129,39 +154,39 @@ void test() {
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);
+ T expected = make<T>(10, 'a', 0xAA);
+ assert_padding(expected, 0xAA);
r = a.compare_exchange_weak(expected, new_value);
if (r) {
- assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+ assert(std::memcmp(&expected, &original_expected, sizeof(T)) == 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);
+ libcpp_assert_padding(expected, 0);
}
}
- Foo loaded = a.load();
+ T 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);
+ libcpp_assert_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);
+ std::atomic<T> a;
+ T stored = make<T>(10, 'a', 0xBB);
+ assert_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);
+ T expected = make<T>(99, 'a', 0xAA);
+ assert_padding(expected, 0xAA);
+ T new_value = make<T>(42, 'b', 0xCC);
+ assert_padding(new_value, 0xCC);
bool r = a.compare_exchange_weak(expected, new_value);
@@ -169,12 +194,12 @@ void test() {
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();
+ libcpp_assert_padding(expected, 0);
+ T 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);
+ libcpp_assert_padding(loaded, 0);
}
{
@@ -195,7 +220,9 @@ void test() {
int main(int, char**) {
// TODO(LLVM-23): Switch to XFAIL: clang-22
#if __has_builtin(__builtin_clear_padding)
- test();
+ test<WithTailPadding>();
+ test<WithInternalPadding>();
+ test<WithInternalAndTailPadding>();
#endif
return 0;
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
new file mode 100644
index 0000000000000..4e17471a2461f
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
@@ -0,0 +1,276 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// UNSUPPORTED: no-localization
+
+// 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 <cstring>
+#include <type_traits>
+#include <iostream>
+
+struct Foo {
+ int i;
+ char c;
+};
+
+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 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() {
+ std::cerr << "test 1" << std::endl;
+ {
+ // compare_exchange_strong
+ // CAS should succeed when only padding differs in expected; expected is unchanged.
+ std::atomic<Foo> a;
+ std::cerr << "test 2" << std::endl;
+
+ Foo init = make_foo(10, 'a', 0xBB);
+ std::cerr << "test 3" << std::endl;
+ assert_foo_padding(init, 0xBB);
+ std::cerr << "test 4" << std::endl;
+ a.store(init);
+
+ Foo expected = make_foo(10, 'a', 0xAA);
+ std::cerr << "test 5" << std::endl;
+ assert_foo_padding(expected, 0xAA);
+ std::cerr << "test 6" << std::endl;
+
+ Foo original_expected; // make a copy including padding bits
+ std::memcpy(&original_expected, &expected, sizeof(Foo));
+
+ Foo new_value = make_foo(42, 'b', 0xCC);
+ std::cerr << "test 7" << std::endl;
+ assert_foo_padding(new_value, 0xCC);
+ std::cerr << "test 8" << std::endl;
+
+ bool r = a.compare_exchange_strong(expected, new_value);
+
+ std::cerr << "test 9" << std::endl;
+ assert(r);
+ std::cerr << "test 10" << std::endl;
+ assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+ std::cerr << "test 11" << std::endl;
+ Foo loaded = a.load();
+ assert(loaded.i == 42);
+ std::cerr << "test 12" << std::endl;
+ assert(loaded.c == 'b');
+ std::cerr << "test 13" << std::endl;
+ // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+ libcpp_assert_foo_padding(loaded, 0);
+ std::cerr << "test 14" << std::endl;
+ }
+
+ {
+ // compare_exchange_strong
+ // atomic and expected values are different; failure
+ std::atomic<Foo> a;
+ Foo stored = make_foo(10, 'a', 0xBB);
+ std::cerr << "test 15" << std::endl;
+ assert_foo_padding(stored, 0xBB);
+ std::cerr << "test 16" << std::endl;
+ a.store(stored);
+
+ Foo expected = make_foo(99, 'a', 0xAA);
+ std::cerr << "test 17" << std::endl;
+ assert_foo_padding(expected, 0xAA);
+ std::cerr << "test 18" << std::endl;
+ Foo new_value = make_foo(42, 'b', 0xCC);
+ std::cerr << "test 19" << std::endl;
+ assert_foo_padding(new_value, 0xCC);
+ std::cerr << "test 10" << std::endl;
+
+ bool r = a.compare_exchange_strong(expected, new_value);
+
+ std::cerr << "test 21" << std::endl;
+ assert(!r);
+ std::cerr << "test 22" << std::endl;
+ assert(expected.i == 10);
+ std::cerr << "test 23" << std::endl;
+ assert(expected.c == 'a');
+ std::cerr << "test 24" << std::endl;
+ // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+ libcpp_assert_foo_padding(expected, 0);
+ std::cerr << "test 25" << std::endl;
+ Foo loaded = a.load();
+ assert(loaded.i == 10);
+ std::cerr << "test 26" << std::endl;
+ assert(loaded.c == 'a');
+ std::cerr << "test 27" << std::endl;
+ // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+ libcpp_assert_foo_padding(loaded, 0);
+ std::cerr << "test 28" << std::endl;
+ }
+
+ {
+ // 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);
+ std::cerr << "test 29" << std::endl;
+ assert_foo_padding(stored, 0xBB);
+ std::cerr << "test 30" << std::endl;
+ a.store(stored);
+
+ Foo new_value = make_foo(42, 'b', 0xCC);
+ std::cerr << "test 31" << std::endl;
+ assert_foo_padding(new_value, 0xCC);
+ std::cerr << "test 32" << std::endl;
+
+ Foo original_expected = make_foo(10, 'a', 0xAA);
+ std::cerr << "test 33" << std::endl;
+ assert_foo_padding(original_expected, 0xAA);
+ std::cerr << "test 34" << std::endl;
+
+ bool r = false;
+ const auto max_attempts = 100;
+ auto current_attempt = 0;
+ while (!r) {
+ ++current_attempt;
+ std::cerr << "test 35" << std::endl;
+ assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
+ std::cerr << "test 36" << std::endl;
+ Foo expected = make_foo(10, 'a', 0xAA);
+ assert_foo_padding(expected, 0xAA);
+ std::cerr << "test 37" << std::endl;
+ r = a.compare_exchange_weak(expected, new_value);
+ if (r) {
+ std::cerr << "test 38" << std::endl;
+ assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
+ std::cerr << "test 39" << std::endl;
+ } else {
+ // Spurious failure: expected is updated to the current atomic value.
+ std::cerr << "test 40" << std::endl;
+ assert(expected.i == 10);
+ std::cerr << "test 41" << std::endl;
+ assert(expected.c == 'a');
+ std::cerr << "test 45" << std::endl;
+ // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+ libcpp_assert_foo_padding(expected, 0);
+ std::cerr << "test 43" << std::endl;
+ }
+ }
+
+ Foo loaded = a.load();
+ std::cerr << "test 44" << std::endl;
+ assert(loaded.i == 42);
+ std::cerr << "test 45" << std::endl;
+ assert(loaded.c == 'b');
+ std::cerr << "test 46" << std::endl;
+ // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+ libcpp_assert_foo_padding(loaded, 0);
+ std::cerr << "test 47" << std::endl;
+ }
+
+ {
+ // compare_exchange_strong
+ // atomic and expected values are different; failure
+ std::atomic<Foo> a;
+ Foo stored = make_foo(10, 'a', 0xBB);
+ std::cerr << "test 48" << std::endl;
+ assert_foo_padding(stored, 0xBB);
+ std::cerr << "test 49" << std::endl;
+ a.store(stored);
+
+ Foo expected = make_foo(99, 'a', 0xAA);
+ std::cerr << "test 50" << std::endl;
+ assert_foo_padding(expected, 0xAA);
+ std::cerr << "test 51" << std::endl;
+ Foo new_value = make_foo(42, 'b', 0xCC);
+ std::cerr << "test 52" << std::endl;
+ assert_foo_padding(new_value, 0xCC);
+ std::cerr << "test 53" << std::endl;
+
+ bool r = a.compare_exchange_weak(expected, new_value);
+
+ std::cerr << "test 54" << std::endl;
+ assert(!r);
+ std::cerr << "test 55" << std::endl;
+ assert(expected.i == 10);
+ std::cerr << "test 56" << std::endl;
+ assert(expected.c == 'a');
+ std::cerr << "test 57" << std::endl;
+ // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
+ libcpp_assert_foo_padding(expected, 0);
+ std::cerr << "test 58" << std::endl;
+ Foo loaded = a.load();
+ std::cerr << "test 59" << std::endl;
+ assert(loaded.i == 10);
+ std::cerr << "test 60" << std::endl;
+ assert(loaded.c == 'a');
+ std::cerr << "test 61" << std::endl;
+ // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+ libcpp_assert_foo_padding(loaded, 0);
+ std::cerr << "test 62" << std::endl;
+ }
+
+ {
+ // Types with unique object representations skip the padding-clearing path.
+ std::atomic<int> a(1);
+ int expected = 1;
+ std::cerr << "test 63" << std::endl;
+ assert(a.compare_exchange_strong(expected, 2));
+ std::cerr << "test 64" << std::endl;
+ assert(expected == 1);
+ std::cerr << "test 65" << std::endl;
+ assert(a.load() == 2);
+ std::cerr << "test 66" << std::endl;
+
+ expected = 3;
+ assert(!a.compare_exchange_strong(expected, 4));
+ std::cerr << "test 67" << std::endl;
+ assert(expected == 2);
+ std::cerr << "test 68" << std::endl;
+ assert(a.load() == 2);
+ std::cerr << "test 69" << std::endl;
+ }
+}
+
+int main(int, char**) {
+ // TODO(LLVM-23): Switch to XFAIL: clang-22
+ std::cerr << "main 1" << std::endl;
+#if __has_builtin(__builtin_clear_padding)
+ std::cerr << "main 2" << std::endl;
+ test();
+ std::cerr << "main 3" << std::endl;
+#endif
+ std::cerr << "main 4" << std::endl;
+ return 0;
+}
>From 996293d9cfb334a462283e783e2fb2af90bb373c Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 17:07:00 +0100
Subject: [PATCH 12/30] review
---
.../atomics.types.generic/padding.pass.cpp | 7 +++--
.../atomics.types.generic/padding.pass.cpp | 27 +++++++++++++++++++
2 files changed, 32 insertions(+), 2 deletions(-)
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index c8176b1940843..afeafb022abc4 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -10,8 +10,11 @@
// atomic_init is deprecated
// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
-// atomic<T>::compare_exchange_weak
-// atomic<T>::compare_exchange_strong
+// atomic<T>::atomic()
+// atomic<T>::atomic(T)
+// atomic<T>::store(T)
+// atomic<T>::exchange(T)
+// atomic_init(T)
// libc++ maintains the invariant of the atomic to have zero for all padding bits
#include <atomic>
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 d7889f2a1ed4d..0467fdd492d7b 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -133,6 +133,33 @@ void test() {
// libc++ always maintains the invariant of the atomic to have zeros in the padding bits
libcpp_assert_padding(loaded, 0);
}
+ {
+ // compare_exchange_strong
+ // atomic and expected are the same, including padding
+
+ T init = make<T>(10, 'a', 0x00);
+ assert_padding(init, 0x00);
+ a.store(init);
+
+ T expected = make<T>(10, 'a', 0x00);
+ assert_padding(expected, 0x00);
+
+ T original_expected; // make a copy including padding bits
+ std::memcpy(&original_expected, &expected, sizeof(T));
+
+ T new_value = make<T>(42, 'b', 0x42);
+ assert_padding(new_value, 0x42);
+
+ bool r = a.compare_exchange_strong(expected, new_value);
+
+ assert(r);
+ assert(std::memcmp(&expected, &original_expected, sizeof(T)) == 0);
+ T 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_padding(loaded, 0);
+ }
{
// compare_exchange_weak
>From f74f609379e9feab234dd430bc758b19d9b1d46b Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 17:08:04 +0100
Subject: [PATCH 13/30] 14
---
libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
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 0467fdd492d7b..e4d83e638ca77 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -47,7 +47,7 @@ T make(int i, char c, unsigned char pad_byte) {
std::memset(&obj, pad_byte, sizeof(T));
obj.i = i;
obj.c = c;
- if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+ if constexpr (std::is_same<T, WithInternalAndTailPadding>::value) {
obj.c2 = c;
}
return obj;
>From 8c0b1998421e1c8e214ee2694544bf3f9d8f5f79 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 17:10:02 +0100
Subject: [PATCH 14/30] 14
---
.../test/libcxx/atomics/atomics.types.generic/padding.pass.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index afeafb022abc4..bf594b45ed54f 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -20,6 +20,7 @@
#include <atomic>
#include <cassert>
#include <cstring>
+#include <type_traits>
struct WithTailPadding {
int i;
@@ -49,7 +50,7 @@ T make(int i, char c, unsigned char pad_byte) {
std::memset(&obj, pad_byte, sizeof(T));
obj.i = i;
obj.c = c;
- if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
+ if constexpr (std::is_same<T, WithInternalAndTailPadding>::value) {
obj.c2 = c;
}
return obj;
>From f436e600ce39701927e30d0074019afc4cdb581c Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 20:42:14 +0100
Subject: [PATCH 15/30] ci
---
libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
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 e4d83e638ca77..0bafdbb1d7ede 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -136,7 +136,7 @@ void test() {
{
// compare_exchange_strong
// atomic and expected are the same, including padding
-
+ std::atomic<T> a;
T init = make<T>(10, 'a', 0x00);
assert_padding(init, 0x00);
a.store(init);
>From 4def68f03c3089677bb5206f56f30e367415ecce Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 8 Jul 2026 22:13:05 +0100
Subject: [PATCH 16/30] ci
---
libcxx/include/__atomic/support/gcc.h | 5 ++--
.../atomics.types.generic/padding.pass.cpp | 24 ++++++++++-------
.../atomics.types.generic/padding.pass.cpp | 27 ++++++++++---------
.../atomics.types.generic/padding2.pass.cpp | 3 ---
4 files changed, 30 insertions(+), 29 deletions(-)
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index 2f6e0d5dbd20a..b162e2c2a63f0 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -50,9 +50,8 @@ struct __cxx_atomic_base_impl {
std::__clear_padding_if_needed(__a_value);
}
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
- std::__clear_padding_if_needed(__a_value);
- }
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT
+ : __a_value(std::__clear_padding_if_needed(__value)) {}
_Tp __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
index bf594b45ed54f..6f894fe6cb605 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -44,15 +44,23 @@ struct WithInternalAndTailPadding {
static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
+template <class T>
+void set(T& obj, int i, char c) {
+ obj.i = i;
+ obj.c = c;
+}
+
+void set(WithInternalAndTailPadding& obj, int i, char c) {
+ obj.i = i;
+ obj.c = c;
+ obj.c2 = c;
+}
+
template <class T>
T make(int i, char c, unsigned char pad_byte) {
T obj;
std::memset(&obj, pad_byte, sizeof(T));
- obj.i = i;
- obj.c = c;
- if constexpr (std::is_same<T, WithInternalAndTailPadding>::value) {
- obj.c2 = c;
- }
+ set(obj, i, c);
return obj;
}
@@ -61,11 +69,7 @@ void assert_padding(const T& obj, unsigned char pad_byte) {
alignas(T) unsigned char buf[sizeof(T)];
std::memset(buf, pad_byte, sizeof(T));
T& reference = *reinterpret_cast<T*>(buf);
- reference.i = obj.i;
- reference.c = obj.c;
- if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
- reference.c2 = obj.c2;
- }
+ set(reference, obj.i, obj.c);
assert(std::memcmp(&obj, &reference, sizeof(T)) == 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 0bafdbb1d7ede..344d08b493e60 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -7,9 +7,6 @@
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03
-// 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
@@ -41,15 +38,23 @@ struct WithInternalAndTailPadding {
static_assert(sizeof(WithInternalAndTailPadding) > sizeof(int) + 2 * sizeof(char), "");
+template <class T>
+void set(T& obj, int i, char c) {
+ obj.i = i;
+ obj.c = c;
+}
+
+void set(WithInternalAndTailPadding& obj, int i, char c) {
+ obj.i = i;
+ obj.c = c;
+ obj.c2 = c;
+}
+
template <class T>
T make(int i, char c, unsigned char pad_byte) {
T obj;
std::memset(&obj, pad_byte, sizeof(T));
- obj.i = i;
- obj.c = c;
- if constexpr (std::is_same<T, WithInternalAndTailPadding>::value) {
- obj.c2 = c;
- }
+ set(obj, i, c);
return obj;
}
@@ -58,11 +63,7 @@ void assert_padding(const T& obj, unsigned char pad_byte) {
alignas(T) unsigned char buf[sizeof(T)];
std::memset(buf, pad_byte, sizeof(T));
T& reference = *reinterpret_cast<T*>(buf);
- reference.i = obj.i;
- reference.c = obj.c;
- if constexpr (std::is_same_v<T, WithInternalAndTailPadding>) {
- reference.c2 = obj.c2;
- }
+ set(reference, obj.i, obj.c);
assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
}
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
index 4e17471a2461f..1040f342b8206 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
@@ -8,9 +8,6 @@
// UNSUPPORTED: c++03
// UNSUPPORTED: no-localization
-// 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
>From eeecefecde068ea1f6e2f8417b52961192ca8bc4 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 10 Jul 2026 08:29:04 +0100
Subject: [PATCH 17/30] CI
---
.../atomics.types.generic/padding.pass.cpp | 2 +-
.../atomics.types.generic/padding.pass.cpp | 2 +-
.../atomics.types.generic/padding2.pass.cpp | 273 ------------------
3 files changed, 2 insertions(+), 275 deletions(-)
delete mode 100644 libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 6f894fe6cb605..388ab0867fe38 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -135,7 +135,7 @@ void test() {
}
int main(int, char**) {
-// TODO(LLVM-23): Switch to XFAIL: clang-22
+// TODO(LLVM-23): Switch to X_F_A_I_L: clang-22
#if __has_builtin(__builtin_clear_padding)
test<WithTailPadding>();
test<WithInternalPadding>();
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 344d08b493e60..f1a534a2916d6 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -246,7 +246,7 @@ void test() {
}
int main(int, char**) {
-// TODO(LLVM-23): Switch to XFAIL: clang-22
+// TODO(LLVM-23): Switch to X_F_A_I_L: clang-22
#if __has_builtin(__builtin_clear_padding)
test<WithTailPadding>();
test<WithInternalPadding>();
diff --git a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
deleted file mode 100644
index 1040f342b8206..0000000000000
--- a/libcxx/test/std/atomics/atomics.types.generic/padding2.pass.cpp
+++ /dev/null
@@ -1,273 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-// UNSUPPORTED: no-localization
-
-// atomic<T>::compare_exchange_weak
-// atomic<T>::compare_exchange_strong
-// CAS should work on types with padding bits
-
-#include <atomic>
-#include <cassert>
-#include <cstring>
-#include <type_traits>
-#include <iostream>
-
-struct Foo {
- int i;
- char c;
-};
-
-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 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() {
- std::cerr << "test 1" << std::endl;
- {
- // compare_exchange_strong
- // CAS should succeed when only padding differs in expected; expected is unchanged.
- std::atomic<Foo> a;
- std::cerr << "test 2" << std::endl;
-
- Foo init = make_foo(10, 'a', 0xBB);
- std::cerr << "test 3" << std::endl;
- assert_foo_padding(init, 0xBB);
- std::cerr << "test 4" << std::endl;
- a.store(init);
-
- Foo expected = make_foo(10, 'a', 0xAA);
- std::cerr << "test 5" << std::endl;
- assert_foo_padding(expected, 0xAA);
- std::cerr << "test 6" << std::endl;
-
- Foo original_expected; // make a copy including padding bits
- std::memcpy(&original_expected, &expected, sizeof(Foo));
-
- Foo new_value = make_foo(42, 'b', 0xCC);
- std::cerr << "test 7" << std::endl;
- assert_foo_padding(new_value, 0xCC);
- std::cerr << "test 8" << std::endl;
-
- bool r = a.compare_exchange_strong(expected, new_value);
-
- std::cerr << "test 9" << std::endl;
- assert(r);
- std::cerr << "test 10" << std::endl;
- assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
- std::cerr << "test 11" << std::endl;
- Foo loaded = a.load();
- assert(loaded.i == 42);
- std::cerr << "test 12" << std::endl;
- assert(loaded.c == 'b');
- std::cerr << "test 13" << std::endl;
- // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
- libcpp_assert_foo_padding(loaded, 0);
- std::cerr << "test 14" << std::endl;
- }
-
- {
- // compare_exchange_strong
- // atomic and expected values are different; failure
- std::atomic<Foo> a;
- Foo stored = make_foo(10, 'a', 0xBB);
- std::cerr << "test 15" << std::endl;
- assert_foo_padding(stored, 0xBB);
- std::cerr << "test 16" << std::endl;
- a.store(stored);
-
- Foo expected = make_foo(99, 'a', 0xAA);
- std::cerr << "test 17" << std::endl;
- assert_foo_padding(expected, 0xAA);
- std::cerr << "test 18" << std::endl;
- Foo new_value = make_foo(42, 'b', 0xCC);
- std::cerr << "test 19" << std::endl;
- assert_foo_padding(new_value, 0xCC);
- std::cerr << "test 10" << std::endl;
-
- bool r = a.compare_exchange_strong(expected, new_value);
-
- std::cerr << "test 21" << std::endl;
- assert(!r);
- std::cerr << "test 22" << std::endl;
- assert(expected.i == 10);
- std::cerr << "test 23" << std::endl;
- assert(expected.c == 'a');
- std::cerr << "test 24" << std::endl;
- // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
- libcpp_assert_foo_padding(expected, 0);
- std::cerr << "test 25" << std::endl;
- Foo loaded = a.load();
- assert(loaded.i == 10);
- std::cerr << "test 26" << std::endl;
- assert(loaded.c == 'a');
- std::cerr << "test 27" << std::endl;
- // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
- libcpp_assert_foo_padding(loaded, 0);
- std::cerr << "test 28" << std::endl;
- }
-
- {
- // 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);
- std::cerr << "test 29" << std::endl;
- assert_foo_padding(stored, 0xBB);
- std::cerr << "test 30" << std::endl;
- a.store(stored);
-
- Foo new_value = make_foo(42, 'b', 0xCC);
- std::cerr << "test 31" << std::endl;
- assert_foo_padding(new_value, 0xCC);
- std::cerr << "test 32" << std::endl;
-
- Foo original_expected = make_foo(10, 'a', 0xAA);
- std::cerr << "test 33" << std::endl;
- assert_foo_padding(original_expected, 0xAA);
- std::cerr << "test 34" << std::endl;
-
- bool r = false;
- const auto max_attempts = 100;
- auto current_attempt = 0;
- while (!r) {
- ++current_attempt;
- std::cerr << "test 35" << std::endl;
- assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
- std::cerr << "test 36" << std::endl;
- Foo expected = make_foo(10, 'a', 0xAA);
- assert_foo_padding(expected, 0xAA);
- std::cerr << "test 37" << std::endl;
- r = a.compare_exchange_weak(expected, new_value);
- if (r) {
- std::cerr << "test 38" << std::endl;
- assert(std::memcmp(&expected, &original_expected, sizeof(Foo)) == 0);
- std::cerr << "test 39" << std::endl;
- } else {
- // Spurious failure: expected is updated to the current atomic value.
- std::cerr << "test 40" << std::endl;
- assert(expected.i == 10);
- std::cerr << "test 41" << std::endl;
- assert(expected.c == 'a');
- std::cerr << "test 45" << std::endl;
- // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
- libcpp_assert_foo_padding(expected, 0);
- std::cerr << "test 43" << std::endl;
- }
- }
-
- Foo loaded = a.load();
- std::cerr << "test 44" << std::endl;
- assert(loaded.i == 42);
- std::cerr << "test 45" << std::endl;
- assert(loaded.c == 'b');
- std::cerr << "test 46" << std::endl;
- // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
- libcpp_assert_foo_padding(loaded, 0);
- std::cerr << "test 47" << std::endl;
- }
-
- {
- // compare_exchange_strong
- // atomic and expected values are different; failure
- std::atomic<Foo> a;
- Foo stored = make_foo(10, 'a', 0xBB);
- std::cerr << "test 48" << std::endl;
- assert_foo_padding(stored, 0xBB);
- std::cerr << "test 49" << std::endl;
- a.store(stored);
-
- Foo expected = make_foo(99, 'a', 0xAA);
- std::cerr << "test 50" << std::endl;
- assert_foo_padding(expected, 0xAA);
- std::cerr << "test 51" << std::endl;
- Foo new_value = make_foo(42, 'b', 0xCC);
- std::cerr << "test 52" << std::endl;
- assert_foo_padding(new_value, 0xCC);
- std::cerr << "test 53" << std::endl;
-
- bool r = a.compare_exchange_weak(expected, new_value);
-
- std::cerr << "test 54" << std::endl;
- assert(!r);
- std::cerr << "test 55" << std::endl;
- assert(expected.i == 10);
- std::cerr << "test 56" << std::endl;
- assert(expected.c == 'a');
- std::cerr << "test 57" << std::endl;
- // expected is updated to contain atomic's value and in libc++, the paddings bits are always zero
- libcpp_assert_foo_padding(expected, 0);
- std::cerr << "test 58" << std::endl;
- Foo loaded = a.load();
- std::cerr << "test 59" << std::endl;
- assert(loaded.i == 10);
- std::cerr << "test 60" << std::endl;
- assert(loaded.c == 'a');
- std::cerr << "test 61" << std::endl;
- // libc++ always maintains the invariant of the atomic to have zeros in the padding bits
- libcpp_assert_foo_padding(loaded, 0);
- std::cerr << "test 62" << std::endl;
- }
-
- {
- // Types with unique object representations skip the padding-clearing path.
- std::atomic<int> a(1);
- int expected = 1;
- std::cerr << "test 63" << std::endl;
- assert(a.compare_exchange_strong(expected, 2));
- std::cerr << "test 64" << std::endl;
- assert(expected == 1);
- std::cerr << "test 65" << std::endl;
- assert(a.load() == 2);
- std::cerr << "test 66" << std::endl;
-
- expected = 3;
- assert(!a.compare_exchange_strong(expected, 4));
- std::cerr << "test 67" << std::endl;
- assert(expected == 2);
- std::cerr << "test 68" << std::endl;
- assert(a.load() == 2);
- std::cerr << "test 69" << std::endl;
- }
-}
-
-int main(int, char**) {
- // TODO(LLVM-23): Switch to XFAIL: clang-22
- std::cerr << "main 1" << std::endl;
-#if __has_builtin(__builtin_clear_padding)
- std::cerr << "main 2" << std::endl;
- test();
- std::cerr << "main 3" << std::endl;
-#endif
- std::cerr << "main 4" << std::endl;
- return 0;
-}
>From bc539fb8d58e16f24a5ccb8bdd2351475e7d4391 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Fri, 10 Jul 2026 08:52:15 +0100
Subject: [PATCH 18/30] ci
---
libcxx/include/__atomic/support/c11.h | 13 ++++++++-----
libcxx/include/__atomic/support/gcc.h | 9 +++++++--
2 files changed, 15 insertions(+), 7 deletions(-)
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 05b22d04962fa..d43a2c67f950a 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -30,13 +30,16 @@ _LIBCPP_BEGIN_NAMESPACE_STD
template <typename _Tp>
struct __cxx_atomic_base_impl {
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
- std::__clear_padding_if_needed(__a_value);
+ _LIBCPP_HIDE_FROM_ABI
+#ifndef _LIBCPP_CXX03_LANG
+ __cxx_atomic_base_impl() _NOEXCEPT = default;
+#else
+ __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
}
+#endif // _LIBCPP_CXX03_LANG
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT : __a_value(__value) {
- std::__clear_padding_if_needed(__a_value);
- }
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT
+ : __a_value(std::__clear_padding_if_needed(__value)) {}
_Atomic(_Tp) __a_value;
};
diff --git a/libcxx/include/__atomic/support/gcc.h b/libcxx/include/__atomic/support/gcc.h
index b162e2c2a63f0..d2d4919d54614 100644
--- a/libcxx/include/__atomic/support/gcc.h
+++ b/libcxx/include/__atomic/support/gcc.h
@@ -46,12 +46,17 @@ _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_assign_volatile(_Tp volatile& __a_value,
template <typename _Tp>
struct __cxx_atomic_base_impl {
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
- std::__clear_padding_if_needed(__a_value);
+ _LIBCPP_HIDE_FROM_ABI
+#ifndef _LIBCPP_CXX03_LANG
+ __cxx_atomic_base_impl() _NOEXCEPT = default;
+#else
+ __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
}
+#endif // _LIBCPP_CXX03_LANG
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __cxx_atomic_base_impl(_Tp __value) _NOEXCEPT
: __a_value(std::__clear_padding_if_needed(__value)) {}
+
_Tp __a_value;
};
>From 30a056210700cd8f89b1c0ca6991f272a0ebba43 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 16:26:08 +0100
Subject: [PATCH 19/30] review
---
libcxx/include/__atomic/clear_padding.h | 14 ++++++--------
.../atomics/atomics.types.generic/padding.pass.cpp | 2 +-
.../atomics/atomics.types.generic/padding.pass.cpp | 2 +-
3 files changed, 8 insertions(+), 10 deletions(-)
diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index 5bc77ff4401ab..f920ef39977b7 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -35,17 +35,16 @@ struct __needs_clear_padding
_Not<is_same<_Tp, float>>,
_Not<is_same<_Tp, double>>> {};
-template <class _Tp>
+template <class _Tp, __enable_if_t<!__needs_clear_padding<__remove_cvref_t<_Tp>>::value, int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __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));
- }
- }
- // return __obj as c++11 does not constexpr function to return void
return __obj;
}
+template <class _Tp, __enable_if_t<__needs_clear_padding<__remove_cvref_t<_Tp>>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
+ return __builtin_is_constant_evaluated() ? __obj : (__builtin_clear_padding(std::addressof(__obj)), __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_cvref_t<_Tp>>::value) {
@@ -67,7 +66,6 @@ _LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up
template <class _Tp>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) _NOEXCEPT {
- // return __obj as c++11 does not constexpr function to return void
return __obj;
}
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 388ab0867fe38..45fb083cccf3f 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -135,7 +135,7 @@ void test() {
}
int main(int, char**) {
-// TODO(LLVM-23): Switch to X_F_A_I_L: clang-22
+// TODO(LLVM-23): Switch to XFAIL with clang-22
#if __has_builtin(__builtin_clear_padding)
test<WithTailPadding>();
test<WithInternalPadding>();
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 f1a534a2916d6..5ffba6ca844c5 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -246,7 +246,7 @@ void test() {
}
int main(int, char**) {
-// TODO(LLVM-23): Switch to X_F_A_I_L: clang-22
+// TODO(LLVM-23): Switch to XFAIL with clang-22
#if __has_builtin(__builtin_clear_padding)
test<WithTailPadding>();
test<WithInternalPadding>();
>From 9b771605f016811c06b3f600e48f45f3268a5269 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 16:33:16 +0100
Subject: [PATCH 20/30] release notes
---
libcxx/docs/ReleaseNotes/24.rst | 1 +
libcxx/docs/Status/Cxx20Papers.csv | 2 +-
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index 70c704ff9d326..a689379321496 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -39,6 +39,7 @@ Implemented Papers
------------------
- P0493R5: Atomic minimum/maximum (`Github <https://llvm.org/PR105418>`__)
+- P0528R3: The Curious Case of Padding Bits, Featuring Atomic Compare-and-Exchange (`Github <https://llvm.org/PR76180>`__)
Improvements and New Features
-----------------------------
diff --git a/libcxx/docs/Status/Cxx20Papers.csv b/libcxx/docs/Status/Cxx20Papers.csv
index 5539767ab800b..8e394b4bc4b3a 100644
--- a/libcxx/docs/Status/Cxx20Papers.csv
+++ b/libcxx/docs/Status/Cxx20Papers.csv
@@ -31,7 +31,7 @@
"`P0458R2 <https://wg21.link/P0458R2>`__","Checking for Existence of an Element in Associative Containers","2018-06 (Rapperswil)","|Complete|","13","`#104029 <https://github.com/llvm/llvm-project/issues/104029>`__",""
"`P0475R1 <https://wg21.link/P0475R1>`__","LWG 2511: guaranteed copy elision for piecewise construction","2018-06 (Rapperswil)","|Complete|","","`#104031 <https://github.com/llvm/llvm-project/issues/104031>`__",""
"`P0476R2 <https://wg21.link/P0476R2>`__","Bit-casting object representations","2018-06 (Rapperswil)","|Complete|","14","`#104087 <https://github.com/llvm/llvm-project/issues/104087>`__",""
-"`P0528R3 <https://wg21.link/P0528R3>`__","The Curious Case of Padding Bits, Featuring Atomic Compare-and-Exchange","2018-06 (Rapperswil)","","","`#99984 <https://github.com/llvm/llvm-project/issues/99984>`__",""
+"`P0528R3 <https://wg21.link/P0528R3>`__","The Curious Case of Padding Bits, Featuring Atomic Compare-and-Exchange","2018-06 (Rapperswil)","|Complete|","24","`#99984 <https://github.com/llvm/llvm-project/issues/99984>`__",""
"`P0542R5 <https://wg21.link/P0542R5>`__","Support for contract based programming in C++","2018-06 (Rapperswil)","|Nothing To Do|","n/a","`#104089 <https://github.com/llvm/llvm-project/issues/104089>`__","Pulled at the 2019-07 meeting in Cologne"
"`P0556R3 <https://wg21.link/P0556R3>`__","Integral power-of-2 operations","2018-06 (Rapperswil)","|Complete|","9","`#104090 <https://github.com/llvm/llvm-project/issues/104090>`__",""
"`P0619R4 <https://wg21.link/P0619R4>`__","Reviewing Deprecated Facilities of C++17 for C++20","2018-06 (Rapperswil)","|Complete|","20","`#99985 <https://github.com/llvm/llvm-project/issues/99985>`__","Removed headers are still provided as an extension, but with deprecation warnings."
>From 46a4ba35dbc30153f7868e9796d44f88512ae602 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 17:01:57 +0100
Subject: [PATCH 21/30] ci
---
libcxx/include/__atomic/clear_padding.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index f920ef39977b7..3099faa8ca6d1 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -12,6 +12,7 @@
#include <__config>
#include <__memory/addressof.h>
#include <__type_traits/conjunction.h>
+#include <__type_traits/enable_if.h>
#include <__type_traits/has_unique_object_representation.h>
#include <__type_traits/integral_constant.h>
#include <__type_traits/is_same.h>
>From 5f475333df1aeee9640364c9fb2f734c731edb7e Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 19:03:39 +0100
Subject: [PATCH 22/30] ci
---
.../atomics.types.generic/padding.pass.cpp | 19 ++++---
.../atomics.types.generic/padding.pass.cpp | 52 ++++++++++++-------
2 files changed, 44 insertions(+), 27 deletions(-)
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 45fb083cccf3f..116a860b3192c 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -57,11 +57,9 @@ void set(WithInternalAndTailPadding& obj, int i, char c) {
}
template <class T>
-T make(int i, char c, unsigned char pad_byte) {
- T obj;
+void initialize(T& obj, int i, char c, unsigned char pad_byte) {
std::memset(&obj, pad_byte, sizeof(T));
set(obj, i, c);
- return obj;
}
template <class T>
@@ -86,7 +84,8 @@ void test() {
{
// atomic(T);
- T init = make<T>(10, 'a', 0xBB);
+ T init;
+ initialize(init, 10, 'a', 0xBB);
assert_padding(init, 0xBB);
std::atomic<T> a(init);
T loaded = a.load();
@@ -97,7 +96,8 @@ void test() {
{
// atomic::store
std::atomic<T> a;
- T value = make<T>(5, 'x', 0xAB);
+ T value;
+ initialize(value, 5, 'x', 0xAB);
assert_padding(value, 0xAB);
a.store(value);
T loaded = a.load();
@@ -107,10 +107,12 @@ void test() {
}
{
// atomic::exchange
- T initial = make<T>(1, 'a', 0x00);
+ T initial;
+ initialize(initial, 1, 'a', 0x00);
assert_padding(initial, 0x00);
std::atomic<T> a(initial);
- T new_val = make<T>(2, 'b', 0xCD);
+ T new_val;
+ initialize(new_val, 2, 'b', 0xCD);
assert_padding(new_val, 0xCD);
T old = a.exchange(new_val);
assert(old.i == 1);
@@ -124,7 +126,8 @@ void test() {
{
// atomic_init
std::atomic<T> a;
- T init = make<T>(7, 'z', 0xEF);
+ T init;
+ initialize(init, 7, 'z', 0xEF);
assert_padding(init, 0xEF);
std::atomic_init(&a, init);
T loaded = a.load();
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 5ffba6ca844c5..fe930f8b05b7d 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -51,11 +51,9 @@ void set(WithInternalAndTailPadding& obj, int i, char c) {
}
template <class T>
-T make(int i, char c, unsigned char pad_byte) {
- T obj;
+void initialize(T& obj, int i, char c, unsigned char pad_byte) {
std::memset(&obj, pad_byte, sizeof(T));
set(obj, i, c);
- return obj;
}
template <class T>
@@ -84,17 +82,20 @@ void test() {
// CAS should succeed when only padding differs in expected; expected is unchanged.
std::atomic<T> a;
- T init = make<T>(10, 'a', 0xBB);
+ T init;
+ initialize(init, 10, 'a', 0xBB);
assert_padding(init, 0xBB);
a.store(init);
- T expected = make<T>(10, 'a', 0xAA);
+ T expected;
+ initialize(expected, 10, 'a', 0xAA);
assert_padding(expected, 0xAA);
T original_expected; // make a copy including padding bits
std::memcpy(&original_expected, &expected, sizeof(T));
- T new_value = make<T>(42, 'b', 0xCC);
+ T new_value;
+ initialize(new_value, 42, 'b', 0xCC);
assert_padding(new_value, 0xCC);
bool r = a.compare_exchange_strong(expected, new_value);
@@ -112,13 +113,16 @@ void test() {
// compare_exchange_strong
// atomic and expected values are different; failure
std::atomic<T> a;
- T stored = make<T>(10, 'a', 0xBB);
+ T stored;
+ initialize(stored, 10, 'a', 0xBB);
assert_padding(stored, 0xBB);
a.store(stored);
- T expected = make<T>(99, 'a', 0xAA);
+ T expected;
+ initialize(expected, 99, 'a', 0xAA);
assert_padding(expected, 0xAA);
- T new_value = make<T>(42, 'b', 0xCC);
+ T new_value;
+ initialize(new_value, 42, 'b', 0xCC);
assert_padding(new_value, 0xCC);
bool r = a.compare_exchange_strong(expected, new_value);
@@ -138,17 +142,20 @@ void test() {
// compare_exchange_strong
// atomic and expected are the same, including padding
std::atomic<T> a;
- T init = make<T>(10, 'a', 0x00);
+ T init;
+ initialize(init, 10, 'a', 0x00);
assert_padding(init, 0x00);
a.store(init);
- T expected = make<T>(10, 'a', 0x00);
+ T expected;
+ initialize(expected, 10, 'a', 0x00);
assert_padding(expected, 0x00);
T original_expected; // make a copy including padding bits
std::memcpy(&original_expected, &expected, sizeof(T));
- T new_value = make<T>(42, 'b', 0x42);
+ T new_value;
+ initialize(new_value, 42, 'b', 0x42);
assert_padding(new_value, 0x42);
bool r = a.compare_exchange_strong(expected, new_value);
@@ -166,14 +173,17 @@ void test() {
// compare_exchange_weak
// atomic and expected only differs in padding bits. It should either succeed or spuriously fail
std::atomic<T> a;
- T stored = make<T>(10, 'a', 0xBB);
+ T stored;
+ initialize(stored, 10, 'a', 0xBB);
assert_padding(stored, 0xBB);
a.store(stored);
- T new_value = make<T>(42, 'b', 0xCC);
+ T new_value;
+ initialize(new_value, 42, 'b', 0xCC);
assert_padding(new_value, 0xCC);
- T original_expected = make<T>(10, 'a', 0xAA);
+ T original_expected;
+ initialize(original_expected, 10, 'a', 0xAA);
assert_padding(original_expected, 0xAA);
bool r = false;
@@ -182,7 +192,8 @@ void test() {
while (!r) {
++current_attempt;
assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
- T expected = make<T>(10, 'a', 0xAA);
+ T expected;
+ initialize(expected, 10, 'a', 0xAA);
assert_padding(expected, 0xAA);
r = a.compare_exchange_weak(expected, new_value);
if (r) {
@@ -207,13 +218,16 @@ void test() {
// compare_exchange_strong
// atomic and expected values are different; failure
std::atomic<T> a;
- T stored = make<T>(10, 'a', 0xBB);
+ T stored;
+ initialize(stored, 10, 'a', 0xBB);
assert_padding(stored, 0xBB);
a.store(stored);
- T expected = make<T>(99, 'a', 0xAA);
+ T expected;
+ initialize(expected, 99, 'a', 0xAA);
assert_padding(expected, 0xAA);
- T new_value = make<T>(42, 'b', 0xCC);
+ T new_value;
+ initialize(new_value, 42, 'b', 0xCC);
assert_padding(new_value, 0xCC);
bool r = a.compare_exchange_weak(expected, new_value);
>From 704a54eb4f1f7989931b7e25ad87abbb60ad6021 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 20:07:26 +0100
Subject: [PATCH 23/30] debug
---
.../atomics.types.generic/padding.pass.cpp | 64 ++++++++++++++++++-
1 file changed, 62 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 fe930f8b05b7d..3b7f13b366e96 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -15,6 +15,7 @@
#include <cassert>
#include <cstring>
#include <type_traits>
+#include <iostream>
struct WithTailPadding {
int i;
@@ -70,11 +71,19 @@ void libcpp_assert_padding(const T& obj, unsigned char pad_byte) {
#ifdef _LIBCPP_VERSION
assert_padding(obj, pad_byte);
#else
- (void)f;
+ (void)obj;
(void)pad_byte;
#endif
}
+template <class T>
+void print_memory(const T& obj) {
+ for (size_t i = 0; i < sizeof(T); ++i) {
+ std::cerr << std::hex << static_cast<int>(reinterpret_cast<const unsigned char*>(&obj)[i]) << " ";
+ }
+ std::cerr << std::endl;
+}
+
template <class T>
void test() {
{
@@ -84,11 +93,15 @@ void test() {
T init;
initialize(init, 10, 'a', 0xBB);
+ std::cerr << "test1 init padding should be 0xBB: " << std::endl;
+ print_memory(init);
assert_padding(init, 0xBB);
a.store(init);
T expected;
initialize(expected, 10, 'a', 0xAA);
+ std::cerr << "test1 expected padding should be 0xAA: " << std::endl;
+ print_memory(expected);
assert_padding(expected, 0xAA);
T original_expected; // make a copy including padding bits
@@ -96,6 +109,8 @@ void test() {
T new_value;
initialize(new_value, 42, 'b', 0xCC);
+ std::cerr << "test1 new_value padding should be 0xCC: " << std::endl;
+ print_memory(new_value);
assert_padding(new_value, 0xCC);
bool r = a.compare_exchange_strong(expected, new_value);
@@ -106,6 +121,8 @@ void test() {
assert(loaded.i == 42);
assert(loaded.c == 'b');
// libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+ std::cerr << "test1 loaded padding should be 0: " << std::endl;
+ print_memory(loaded);
libcpp_assert_padding(loaded, 0);
}
@@ -115,14 +132,20 @@ void test() {
std::atomic<T> a;
T stored;
initialize(stored, 10, 'a', 0xBB);
+ std::cerr << "test2 stored padding should be 0xBB: " << std::endl;
+ print_memory(stored);
assert_padding(stored, 0xBB);
a.store(stored);
T expected;
initialize(expected, 99, 'a', 0xAA);
+ std::cerr << "test2 expected padding should be 0xAA: " << std::endl;
+ print_memory(expected);
assert_padding(expected, 0xAA);
T new_value;
initialize(new_value, 42, 'b', 0xCC);
+ std::cerr << "test2 new_value padding should be 0xCC: " << std::endl;
+ print_memory(new_value);
assert_padding(new_value, 0xCC);
bool r = a.compare_exchange_strong(expected, new_value);
@@ -131,11 +154,15 @@ void test() {
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
+ std::cerr << "test2 expected padding should be 0: " << std::endl;
+ print_memory(expected);
libcpp_assert_padding(expected, 0);
T 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
+ std::cerr << "test2 loaded padding should be 0: " << std::endl;
+ print_memory(loaded);
libcpp_assert_padding(loaded, 0);
}
{
@@ -144,11 +171,15 @@ void test() {
std::atomic<T> a;
T init;
initialize(init, 10, 'a', 0x00);
+ std::cerr << "test3 init padding should be 0x00: " << std::endl;
+ print_memory(init);
assert_padding(init, 0x00);
a.store(init);
T expected;
initialize(expected, 10, 'a', 0x00);
+ std::cerr << "test3 expected padding should be 0x00: " << std::endl;
+ print_memory(expected);
assert_padding(expected, 0x00);
T original_expected; // make a copy including padding bits
@@ -156,6 +187,8 @@ void test() {
T new_value;
initialize(new_value, 42, 'b', 0x42);
+ std::cerr << "test3 new_value padding should be 0x42: " << std::endl;
+ print_memory(new_value);
assert_padding(new_value, 0x42);
bool r = a.compare_exchange_strong(expected, new_value);
@@ -166,6 +199,8 @@ void test() {
assert(loaded.i == 42);
assert(loaded.c == 'b');
// libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+ std::cerr << "test3 loaded padding should be 0: " << std::endl;
+ print_memory(loaded);
libcpp_assert_padding(loaded, 0);
}
@@ -175,15 +210,21 @@ void test() {
std::atomic<T> a;
T stored;
initialize(stored, 10, 'a', 0xBB);
+ std::cerr << "test4 stored padding should be 0xBB: " << std::endl;
+ print_memory(stored);
assert_padding(stored, 0xBB);
a.store(stored);
T new_value;
initialize(new_value, 42, 'b', 0xCC);
+ std::cerr << "test4 new_value padding should be 0xCC: " << std::endl;
+ print_memory(new_value);
assert_padding(new_value, 0xCC);
T original_expected;
initialize(original_expected, 10, 'a', 0xAA);
+ std::cerr << "test4 original_expected padding should be 0xAA: " << std::endl;
+ print_memory(original_expected);
assert_padding(original_expected, 0xAA);
bool r = false;
@@ -194,6 +235,8 @@ void test() {
assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
T expected;
initialize(expected, 10, 'a', 0xAA);
+ std::cerr << "test4 expected padding should be 0xAA: " << std::endl;
+ print_memory(expected);
assert_padding(expected, 0xAA);
r = a.compare_exchange_weak(expected, new_value);
if (r) {
@@ -203,6 +246,8 @@ void test() {
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
+ std::cerr << "test4 expected padding should be 0: " << std::endl;
+ print_memory(expected);
libcpp_assert_padding(expected, 0);
}
}
@@ -211,6 +256,8 @@ void test() {
assert(loaded.i == 42);
assert(loaded.c == 'b');
// libc++ always maintains the invariant of the atomic to have zeros in the padding bits
+ std::cerr << "test4 loaded padding should be 0: " << std::endl;
+ print_memory(loaded);
libcpp_assert_padding(loaded, 0);
}
@@ -220,14 +267,20 @@ void test() {
std::atomic<T> a;
T stored;
initialize(stored, 10, 'a', 0xBB);
+ std::cerr << "test5 stored padding should be 0xBB: " << std::endl;
+ print_memory(stored);
assert_padding(stored, 0xBB);
a.store(stored);
T expected;
initialize(expected, 99, 'a', 0xAA);
+ std::cerr << "test5 expected padding should be 0xAA: " << std::endl;
+ print_memory(expected);
assert_padding(expected, 0xAA);
T new_value;
initialize(new_value, 42, 'b', 0xCC);
+ std::cerr << "test5 new_value padding should be 0xCC: " << std::endl;
+ print_memory(new_value);
assert_padding(new_value, 0xCC);
bool r = a.compare_exchange_weak(expected, new_value);
@@ -236,11 +289,15 @@ void test() {
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
+ std::cerr << "test5 expected padding should be 0: " << std::endl;
+ print_memory(expected);
libcpp_assert_padding(expected, 0);
T 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
+ std::cerr << "test5 loaded padding should be 0: " << std::endl;
+ print_memory(loaded);
libcpp_assert_padding(loaded, 0);
}
@@ -262,10 +319,13 @@ void test() {
int main(int, char**) {
// TODO(LLVM-23): Switch to XFAIL with clang-22
#if __has_builtin(__builtin_clear_padding)
+ std::cerr << "\nWithTailPadding\n" << std::endl;
test<WithTailPadding>();
+ std::cerr << "\nWithInternalPadding\n" << std::endl;
test<WithInternalPadding>();
+ std::cerr << "\nWithInternalAndTailPadding\n" << std::endl;
test<WithInternalAndTailPadding>();
#endif
- return 0;
+ return 1;
}
>From 8c622e41d2428f630855f0aaa215b1463d8fe4ca Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 20:38:19 +0100
Subject: [PATCH 24/30] Revert "debug"
This reverts commit 8be9a83ee040faff62f0756555890484bf502a31.
---
.../atomics.types.generic/padding.pass.cpp | 64 +------------------
1 file changed, 2 insertions(+), 62 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 3b7f13b366e96..fe930f8b05b7d 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -15,7 +15,6 @@
#include <cassert>
#include <cstring>
#include <type_traits>
-#include <iostream>
struct WithTailPadding {
int i;
@@ -71,19 +70,11 @@ void libcpp_assert_padding(const T& obj, unsigned char pad_byte) {
#ifdef _LIBCPP_VERSION
assert_padding(obj, pad_byte);
#else
- (void)obj;
+ (void)f;
(void)pad_byte;
#endif
}
-template <class T>
-void print_memory(const T& obj) {
- for (size_t i = 0; i < sizeof(T); ++i) {
- std::cerr << std::hex << static_cast<int>(reinterpret_cast<const unsigned char*>(&obj)[i]) << " ";
- }
- std::cerr << std::endl;
-}
-
template <class T>
void test() {
{
@@ -93,15 +84,11 @@ void test() {
T init;
initialize(init, 10, 'a', 0xBB);
- std::cerr << "test1 init padding should be 0xBB: " << std::endl;
- print_memory(init);
assert_padding(init, 0xBB);
a.store(init);
T expected;
initialize(expected, 10, 'a', 0xAA);
- std::cerr << "test1 expected padding should be 0xAA: " << std::endl;
- print_memory(expected);
assert_padding(expected, 0xAA);
T original_expected; // make a copy including padding bits
@@ -109,8 +96,6 @@ void test() {
T new_value;
initialize(new_value, 42, 'b', 0xCC);
- std::cerr << "test1 new_value padding should be 0xCC: " << std::endl;
- print_memory(new_value);
assert_padding(new_value, 0xCC);
bool r = a.compare_exchange_strong(expected, new_value);
@@ -121,8 +106,6 @@ void test() {
assert(loaded.i == 42);
assert(loaded.c == 'b');
// libc++ always maintains the invariant of the atomic to have zeros in the padding bits
- std::cerr << "test1 loaded padding should be 0: " << std::endl;
- print_memory(loaded);
libcpp_assert_padding(loaded, 0);
}
@@ -132,20 +115,14 @@ void test() {
std::atomic<T> a;
T stored;
initialize(stored, 10, 'a', 0xBB);
- std::cerr << "test2 stored padding should be 0xBB: " << std::endl;
- print_memory(stored);
assert_padding(stored, 0xBB);
a.store(stored);
T expected;
initialize(expected, 99, 'a', 0xAA);
- std::cerr << "test2 expected padding should be 0xAA: " << std::endl;
- print_memory(expected);
assert_padding(expected, 0xAA);
T new_value;
initialize(new_value, 42, 'b', 0xCC);
- std::cerr << "test2 new_value padding should be 0xCC: " << std::endl;
- print_memory(new_value);
assert_padding(new_value, 0xCC);
bool r = a.compare_exchange_strong(expected, new_value);
@@ -154,15 +131,11 @@ void test() {
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
- std::cerr << "test2 expected padding should be 0: " << std::endl;
- print_memory(expected);
libcpp_assert_padding(expected, 0);
T 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
- std::cerr << "test2 loaded padding should be 0: " << std::endl;
- print_memory(loaded);
libcpp_assert_padding(loaded, 0);
}
{
@@ -171,15 +144,11 @@ void test() {
std::atomic<T> a;
T init;
initialize(init, 10, 'a', 0x00);
- std::cerr << "test3 init padding should be 0x00: " << std::endl;
- print_memory(init);
assert_padding(init, 0x00);
a.store(init);
T expected;
initialize(expected, 10, 'a', 0x00);
- std::cerr << "test3 expected padding should be 0x00: " << std::endl;
- print_memory(expected);
assert_padding(expected, 0x00);
T original_expected; // make a copy including padding bits
@@ -187,8 +156,6 @@ void test() {
T new_value;
initialize(new_value, 42, 'b', 0x42);
- std::cerr << "test3 new_value padding should be 0x42: " << std::endl;
- print_memory(new_value);
assert_padding(new_value, 0x42);
bool r = a.compare_exchange_strong(expected, new_value);
@@ -199,8 +166,6 @@ void test() {
assert(loaded.i == 42);
assert(loaded.c == 'b');
// libc++ always maintains the invariant of the atomic to have zeros in the padding bits
- std::cerr << "test3 loaded padding should be 0: " << std::endl;
- print_memory(loaded);
libcpp_assert_padding(loaded, 0);
}
@@ -210,21 +175,15 @@ void test() {
std::atomic<T> a;
T stored;
initialize(stored, 10, 'a', 0xBB);
- std::cerr << "test4 stored padding should be 0xBB: " << std::endl;
- print_memory(stored);
assert_padding(stored, 0xBB);
a.store(stored);
T new_value;
initialize(new_value, 42, 'b', 0xCC);
- std::cerr << "test4 new_value padding should be 0xCC: " << std::endl;
- print_memory(new_value);
assert_padding(new_value, 0xCC);
T original_expected;
initialize(original_expected, 10, 'a', 0xAA);
- std::cerr << "test4 original_expected padding should be 0xAA: " << std::endl;
- print_memory(original_expected);
assert_padding(original_expected, 0xAA);
bool r = false;
@@ -235,8 +194,6 @@ void test() {
assert(current_attempt < max_attempts && "compare_exchange_weak did not succeed within 3 seconds");
T expected;
initialize(expected, 10, 'a', 0xAA);
- std::cerr << "test4 expected padding should be 0xAA: " << std::endl;
- print_memory(expected);
assert_padding(expected, 0xAA);
r = a.compare_exchange_weak(expected, new_value);
if (r) {
@@ -246,8 +203,6 @@ void test() {
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
- std::cerr << "test4 expected padding should be 0: " << std::endl;
- print_memory(expected);
libcpp_assert_padding(expected, 0);
}
}
@@ -256,8 +211,6 @@ void test() {
assert(loaded.i == 42);
assert(loaded.c == 'b');
// libc++ always maintains the invariant of the atomic to have zeros in the padding bits
- std::cerr << "test4 loaded padding should be 0: " << std::endl;
- print_memory(loaded);
libcpp_assert_padding(loaded, 0);
}
@@ -267,20 +220,14 @@ void test() {
std::atomic<T> a;
T stored;
initialize(stored, 10, 'a', 0xBB);
- std::cerr << "test5 stored padding should be 0xBB: " << std::endl;
- print_memory(stored);
assert_padding(stored, 0xBB);
a.store(stored);
T expected;
initialize(expected, 99, 'a', 0xAA);
- std::cerr << "test5 expected padding should be 0xAA: " << std::endl;
- print_memory(expected);
assert_padding(expected, 0xAA);
T new_value;
initialize(new_value, 42, 'b', 0xCC);
- std::cerr << "test5 new_value padding should be 0xCC: " << std::endl;
- print_memory(new_value);
assert_padding(new_value, 0xCC);
bool r = a.compare_exchange_weak(expected, new_value);
@@ -289,15 +236,11 @@ void test() {
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
- std::cerr << "test5 expected padding should be 0: " << std::endl;
- print_memory(expected);
libcpp_assert_padding(expected, 0);
T 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
- std::cerr << "test5 loaded padding should be 0: " << std::endl;
- print_memory(loaded);
libcpp_assert_padding(loaded, 0);
}
@@ -319,13 +262,10 @@ void test() {
int main(int, char**) {
// TODO(LLVM-23): Switch to XFAIL with clang-22
#if __has_builtin(__builtin_clear_padding)
- std::cerr << "\nWithTailPadding\n" << std::endl;
test<WithTailPadding>();
- std::cerr << "\nWithInternalPadding\n" << std::endl;
test<WithInternalPadding>();
- std::cerr << "\nWithInternalAndTailPadding\n" << std::endl;
test<WithInternalAndTailPadding>();
#endif
- return 1;
+ return 0;
}
>From 789912ba84ccbb3a3efea236fde31835783a7415 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Mon, 20 Jul 2026 20:44:00 +0100
Subject: [PATCH 25/30] test ci
---
.../atomics.types.generic/padding.pass.cpp | 20 ++++++++++++++-----
.../atomics.types.generic/padding.pass.cpp | 12 +----------
2 files changed, 16 insertions(+), 16 deletions(-)
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index 116a860b3192c..cd41f2893864c 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -71,6 +71,16 @@ void assert_padding(const T& obj, unsigned char pad_byte) {
assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
}
+template <class T>
+void assert_padding(const std::atomic<T>& obj, unsigned char pad_byte) {
+ alignas(T) unsigned char buf[sizeof(T)];
+ std::memset(buf, pad_byte, sizeof(T));
+ T& reference = *reinterpret_cast<T*>(buf);
+ T loaded = obj.load();
+ set(reference, loaded.i, loaded.c);
+ assert(std::memcmp(&obj, &reference, sizeof(T)) == 0);
+}
+
template <class T>
void test() {
{
@@ -79,7 +89,7 @@ void test() {
T loaded = a.load();
assert(loaded.i == 0);
assert(loaded.c == '\0');
- assert_padding(loaded, 0);
+ assert_padding(a, 0);
}
{
@@ -91,7 +101,7 @@ void test() {
T loaded = a.load();
assert(loaded.i == 10);
assert(loaded.c == 'a');
- assert_padding(loaded, 0);
+ assert_padding(a, 0);
}
{
// atomic::store
@@ -103,7 +113,7 @@ void test() {
T loaded = a.load();
assert(loaded.i == 5);
assert(loaded.c == 'x');
- assert_padding(loaded, 0);
+ assert_padding(a, 0);
}
{
// atomic::exchange
@@ -121,7 +131,7 @@ void test() {
T loaded = a.load();
assert(loaded.i == 2);
assert(loaded.c == 'b');
- assert_padding(loaded, 0);
+ assert_padding(a, 0);
}
{
// atomic_init
@@ -133,7 +143,7 @@ void test() {
T loaded = a.load();
assert(loaded.i == 7);
assert(loaded.c == 'z');
- assert_padding(loaded, 0);
+ assert_padding(a, 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 fe930f8b05b7d..10c15523cd70b 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -70,7 +70,7 @@ void libcpp_assert_padding(const T& obj, unsigned char pad_byte) {
#ifdef _LIBCPP_VERSION
assert_padding(obj, pad_byte);
#else
- (void)f;
+ (void)obj;
(void)pad_byte;
#endif
}
@@ -105,8 +105,6 @@ void test() {
T 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_padding(loaded, 0);
}
{
@@ -135,8 +133,6 @@ void test() {
T 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_padding(loaded, 0);
}
{
// compare_exchange_strong
@@ -165,8 +161,6 @@ void test() {
T 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_padding(loaded, 0);
}
{
@@ -210,8 +204,6 @@ void test() {
T 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_padding(loaded, 0);
}
{
@@ -240,8 +232,6 @@ void test() {
T 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_padding(loaded, 0);
}
{
>From 81770cda9f2171565c1c2980ad8306b9665752b2 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 22 Jul 2026 12:18:49 +0100
Subject: [PATCH 26/30] padding
---
.../test/libcxx/atomics/atomics.types.generic/padding.pass.cpp | 1 -
1 file changed, 1 deletion(-)
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index cd41f2893864c..a178ce74578ec 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -127,7 +127,6 @@ void test() {
T old = a.exchange(new_val);
assert(old.i == 1);
assert(old.c == 'a');
- assert_padding(old, 0);
T loaded = a.load();
assert(loaded.i == 2);
assert(loaded.c == 'b');
>From b90bec87725f4bde5722a3aa857a5c9d22c275e5 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 22 Jul 2026 12:40:31 +0100
Subject: [PATCH 27/30] clang22
---
.../libcxx/atomics/atomics.types.generic/padding.pass.cpp | 5 ++---
.../test/std/atomics/atomics.types.generic/padding.pass.cpp | 4 +---
2 files changed, 3 insertions(+), 6 deletions(-)
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
index a178ce74578ec..48ae68a8e97ba 100644
--- a/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp
@@ -6,6 +6,7 @@
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03
+// XFAIL: clang-21, apple-clang-21, clang-22
// atomic_init is deprecated
// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
@@ -147,11 +148,9 @@ void test() {
}
int main(int, char**) {
-// TODO(LLVM-23): Switch to XFAIL with clang-22
-#if __has_builtin(__builtin_clear_padding)
test<WithTailPadding>();
test<WithInternalPadding>();
test<WithInternalAndTailPadding>();
-#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 10c15523cd70b..dc0afa439dc12 100644
--- a/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp
@@ -6,6 +6,7 @@
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03
+// XFAIL: clang-21, apple-clang-21, clang-22
// atomic<T>::compare_exchange_weak
// atomic<T>::compare_exchange_strong
@@ -250,12 +251,9 @@ void test() {
}
int main(int, char**) {
-// TODO(LLVM-23): Switch to XFAIL with clang-22
-#if __has_builtin(__builtin_clear_padding)
test<WithTailPadding>();
test<WithInternalPadding>();
test<WithInternalAndTailPadding>();
-#endif
return 0;
}
>From 05729850c8bf62df54a60b12201ac475e5c55fb5 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 22 Jul 2026 13:26:21 +0100
Subject: [PATCH 28/30] cxx 03
---
libcxx/include/__atomic/clear_padding.h | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index 3099faa8ca6d1..dad48ed27e6a4 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -32,23 +32,23 @@ _LIBCPP_BEGIN_NAMESPACE_STD
template <class _Tp>
struct __needs_clear_padding
- : _And<_Not<integral_constant<bool, __has_unique_object_representations(_Tp)>>,
- _Not<is_same<_Tp, float>>,
- _Not<is_same<_Tp, double>>> {};
+ : _And<_Not<integral_constant<bool, __has_unique_object_representations(_Tp)> >,
+ _Not<is_same<_Tp, float> >,
+ _Not<is_same<_Tp, double> > > {};
-template <class _Tp, __enable_if_t<!__needs_clear_padding<__remove_cvref_t<_Tp>>::value, int> = 0>
+template <class _Tp, __enable_if_t<!__needs_clear_padding<__remove_cvref_t<_Tp> >::value, int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
return __obj;
}
-template <class _Tp, __enable_if_t<__needs_clear_padding<__remove_cvref_t<_Tp>>::value, int> = 0>
+template <class _Tp, __enable_if_t<__needs_clear_padding<__remove_cvref_t<_Tp> >::value, int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
return __builtin_is_constant_evaluated() ? __obj : (__builtin_clear_padding(std::addressof(__obj)), __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_cvref_t<_Tp>>::value) {
+ if constexpr (!__needs_clear_padding<__remove_cvref_t<_Tp> >::value) {
return __cas_func(__expected, __value);
} else {
std::__clear_padding_if_needed(__value);
>From 2d00c0b818d180cc4d991508be2ad3dd8b7ee878 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 22 Jul 2026 14:26:21 +0100
Subject: [PATCH 29/30] ci
---
libcxx/include/__atomic/clear_padding.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index dad48ed27e6a4..1a4223fd97763 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -37,12 +37,12 @@ struct __needs_clear_padding
_Not<is_same<_Tp, double> > > {};
template <class _Tp, __enable_if_t<!__needs_clear_padding<__remove_cvref_t<_Tp> >::value, int> = 0>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) _NOEXCEPT {
return __obj;
}
template <class _Tp, __enable_if_t<__needs_clear_padding<__remove_cvref_t<_Tp> >::value, int> = 0>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) noexcept {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __obj) _NOEXCEPT {
return __builtin_is_constant_evaluated() ? __obj : (__builtin_clear_padding(std::addressof(__obj)), __obj);
}
>From 30d5c984f33655f34946afb790bdc85c1f29d7a4 Mon Sep 17 00:00:00 2001
From: Hui Xie <hui.xie1990 at gmail.com>
Date: Wed, 22 Jul 2026 15:49:25 +0100
Subject: [PATCH 30/30] ci
---
libcxx/include/__atomic/clear_padding.h | 31 +++++++++++++++----------
1 file changed, 19 insertions(+), 12 deletions(-)
diff --git a/libcxx/include/__atomic/clear_padding.h b/libcxx/include/__atomic/clear_padding.h
index 1a4223fd97763..be55837cd8ec8 100644
--- a/libcxx/include/__atomic/clear_padding.h
+++ b/libcxx/include/__atomic/clear_padding.h
@@ -46,20 +46,27 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp& __clear_padding_if_needed(_Tp& __ob
return __builtin_is_constant_evaluated() ? __obj : (__builtin_clear_padding(std::addressof(__obj)), __obj);
}
-template <class _Tp, class _Up, class _CasFunc>
+template <class _Tp,
+ class _Up,
+ class _CasFunc,
+ __enable_if_t<!__needs_clear_padding<__remove_cvref_t<_Tp> >::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
+ return __cas_func(__expected, __value);
+}
+
+template <class _Tp,
+ class _Up,
+ class _CasFunc,
+ __enable_if_t<__needs_clear_padding<__remove_cvref_t<_Tp> >::value, int> = 0>
_LIBCPP_HIDE_FROM_ABI bool __atomic_cas_with_clear_padding(_Tp* __expected, _Up __value, _CasFunc&& __cas_func) {
- if constexpr (!__needs_clear_padding<__remove_cvref_t<_Tp> >::value) {
- return __cas_func(__expected, __value);
+ std::__clear_padding_if_needed(__value);
+ __remove_cvref_t<_Tp> __expected_copy = *__expected;
+ std::__clear_padding_if_needed(__expected_copy);
+ if (__cas_func(std::addressof(__expected_copy), __value)) {
+ return true;
} else {
- std::__clear_padding_if_needed(__value);
- __remove_cvref_t<_Tp> __expected_copy = *__expected;
- std::__clear_padding_if_needed(__expected_copy);
- if (__cas_func(std::addressof(__expected_copy), __value)) {
- return true;
- } else {
- std::memcpy(__expected, std::addressof(__expected_copy), sizeof(__remove_cvref_t<_Tp>));
- return false;
- }
+ std::memcpy(__expected, std::addressof(__expected_copy), sizeof(__remove_cvref_t<_Tp>));
+ return false;
}
}
More information about the libcxx-commits
mailing list