Skip to content

Commit 6aeb378

Browse files
committed
Simplified design
1 parent bed3d18 commit 6aeb378

File tree

6 files changed

+223
-784
lines changed

6 files changed

+223
-784
lines changed

libcxx/include/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -538,12 +538,12 @@ set(files
538538
__memory/inout_ptr.h
539539
__memory/out_ptr.h
540540
__memory/pointer_traits.h
541+
__memory/pointer_tag_pair.h
541542
__memory/ranges_construct_at.h
542543
__memory/ranges_uninitialized_algorithms.h
543544
__memory/raw_storage_iterator.h
544545
__memory/shared_ptr.h
545546
__memory/swap_allocator.h
546-
__memory/tagged_ptr.h
547547
__memory/temp_value.h
548548
__memory/temporary_buffer.h
549549
__memory/uninitialized_algorithms.h
Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
// -*- C++ -*-
2+
//===----------------------------------------------------------------------===//
3+
//
4+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5+
// See https://llvm.org/LICENSE.txt for license information.
6+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7+
//
8+
//===----------------------------------------------------------------------===//
9+
10+
#ifndef _LIBCPP___POINTER_TAG_PAIR_H
11+
#define _LIBCPP___POINTER_TAG_PAIR_H
12+
13+
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
14+
# pragma GCC system_header
15+
#endif
16+
17+
#if _LIBCPP_STD_VER >= 26
18+
19+
#include <__assert>
20+
#include <__bit/bit_width.h>
21+
#include <__bit/countr.h>
22+
#include <__config>
23+
#include <__tuple/tuple_element.h>
24+
#include <__tuple/tuple_size.h>
25+
#include <__type_traits/conditional.h>
26+
#include <__type_traits/is_enum.h>
27+
#include <__type_traits/is_integral.h>
28+
#include <__type_traits/is_object.h>
29+
#include <__type_traits/make_unsigned.h>
30+
#include <__type_traits/remove_cvref.h>
31+
#include <__type_traits/underlying_type.h>
32+
#include <__utility/swap.h>
33+
#include <climits>
34+
#include <compare>
35+
36+
_LIBCPP_BEGIN_NAMESPACE_STD
37+
38+
constexpr bool __is_ptr_aligned(const void* _ptr, size_t _alignment) noexcept {
39+
# if __has_builtin(__builtin_is_aligned)
40+
return __builtin_is_aligned(_ptr, _alignment);
41+
# else
42+
return reinterpret_cast<uintptr_t>(_ptr) % _alignment == 0;
43+
# endif
44+
}
45+
46+
template <typename T>
47+
struct __tag_underlying_type {
48+
using type = T;
49+
};
50+
51+
template <typename T>
52+
requires(std::is_enum_v<T>)
53+
struct __tag_underlying_type<T> {
54+
using type = std::underlying_type_t<T>;
55+
};
56+
57+
template <typename T>
58+
constexpr unsigned _alignment_bits_available = std::countr_zero(alignof(T));
59+
60+
template <typename T, unsigned Bits> struct _few_bits {
61+
T value : Bits;
62+
};
63+
64+
template <typename T> struct _few_bits<T, 0> {
65+
struct zero {
66+
constexpr zero(T) noexcept { }
67+
constexpr operator T() const noexcept {
68+
return T{};
69+
}
70+
} value;
71+
};
72+
73+
template <typename PointeeT, typename TagT, unsigned Bits = _alignment_bits_available<PointeeT>>
74+
requires (std::is_object_v<PointeeT> && (std::is_integral_v<TagT> || std::is_enum_v<TagT>) && std::is_same_v<TagT, std::remove_cvref_t<TagT>>)
75+
struct pointer_tag_pair {
76+
public:
77+
using element_type = PointeeT;
78+
using pointer_type = PointeeT*;
79+
using tagged_pointer_type = void *; // or uintptr_t?
80+
using tag_type = TagT;
81+
82+
private:
83+
static constexpr unsigned _alignment_needed = (1u << Bits);
84+
static constexpr uintptr_t _tag_mask = (_alignment_needed - 1u);
85+
static constexpr uintptr_t _pointer_mask = ~_tag_mask;
86+
87+
using _underlaying_tag_type = typename __tag_underlying_type<tag_type>::type;
88+
// using unsigned_tag_type = std::make_unsigned_t<_underlaying_tag_type>;
89+
using unspecified_pointer_type = pointer_type;
90+
using _real_tag_type = _few_bits<_underlaying_tag_type, Bits>;
91+
92+
unspecified_pointer_type _pointer{nullptr}; // required to have size same as sizeof(Pointee *)
93+
94+
static_assert(Bits < sizeof(pointer_type) * CHAR_BIT);
95+
96+
struct _tagged_t {};
97+
98+
constexpr pointer_tag_pair(_tagged_t, unspecified_pointer_type _ptr) noexcept : _pointer{_ptr} {}
99+
100+
public:
101+
pointer_tag_pair() = default; // always noexcept
102+
constexpr pointer_tag_pair(nullptr_t) noexcept;
103+
pointer_tag_pair(const pointer_tag_pair&) = default;
104+
pointer_tag_pair(pointer_tag_pair&&) = default;
105+
pointer_tag_pair& operator=(const pointer_tag_pair&) = default;
106+
pointer_tag_pair& operator=(pointer_tag_pair&&) = default;
107+
108+
// to store and tag pointer (only if eligible)
109+
// Precondition: bit_width(static_cast<make_usigned_t<U>>(tag)) <= Bits is true, where U is
110+
// underlying_type_t<TagType>> if is_enum_v<TagType> and TagType otherwise. Precondition: alignof(ptr) (there is
111+
// enough of bits) Mandates: alignment of element type >= bits requested This turn them into unsigned.
112+
constexpr pointer_tag_pair(pointer_type _ptr, tag_type _tag)
113+
requires(alignof(element_type) >= _alignment_needed)
114+
{
115+
const auto _native_tag = _real_tag_type{static_cast<_underlaying_tag_type>(_tag)}.value;
116+
_LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
117+
_native_tag == (static_cast<_underlaying_tag_type>(_tag)), "Tag value must fit into requested bits");
118+
//_LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(std::bit_width(_native_tag) <= Bits, "Tag type value must fits bits
119+
//requested");
120+
_LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
121+
std::__is_ptr_aligned(_ptr, _alignment_needed), "Pointer must be aligned by provided alignment for tagging");
122+
123+
void* _tagged_ptr = __builtin_tag_pointer_mask_or((void*)_ptr, _native_tag, _tag_mask);
124+
_pointer = static_cast<unspecified_pointer_type>(_tagged_ptr);
125+
}
126+
127+
// Preconditions: p points to an object X of a type similar ([conv.qual]) to element_type, where X has alignment
128+
// byte_alignment (inspired by aligned_accessor)
129+
// The precondition needa to say null pointer value or the thing about pointing to object with aligment. ??
130+
template <unsigned _AlignmentPromised>
131+
static constexpr pointer_tag_pair from_overaligned(pointer_type _ptr, tag_type _tag) {
132+
const auto _native_tag = _real_tag_type{static_cast<_underlaying_tag_type>(_tag)}.value;
133+
_LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
134+
_native_tag == (static_cast<_underlaying_tag_type>(_tag)), "Tag value must fit into requested bits");
135+
//_LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(std::bit_width(static_cast<unsigned_tag_type>(_tag)) <= Bits, "Tag type
136+
//value must fits bits requested");
137+
_LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
138+
std::__is_ptr_aligned(_ptr, _AlignmentPromised), "Pointer must be aligned by provided alignment for tagging");
139+
140+
void* _tagged_ptr = __builtin_tag_pointer_mask_or((void*)_ptr, _native_tag, _tag_mask);
141+
return pointer_tag_pair{_tagged_t{}, static_cast<unspecified_pointer_type>(_tagged_ptr)};
142+
}
143+
144+
pointer_tag_pair from_tagged(tagged_pointer_type ptr) { // no-constexpr
145+
// Precondition: valid pointer if untagged??
146+
return pointer_tag_pair{_tagged_t{}, reinterpret_cast<unspecified_pointer_type>(ptr)};
147+
}
148+
149+
// destructor
150+
~pointer_tag_pair() = default;
151+
152+
// accessors
153+
tagged_pointer_type tagged_pointer() const noexcept {
154+
return reinterpret_cast<tagged_pointer_type>(_pointer);
155+
}
156+
constexpr pointer_type pointer() const noexcept {
157+
return static_cast<pointer_type>(__builtin_tag_pointer_mask((void*)_pointer, _pointer_mask));
158+
}
159+
constexpr tag_type tag() const noexcept {
160+
const uintptr_t r = __builtin_tag_pointer_mask_as_int((void*)_pointer, _tag_mask);
161+
return static_cast<tag_type>(_real_tag_type{.value = static_cast<_underlaying_tag_type>(r)}.value);
162+
}
163+
164+
// swap
165+
constexpr void swap(pointer_tag_pair& _rhs) noexcept { std::swap(_pointer, _rhs._pointer); }
166+
167+
// comparing {pointer(), tag()} <=> {pointer(), tag()} for consistency
168+
friend constexpr auto operator<=>(pointer_tag_pair lhs, pointer_tag_pair rhs) noexcept {
169+
const auto _ptr_comp = lhs.pointer() <=> rhs.pointer();
170+
if (!std::is_eq(_ptr_comp)) {
171+
return _ptr_comp;
172+
}
173+
174+
return lhs.tag() <=> rhs.tag();
175+
}
176+
friend bool operator==(pointer_tag_pair, pointer_tag_pair) = default;
177+
};
178+
179+
// support for structured bindings
180+
template <typename _Pointee, typename _Tag, unsigned _Bits>
181+
struct tuple_size<pointer_tag_pair<_Pointee, _Tag, _Bits>> : std::integral_constant<std::size_t, 2> {};
182+
183+
template <typename _Pointee, typename _Tag, unsigned _Bits>
184+
struct tuple_element<0, pointer_tag_pair<_Pointee, _Tag, _Bits>> {
185+
using type = _Pointee*;
186+
};
187+
188+
template <typename _Pointee, typename _Tag, unsigned _Bits>
189+
struct tuple_element<1, pointer_tag_pair<_Pointee, _Tag, _Bits>> {
190+
using type = _Tag;
191+
};
192+
193+
template <typename _Pointee, typename _Tag, unsigned _Bits>
194+
struct tuple_element<0, const pointer_tag_pair<_Pointee, _Tag, _Bits>> {
195+
using type = _Pointee*;
196+
};
197+
198+
template <typename _Pointee, typename _Tag, unsigned _Bits>
199+
struct tuple_element<1, const pointer_tag_pair<_Pointee, _Tag, _Bits>> {
200+
using type = _Tag;
201+
};
202+
203+
// helpers
204+
205+
// std::get (with one overload as copying pointer_tag_pair is cheap)
206+
template <size_t _I, typename _Pointee, typename _Tag, unsigned _Bits>
207+
constexpr auto get(pointer_tag_pair<_Pointee, _Tag, _Bits> _pair) noexcept
208+
-> tuple_element<_I, pointer_tag_pair<_Pointee, _Tag, _Bits>>::type {
209+
if constexpr (_I == 0) {
210+
return _pair.pointer();
211+
} else {
212+
static_assert(_I == 1);
213+
return _pair.tag();
214+
}
215+
}
216+
217+
_LIBCPP_END_NAMESPACE_STD
218+
219+
#endif // _LIBCPP_STD_VER >= 26
220+
221+
#endif // _LIBCPP___POINTER_TAG_PAIR_H

0 commit comments

Comments
 (0)