diff --git a/libcxx/include/atomic b/libcxx/include/atomic --- a/libcxx/include/atomic +++ b/libcxx/include/atomic @@ -1017,26 +1017,33 @@ return __c11_atomic_exchange(&__a->__a_value, __value, static_cast<__memory_order_underlying_t>(__order)); } +_LIBCPP_INLINE_VISIBILITY inline _LIBCPP_CONSTEXPR memory_order __to_failure_order(memory_order __order) { + // Avoid switch statement to make this a constexpr. + return __order == memory_order_release ? memory_order_relaxed: + (__order == memory_order_acq_rel ? memory_order_acquire: + __order); +} + template _LIBCPP_INLINE_VISIBILITY bool __cxx_atomic_compare_exchange_strong(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) _NOEXCEPT { - return __c11_atomic_compare_exchange_strong(&__a->__a_value, __expected, __value, static_cast<__memory_order_underlying_t>(__success), static_cast<__memory_order_underlying_t>(__failure)); + return __c11_atomic_compare_exchange_strong(&__a->__a_value, __expected, __value, static_cast<__memory_order_underlying_t>(__success), static_cast<__memory_order_underlying_t>(__to_failure_order(__failure))); } template _LIBCPP_INLINE_VISIBILITY 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(&__a->__a_value, __expected, __value, static_cast<__memory_order_underlying_t>(__success), static_cast<__memory_order_underlying_t>(__failure)); + return __c11_atomic_compare_exchange_strong(&__a->__a_value, __expected, __value, static_cast<__memory_order_underlying_t>(__success), static_cast<__memory_order_underlying_t>(__to_failure_order(__failure))); } template _LIBCPP_INLINE_VISIBILITY bool __cxx_atomic_compare_exchange_weak(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp* __expected, _Tp __value, memory_order __success, memory_order __failure) _NOEXCEPT { - return __c11_atomic_compare_exchange_weak(&__a->__a_value, __expected, __value, static_cast<__memory_order_underlying_t>(__success), static_cast<__memory_order_underlying_t>(__failure)); + return __c11_atomic_compare_exchange_weak(&__a->__a_value, __expected, __value, static_cast<__memory_order_underlying_t>(__success), static_cast<__memory_order_underlying_t>(__to_failure_order(__failure))); } template _LIBCPP_INLINE_VISIBILITY 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(&__a->__a_value, __expected, __value, static_cast<__memory_order_underlying_t>(__success), static_cast<__memory_order_underlying_t>(__failure)); + return __c11_atomic_compare_exchange_weak(&__a->__a_value, __expected, __value, static_cast<__memory_order_underlying_t>(__success), static_cast<__memory_order_underlying_t>(__to_failure_order(__failure))); } template diff --git a/libcxx/test/std/atomics/atomics.general/replace_failure_order_codegen.sh.cpp b/libcxx/test/std/atomics/atomics.general/replace_failure_order_codegen.sh.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/atomics/atomics.general/replace_failure_order_codegen.sh.cpp @@ -0,0 +1,38 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: clang +// UNSUPPORTED: libcpp-has-no-threads + +// Adding "-fsanitize=thread" directly causes many platforms to fail (because +// they don't support tsan), and causes other sanitizer builds to fail (e.g. +// asan and tsan don't mix). Instead, require the tsan feature. +// REQUIRES: tsan + +// This test verifies behavior specified by [atomics.types.operations.req]/21: +// +// When only one memory_order argument is supplied, the value of success is +// order, and the value of failure is order except that a value of +// memory_order_acq_rel shall be replaced by the value memory_order_acquire +// and a value of memory_order_release shall be replaced by the value +// memory_order_relaxed. +// +// This test mirrors replace_failure_order.pass.cpp. However, we also want to +// verify the codegen is correct. This verifies a bug where memory_order_acq_rel +// was not being replaced with memory_order_acquire in external +// TSAN-instrumented tests. + +// RUN: %{cxx} -c %s %{flags} %{compile_flags} -O2 -stdlib=libc++ -S -emit-llvm -o %t.ll + +#include + +// Note: libc++ tests do not use on FileCheck. +// RUN: grep -E "call i32 @__tsan_atomic32_compare_exchange_val\(.*, i32 1, i32 4, i32 2\)" %t.ll +bool strong_memory_order_acq_rel(std::atomic* a, int cmp) { + return a->compare_exchange_strong(cmp, 1, std::memory_order_acq_rel); +}