Commit 512068cc authored by Andrey Semashev's avatar Andrey Semashev
Browse files

Added bitwise_cast implementation based on bit_cast intrinsics.

This allows to mark bitwise_cast constexpr when the conditions to use
bit_cast are met:

 - both source and target types have the same size, and
 - the source type has no padding bits.

The latter is checked using has_unique_object_representations trait, which
we also implement using intrinsics when not available in the standard
library.

This allows atomic constructors to become constexpr for structs with no
padding and whose size matches the atomic storage size.
parent 746ea264
......@@ -1486,9 +1486,10 @@ limitations that cannot be lifted without compiler support:
[*Advice]: Always use direct initialization (`atomic<int> a(10)`) or unified
initialization (`atomic<int> a{10}`) syntax.
* [*Initializing constructor is not `constexpr` for some types]: For value types
other than integral types, `bool` and enums, `atomic<>` initializing constructor needs
to perform runtime conversion to the storage type. This limitation may be
lifted for more categories of types in the future.
other than integral types, `bool`, enums, and classes without padding, `atomic<>`
initializing constructor needs to perform runtime conversion to the storage type
and potentially clear padding bits. This limitation may be lifted for more
categories of types in the future.
* [*Default constructor is not trivial in C++03]: Because the initializing
constructor has to be defined in `atomic<>`, the default constructor
must also be defined. In C++03 the constructor cannot be defined as defaulted
......
......@@ -16,7 +16,7 @@
* On Windows, corrected discrepancy between [^['atomic-type]::always_has_native_wait_notify] and the corresponding capability macros when targeting Windows 8 or later. The library will now directly use `WaitOnAddress` and related APIs from public headers and therefore require user to link with `synchronization.lib` if the user requires Windows 8 or later by defining `BOOST_USE_WINAPI_VERSION`, `_WIN32_WINNT` or similar macros. The library is linked automatically on compilers that support auto-linking (e.g. MSVC).
* Added support for types with padding bits, except unions, on compilers that provide a way to clear the padding bits. This feature is supported by gcc 11 and MSVC 14.2 (compiler version 19.27) and newer, as well as other compilers supporting similar intrinsics. On compilers that don't allow to clear the padding bits, types with padding are still generally not supported with the exception of 80-bit `long double` on x86 targets. A new `BOOST_ATOMIC_NO_CLEAR_PADDING` capability macro is defined to indicate when clearing the padding is not supported.
* Initializing constructors of `atomic_ref` and `ipc_atomic_ref` no longer use atomic instructions to clear the padding bits in the referenced object. This reduces the cost of the atomic reference construction. This is considered safe because clearing the padding does not issue writes to the bytes that contribute to the object value. However, some thread safety checking software may falsely detect this as a data race.
* Initializing constructors of `atomic` and `ipc_atomic` are now `constexpr` for enums.
* Initializing constructors of `atomic` and `ipc_atomic` are now `constexpr` for enums and classes. For classes, the constructors are `constexpr` if the class has no padding bytes and no padding is required to implement native atomic operations (i.e. the class fits exactly in the internal atomic storage).
[heading Boost 1.77]
......
......@@ -45,6 +45,12 @@
#pragma once
#endif
#if !defined(BOOST_ATOMIC_DETAIL_NO_CXX11_CONSTEXPR_UNION_INIT) && !defined(BOOST_ATOMIC_DETAIL_NO_CXX11_CONSTEXPR_BITWISE_CAST)
#define BOOST_ATOMIC_DETAIL_CONSTEXPR_ATOMIC_CTOR BOOST_CONSTEXPR
#else
#define BOOST_ATOMIC_DETAIL_CONSTEXPR_ATOMIC_CTOR
#endif
/*
* IMPLEMENTATION NOTE: All interface functions MUST be declared with BOOST_FORCEINLINE,
* see comment for convert_memory_order_to_gcc in gcc_atomic_memory_order_utils.hpp.
......@@ -141,7 +147,7 @@ protected:
public:
BOOST_DEFAULTED_FUNCTION(base_atomic_generic() BOOST_ATOMIC_DETAIL_DEF_NOEXCEPT_DECL, BOOST_ATOMIC_DETAIL_DEF_NOEXCEPT_IMPL {})
BOOST_FORCEINLINE explicit base_atomic_generic(value_arg_type v) BOOST_NOEXCEPT :
BOOST_FORCEINLINE BOOST_ATOMIC_DETAIL_CONSTEXPR_ATOMIC_CTOR explicit base_atomic_generic(value_arg_type v) BOOST_NOEXCEPT :
base_type(atomics::detail::bitwise_cast< storage_type >(v))
{
}
......@@ -162,7 +168,7 @@ protected:
typedef typename base_type::value_arg_type value_arg_type;
public:
BOOST_FORCEINLINE explicit base_atomic_generic(value_arg_type v = value_type()) BOOST_NOEXCEPT :
BOOST_FORCEINLINE BOOST_ATOMIC_DETAIL_CONSTEXPR_ATOMIC_CTOR explicit base_atomic_generic(value_arg_type v = value_type()) BOOST_NOEXCEPT :
base_type(atomics::detail::bitwise_cast< storage_type >(v))
{
}
......@@ -198,7 +204,7 @@ private:
public:
BOOST_DEFAULTED_FUNCTION(base_atomic() BOOST_ATOMIC_DETAIL_DEF_NOEXCEPT_DECL, BOOST_ATOMIC_DETAIL_DEF_NOEXCEPT_IMPL {})
BOOST_FORCEINLINE explicit base_atomic(value_arg_type v) BOOST_NOEXCEPT : base_type(v)
BOOST_FORCEINLINE BOOST_ATOMIC_DETAIL_CONSTEXPR_ATOMIC_CTOR explicit base_atomic(value_arg_type v) BOOST_NOEXCEPT : base_type(v)
{
}
......@@ -890,7 +896,7 @@ private:
public:
BOOST_DEFAULTED_FUNCTION(base_atomic() BOOST_ATOMIC_DETAIL_DEF_NOEXCEPT_DECL, BOOST_ATOMIC_DETAIL_DEF_NOEXCEPT_IMPL {})
BOOST_FORCEINLINE explicit base_atomic(value_arg_type v) BOOST_NOEXCEPT :
BOOST_FORCEINLINE BOOST_ATOMIC_DETAIL_CONSTEXPR_ATOMIC_CTOR explicit base_atomic(value_arg_type v) BOOST_NOEXCEPT :
base_type(atomics::detail::bitwise_fp_cast< storage_type >(v))
{
}
......@@ -1076,7 +1082,7 @@ private:
public:
BOOST_DEFAULTED_FUNCTION(base_atomic() BOOST_ATOMIC_DETAIL_DEF_NOEXCEPT_DECL, BOOST_ATOMIC_DETAIL_DEF_NOEXCEPT_IMPL {})
BOOST_FORCEINLINE explicit base_atomic(value_arg_type v) BOOST_NOEXCEPT :
BOOST_FORCEINLINE BOOST_ATOMIC_DETAIL_CONSTEXPR_ATOMIC_CTOR explicit base_atomic(value_arg_type v) BOOST_NOEXCEPT :
base_type(atomics::detail::bitwise_cast< uintptr_storage_type >(v))
{
}
......
......@@ -21,12 +21,37 @@
#include <boost/atomic/detail/addressof.hpp>
#include <boost/atomic/detail/string_ops.hpp>
#include <boost/atomic/detail/type_traits/integral_constant.hpp>
#include <boost/atomic/detail/type_traits/has_unique_object_representations.hpp>
#include <boost/atomic/detail/header.hpp>
#ifdef BOOST_HAS_PRAGMA_ONCE
#pragma once
#endif
#if !defined(BOOST_ATOMIC_DETAIL_NO_HAS_UNIQUE_OBJECT_REPRESENTATIONS)
#if defined(__has_builtin)
#if __has_builtin(__builtin_bit_cast)
#define BOOST_ATOMIC_DETAIL_BIT_CAST(x, y) __builtin_bit_cast(x, y)
#endif
#endif
#if !defined(BOOST_ATOMIC_DETAIL_BIT_CAST) && defined(BOOST_MSVC) && BOOST_MSVC >= 1926
#define BOOST_ATOMIC_DETAIL_BIT_CAST(x, y) __builtin_bit_cast(x, y)
#endif
#endif // !defined(BOOST_ATOMIC_DETAIL_NO_HAS_UNIQUE_OBJECT_REPRESENTATIONS)
#if defined(BOOST_NO_CXX11_CONSTEXPR) || !defined(BOOST_ATOMIC_DETAIL_BIT_CAST) || !defined(BOOST_ATOMIC_DETAIL_HAS_BUILTIN_ADDRESSOF)
#define BOOST_ATOMIC_DETAIL_NO_CXX11_CONSTEXPR_BITWISE_CAST
#endif
#if !defined(BOOST_ATOMIC_DETAIL_NO_CXX11_CONSTEXPR_BITWISE_CAST)
#define BOOST_ATOMIC_DETAIL_CONSTEXPR_BITWISE_CAST BOOST_CONSTEXPR
#else
#define BOOST_ATOMIC_DETAIL_CONSTEXPR_BITWISE_CAST
#endif
#if defined(BOOST_GCC) && BOOST_GCC >= 80000
#pragma GCC diagnostic push
// copying an object of non-trivial type X from an array of Y. This is benign because we use memcpy to copy trivially copyable objects.
......@@ -54,6 +79,49 @@ BOOST_FORCEINLINE void clear_tail_padding_bits(To& to) BOOST_NOEXCEPT
atomics::detail::clear_tail_padding_bits< ValueSize >(to, atomics::detail::integral_constant< bool, ValueSize < sizeof(To) >());
}
#if defined(BOOST_ATOMIC_DETAIL_BIT_CAST)
template< typename To, std::size_t FromValueSize, typename From >
BOOST_FORCEINLINE BOOST_ATOMIC_DETAIL_CONSTEXPR_BITWISE_CAST To bitwise_cast_impl(From const& from, atomics::detail::true_type) BOOST_NOEXCEPT
{
// This implementation is only called when the From type has no padding and From and To have the same size
return BOOST_ATOMIC_DETAIL_BIT_CAST(To, from);
}
template< typename To, std::size_t FromValueSize, typename From >
BOOST_FORCEINLINE To bitwise_cast_impl(From const& from, atomics::detail::false_type) BOOST_NOEXCEPT
{
To to;
#if !defined(BOOST_ATOMIC_NO_CLEAR_PADDING)
From from2(from);
BOOST_ATOMIC_DETAIL_CLEAR_PADDING(atomics::detail::addressof(from2));
BOOST_ATOMIC_DETAIL_MEMCPY
(
atomics::detail::addressof(to),
atomics::detail::addressof(from2),
(FromValueSize < sizeof(To) ? FromValueSize : sizeof(To))
);
#else
BOOST_ATOMIC_DETAIL_MEMCPY
(
atomics::detail::addressof(to),
atomics::detail::addressof(from),
(FromValueSize < sizeof(To) ? FromValueSize : sizeof(To))
);
#endif
atomics::detail::clear_tail_padding_bits< FromValueSize >(to);
return to;
}
template< typename To, std::size_t FromValueSize, typename From >
BOOST_FORCEINLINE BOOST_ATOMIC_DETAIL_CONSTEXPR_BITWISE_CAST To bitwise_cast(From const& from) BOOST_NOEXCEPT
{
return atomics::detail::bitwise_cast_impl< To, FromValueSize >(from, atomics::detail::integral_constant< bool,
FromValueSize == sizeof(To) && atomics::detail::has_unique_object_representations< From >::value >());
}
#else // defined(BOOST_ATOMIC_DETAIL_BIT_CAST)
template< typename To, std::size_t FromValueSize, typename From >
BOOST_FORCEINLINE To bitwise_cast(From const& from) BOOST_NOEXCEPT
{
......@@ -79,8 +147,10 @@ BOOST_FORCEINLINE To bitwise_cast(From const& from) BOOST_NOEXCEPT
return to;
}
#endif // defined(BOOST_ATOMIC_DETAIL_BIT_CAST)
template< typename To, typename From >
BOOST_FORCEINLINE To bitwise_cast(From const& from) BOOST_NOEXCEPT
BOOST_FORCEINLINE BOOST_ATOMIC_DETAIL_CONSTEXPR_BITWISE_CAST To bitwise_cast(From const& from) BOOST_NOEXCEPT
{
return atomics::detail::bitwise_cast< To, sizeof(From) >(from);
}
......
......@@ -75,7 +75,7 @@ struct value_sizeof< const volatile T > : value_sizeof< T > {};
template< typename To, typename From >
BOOST_FORCEINLINE To bitwise_fp_cast(From const& from) BOOST_NOEXCEPT
BOOST_FORCEINLINE BOOST_ATOMIC_DETAIL_CONSTEXPR_BITWISE_CAST To bitwise_fp_cast(From const& from) BOOST_NOEXCEPT
{
#if !defined(BOOST_ATOMIC_NO_CLEAR_PADDING)
// BOOST_ATOMIC_DETAIL_CLEAR_PADDING, which is used in bitwise_cast, will clear the tail padding bits in the from object.
......
/*
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* Copyright (c) 2021 Andrey Semashev
*/
/*!
* \file atomic/detail/type_traits/has_unique_object_representations.hpp
*
* This header defines \c has_unique_object_representations type trait
*/
#ifndef BOOST_ATOMIC_DETAIL_TYPE_TRAITS_HAS_UNIQUE_OBJECT_REPRESENTATIONS_HPP_INCLUDED_
#define BOOST_ATOMIC_DETAIL_TYPE_TRAITS_HAS_UNIQUE_OBJECT_REPRESENTATIONS_HPP_INCLUDED_
#include <boost/atomic/detail/config.hpp>
#if !defined(BOOST_ATOMIC_DETAIL_NO_CXX11_BASIC_HDR_TYPE_TRAITS)
#include <type_traits>
#endif
#ifdef BOOST_HAS_PRAGMA_ONCE
#pragma once
#endif
#if (defined(__cpp_lib_has_unique_object_representations) && __cpp_lib_has_unique_object_representations >= 201606) || \
(defined(_CPPLIB_VER) && _CPPLIB_VER >= 650 && defined(_HAS_CXX17) && _HAS_CXX17 != 0)
namespace boost {
namespace atomics {
namespace detail {
using std::has_unique_object_representations;
} // namespace detail
} // namespace atomics
} // namespace boost
#else // defined(__cpp_lib_has_unique_object_representations) && __cpp_lib_has_unique_object_representations >= 201606
#if (defined(__GNUC__) && __GNUC__ >= 7) || (defined(BOOST_MSVC) && BOOST_MSVC >= 1929)
#define BOOST_ATOMIC_DETAIL_HAS_UNIQUE_OBJECT_REPRESENTATIONS(x) __has_unique_object_representations(x)
#elif defined(__is_identifier)
#if __is_identifier(__has_unique_object_representations)
#define BOOST_ATOMIC_DETAIL_HAS_UNIQUE_OBJECT_REPRESENTATIONS(x) __has_unique_object_representations(x)
#endif
#endif
#if defined(BOOST_ATOMIC_DETAIL_HAS_UNIQUE_OBJECT_REPRESENTATIONS)
#include <boost/atomic/detail/type_traits/integral_constant.hpp>
namespace boost {
namespace atomics {
namespace detail {
#if !defined(BOOST_NO_CXX11_TEMPLATE_ALIASES)
template< typename T >
using has_unique_object_representations =
atomics::detail::integral_constant< bool, BOOST_ATOMIC_DETAIL_HAS_UNIQUE_OBJECT_REPRESENTATIONS(T) >;
#else // !defined(BOOST_NO_CXX11_TEMPLATE_ALIASES)
template< typename T >
struct has_unique_object_representations :
public atomics::detail::integral_constant< bool, BOOST_ATOMIC_DETAIL_HAS_UNIQUE_OBJECT_REPRESENTATIONS(T) >
{
};
#endif // !defined(BOOST_NO_CXX11_TEMPLATE_ALIASES)
} // namespace detail
} // namespace atomics
} // namespace boost
#else // defined(BOOST_ATOMIC_DETAIL_HAS_UNIQUE_OBJECT_REPRESENTATIONS)
#define BOOST_ATOMIC_DETAIL_NO_HAS_UNIQUE_OBJECT_REPRESENTATIONS
#endif // defined(BOOST_ATOMIC_DETAIL_HAS_UNIQUE_OBJECT_REPRESENTATIONS)
#endif // defined(__cpp_lib_has_unique_object_representations) ...
#endif // BOOST_ATOMIC_DETAIL_TYPE_TRAITS_HAS_UNIQUE_OBJECT_REPRESENTATIONS_HPP_INCLUDED_
......@@ -46,9 +46,11 @@ int main(int, char *[])
test_constexpr_ctor< int >();
test_constexpr_ctor< long >();
test_constexpr_ctor< test_enum >();
// For pointers and structs we're not offering a constexpr constructor because of bitwise_cast
#if !defined(BOOST_ATOMIC_DETAIL_NO_CXX11_CONSTEXPR_BITWISE_CAST)
// As of gcc 11, clang 12 and MSVC 19.27, compilers don't support __builtin_bit_cast from pointers in constant expressions.
// test_constexpr_ctor< int* >();
// test_constexpr_ctor< test_struct< int > >();
test_constexpr_ctor< test_struct< int > >();
#endif
#if !defined(BOOST_ATOMIC_NO_FLOATING_POINT)
test_floating_point_api< atomic_wrapper, float >();
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment