Skip to content

Commit 98c61df

Browse files
committed
[libc++] Add __pointer_int_pair
1 parent 5367b2c commit 98c61df

File tree

7 files changed

+348
-0
lines changed

7 files changed

+348
-0
lines changed

libcxx/include/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -857,6 +857,7 @@ set(files
857857
__utility/no_destroy.h
858858
__utility/pair.h
859859
__utility/piecewise_construct.h
860+
__utility/pointer_int_pair.h
860861
__utility/priority_tag.h
861862
__utility/private_constructor_tag.h
862863
__utility/rel_ops.h
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
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___UTILITY_POINTER_INT_PAIR_H
10+
#define _LIBCPP___UTILITY_POINTER_INT_PAIR_H
11+
12+
#include <__assert>
13+
#include <__bit/bit_log2.h>
14+
#include <__concepts/derived_from.h>
15+
#include <__config>
16+
#include <__tuple/tuple_element.h>
17+
#include <__tuple/tuple_size.h>
18+
#include <__type_traits/is_pointer.h>
19+
#include <__type_traits/is_unsigned.h>
20+
#include <__type_traits/is_void.h>
21+
#include <__type_traits/remove_pointer.h>
22+
#include <__utility/swap.h>
23+
#include <cstddef>
24+
#include <cstdint>
25+
26+
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
27+
# pragma GCC system_header
28+
#endif
29+
30+
#if _LIBCPP_STD_VER >= 23
31+
32+
// A __pointer_int_pair is a pair of a pointer and an integral type. The lower bits of the pointer that are free
33+
// due to the alignment requirement of the pointee are used to store the integral type.
34+
//
35+
// This imposes a constraint on the number of bits available for the integral type -- the integral type can use
36+
// at most log2(alignof(T)) bits. This technique allows storing the integral type without additional storage
37+
// beyond that of the pointer itself, at the cost of some bit twiddling.
38+
39+
_LIBCPP_BEGIN_NAMESPACE_STD
40+
41+
template <class>
42+
struct _PointerLikeTraits;
43+
44+
template <class _Tp>
45+
requires(!is_void_v<_Tp>)
46+
struct _PointerLikeTraits<_Tp*> {
47+
static constexpr size_t __low_bits_available = std::__bit_log2(alignof(_Tp));
48+
49+
static _LIBCPP_HIDE_FROM_ABI uintptr_t __to_uintptr(_Tp* __ptr) { return reinterpret_cast<uintptr_t>(__ptr); }
50+
static _LIBCPP_HIDE_FROM_ABI _Tp* __to_pointer(uintptr_t __ptr) { return reinterpret_cast<_Tp*>(__ptr); }
51+
};
52+
53+
template <class _Tp>
54+
requires is_void_v<_Tp>
55+
struct _PointerLikeTraits<_Tp*> {
56+
static constexpr size_t __low_bits_available = 0;
57+
58+
static _LIBCPP_HIDE_FROM_ABI uintptr_t __to_uintptr(_Tp* __ptr) { return reinterpret_cast<uintptr_t>(__ptr); }
59+
static _LIBCPP_HIDE_FROM_ABI _Tp* __to_pointer(uintptr_t __ptr) { return reinterpret_cast<_Tp*>(__ptr); }
60+
};
61+
62+
enum class __integer_width : size_t {};
63+
64+
template <class _Pointer, class _IntType, __integer_width __int_bit_count>
65+
class __pointer_int_pair {
66+
using _PointerTraits = _PointerLikeTraits<_Pointer>;
67+
68+
static constexpr auto __int_width = static_cast<size_t>(__int_bit_count);
69+
70+
static_assert(__int_width <= _PointerTraits::__low_bits_available,
71+
"Not enough bits available for requested bit count");
72+
static_assert(is_integral_v<_IntType>, "_IntType has to be an integral type");
73+
static_assert(is_unsigned_v<_IntType>, "__pointer_int_pair doesn't work for signed types");
74+
75+
static constexpr size_t __extra_bits = _PointerTraits::__low_bits_available - __int_width;
76+
static constexpr uintptr_t __int_mask = static_cast<uintptr_t>(1 << _PointerTraits::__low_bits_available) - 1;
77+
static constexpr uintptr_t __ptr_mask = ~__int_mask;
78+
79+
uintptr_t __value_ = 0;
80+
81+
public:
82+
__pointer_int_pair() = default;
83+
__pointer_int_pair(const __pointer_int_pair&) = default;
84+
__pointer_int_pair(__pointer_int_pair&&) = default;
85+
__pointer_int_pair& operator=(const __pointer_int_pair&) = default;
86+
__pointer_int_pair& operator=(__pointer_int_pair&&) = default;
87+
~__pointer_int_pair() = default;
88+
89+
_LIBCPP_HIDE_FROM_ABI __pointer_int_pair(_Pointer __ptr_value, _IntType __int_value)
90+
: __value_(_PointerTraits::__to_uintptr(__ptr_value) | (__int_value << __extra_bits)) {
91+
_LIBCPP_ASSERT_INTERNAL((__int_value & (__int_mask >> __extra_bits)) == __int_value, "integer is too large!");
92+
_LIBCPP_ASSERT_INTERNAL(
93+
(_PointerTraits::__to_uintptr(__ptr_value) & __ptr_mask) == _PointerTraits::__to_uintptr(__ptr_value),
94+
"Pointer alignment is too low!");
95+
}
96+
97+
_LIBCPP_HIDE_FROM_ABI _IntType __get_value() const { return (__value_ & __int_mask) >> __extra_bits; }
98+
_LIBCPP_HIDE_FROM_ABI _Pointer __get_ptr() const { return _PointerTraits::__to_pointer(__value_ & __ptr_mask); }
99+
100+
template <class>
101+
friend struct _PointerLikeTraits;
102+
};
103+
104+
template <class _Pointer, __integer_width __int_bit_count, class _IntType>
105+
struct _PointerLikeTraits<__pointer_int_pair<_Pointer, _IntType, __int_bit_count>> {
106+
private:
107+
using _PointerIntPair = __pointer_int_pair<_Pointer, _IntType, __int_bit_count>;
108+
109+
static_assert(_PointerLikeTraits<_Pointer>::__low_bits_available >= static_cast<size_t>(__int_bit_count));
110+
111+
public:
112+
static constexpr size_t __low_bits_available =
113+
_PointerLikeTraits<_Pointer>::__low_bits_available - static_cast<size_t>(__int_bit_count);
114+
115+
static _LIBCPP_HIDE_FROM_ABI uintptr_t __to_uintptr(_PointerIntPair __ptr) { return __ptr.__value_; }
116+
117+
static _LIBCPP_HIDE_FROM_ABI _PointerIntPair __to_pointer(uintptr_t __ptr) {
118+
_PointerIntPair __tmp{};
119+
__tmp.__value_ = __ptr;
120+
return __tmp;
121+
}
122+
};
123+
124+
// Make __pointer_int_pair tuple-like
125+
126+
template <class _Pointer, class _IntType, __integer_width __int_bit_count>
127+
struct tuple_size<__pointer_int_pair<_Pointer, _IntType, __int_bit_count>> : integral_constant<size_t, 2> {};
128+
129+
template <class _Pointer, class _IntType, __integer_width __int_bit_count>
130+
struct tuple_element<0, __pointer_int_pair<_Pointer, _IntType, __int_bit_count>> {
131+
using type = _Pointer;
132+
};
133+
134+
template <class _Pointer, class _IntType, __integer_width __int_bit_count>
135+
struct tuple_element<1, __pointer_int_pair<_Pointer, _IntType, __int_bit_count>> {
136+
using type = _IntType;
137+
};
138+
139+
template <size_t __i, class _Pointer, class _IntType, __integer_width __int_bit_count>
140+
_LIBCPP_HIDE_FROM_ABI auto get(__pointer_int_pair<_Pointer, _IntType, __int_bit_count> __pair) {
141+
if constexpr (__i == 0) {
142+
return __pair.__get_ptr();
143+
} else if constexpr (__i == 1) {
144+
return __pair.__get_value();
145+
} else {
146+
static_assert(__i == 0, "Index out of bounds");
147+
}
148+
}
149+
150+
_LIBCPP_END_NAMESPACE_STD
151+
152+
#endif // _LIBCPP_STD_VER >= 23
153+
154+
#endif // _LIBCPP___UTILITY_POINTER_INT_PAIR_H

libcxx/include/module.modulemap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2089,6 +2089,7 @@ module std_private_utility_pair [system] {
20892089
}
20902090
module std_private_utility_pair_fwd [system] { header "__fwd/pair.h" }
20912091
module std_private_utility_piecewise_construct [system] { header "__utility/piecewise_construct.h" }
2092+
module std_private_utility_pointer_int_pair [system] { header "__utility/pointer_int_pair.h" }
20922093
module std_private_utility_priority_tag [system] { header "__utility/priority_tag.h" }
20932094
module std_private_utility_private_constructor_tag [system] { header "__utility/private_constructor_tag.h" }
20942095
module std_private_utility_rel_ops [system] { header "__utility/rel_ops.h" }
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
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+
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
10+
11+
// REQUIRES: has-unix-headers
12+
// REQUIRES: libcpp-hardening-mode=debug
13+
// XFAIL: availability-verbose_abort-missing
14+
15+
#include "test_macros.h"
16+
17+
TEST_DIAGNOSTIC_PUSH
18+
TEST_CLANG_DIAGNOSTIC_IGNORED("-Wprivate-header")
19+
#include <__utility/pointer_int_pair.h>
20+
TEST_DIAGNOSTIC_POP
21+
22+
#include <cassert>
23+
24+
#include "check_assertion.h"
25+
26+
struct [[gnu::packed]] Packed {
27+
char c;
28+
int i;
29+
};
30+
31+
int main(int, char**) {
32+
TEST_LIBCPP_ASSERT_FAILURE(
33+
(std::__pointer_int_pair<int*, size_t, std::__integer_width{1}>{nullptr, 2}), "integer is too large!");
34+
35+
TEST_DIAGNOSTIC_PUSH
36+
TEST_CLANG_DIAGNOSTIC_IGNORED("-Waddress-of-packed-member") // That's what we're trying to test
37+
TEST_GCC_DIAGNOSTIC_IGNORED("-Waddress-of-packed-member")
38+
alignas(int) Packed p;
39+
TEST_LIBCPP_ASSERT_FAILURE(
40+
(std::__pointer_int_pair<int*, size_t, std::__integer_width{1}>{&p.i, 0}), "Pointer alignment is too low!");
41+
TEST_DIAGNOSTIC_POP
42+
43+
return 0;
44+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
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+
// Ensure that __pointer_int_pair cannot be constant initialized with a value.
10+
// This would mean that the constructor is `constexpr`, which should only be
11+
// possible with compiler magic.
12+
13+
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
14+
15+
#include <__utility/pointer_int_pair.h>
16+
17+
template <class Ptr, class UnderlyingType>
18+
using single_bit_pair = std::__pointer_int_pair<Ptr, UnderlyingType, std::__integer_width{1}>;
19+
20+
constinit int ptr = 0;
21+
constinit single_bit_pair<int*, size_t> continitiable_pointer_int_pair_values{&ptr, 0}; // expected-error {{variable does not have a constant initializer}}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
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+
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
10+
11+
#include <__utility/pointer_int_pair.h>
12+
#include <cassert>
13+
14+
template <class Ptr, class UnderlyingType>
15+
using single_bit_pair = std::__pointer_int_pair<Ptr, UnderlyingType, std::__integer_width{1}>;
16+
17+
template <class Ptr, class UnderlyingType>
18+
using two_bit_pair = std::__pointer_int_pair<Ptr, UnderlyingType, std::__integer_width{2}>;
19+
20+
constinit single_bit_pair<int*, size_t> continitiable_pointer_int_pair;
21+
22+
int main(int, char**) {
23+
{ // __point_int_pair() constructor
24+
single_bit_pair<int*, size_t> pair = {};
25+
assert(pair.__get_value() == 0);
26+
assert(pair.__get_ptr() == nullptr);
27+
}
28+
29+
{ // __pointer_int_pair(pointer, int) constructor
30+
single_bit_pair<int*, size_t> pair(nullptr, 1);
31+
assert(pair.__get_value() == 1);
32+
assert(pair.__get_ptr() == nullptr);
33+
}
34+
35+
{ // pointer is correctly packed/unpacked (with different types and values)
36+
int i;
37+
single_bit_pair<int*, size_t> pair(&i, 0);
38+
assert(pair.__get_value() == 0);
39+
assert(pair.__get_ptr() == &i);
40+
}
41+
{
42+
int i;
43+
two_bit_pair<int*, size_t> pair(&i, 2);
44+
assert(pair.__get_value() == 2);
45+
assert(pair.__get_ptr() == &i);
46+
}
47+
{
48+
short i;
49+
single_bit_pair<short*, size_t> pair(&i, 1);
50+
assert(pair.__get_value() == 1);
51+
assert(pair.__get_ptr() == &i);
52+
}
53+
54+
{ // check that a __pointer_int_pair<__pointer_int_pair> works
55+
int i;
56+
single_bit_pair<single_bit_pair<int*, size_t>, size_t> pair({&i, 1}, 0);
57+
assert(pair.__get_value() == 0);
58+
assert(pair.__get_ptr().__get_ptr() == &i);
59+
assert(pair.__get_ptr().__get_value() == 1);
60+
}
61+
62+
{ // check that the tuple protocol is correctly implemented
63+
int i;
64+
two_bit_pair<int*, size_t> pair{&i, 3};
65+
auto [ptr, value] = pair;
66+
assert(ptr == &i);
67+
assert(value == 3);
68+
}
69+
70+
{ // check that the (pointer, int) constructor is implicit
71+
int i;
72+
two_bit_pair<int*, size_t> pair = {&i, 3};
73+
assert(pair.__get_ptr() == &i);
74+
assert(pair.__get_value() == 3);
75+
}
76+
77+
{ // check that overaligned types work as expected
78+
struct alignas(32) Overaligned {
79+
int i;
80+
};
81+
82+
Overaligned i;
83+
std::__pointer_int_pair<Overaligned*, size_t, std::__integer_width{4}> pair = {&i, 13};
84+
assert(pair.__get_ptr() == &i);
85+
assert(pair.__get_value() == 13);
86+
}
87+
88+
{ // check that types other than size_t work as well
89+
int i;
90+
single_bit_pair<int*, bool> pair = {&i, true};
91+
assert(pair.__get_ptr() == &i);
92+
assert(pair.__get_value());
93+
static_assert(std::is_same_v<decltype(pair.__get_value()), bool>);
94+
}
95+
{
96+
int i;
97+
single_bit_pair<int*, unsigned char> pair = {&i, 1};
98+
assert(pair.__get_ptr() == &i);
99+
assert(pair.__get_value() == 1);
100+
static_assert(std::is_same_v<decltype(pair.__get_value()), unsigned char>);
101+
}
102+
103+
return 0;
104+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
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+
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
10+
11+
#include "test_macros.h"
12+
13+
TEST_DIAGNOSTIC_PUSH
14+
TEST_CLANG_DIAGNOSTIC_IGNORED("-Wprivate-header")
15+
#include <__utility/pointer_int_pair.h>
16+
TEST_DIAGNOSTIC_POP
17+
18+
// expected-error-re@*:* {{{{static_assert|static assertion}} failed {{.*}}Not enough bits available for requested bit count}}
19+
std::__pointer_int_pair<char*, size_t, std::__integer_width{2}> ptr1; // expected-note {{here}}
20+
// expected-error-re@*:* {{{{static_assert|static assertion}} failed {{.*}}_IntType has to be an integral type}}
21+
std::__pointer_int_pair<int*, int*, std::__integer_width{2}> ptr2; // expected-note {{here}}
22+
// expected-error-re@*:* {{{{static_assert|static assertion}} failed {{.*}}__pointer_int_pair doesn't work for signed types}}
23+
std::__pointer_int_pair<int*, signed, std::__integer_width{2}> ptr3; // expected-note {{here}}

0 commit comments

Comments
 (0)