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