Skip to content

Commit 9790cf1

Browse files
committed
[libc++][WIP] Add relocation facilities that P2687 could provide
1 parent d681e10 commit 9790cf1

File tree

7 files changed

+228
-20
lines changed

7 files changed

+228
-20
lines changed

libcxx/include/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -551,18 +551,21 @@ set(files
551551
__memory/construct_at.h
552552
__memory/destruct_n.h
553553
__memory/inout_ptr.h
554+
__memory/is_trivially_allocator_relocatable.h
554555
__memory/noexcept_move_assign_container.h
555556
__memory/out_ptr.h
556557
__memory/pointer_traits.h
557558
__memory/ranges_construct_at.h
558559
__memory/ranges_uninitialized_algorithms.h
559560
__memory/raw_storage_iterator.h
561+
__memory/relocate_at.h
560562
__memory/shared_count.h
561563
__memory/shared_ptr.h
562564
__memory/swap_allocator.h
563565
__memory/temp_value.h
564566
__memory/temporary_buffer.h
565567
__memory/uninitialized_algorithms.h
568+
__memory/uninitialized_relocate.h
566569
__memory/unique_ptr.h
567570
__memory/unique_temporary_buffer.h
568571
__memory/uses_allocator.h
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef _LIBCPP___MEMORY_IS_TRIVIALLY_ALLOCATOR_RELOCATABLE_H
10+
#define _LIBCPP___MEMORY_IS_TRIVIALLY_ALLOCATOR_RELOCATABLE_H
11+
12+
#include <__config>
13+
#include <__memory/allocator_traits.h>
14+
#include <__type_traits/integral_constant.h>
15+
#include <__type_traits/is_trivially_relocatable.h>
16+
#include <__type_traits/negation.h>
17+
18+
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
19+
# pragma GCC system_header
20+
#endif
21+
22+
_LIBCPP_BEGIN_NAMESPACE_STD
23+
24+
// A type is trivially allocator relocatable if the allocator's move construction and destruction
25+
// don't do anything beyond calling the type's move constructor and destructor, and if the type
26+
// itself is trivially relocatable.
27+
28+
template <class _Alloc, class _Type>
29+
struct __allocator_has_trivial_move_construct : _Not<__has_construct<_Alloc, _Type*, _Type&&> > {};
30+
31+
template <class _Type>
32+
struct __allocator_has_trivial_move_construct<allocator<_Type>, _Type> : true_type {};
33+
34+
template <class _Alloc, class _Tp>
35+
struct __allocator_has_trivial_destroy : _Not<__has_destroy<_Alloc, _Tp*> > {};
36+
37+
template <class _Tp, class _Up>
38+
struct __allocator_has_trivial_destroy<allocator<_Tp>, _Up> : true_type {};
39+
40+
template <class _Alloc, class _Tp>
41+
struct __is_trivially_allocator_relocatable
42+
: integral_constant<bool,
43+
__allocator_has_trivial_move_construct<_Alloc, _Tp>::value &&
44+
__allocator_has_trivial_destroy<_Alloc, _Tp>::value &&
45+
__libcpp_is_trivially_relocatable<_Tp>::value> {};
46+
47+
_LIBCPP_END_NAMESPACE_STD
48+
49+
#endif // _LIBCPP___MEMORY_IS_TRIVIALLY_ALLOCATOR_RELOCATABLE_H

libcxx/include/__memory/relocate_at.h

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef _LIBCPP___MEMORY_RELOCATE_AT_H
10+
#define _LIBCPP___MEMORY_RELOCATE_AT_H
11+
12+
#include <__memory/allocator_traits.h>
13+
#include <__memory/construct_at.h>
14+
#include <__memory/is_trivially_allocator_relocatable.h>
15+
#include <__type_traits/enable_if.h>
16+
#include <__type_traits/is_constant_evaluated.h>
17+
#include <__type_traits/is_trivially_relocatable.h>
18+
#include <__utility/move.h>
19+
#include <__utility/scope_guard.h>
20+
21+
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
22+
# pragma GCC system_header
23+
#endif
24+
25+
_LIBCPP_PUSH_MACROS
26+
#include <__undef_macros>
27+
28+
_LIBCPP_BEGIN_NAMESPACE_STD
29+
30+
template <class _Tp>
31+
struct __destroy_object {
32+
_LIBCPP_CONSTEXPR_SINCE_CXX14 void operator()() const { std::__destroy_at(__obj_); }
33+
_Tp* __obj_;
34+
};
35+
36+
template <class _Tp, __enable_if_t<!__libcpp_is_trivially_relocatable<_Tp>::value, int> = 0>
37+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _Tp* __relocate_at(_Tp* __source, _Tp* __dest) {
38+
__scope_guard<__destroy_object<_Tp> > __guard(__destroy_object<_Tp>{__source});
39+
return std::__construct_at(__dest, std::move(*__source));
40+
}
41+
42+
template <class _Tp, __enable_if_t<__libcpp_is_trivially_relocatable<_Tp>::value, int> = 0>
43+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _Tp* __relocate_at(_Tp* __source, _Tp* __dest) {
44+
if (__libcpp_is_constant_evaluated()) {
45+
std::__construct_at(__dest, std::move(*__source));
46+
} else {
47+
// Casting to void* to suppress clang complaining that this is technically UB.
48+
__builtin_memcpy(static_cast<void*>(__dest), __source, sizeof(_Tp));
49+
}
50+
return __dest;
51+
}
52+
53+
template <class _Alloc, class _Tp, __enable_if_t<!__is_trivially_allocator_relocatable<_Alloc, _Tp>::value, int> = 0>
54+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _Tp*
55+
__allocator_relocate_at(_Alloc& __alloc, _Tp* __source, _Tp* __dest) {
56+
allocator_traits<_Alloc>::construct(__alloc, __dest, std::move(*__source));
57+
allocator_traits<_Alloc>::destroy(__alloc, __source);
58+
return __dest;
59+
}
60+
61+
template <class _Alloc, class _Tp, __enable_if_t<__is_trivially_allocator_relocatable<_Alloc, _Tp>::value, int> = 0>
62+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _Tp* __allocator_relocate_at(_Alloc&, _Tp* __source, _Tp* __dest) {
63+
return std::__relocate_at(__source, __dest);
64+
}
65+
66+
_LIBCPP_END_NAMESPACE_STD
67+
68+
_LIBCPP_POP_MACROS
69+
70+
#endif // _LIBCPP___MEMORY_RELOCATE_AT_H

libcxx/include/__memory/uninitialized_algorithms.h

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <__memory/addressof.h>
2121
#include <__memory/allocator_traits.h>
2222
#include <__memory/construct_at.h>
23+
#include <__memory/is_trivially_allocator_relocatable.h>
2324
#include <__memory/pointer_traits.h>
2425
#include <__type_traits/enable_if.h>
2526
#include <__type_traits/extent.h>
@@ -591,19 +592,7 @@ __uninitialized_allocator_copy(_Alloc& __alloc, _Iter1 __first1, _Sent1 __last1,
591592
return std::__rewrap_iter(__first2, __result);
592593
}
593594

594-
template <class _Alloc, class _Type>
595-
struct __allocator_has_trivial_move_construct : _Not<__has_construct<_Alloc, _Type*, _Type&&> > {};
596-
597-
template <class _Type>
598-
struct __allocator_has_trivial_move_construct<allocator<_Type>, _Type> : true_type {};
599-
600-
template <class _Alloc, class _Tp>
601-
struct __allocator_has_trivial_destroy : _Not<__has_destroy<_Alloc, _Tp*> > {};
602-
603-
template <class _Tp, class _Up>
604-
struct __allocator_has_trivial_destroy<allocator<_Tp>, _Up> : true_type {};
605-
606-
// __uninitialized_allocator_relocate relocates the objects in [__first, __last) into __result.
595+
// __uninitialized_allocator_relocate_for_vector relocates the objects in [__first, __last) into __result.
607596
// Relocation means that the objects in [__first, __last) are placed into __result as-if by move-construct and destroy,
608597
// except that the move constructor and destructor may never be called if they are known to be equivalent to a memcpy.
609598
//
@@ -616,15 +605,13 @@ struct __allocator_has_trivial_destroy<allocator<_Tp>, _Up> : true_type {};
616605
// - is_copy_constructible<_ValueType>
617606
// - __libcpp_is_trivially_relocatable<_ValueType>
618607
template <class _Alloc, class _ContiguousIterator>
619-
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __uninitialized_allocator_relocate(
608+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __uninitialized_allocator_relocate_for_vector(
620609
_Alloc& __alloc, _ContiguousIterator __first, _ContiguousIterator __last, _ContiguousIterator __result) {
621610
static_assert(__libcpp_is_contiguous_iterator<_ContiguousIterator>::value, "");
622611
using _ValueType = typename iterator_traits<_ContiguousIterator>::value_type;
623612
static_assert(__is_cpp17_move_insertable<_Alloc>::value,
624613
"The specified type does not meet the requirements of Cpp17MoveInsertable");
625-
if (__libcpp_is_constant_evaluated() || !__libcpp_is_trivially_relocatable<_ValueType>::value ||
626-
!__allocator_has_trivial_move_construct<_Alloc, _ValueType>::value ||
627-
!__allocator_has_trivial_destroy<_Alloc, _ValueType>::value) {
614+
if (__libcpp_is_constant_evaluated() || !__is_trivially_allocator_relocatable<_Alloc, _ValueType>::value) {
628615
auto __destruct_first = __result;
629616
auto __guard = std::__make_exception_guard(
630617
_AllocatorDestroyRangeReverse<_Alloc, _ContiguousIterator>(__alloc, __destruct_first, __result));
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef _LIBCPP___MEMORY_UNINITIALIZED_RELOCATE_H
10+
#define _LIBCPP___MEMORY_UNINITIALIZED_RELOCATE_H
11+
12+
#include <__config>
13+
#include <__iterator/iterator_traits.h>
14+
#include <__memory/allocator_traits.h>
15+
#include <__memory/is_trivially_allocator_relocatable.h>
16+
#include <__memory/pointer_traits.h>
17+
#include <__memory/relocate_at.h>
18+
#include <__type_traits/is_constant_evaluated.h>
19+
#include <__type_traits/is_nothrow_constructible.h>
20+
#include <__type_traits/is_trivially_relocatable.h>
21+
#include <__utility/move.h>
22+
23+
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
24+
# pragma GCC system_header
25+
#endif
26+
27+
_LIBCPP_PUSH_MACROS
28+
#include <__undef_macros>
29+
30+
_LIBCPP_BEGIN_NAMESPACE_STD
31+
32+
// __uninitialized_relocate relocates the objects in [__first, __last) into __result.
33+
//
34+
// Relocation means that the objects in [__first, __last) are placed into __result as-if by move-construct and destroy,
35+
// except that the move constructor and destructor may never be called if they are known to be equivalent to a memcpy.
36+
//
37+
// This algorithm works even if part of the resulting range overlaps with [__first, __last), as long as __result itself
38+
// is not in [__first, last).
39+
//
40+
// This algorithm doesn't throw any exceptions. However, it requires the types in the range to be nothrow
41+
// move-constructible and the iterator operations not to throw any exceptions.
42+
//
43+
// Preconditions:
44+
// - __result doesn't contain any objects and [__first, __last) contains objects
45+
// - __result is not in [__first, __last)
46+
// Postconditions: __result contains the objects from [__first, __last) and
47+
// [__first, __last) doesn't contain any objects
48+
template <class _ContiguousIterator>
49+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _ContiguousIterator __uninitialized_relocate(
50+
_ContiguousIterator __first, _ContiguousIterator __last, _ContiguousIterator __result) _NOEXCEPT {
51+
using _ValueType = typename iterator_traits<_ContiguousIterator>::value_type;
52+
static_assert(__libcpp_is_contiguous_iterator<_ContiguousIterator>::value, "");
53+
static_assert(is_nothrow_move_constructible<_ValueType>::value, "");
54+
if (!__libcpp_is_constant_evaluated() && __libcpp_is_trivially_relocatable<_ValueType>::value) {
55+
auto const __n = __last - __first;
56+
// Casting to void* to suppress clang complaining that this is technically UB.
57+
__builtin_memmove(
58+
static_cast<void*>(std::__to_address(__result)), std::__to_address(__first), sizeof(_ValueType) * __n);
59+
return __result + __n;
60+
} else {
61+
while (__first != __last) {
62+
std::__relocate_at(std::__to_address(__first), std::__to_address(__result));
63+
++__first;
64+
++__result;
65+
}
66+
return __result;
67+
}
68+
}
69+
70+
// Equivalent to __uninitialized_relocate, but uses the provided allocator's construct() and destroy() methods.
71+
template <class _Alloc, class _ContiguousIterator>
72+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _ContiguousIterator __uninitialized_allocator_relocate(
73+
_Alloc& __alloc, _ContiguousIterator __first, _ContiguousIterator __last, _ContiguousIterator __result) _NOEXCEPT {
74+
using _ValueType = typename iterator_traits<_ContiguousIterator>::value_type;
75+
static_assert(__libcpp_is_contiguous_iterator<_ContiguousIterator>::value, "");
76+
static_assert(is_nothrow_move_constructible<_ValueType>::value, "");
77+
if (!__libcpp_is_constant_evaluated() && __is_trivially_allocator_relocatable<_Alloc, _ValueType>::value) {
78+
(void)__alloc; // ignore the allocator
79+
return std::__uninitialized_relocate(std::move(__first), std::move(__last), std::move(__result));
80+
} else {
81+
while (__first != __last) {
82+
std::__allocator_relocate_at(__alloc, std::__to_address(__first), std::__to_address(__result));
83+
++__first;
84+
++__result;
85+
}
86+
return __result;
87+
}
88+
}
89+
90+
_LIBCPP_END_NAMESPACE_STD
91+
92+
_LIBCPP_POP_MACROS
93+
94+
#endif // _LIBCPP___MEMORY_UNINITIALIZED_RELOCATE_H

libcxx/include/__vector/vector.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -806,7 +806,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void
806806
vector<_Tp, _Allocator>::__swap_out_circular_buffer(__split_buffer<value_type, allocator_type&>& __v) {
807807
__annotate_delete();
808808
auto __new_begin = __v.__begin_ - (__end_ - __begin_);
809-
std::__uninitialized_allocator_relocate(
809+
std::__uninitialized_allocator_relocate_for_vector(
810810
this->__alloc_, std::__to_address(__begin_), std::__to_address(__end_), std::__to_address(__new_begin));
811811
__v.__begin_ = __new_begin;
812812
__end_ = __begin_; // All the objects have been destroyed by relocating them.
@@ -829,13 +829,13 @@ vector<_Tp, _Allocator>::__swap_out_circular_buffer(__split_buffer<value_type, a
829829

830830
// Relocate [__p, __end_) first to avoid having a hole in [__begin_, __end_)
831831
// in case something in [__begin_, __p) throws.
832-
std::__uninitialized_allocator_relocate(
832+
std::__uninitialized_allocator_relocate_for_vector(
833833
this->__alloc_, std::__to_address(__p), std::__to_address(__end_), std::__to_address(__v.__end_));
834834
__v.__end_ += (__end_ - __p);
835835
__end_ = __p; // The objects in [__p, __end_) have been destroyed by relocating them.
836836
auto __new_begin = __v.__begin_ - (__p - __begin_);
837837

838-
std::__uninitialized_allocator_relocate(
838+
std::__uninitialized_allocator_relocate_for_vector(
839839
this->__alloc_, std::__to_address(__begin_), std::__to_address(__p), std::__to_address(__new_begin));
840840
__v.__begin_ = __new_begin;
841841
__end_ = __begin_; // All the objects have been destroyed by relocating them.

libcxx/include/module.modulemap

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1535,6 +1535,7 @@ module std [system] {
15351535
module destruct_n { header "__memory/destruct_n.h" }
15361536
module fwd { header "__fwd/memory.h" }
15371537
module inout_ptr { header "__memory/inout_ptr.h" }
1538+
module is_trivially_allocator_relocatable { header "__memory/is_trivially_allocator_relocatable.h" }
15381539
module noexcept_move_assign_container { header "__memory/noexcept_move_assign_container.h" }
15391540
module out_ptr { header "__memory/out_ptr.h" }
15401541
module pointer_traits { header "__memory/pointer_traits.h" }
@@ -1544,6 +1545,7 @@ module std [system] {
15441545
export std.algorithm.in_out_result
15451546
}
15461547
module raw_storage_iterator { header "__memory/raw_storage_iterator.h" }
1548+
module relocate_at { header "__memory/relocate_at.h" }
15471549
module shared_count { header "__memory/shared_count.h" }
15481550
module shared_ptr { header "__memory/shared_ptr.h" }
15491551
module swap_allocator { header "__memory/swap_allocator.h" }
@@ -1556,6 +1558,9 @@ module std [system] {
15561558
header "__memory/uninitialized_algorithms.h"
15571559
export std.utility.pair
15581560
}
1561+
module uninitialized_relocate {
1562+
header "__memory/uninitialized_relocate.h"
1563+
}
15591564
module unique_ptr {
15601565
header "__memory/unique_ptr.h"
15611566
}

0 commit comments

Comments
 (0)