Skip to content

Commit 2580273

Browse files
committed
[libc++] Add tombstone traits to use in optional, variant
1 parent e8b7d8b commit 2580273

File tree

16 files changed

+545
-7
lines changed

16 files changed

+545
-7
lines changed

libcxx/include/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,7 @@ set(files
560560
__memory/swap_allocator.h
561561
__memory/temp_value.h
562562
__memory/temporary_buffer.h
563+
__memory/tombstone_traits.h
563564
__memory/uninitialized_algorithms.h
564565
__memory/unique_ptr.h
565566
__memory/unique_temporary_buffer.h

libcxx/include/__configuration/abi.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@
124124
// This setting disables the addition of such artificial padding, leading to a more optimal
125125
// representation for several types.
126126
# define _LIBCPP_ABI_NO_COMPRESSED_PAIR_PADDING
127+
// Use __tombstone_traits to optimize the memory layout of std::optional.
128+
# define _LIBCPP_ABI_OPTIONAL_USE_TOMBSTONE_TRAITS
127129
#elif _LIBCPP_ABI_VERSION == 1
128130
# if !(defined(_LIBCPP_OBJECT_FORMAT_COFF) || defined(_LIBCPP_OBJECT_FORMAT_XCOFF))
129131
// Enable compiling copies of now inline methods into the dylib to support
@@ -141,6 +143,9 @@
141143
# if defined(__FreeBSD__) && __FreeBSD__ < 14
142144
# define _LIBCPP_DEPRECATED_ABI_DISABLE_PAIR_TRIVIAL_COPY_CTOR
143145
# endif
146+
147+
// TODO: This shouldn't be in the final commit - this just to test the changes across all the different configurations
148+
# define _LIBCPP_ABI_OPTIONAL_USE_TOMBSTONE_TRAITS
144149
#endif
145150

146151
// We had some bugs where we use [[no_unique_address]] together with construct_at,

libcxx/include/__functional/reference_wrapper.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,18 @@
1313
#include <__compare/synth_three_way.h>
1414
#include <__concepts/boolean_testable.h>
1515
#include <__config>
16+
#include <__cstddef/size_t.h>
1617
#include <__functional/weak_result_type.h>
1718
#include <__memory/addressof.h>
19+
#include <__memory/tombstone_traits.h>
1820
#include <__type_traits/enable_if.h>
1921
#include <__type_traits/invoke.h>
2022
#include <__type_traits/is_const.h>
2123
#include <__type_traits/remove_cvref.h>
2224
#include <__type_traits/void_t.h>
2325
#include <__utility/declval.h>
2426
#include <__utility/forward.h>
27+
#include <cstdint>
2528

2629
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
2730
# pragma GCC system_header
@@ -120,7 +123,13 @@ class _LIBCPP_TEMPLATE_VIS reference_wrapper : public __weak_result_type<_Tp> {
120123
#if _LIBCPP_STD_VER >= 17
121124
template <class _Tp>
122125
reference_wrapper(_Tp&) -> reference_wrapper<_Tp>;
123-
#endif
126+
127+
template <class _Tp>
128+
struct __tombstone_traits<reference_wrapper<_Tp>> {
129+
static constexpr uintptr_t __disengaged_value_ = 0;
130+
static constexpr size_t __is_disengaged_offset_ = 0;
131+
};
132+
#endif // _LIBCPP_STD_VER >= 17
124133

125134
template <class _Tp>
126135
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 reference_wrapper<_Tp> ref(_Tp& __t) _NOEXCEPT {

libcxx/include/__locale

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <__config>
1414
#include <__locale_dir/locale_base_api.h>
1515
#include <__memory/shared_ptr.h> // __shared_count
16+
#include <__memory/tombstone_traits.h>
1617
#include <__mutex/once_flag.h>
1718
#include <__type_traits/make_unsigned.h>
1819
#include <__utility/no_destroy.h>
@@ -114,6 +115,11 @@ private:
114115
friend const _Facet& use_facet(const locale&);
115116
};
116117

118+
#if _LIBCPP_STD_VER >= 17
119+
template <>
120+
struct __tombstone_traits<locale> : __tombstone_traits_assume_aligned_pointer {};
121+
#endif
122+
117123
class _LIBCPP_EXPORTED_FROM_ABI locale::facet : public __shared_count {
118124
protected:
119125
_LIBCPP_HIDE_FROM_ABI explicit facet(size_t __refs = 0) : __shared_count(static_cast<long>(__refs) - 1) {}

libcxx/include/__memory/shared_ptr.h

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include <__memory/compressed_pair.h>
3131
#include <__memory/construct_at.h>
3232
#include <__memory/pointer_traits.h>
33+
#include <__memory/tombstone_traits.h>
3334
#include <__memory/uninitialized_algorithms.h>
3435
#include <__memory/unique_ptr.h>
3536
#include <__type_traits/add_lvalue_reference.h>
@@ -831,7 +832,14 @@ template <class _Tp>
831832
shared_ptr(weak_ptr<_Tp>) -> shared_ptr<_Tp>;
832833
template <class _Tp, class _Dp>
833834
shared_ptr(unique_ptr<_Tp, _Dp>) -> shared_ptr<_Tp>;
834-
#endif
835+
836+
template <class _Tp>
837+
struct __tombstone_traits<shared_ptr<_Tp>> {
838+
static constexpr auto __disengaged_value_ = __tombstone_traits_assume_aligned_pointer::__disengaged_value_;
839+
static constexpr size_t __is_disengaged_offset_ =
840+
sizeof(void*) + __tombstone_traits_assume_aligned_pointer::__is_disengaged_offset_;
841+
};
842+
#endif // _LIBCPP_STD_VER >= 17
835843

836844
//
837845
// std::allocate_shared and std::make_shared
@@ -1381,7 +1389,14 @@ class _LIBCPP_SHARED_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS weak_ptr {
13811389
#if _LIBCPP_STD_VER >= 17
13821390
template <class _Tp>
13831391
weak_ptr(shared_ptr<_Tp>) -> weak_ptr<_Tp>;
1384-
#endif
1392+
1393+
template <class _Tp>
1394+
struct __tombstone_traits<weak_ptr<_Tp>> {
1395+
static constexpr auto __disengaged_value_ = __tombstone_traits_assume_aligned_pointer::__disengaged_value_;
1396+
static constexpr size_t __is_disengaged_offset_ =
1397+
sizeof(void*) + __tombstone_traits_assume_aligned_pointer::__is_disengaged_offset_;
1398+
};
1399+
#endif // _LIBCPP_STD_VER >= 17
13851400

13861401
template <class _Tp>
13871402
inline _LIBCPP_CONSTEXPR weak_ptr<_Tp>::weak_ptr() _NOEXCEPT : __ptr_(nullptr), __cntrl_(nullptr) {}
Lines changed: 277 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
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___TYPE_TRAITS_DISENGAGED_TRAITS_H
10+
#define _LIBCPP___TYPE_TRAITS_DISENGAGED_TRAITS_H
11+
12+
#include <__assert>
13+
#include <__config>
14+
#include <__cstddef/size_t.h>
15+
#include <__memory/construct_at.h>
16+
#include <__type_traits/datasizeof.h>
17+
#include <__type_traits/enable_if.h>
18+
#include <__type_traits/is_constant_evaluated.h>
19+
#include <__type_traits/is_fundamental.h>
20+
#include <__type_traits/is_integral.h>
21+
#include <__type_traits/is_trivial.h>
22+
#include <__type_traits/is_trivially_destructible.h>
23+
#include <__type_traits/remove_cv.h>
24+
#include <__type_traits/void_t.h>
25+
#include <__utility/forward.h>
26+
#include <__utility/move.h>
27+
#include <__utility/piecewise_construct.h>
28+
#include <cstdint>
29+
30+
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
31+
# pragma GCC system_header
32+
#endif
33+
34+
#if _LIBCPP_STD_VER >= 17
35+
36+
_LIBCPP_BEGIN_NAMESPACE_STD
37+
38+
template <class>
39+
struct __tombstone_traits;
40+
41+
// bools have always exactly one bit set. If there is more than one set it's disengaged.
42+
template <>
43+
struct __tombstone_traits<bool> {
44+
static constexpr uint8_t __disengaged_value_ = 3;
45+
static constexpr size_t __is_disengaged_offset_ = 0;
46+
};
47+
48+
struct __tombstone_traits_assume_aligned_pointer {
49+
static constexpr uint8_t __disengaged_value_ = 1;
50+
#ifdef _LIBCPP_LITTLE_ENDIAN
51+
static constexpr size_t __is_disengaged_offset_ = 0;
52+
#else
53+
static constexpr size_t __is_disengaged_offset_ = sizeof(void*) - 1;
54+
#endif
55+
};
56+
57+
// TODO: Look into
58+
// - filesystem::directory_iterator
59+
// - vector<T> with alignof(T) == 1
60+
// - string_view (basic_string_view<T> works with alignof(T) >= 2)
61+
62+
// This is constrained on fundamental types because we might not always know the alignment of a user-defined type.
63+
// For example, in one TU there may only be a forward declaration and in another there is already the definition
64+
// available. If we made this optimization conditional on the completeness of the type this would result in a non-benign
65+
// ODR violation.
66+
template <class _Tp>
67+
struct __tombstone_traits<__enable_specialization_if<is_fundamental_v<_Tp> && alignof(_Tp) >= 2, _Tp*>>
68+
: __tombstone_traits_assume_aligned_pointer {};
69+
70+
template <class _Tp>
71+
struct __tombstone_traits<_Tp**> : __tombstone_traits_assume_aligned_pointer {
72+
static_assert(alignof(_Tp*) >= 2, "alignment of a pointer isn't at least 2!?");
73+
};
74+
75+
inline constexpr struct __init_engaged_t {
76+
} __init_engaged;
77+
inline constexpr struct __init_disengaged_t {
78+
} __init_disengaged;
79+
80+
template <class _Tp, class _Payload, bool = __tombstone_traits<_Tp>::__is_disengaged_offset_ == 0>
81+
struct __tombstone_is_disengaged {
82+
using _TombstoneLayout = __tombstone_traits<_Tp>;
83+
using _IsDisengagedT = remove_cv_t<decltype(_TombstoneLayout::__disengaged_value_)>;
84+
85+
char __padding_[_TombstoneLayout::__is_disengaged_offset_];
86+
_IsDisengagedT __is_disengaged_;
87+
};
88+
89+
template <class _Tp, class _Payload>
90+
struct __tombstone_is_disengaged<_Tp, _Payload, true> {
91+
using _TombstoneLayout = __tombstone_traits<_Tp>;
92+
using _IsDisengagedT = remove_cv_t<decltype(_TombstoneLayout::__disengaged_value_)>;
93+
94+
_IsDisengagedT __is_disengaged_;
95+
};
96+
97+
template <class _Tp, class _Payload, bool = __tombstone_traits<_Tp>::__is_disengaged_offset_ == 0>
98+
struct __tombstone_data {
99+
using _TombstoneLayout = __tombstone_traits<_Tp>;
100+
using _IsDisengagedT = remove_cv_t<decltype(_TombstoneLayout::__disengaged_value_)>;
101+
102+
static_assert(is_trivial<_IsDisengagedT>::value, "disengaged type has to be trivial!");
103+
static_assert(_TombstoneLayout::__is_disengaged_offset_ >= __datasizeof_v<_Payload>);
104+
105+
_LIBCPP_NO_UNIQUE_ADDRESS _Payload __payload_;
106+
char __padding_[_TombstoneLayout::__is_disengaged_offset_ - __datasizeof_v<_Payload>];
107+
_IsDisengagedT __is_disengaged_;
108+
109+
template <class... _Args>
110+
_LIBCPP_HIDE_FROM_ABI constexpr __tombstone_data(_Args&&... __args)
111+
: __payload_(std::forward<_Args>(__args)...), __is_disengaged_(_TombstoneLayout::__disengaged_value_) {}
112+
};
113+
114+
template <class _Tp, class _Payload>
115+
struct __tombstone_data<_Tp, _Payload, true> {
116+
using _TombstoneLayout = __tombstone_traits<_Tp>;
117+
using _IsDisengagedT = remove_cv_t<decltype(_TombstoneLayout::__disengaged_value_)>;
118+
119+
_IsDisengagedT __is_disengaged_;
120+
_LIBCPP_NO_UNIQUE_ADDRESS _Payload __payload_;
121+
122+
template <class... _Args>
123+
_LIBCPP_HIDE_FROM_ABI constexpr __tombstone_data(_Args&&... __args)
124+
: __is_disengaged_(_TombstoneLayout::__disengaged_value_), __payload_(std::forward<_Args>(__args)...) {}
125+
};
126+
127+
template <class _Tp, class _Payload>
128+
struct __tombstoned_value final {
129+
using _TombstoneLayout = __tombstone_traits<_Tp>;
130+
using _TombstoneData = __tombstone_data<_Tp, _Payload>;
131+
132+
template <bool = is_trivially_destructible_v<_Tp> && is_trivially_destructible_v<_Payload>>
133+
union _MaybeTombstone {
134+
_Tp __value_;
135+
_TombstoneData __tombstone_;
136+
137+
template <class... _Args>
138+
constexpr _MaybeTombstone(__init_disengaged_t, _Args&&... __args) : __tombstone_(std::forward<_Args>(__args)...) {}
139+
140+
template <class... _Args>
141+
constexpr _MaybeTombstone(__init_engaged_t, _Args&&... __args) : __value_(std::forward<_Args>(__args)...) {}
142+
143+
_MaybeTombstone(const _MaybeTombstone&) = default;
144+
_MaybeTombstone(_MaybeTombstone&&) = default;
145+
_MaybeTombstone& operator=(const _MaybeTombstone&) = default;
146+
_MaybeTombstone& operator=(_MaybeTombstone&&) = default;
147+
148+
_LIBCPP_HIDE_FROM_ABI constexpr bool __is_engaged() const noexcept {
149+
if (__libcpp_is_constant_evaluated())
150+
return !__builtin_constant_p(__tombstone_.__is_disengaged_ == _TombstoneLayout::__disengaged_value_);
151+
__tombstone_is_disengaged<_Tp, _Payload> __is_disengaged;
152+
static_assert(sizeof(__tombstone_is_disengaged<_Tp, _Payload>) <= sizeof(_MaybeTombstone));
153+
__builtin_memcpy(&__is_disengaged, this, sizeof(__tombstone_is_disengaged<_Tp, _Payload>));
154+
155+
return __is_disengaged.__is_disengaged_ != _TombstoneLayout::__disengaged_value_;
156+
}
157+
158+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 ~_MaybeTombstone() {
159+
if (__is_engaged()) {
160+
std::destroy_at(&__value_);
161+
} else {
162+
std::destroy_at(&__tombstone_);
163+
}
164+
}
165+
};
166+
167+
template <>
168+
union _MaybeTombstone<true> {
169+
_Tp __value_;
170+
_TombstoneData __tombstone_;
171+
172+
template <class... _Args>
173+
constexpr _MaybeTombstone(__init_disengaged_t, _Args&&... __args) : __tombstone_(std::forward<_Args>(__args)...) {}
174+
175+
template <class... _Args>
176+
constexpr _MaybeTombstone(__init_engaged_t, _Args&&... __args) : __value_(std::forward<_Args>(__args)...) {}
177+
178+
_MaybeTombstone(const _MaybeTombstone&) = default;
179+
_MaybeTombstone(_MaybeTombstone&&) = default;
180+
_MaybeTombstone& operator=(const _MaybeTombstone&) = default;
181+
_MaybeTombstone& operator=(_MaybeTombstone&&) = default;
182+
183+
_LIBCPP_HIDE_FROM_ABI constexpr bool __is_engaged() const noexcept {
184+
if (__libcpp_is_constant_evaluated())
185+
return !__builtin_constant_p(__tombstone_.__is_disengaged_ == _TombstoneLayout::__disengaged_value_);
186+
__tombstone_is_disengaged<_Tp, _Payload> __is_disengaged;
187+
static_assert(sizeof(__tombstone_is_disengaged<_Tp, _Payload>) <= sizeof(_MaybeTombstone));
188+
__builtin_memcpy(&__is_disengaged, this, sizeof(__tombstone_is_disengaged<_Tp, _Payload>));
189+
190+
return __is_disengaged.__is_disengaged_ != _TombstoneLayout::__disengaged_value_;
191+
}
192+
193+
_LIBCPP_CONSTEXPR_SINCE_CXX20 ~_MaybeTombstone() = default;
194+
};
195+
196+
_MaybeTombstone<> __data_;
197+
198+
static_assert(sizeof(__tombstone_data<_Tp, _Payload>) <= sizeof(_Tp));
199+
static_assert(is_integral_v<decltype(_TombstoneLayout::__disengaged_value_)>);
200+
static_assert(__builtin_offsetof(_TombstoneData, __is_disengaged_) == _TombstoneLayout::__is_disengaged_offset_);
201+
202+
template <class... _Args>
203+
_LIBCPP_HIDE_FROM_ABI constexpr __tombstoned_value(__init_disengaged_t, _Args&&... __args)
204+
: __data_(__init_disengaged, std::forward<_Args>(__args)...) {}
205+
206+
template <class... _Args>
207+
_LIBCPP_HIDE_FROM_ABI constexpr __tombstoned_value(__init_engaged_t, _Args&&... __args)
208+
: __data_(__init_engaged, std::forward<_Args>(__args)...) {}
209+
210+
_LIBCPP_HIDE_FROM_ABI constexpr bool __is_engaged() const noexcept { return __data_.__is_engaged(); }
211+
212+
_LIBCPP_HIDE_FROM_ABI constexpr auto&& __get_value() & noexcept {
213+
_LIBCPP_ASSERT_INTERNAL(__self.__is_engaged(), "Trying to get the value of a disenagaged tombstoned value");
214+
return __data_.__value_;
215+
}
216+
217+
_LIBCPP_HIDE_FROM_ABI constexpr auto&& __get_value() const & noexcept {
218+
_LIBCPP_ASSERT_INTERNAL(__self.__is_engaged(), "Trying to get the value of a disenagaged tombstoned value");
219+
return __data_.__value_;
220+
}
221+
222+
_LIBCPP_HIDE_FROM_ABI constexpr auto&& __get_value() && noexcept {
223+
_LIBCPP_ASSERT_INTERNAL(__self.__is_engaged(), "Trying to get the value of a disenagaged tombstoned value");
224+
return std::move(__data_.__value_);
225+
}
226+
227+
_LIBCPP_HIDE_FROM_ABI constexpr auto&& __get_value() const && noexcept {
228+
_LIBCPP_ASSERT_INTERNAL(__self.__is_engaged(), "Trying to get the value of a disenagaged tombstoned value");
229+
return std::move(__data_.__value_);
230+
}
231+
232+
_LIBCPP_HIDE_FROM_ABI constexpr auto&& __get_payload() & noexcept {
233+
_LIBCPP_ASSERT_INTERNAL(!__self.__is_engaged(), "Trying to get the payload of an enagaged tombstoned value");
234+
return __data_.__tombstone_.__payload_;
235+
}
236+
237+
_LIBCPP_HIDE_FROM_ABI constexpr auto&& __get_payload() const & noexcept {
238+
_LIBCPP_ASSERT_INTERNAL(!__self.__is_engaged(), "Trying to get the payload of an enagaged tombstoned value");
239+
return __data_.__tombstone_.__payload_;
240+
}
241+
242+
_LIBCPP_HIDE_FROM_ABI constexpr auto&& __get_payload() && noexcept {
243+
_LIBCPP_ASSERT_INTERNAL(!__self.__is_engaged(), "Trying to get the payload of an enagaged tombstoned value");
244+
return std::move(__data_.__tombstone_.__payload_);
245+
}
246+
247+
_LIBCPP_HIDE_FROM_ABI constexpr auto&& __get_payload() const && noexcept {
248+
_LIBCPP_ASSERT_INTERNAL(!__self.__is_engaged(), "Trying to get the payload of an enagaged tombstoned value");
249+
return std::move(__data_.__tombstone_.__payload_);
250+
}
251+
252+
template <class... _Args>
253+
_LIBCPP_HIDE_FROM_ABI constexpr void __engage(piecewise_construct_t, _Args&&... __args) {
254+
_LIBCPP_ASSERT_INTERNAL(!__self.__is_engaged(), "Trying to enage a already engaged tombstoned value");
255+
std::destroy_at(&__data_.__tombstone_);
256+
std::__construct_at(&__data_.__value_, std::forward<_Args>(__args)...);
257+
}
258+
259+
template <class... _Args>
260+
_LIBCPP_HIDE_FROM_ABI constexpr void __disengage(piecewise_construct_t, _Args&&... __args) {
261+
_LIBCPP_ASSERT_INTERNAL(!__self.__is_engaged(), "Trying to disenage a disengaged tombstoned value");
262+
std::destroy_at(&__data_.__value_);
263+
std::__construct_at(&__data_.__tombstone_, std::forward<_Args>(__args)...);
264+
}
265+
};
266+
267+
template <class _Tp, class = void>
268+
inline constexpr bool __has_tombstone_v = false;
269+
270+
template <class _Tp>
271+
inline constexpr bool __has_tombstone_v<_Tp, void_t<decltype(sizeof(__tombstone_traits<_Tp>))>> = true;
272+
273+
_LIBCPP_END_NAMESPACE_STD
274+
275+
#endif // _LIBCPP_STD_VER >= 17
276+
277+
#endif // _LIBCPP___TYPE_TRAITS_DISENGAGED_TRAITS_H

0 commit comments

Comments
 (0)