diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -690,6 +690,7 @@ __type_traits/conjunction.h __type_traits/copy_cv.h __type_traits/copy_cvref.h + __type_traits/datasizeof.h __type_traits/decay.h __type_traits/dependent_type.h __type_traits/disjunction.h diff --git a/libcxx/include/__algorithm/copy_move_common.h b/libcxx/include/__algorithm/copy_move_common.h --- a/libcxx/include/__algorithm/copy_move_common.h +++ b/libcxx/include/__algorithm/copy_move_common.h @@ -15,6 +15,7 @@ #include <__config> #include <__iterator/iterator_traits.h> #include <__memory/pointer_traits.h> +#include <__string/constexpr_c_functions.h> #include <__type_traits/enable_if.h> #include <__type_traits/is_always_bitcastable.h> #include <__type_traits/is_constant_evaluated.h> @@ -61,7 +62,8 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 pair<_In*, _Out*> __copy_trivial_impl(_In* __first, _In* __last, _Out* __result) { const size_t __n = static_cast(__last - __first); - ::__builtin_memmove(__result, __first, __n * sizeof(_Out)); + + std::__constexpr_memmove_n(__result, __first, __n); return std::make_pair(__last, __result + __n); } @@ -72,7 +74,7 @@ const size_t __n = static_cast(__last - __first); __result -= __n; - ::__builtin_memmove(__result, __first, __n * sizeof(_Out)); + std::__constexpr_memmove_n(__result, __first, __n); return std::make_pair(__last, __result); } @@ -119,16 +121,6 @@ return _Algorithm()(std::move(__first), std::move(__last), std::move(__out_first)); } -template -struct __can_copy_without_conversion : false_type {}; - -template -struct __can_copy_without_conversion< - _IterOps, - _InValue, - _OutIter, - __enable_if_t >::value> > : true_type {}; - template _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 pair<_InIter, _OutIter> __dispatch_copy_or_move(_InIter __first, _Sent __last, _OutIter __out_first) { -#ifdef _LIBCPP_COMPILER_GCC - // GCC doesn't support `__builtin_memmove` during constant evaluation. - if (__libcpp_is_constant_evaluated()) { - return std::__unwrap_and_dispatch<_NaiveAlgorithm>(std::move(__first), std::move(__last), std::move(__out_first)); - } -#else - // In Clang, `__builtin_memmove` only supports fully trivially copyable types (just having trivial copy assignment is - // insufficient). Also, conversions are not supported. - if (__libcpp_is_constant_evaluated()) { - using _InValue = typename _IterOps<_AlgPolicy>::template __value_type<_InIter>; - if (!is_trivially_copyable<_InValue>::value || - !__can_copy_without_conversion<_IterOps<_AlgPolicy>, _InValue, _OutIter>::value) { - return std::__unwrap_and_dispatch<_NaiveAlgorithm>(std::move(__first), std::move(__last), std::move(__out_first)); - } - } -#endif // _LIBCPP_COMPILER_GCC - using _Algorithm = __overload<_NaiveAlgorithm, _OptimizedAlgorithm>; return std::__unwrap_and_dispatch<_Algorithm>(std::move(__first), std::move(__last), std::move(__out_first)); } diff --git a/libcxx/include/__string/constexpr_c_functions.h b/libcxx/include/__string/constexpr_c_functions.h --- a/libcxx/include/__string/constexpr_c_functions.h +++ b/libcxx/include/__string/constexpr_c_functions.h @@ -10,11 +10,14 @@ #define _LIBCPP___STRING_CONSTEXPR_C_FUNCTIONS_H #include <__config> +#include <__type_traits/datasizeof.h> +#include <__type_traits/is_always_bitcastable.h> #include <__type_traits/is_constant_evaluated.h> #include <__type_traits/is_equality_comparable.h> #include <__type_traits/is_same.h> #include <__type_traits/is_trivially_lexicographically_comparable.h> #include <__type_traits/remove_cv.h> +#include <__utility/is_pointer_in_range.h> #include #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) @@ -121,6 +124,30 @@ } } +// TODO: find a better name +template ::value, int> = 0> +_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _Tp* +__constexpr_memmove_n(_Tp* __dest, _Up* __src, size_t __count) { + if (__libcpp_is_constant_evaluated()) { +#ifdef _LIBCPP_COMPILER_CLANG_BASED + if (is_trivially_copyable<_Tp>::value && is_same<__remove_cv_t<_Tp>, __remove_cv_t<_Up> >::value) { + ::__builtin_memmove(__dest, __src, __count * sizeof(_Tp)); + return __dest; + } +#endif + if (std::__is_pointer_in_range(__dest, __dest + __count, __src)) { + for (; __count > 0; --__count) + __dest[__count - 1] = __src[__count - 1]; + } else { + for (size_t __i = 0; __i != __count; ++__i) + __dest[__i] = __src[__i]; + } + } else if (__count > 0) { + ::__builtin_memmove(__dest, __src, (__count - 1) * sizeof(_Tp) + __libcpp_datasizeof<_Tp>::value); + } + return __dest; +} + _LIBCPP_END_NAMESPACE_STD #endif // _LIBCPP___STRING_CONSTEXPR_C_FUNCTIONS_H diff --git a/libcxx/include/__type_traits/datasizeof.h b/libcxx/include/__type_traits/datasizeof.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__type_traits/datasizeof.h @@ -0,0 +1,48 @@ +//===----------------------------------------------------------------------===// +// +// 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___TYPE_TRAITS_DATASIZEOF_H +#define _LIBCPP___TYPE_TRAITS_DATASIZEOF_H + +#include <__config> +#include <__type_traits/is_final.h> +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +template +struct __libcpp_datasizeof { +#if __has_cpp_attribute (__no_unique_address__) + template + struct _FirstPaddingByte { + [[__no_unique_address__]] _Tp __v_; + char __first_padding_byte_; + }; +#else + template ::value> + struct _FirstPaddingByte : _Tp { + char __first_padding_byte_; + }; + + template <> + struct _FirstPaddingByte { + _Tp __v_; + char __first_padding_byte_; + }; +#endif + + static const size_t value = offsetof(_FirstPaddingByte<>, __first_padding_byte_); +}; + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___TYPE_TRAITS_DATASIZEOF_H diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy.pass.cpp --- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy.pass.cpp +++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy.pass.cpp @@ -18,6 +18,23 @@ #include "test_macros.h" #include "test_iterators.h" +class PaddedBase { +public: + constexpr PaddedBase(std::int16_t a, std::int8_t b) : a_(a), b_(b) {} + + std::int16_t a_; + std::int8_t b_; +}; + +class Derived : public PaddedBase { +public: + constexpr Derived(std::int16_t a, std::int8_t b, std::int8_t c) : PaddedBase(a, b), c_(c) {} + + std::int8_t c_; +}; + +static_assert(sizeof(Derived) == 4); + template TEST_CONSTEXPR_CXX20 void test_copy() @@ -32,6 +49,14 @@ assert(base(r) == ib+N); for (unsigned i = 0; i < N; ++i) assert(ia[i] == ib[i]); + + // Make sure that padding bits aren't copied + Derived src(1, 2, 3); + Derived dst(4, 5, 6); + std::copy(static_cast(&src), static_cast(&src) + 1, static_cast(&dst)); + assert(dst.a_ == 1); + assert(dst.b_ == 2); + assert(dst.c_ == 6); } TEST_CONSTEXPR_CXX20 bool diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/move.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/move.pass.cpp --- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/move.pass.cpp +++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/move.pass.cpp @@ -6,6 +6,8 @@ // //===----------------------------------------------------------------------===// +// UNSUPPORTED: c++03 && !stdlib=libc++ + // // template @@ -20,6 +22,21 @@ #include "test_macros.h" #include "test_iterators.h" +class PaddedBase { +public: + constexpr PaddedBase(std::int16_t a, std::int8_t b) : a_(a), b_(b) {} + + std::int16_t a_; + std::int8_t b_; +}; + +class Derived : public PaddedBase { +public: + constexpr Derived(std::int16_t a, std::int8_t b, std::int8_t c) : PaddedBase(a, b), c_(c) {} + + std::int8_t c_; +}; + template TEST_CONSTEXPR_CXX17 bool test() @@ -35,10 +52,17 @@ for (unsigned i = 0; i < N; ++i) assert(ia[i] == ib[i]); + // Make sure that padding bits aren't copied + Derived src(1, 2, 3); + Derived dst(4, 5, 6); + std::copy(static_cast(&src), static_cast(&src) + 1, static_cast(&dst)); + assert(dst.a_ == 1); + assert(dst.b_ == 2); + assert(dst.c_ == 6); + return true; } -#if TEST_STD_VER >= 11 template void test1() @@ -54,7 +78,6 @@ for (unsigned i = 0; i < N; ++i) assert(*ib[i] == static_cast(i)); } -#endif int main(int, char**) { @@ -88,7 +111,6 @@ test >(); test(); -#if TEST_STD_VER >= 11 test1*>, cpp17_output_iterator*> >(); test1*>, forward_iterator*> >(); test1*>, bidirectional_iterator*> >(); @@ -118,7 +140,6 @@ test1*, bidirectional_iterator*> >(); test1*, random_access_iterator*> >(); test1*, std::unique_ptr*>(); -#endif // TEST_STD_VER >= 11 #if TEST_STD_VER > 17 test, contiguous_iterator>();