Skip to content

[libc++] Implement P0528R3 std::atomic CAS for types with padding - #76180

Merged
huixie90 merged 41 commits into
llvm:mainfrom
huixie90:atomic_padding
Jul 28, 2026
Merged

[libc++] Implement P0528R3 std::atomic CAS for types with padding#76180
huixie90 merged 41 commits into
llvm:mainfrom
huixie90:atomic_padding

Conversation

@huixie90

@huixie90 huixie90 commented Dec 21, 2023

Copy link
Copy Markdown
Member

Fixes #99984

Implement P0528R3 The Curious Case of Padding Bits, Featuring Atomic Compare-and-Exchange

The unit test padding.pass.cpp is assisted by Cursor. However, all tests have been modified to test what I wanted to test

@huixie90
huixie90 requested a review from a team as a code owner December 21, 2023 20:31
@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Dec 21, 2023
@huixie90
huixie90 marked this pull request as draft December 21, 2023 20:32
@llvmbot

llvmbot commented Dec 21, 2023

Copy link
Copy Markdown
Member

@llvm/pr-subscribers-libcxx

Author: Hui (huixie90)

Changes

Patch is 21.72 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/76180.diff

2 Files Affected:

  • (modified) libcxx/include/__atomic/cxx_atomic_impl.h (+190-92)
  • (added) libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp (+54)
diff --git a/libcxx/include/__atomic/cxx_atomic_impl.h b/libcxx/include/__atomic/cxx_atomic_impl.h
index 1a0b808a0cb1c4..3efdc077a7428a 100644
--- a/libcxx/include/__atomic/cxx_atomic_impl.h
+++ b/libcxx/include/__atomic/cxx_atomic_impl.h
@@ -15,6 +15,7 @@
 #include <__memory/addressof.h>
 #include <__type_traits/conditional.h>
 #include <__type_traits/is_assignable.h>
+#include <__type_traits/is_same.h>
 #include <__type_traits/is_trivially_copyable.h>
 #include <__type_traits/remove_const.h>
 #include <cstddef>
@@ -26,6 +27,32 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp* __ptr) noexcept {
+#if _LIBCPP_STD_VER >= 20
+  constexpr bool __needs_clear_padding =
+      !__has_unique_object_representations(_Tp) && !is_same<_Tp, float>::value && !is_same<_Tp, double>::value;
+  if constexpr (__needs_clear_padding) {
+    if (!__builtin_is_constant_evaluated()) {
+      __builtin_clear_padding(__ptr);
+    }
+  }
+#endif
+}
+
+template <typename _Tp, typename _ImplFunc>
+_LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_impl(_Tp* __expected, _Tp __value, _ImplFunc&& __impl_fun) {
+  std::__clear_padding_if_needed(std::addressof(__value));
+  _Tp __expected_copy = *__expected;
+  std::__clear_padding_if_needed(std::addressof(__expected_copy));
+  if (__impl_fun(std::addressof(__expected_copy), __value)) {
+    return true;
+  } else {
+    std::memcpy(std::addressof(__expected_copy), __expected, sizeof(_Tp));
+    return false;
+  }
+}
+
 #if defined(_LIBCPP_HAS_GCC_ATOMIC_IMP) || defined(_LIBCPP_ATOMIC_ONLY_USE_BUILTINS)
 
 // [atomics.types.generic]p1 guarantees _Tp is trivially copyable. Because
@@ -52,12 +79,20 @@ template <typename _Tp>
 struct __cxx_atomic_base_impl {
   _LIBCPP_HIDE_FROM_ABI
 #  ifndef _LIBCPP_CXX03_LANG
+#    if _LIBCPP_STD_VER >= 20
+  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
+    std::__clear_padding_if_needed(std::addressof(__a_value));
+  }
+#    else
   __cxx_atomic_base_impl() _NOEXCEPT = default;
+#    endif
 #  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(std::addressof(__a_value));
+  }
   _Tp __a_value;
 };
 
@@ -108,11 +143,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(std::addressof(__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(std::addressof(__val));
   __atomic_store(std::addressof(__a->__a_value), std::addressof(__val), __to_gcc_order(__order));
 }
 
@@ -146,6 +183,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(std::addressof(__value));
   __atomic_exchange(
       std::addressof(__a->__a_value), std::addressof(__value), std::addressof(__ret), __to_gcc_order(__order));
   return __ret;
@@ -154,6 +192,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(std::addressof(__value));
   __atomic_exchange(
       std::addressof(__a->__a_value), std::addressof(__value), std::addressof(__ret), __to_gcc_order(__order));
   return __ret;
@@ -166,25 +205,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 std::__cxx_atomic_compare_exchange_impl(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_cleared_padding, _Tp& __value_cleared_padding) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_cleared_padding,
+            __value_cleared_padding,
+            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 std::__cxx_atomic_compare_exchange_impl(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_cleared_padding, _Tp& __value_cleared_padding) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_cleared_padding,
+            __value_cleared_padding,
+            false,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
@@ -194,25 +239,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 std::__cxx_atomic_compare_exchange_impl(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_cleared_padding, _Tp& __value_cleared_padding) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_cleared_padding,
+            __value_cleared_padding,
+            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 std::__cxx_atomic_compare_exchange_impl(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_cleared_padding, _Tp& __value_cleared_padding) {
+        return __atomic_compare_exchange(
+            std::addressof(__a->__a_value),
+            __expected_cleared_padding,
+            __value_cleared_padding,
+            true,
+            __to_gcc_order(__success),
+            __to_gcc_failure_order(__failure));
+      });
 }
 
 template <typename _Tp>
@@ -297,12 +348,20 @@ template <typename _Tp>
 struct __cxx_atomic_base_impl {
   _LIBCPP_HIDE_FROM_ABI
 #  ifndef _LIBCPP_CXX03_LANG
+#    if _LIBCPP_STD_VER >= 20
+  __cxx_atomic_base_impl() _NOEXCEPT : __a_value() {
+    std::__clear_padding_if_needed(std::addressof(__a_value));
+  }
+#    else
   __cxx_atomic_base_impl() _NOEXCEPT = default;
+#    endif
 #  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(std::addressof(__a_value));
+  }
   _LIBCPP_DISABLE_EXTENSION_WARNING _Atomic(_Tp) __a_value;
 };
 
@@ -328,11 +387,13 @@ _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_base_impl<_Tp>* __a, _
 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(std::addressof(__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(std::addressof(__val));
   __c11_atomic_store(std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
 }
 
@@ -368,12 +429,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(std::addressof(__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(std::addressof(__value));
   return __c11_atomic_exchange(
       std::addressof(__a->__a_value), __value, static_cast<__memory_order_underlying_t>(__order));
 }
@@ -392,23 +455,30 @@ _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 std::__cxx_atomic_compare_exchange_impl(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_cleared_padding, _Tp& __value_cleared_padding) {
+        return __c11_atomic_compare_exchange_strong(
+            std::addressof(__a->__a_value),
+            __expected_cleared_padding,
+            __value_cleared_padding,
+            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 std::__cxx_atomic_compare_exchange_impl(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_cleared_padding, _Tp& __value_cleared_padding) {
+        return __c11_atomic_compare_exchange_strong(
+            std::addressof(__a->__a_value),
+            __expected_cleared_padding,
+            __value_cleared_padding,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 
 template <class _Tp>
@@ -418,23 +488,29 @@ _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 std::__cxx_atomic_compare_exchange_impl(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_cleared_padding, _Tp& __value_cleared_padding) {
+        return __c11_atomic_compare_exchange_weak(
+            std::addressof(__a->__a_value),
+            __expected_cleared_padding,
+            __value_cleared_padding,
+            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 std::__cxx_atomic_compare_exchange_impl(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_cleared_padding, _Tp& __value_cleared_padding) {
+        return __c11_atomic_compare_exchange_weak(
+            std::addressof(__a->__a_value),
+            __expected_cleared_padding,
+            __value_cleared_padding,
+            static_cast<__memory_order_underlying_t>(__success),
+            static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
+      });
 }
 
 template <class _Tp>
@@ -533,10 +609,14 @@ __cxx_atomic_fetch_xor(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
 
 template <typename _Tp>
 struct __cxx_atomic_lock_impl {
-  _LIBCPP_HIDE_FROM_ABI __cxx_atomic_lock_impl() _NOEXCEPT : __a_value(), __a_lock(0) {}
+  _LIBCPP_HIDE_FROM_ABI __cxx_atomic_lock_impl() _NOEXCEPT : __a_value(), __a_lock(0) {
+    std::__clear_padding_if_needed(std::addressof(__a_value));
+  }
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR explicit __cxx_atomic_lock_impl(_Tp value) _NOEXCEPT
       : __a_value(value),
-        __a_lock(0) {}
+        __a_lock(0) {
+    std::__clear_padding_if_needed(std::addressof(__a_value));
+  }
 
   _Tp __a_value;
   mutable __cxx_atomic_base_impl<_LIBCPP_ATOMIC_FLAG_TYPE> __a_lock;
@@ -592,12 +672,14 @@ _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_init(__cxx_atomic_lock_impl<_Tp>* __a, _
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_store(volatile __cxx_atomic_lock_impl<_Tp>* __a, _Tp __val, memory_order) {
   __a->__lock();
+  std::__clear_padding_if_needed(std::addressof(__val));
   __cxx_atomic_assign_volatile(__a->__a_value, __val);
   __a->__unlock();
 }
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_store(__cxx_atomic_lock_impl<_Tp>* __a, _Tp __val, memory_order) {
   __a->__lock();
+  std::__clear_padding_if_needed(std::addressof(__val));
   __a->__a_value = __val;
   __a->__unlock();
 }
@@ -625,6 +707,7 @@ template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp __cxx_atomic_exchange(volatile __cxx_atomic_lock_impl<_Tp>* __a, _Tp __value, memory_order) {
   __a->__lock();
   _Tp __old;
+  std::__clear_padding_if_needed(std::addressof(__value));
   __cxx_atomic_assign_volatile(__old, __a->__a_value);
   __cxx_atomic_assign_volatile(__a->__a_value, __value);
   __a->__unlock();
@@ -633,6 +716,7 @@ _LIBCPP_HIDE_FROM_ABI _Tp __cxx_atomic_exchange(volatile __cxx_atomic_lock_impl<
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI _Tp __cxx_atomic_exchange(__cxx_atomic_lock_impl<_Tp>* __a, _Tp __value, memory_order) {
   __a->__lock();
+  std::__clear_padding_if_needed(std::addressof(__value));
   _Tp __old      = __a->__a_value;
   __a->__a_value = __value;
   __a->__unlock();
@@ -642,55 +726,69 @@ _LIBCPP_HIDE_FROM_ABI _Tp __cxx_atomic_exchange(__cxx_atomic_lock_impl<_Tp>* __a
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     volatile __cxx_atomic_lock_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order, memory_order) {
-  _Tp __temp;
-  __a->__lock();
-  __cxx_atomic_assign_volatile(__temp, __a->__a_value);
-  bool __ret = (std::memcmp(&__temp, __expected, sizeof(_Tp)) == 0);
-  if (__ret)
-    __cxx_atomic_assign_volatile(__a->__a_value, __value);
-  else
-    __cxx_atomic_assign_volatile(*__expected, __a->__a_value);
-  __a->__unlock();
-  return __ret;
+  return std::__cxx_atomic_compare_exchange_impl(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_cleared_padding, _Tp& __value_cleared_padding) {
+        _Tp __temp;
+        __a->__lock();
+        __cxx_atomic_assign_volatile(__temp, __a->__a_value);
+        bool __ret = (std::memcmp(&__temp, __expected_cleared_padding, sizeof(_Tp)) == 0);
+        if (__ret)
+          __cxx_atomic_assign_volatile(__a->__a_value, __value_cleared_padding);
+        else
+          __cxx_atomic_assign_volatile(*__expected_cleared_padding, __a->__a_value);
+        __a->__unlock();
+        return __ret;
+      });
 }
+
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
     __cxx_atomic_lock_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order, memory_order) {
-  __a->__lock();
-  bool __ret = (std::memcmp(&__a->__a_value, __expected, sizeof(_Tp)) == 0);
-  if (__ret)
-    std::memcpy(&__a->__a_value, &__value, sizeof(_Tp));
-  else
-    std::memcpy(__expected, &__a->__a_value, sizeof(_Tp));
-  __a->__unlock();
-  return __ret;
+  return std::__cxx_atomic_compare_exchange_impl(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_cleared_padding, _Tp& __value_cleared_padding) {
+        __a->__lock();
+        bool __ret = (std::memcmp(&__a->__a_value, __expected_cleared_padding, sizeof(_Tp)) == 0);
+        if (__ret)
+          std::memcpy(&__a->__a_value, &__value_cleared_padding, sizeof(_Tp));
+        else
+          std::memcpy(__expected_cleared_padding, &__a->__a_value, sizeof(_Tp));
+        __a->__unlock();
+        return __ret;
+      });
 }
 
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     volatile __cxx_atomic_lock_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order, memory_order) {
-  _Tp __temp;
-  __a->__lock();
-  __cxx_atomic_assign_volatile(__temp, __a->__a_value);
-  bool __ret = (std::memcmp(&__temp, __expected, sizeof(_Tp)) == 0);
-  if (__ret)
-    __cxx_atomic_assign_volatile(__a->__a_value, __value);
-  else
-    __cxx_atomic_assign_volatile(*__expected, __a->__a_value);
-  __a->__unlock();
-  return __ret;
+  return std::__cxx_atomic_compare_exchange_impl(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_cleared_padding, _Tp& __value_cleared_padding) {
+        _Tp __temp;
+        __a->__lock();
+        __cxx_atomic_assign_volatile(__temp, __a->__a_value);
+        bool __ret = (std::memcmp(&__temp, __expected_cleared_padding, sizeof(_Tp)) == 0);
+        if (__ret)
+          __cxx_atomic_assign_volatile(__a->__a_value, __value_cleared_padding);
+        else
+          __cxx_atomic_assign_volatile(*__expected_cleared_padding, __a->__a_value);
+        __a->__unlock();
+        return __ret;
+      });
 }
+
 template <typename _Tp>
 _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_weak(
     __cxx_atomic_lock_impl<_Tp>* __a, _Tp* __expected, _Tp __value, memory_order, memory_order) {
-  __a->__lock();
-  bool __ret = (std::memcmp(&__a->__a_value, __expected, sizeof(_Tp)) == 0);
-  if (__ret)
-    std::memcpy(&__a->__a_value, &__value, sizeof(_Tp));
-  else
-    std::memcpy(__expected, &__a->__a_value, sizeof(_Tp));
-  __a->__unlock();
-  return __ret;
+  return std::__cxx_atomic_compare_exchange_impl(
+      __expected, __value, [__a, __success, __failure](_Tp* __expected_cleared_padding, _Tp& __value_cleared_padding) {
+        __a->__lock();
+        bool __ret = (std::memcmp(&__a->__a_value, __expected_cleared_padding, sizeof(_Tp)) == 0);
+        if (__ret)
+          std::memcpy(&__a->__a_value, &__value_cleared_padding, sizeof(_Tp));
+        else
+          std::memcpy(__expected_cleared_padding,...
[truncated]

@mordante mordante left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mainly glossed over the patch since it's still a draft.

_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR void __clear_padding_if_needed(_Tp* __ptr) noexcept {
#if _LIBCPP_STD_VER >= 20
constexpr bool __needs_clear_padding =
!__has_unique_object_representations(_Tp) && !is_same<_Tp, float>::value && !is_same<_Tp, double>::value;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it guaranteed float and double never have padding. Wouldn't it be better to have a builtin that tells whether padding should be removed? Or even make the builtin a no-op when it's not needed for the type.

constexpr bool __needs_clear_padding =
!__has_unique_object_representations(_Tp) && !is_same<_Tp, float>::value && !is_same<_Tp, double>::value;
if constexpr (__needs_clear_padding) {
if (!__builtin_is_constant_evaluated()) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not std::is_constant_evaluated?

@ldionne ldionne self-assigned this Jan 10, 2024
Comment thread libcxx/include/__atomic/cxx_atomic_impl.h Outdated
huixie90 added a commit that referenced this pull request May 27, 2026
)

Add builtin to clear padding bytes. This is the pre-work to implement
`std::atomic::compare_exchange_[weak/strong]` that ignores padding bits.
PR draft here: #76180

This PR picked up this patch from 3 years ago
https://reviews.llvm.org/D87974

The above patch no longer works as things changed quite a lot. I've made
some changes on top of the above patch:


it handles:
- struct
- builtin types with paddings (like `long double` and types with
`__attribute__((ext_vector_type(N)))`
- _Complex long double
- constant array
- union
- bit field
- types with virtual functions
- types with virtual bases

---------

Co-authored-by: zoecarver <z.zoelec2@gmail.com>
llvm-upstreamsync Bot pushed a commit to qualcomm/cpullvm-toolchain that referenced this pull request May 27, 2026
…528R3) (#75371)

Add builtin to clear padding bytes. This is the pre-work to implement
`std::atomic::compare_exchange_[weak/strong]` that ignores padding bits.
PR draft here: llvm/llvm-project#76180

This PR picked up this patch from 3 years ago
https://reviews.llvm.org/D87974

The above patch no longer works as things changed quite a lot. I've made
some changes on top of the above patch:

it handles:
- struct
- builtin types with paddings (like `long double` and types with
`__attribute__((ext_vector_type(N)))`
- _Complex long double
- constant array
- union
- bit field
- types with virtual functions
- types with virtual bases

---------

Co-authored-by: zoecarver <z.zoelec2@gmail.com>
llvm-sync Bot pushed a commit to arm/arm-toolchain that referenced this pull request May 27, 2026
…528R3) (#75371)

Add builtin to clear padding bytes. This is the pre-work to implement
`std::atomic::compare_exchange_[weak/strong]` that ignores padding bits.
PR draft here: llvm/llvm-project#76180

This PR picked up this patch from 3 years ago
https://reviews.llvm.org/D87974

The above patch no longer works as things changed quite a lot. I've made
some changes on top of the above patch:

it handles:
- struct
- builtin types with paddings (like `long double` and types with
`__attribute__((ext_vector_type(N)))`
- _Complex long double
- constant array
- union
- bit field
- types with virtual functions
- types with virtual bases

---------

Co-authored-by: zoecarver <z.zoelec2@gmail.com>
@huixie90 huixie90 changed the title [libc++] make std::atomic works with types with paddings [libc++] Implement P0528R3 std::atomic CAS for types with paddings Jun 7, 2026
@huixie90
huixie90 marked this pull request as ready for review June 7, 2026 20:41
@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

✅ With the latest revision this PR passed the C/C++ code formatter.

@huixie90
huixie90 force-pushed the atomic_padding branch 4 times, most recently from e5e734b to 1f576b7 Compare June 14, 2026 14:06
Comment thread libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp Outdated
@ldionne
ldionne requested a review from dalg24 June 19, 2026 18:19
@ldionne

ldionne commented Jun 19, 2026

Copy link
Copy Markdown
Member

CC @jiixyj in case you're interested to help review this

@huixie90
huixie90 force-pushed the atomic_padding branch 2 times, most recently from b208bae to 33732fe Compare June 21, 2026 13:24
@ldionne ldionne assigned huixie90 and unassigned ldionne Jul 3, 2026

@ldionne ldionne left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm glad to see this making progress! This is a non-trivial patch, I have some comments but it's looking very reasonable.

Comment thread libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp Outdated
Comment thread libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp Outdated
Comment thread libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp Outdated
Comment thread libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp Outdated
Comment thread libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp Outdated
Comment thread libcxx/include/__atomic/support/common.h Outdated
Comment thread libcxx/include/__atomic/support/common.h Outdated
Comment thread libcxx/include/__atomic/support/common.h Outdated
Comment thread libcxx/include/__atomic/support/common.h Outdated
Comment thread libcxx/include/__atomic/support/gcc.h Outdated

@ldionne ldionne left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM assuming the C++11-ifying works with GCC.

Comment thread libcxx/include/__atomic/clear_padding.h Outdated
Comment thread libcxx/include/__atomic/clear_padding.h Outdated
Comment thread libcxx/test/libcxx/atomics/atomics.types.generic/padding.pass.cpp Outdated
Comment thread libcxx/test/std/atomics/atomics.types.generic/padding.pass.cpp Outdated
Comment thread libcxx/include/__atomic/clear_padding.h Outdated
// for non trivially copyable types, __builtin_clear_padding matches gcc's behaviour
// where the pointer to the object must be known to the compiler
// using libc++'s __clear_padding_if_needed would make the object "unknown" to the compiler
// note that in atomic<T>, T must be trivially copyable
__builtin_clear_padding(&s2);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this not __clear_padding_if_needed?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nevermind, I didn't read the comment above carefully enough.

Comment thread libcxx/test/libcxx/atomics/builtin_clear_padding.pass.cpp

@ldionne ldionne left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

// for non trivially copyable types, __builtin_clear_padding matches gcc's behaviour
// where the pointer to the object must be known to the compiler
// using libc++'s __clear_padding_if_needed would make the object "unknown" to the compiler
// note that in atomic<T>, T must be trivially copyable
__builtin_clear_padding(&s2);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nevermind, I didn't read the comment above carefully enough.

@ldionne ldionne changed the title [libc++] Implement P0528R3 std::atomic CAS for types with paddings [libc++] Implement P0528R3 std::atomic CAS for types with padding Jul 27, 2026
@ldionne ldionne added the pending-ci Merging the PR is only pending completion of CI label Jul 27, 2026
@huixie90
huixie90 merged commit de962dd into llvm:main Jul 28, 2026
85 checks passed
static_cast<__memory_order_underlying_t>(__success),
static_cast<__memory_order_underlying_t>(__to_failure_order(__failure)));
// Use always_inline since Clang fails to inline the lambda, even when the memory order is a constant
[__a, __success, __failure](_Tp* __expected_or_copy, _Tp __value_maybe_padding_cleared) _LIBCPP_ALWAYS_INLINE {

@philnik777 philnik777 Jul 28, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this marked with _LIBCPP_ALWAYS_INLINE?

Edit: Sorry, missed the comment above.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah Ok. Let me know if the comment is unclear. The detailed discussion is here #76180 (comment)

@frederick-vs-ja frederick-vs-ja removed the pending-ci Merging the PR is only pending completion of CI label Jul 28, 2026
asudarsa-qti pushed a commit to asudarsa-qti/llvm-project that referenced this pull request Jul 29, 2026
…lvm#76180)

Fixes llvm#99984

Implement P0528R3 The Curious Case of Padding Bits, Featuring Atomic
Compare-and-Exchange


The unit test padding.pass.cpp is assisted by Cursor. However, all tests
have been modified to test what I wanted to test

---------

Co-authored-by: Louis Dionne <ldionne.2@gmail.com>
jgreenbaum pushed a commit to jgreenbaum/llvm-project that referenced this pull request Aug 3, 2026
…lvm#76180)

Fixes llvm#99984

Implement P0528R3 The Curious Case of Padding Bits, Featuring Atomic
Compare-and-Exchange


The unit test padding.pass.cpp is assisted by Cursor. However, all tests
have been modified to test what I wanted to test

---------

Co-authored-by: Louis Dionne <ldionne.2@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

P0528R3: The Curious Case of Padding Bits, Featuring Atomic Compare-and-Exchange

6 participants