[PATCH] Use __builtin_memmove for trivially copy assignable types
Jonathan Wakely
jwakely@redhat.com
Thu Jul 19 13:25:00 GMT 2018
On 19/07/18 07:59 -0400, Glen Fernandes wrote:
>Updated patch to simplify the helper trait, and to include <memory>
>instead of <algorithm> in the unit test for copy_uninitialized:
>
>Use __builtin_memmove for trivially copy assignable types
>
>2018-07-19 Glen Joseph Fernandes <glenjofe@gmail.com>
>
> * include/bits/stl_algobase.h
> (__is_simple_copy_move): Defined helper.
> (__copy_move_a): Used helper.
> (__copy_move_backward_a): Likewise.
> * testsuite/20_util/specialized_algorithms/uninitialized_copy/1.cc:
> New test.
> * testsuite/25_algorithms/copy/58982.cc: Updated tests.
> * testsuite/25_algorithms/copy_n/58982.cc: Likewise.
>
>Attached: patch.txt
>
>Glen
>commit 1af8d465545fda2451928fe100901db37c3e632c
>Author: Glen Fernandes <glen.fernandes@gmail.com>
>Date: Thu Jul 19 07:40:17 2018 -0400
>
> Use __builtin_memmove for trivially copy assignable types
>
> 2018-07-19 Glen Joseph Fernandes <glenjofe@gmail.com>
>
> * include/bits/stl_algobase.h
> (__is_simple_copy_move): Defined helper.
> (__copy_move_a): Used helper.
> (__copy_move_backward_a): Likewise.
> * testsuite/20_util/specialized_algorithms/uninitialized_copy/1.cc:
> New test.
> * testsuite/25_algorithms/copy/58982.cc: Updated tests.
> * testsuite/25_algorithms/copy_n/58982.cc: Likewise.
>
>diff --git a/libstdc++-v3/include/bits/stl_algobase.h b/libstdc++-v3/include/bits/stl_algobase.h
>index 16a3f83b6..4488207f0 100644
>--- a/libstdc++-v3/include/bits/stl_algobase.h
>+++ b/libstdc++-v3/include/bits/stl_algobase.h
>@@ -72,10 +72,16 @@
>
> namespace std _GLIBCXX_VISIBILITY(default)
> {
> _GLIBCXX_BEGIN_NAMESPACE_VERSION
>
>+ template<typename _Tp>
>+ struct __is_simple_copy_move
>+ {
>+ enum { __value = __is_trivially_assignable(_Tp, const _Tp&) };
>+ };
>+
> #if __cplusplus < 201103L
> // See http://gcc.gnu.org/ml/libstdc++/2004-08/msg00167.html: in a
> // nutshell, we are partially implementing the resolution of DR 187,
> // when it's safe, i.e., the value_types are equal.
> template<bool _BoolType>
>@@ -389,11 +395,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> __copy_move_a(_II __first, _II __last, _OI __result)
> {
> typedef typename iterator_traits<_II>::value_type _ValueTypeI;
> typedef typename iterator_traits<_OI>::value_type _ValueTypeO;
> typedef typename iterator_traits<_II>::iterator_category _Category;
>- const bool __simple = (__is_trivial(_ValueTypeI)
>+ const bool __simple = (__is_simple_copy_move<_ValueTypeI>::__value
Sorry for the delay in reviewing this properly, as I've only just
realised that this introduces undefined behaviour, doesn't it?
It's undefined to use memmove for a type that is not trivially
copyable. All trivial types are trivially copyable, so __is_trivial
was too conservative, but safe (IIRC we used it because there was no
__is_trivially_copyable trait at the time, so __is_trivial was the
best we had).
There are types which are trivially assignable but not trivially
copyable, and it's undefined to use memmove for such types. With your
patch applied I get a warning for this code, where there was none
before:
#include <memory>
#include <type_traits>
struct T
{
T() { }
T(const T&) { }
};
static_assert(std::is_trivially_copy_assignable<T>::value
&& !std::is_trivially_copyable<T>::value,
"T is only trivially copy assignable, not trivially copyable");
void
test01(T* result)
{
T t[1];
std::copy(t, t+1, result);
}
In file included from /home/jwakely/gcc/9/include/c++/9.0.0/memory:62,
from copy.cc:1:
/home/jwakely/gcc/9/include/c++/9.0.0/bits/stl_algobase.h: In instantiation of 'static _Tp* std::__copy_move<_IsMove, true, std::random_access_iterator_tag>::__copy_m(const _Tp*, const _Tp*, _Tp*) [with _Tp = T; bool _IsMove = false]':
/home/jwakely/gcc/9/include/c++/9.0.0/bits/stl_algobase.h:406:30: required from '_OI std::__copy_move_a(_II, _II, _OI) [with bool _IsMove = false; _II = T*; _OI = T*]'
/home/jwakely/gcc/9/include/c++/9.0.0/bits/stl_algobase.h:443:30: required from '_OI std::__copy_move_a2(_II, _II, _OI) [with bool _IsMove = false; _II = T*; _OI = T*]'
/home/jwakely/gcc/9/include/c++/9.0.0/bits/stl_algobase.h:476:7: required from '_OI std::copy(_II, _II, _OI) [with _II = T*; _OI = T*]'
copy.cc:18:27: required from here
/home/jwakely/gcc/9/include/c++/9.0.0/bits/stl_algobase.h:388:23: warning: 'void* __builtin_memmove(void*, const void*, long unsigned int)' writing to an object of non-trivially copyable type 'struct T'; use copy-assignment or copy-initialization instead [-Wclass-memaccess]
__builtin_memmove(__result, __first, sizeof(_Tp) * _Num);
~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
copy.cc:4:8: note: 'struct T' declared here
struct T
^
I think the best we can do here is simply replace __is_trivial with
__is_trivially_copyable, which will enable memmove for trivially
copyable types for which !is_trivially_default_constructible_v<T>.
More information about the Libstdc++
mailing list