Skip to content

Commit 4b4b6e4

Browse files
committed
[libc++] Add __pointer_int_pair
1 parent 44cd502 commit 4b4b6e4

File tree

9 files changed

+366
-2
lines changed

9 files changed

+366
-2
lines changed

libcxx/include/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -926,6 +926,7 @@ set(files
926926
__utility/no_destroy.h
927927
__utility/pair.h
928928
__utility/piecewise_construct.h
929+
__utility/pointer_int_pair.h
929930
__utility/priority_tag.h
930931
__utility/private_constructor_tag.h
931932
__utility/rel_ops.h

libcxx/include/__bit/bit_log2.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
_LIBCPP_BEGIN_NAMESPACE_STD
2222

2323
template <class _Tp>
24-
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _Tp __bit_log2(_Tp __t) _NOEXCEPT {
24+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp __bit_log2(_Tp __t) _NOEXCEPT {
2525
static_assert(__is_unsigned_integer_v<_Tp>, "__bit_log2 requires an unsigned integer type");
2626
return numeric_limits<_Tp>::digits - 1 - std::__countl_zero(__t);
2727
}

libcxx/include/__bit/countl.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ _LIBCPP_PUSH_MACROS
2323
_LIBCPP_BEGIN_NAMESPACE_STD
2424

2525
template <class _Tp>
26-
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 int __countl_zero(_Tp __t) _NOEXCEPT {
26+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __countl_zero(_Tp __t) _NOEXCEPT {
2727
static_assert(__is_unsigned_integer_v<_Tp>, "__countl_zero requires an unsigned integer type");
2828
return __builtin_clzg(__t, numeric_limits<_Tp>::digits);
2929
}
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
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 <__config>
14+
#include <__cstddef/size_t.h>
15+
#include <__fwd/tuple.h>
16+
#include <__type_traits/enable_if.h>
17+
#include <__type_traits/is_integral.h>
18+
#include <__type_traits/is_unsigned.h>
19+
#include <__type_traits/is_void.h>
20+
#include <cstdint>
21+
#include <limits>
22+
23+
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
24+
# pragma GCC system_header
25+
#endif
26+
27+
// A __pointer_int_pair is a pair of a pointer and an integral type. The lower bits of the pointer that are free
28+
// due to the alignment requirement of the pointee are used to store the integral type.
29+
//
30+
// This imposes a constraint on the number of bits available for the integral type -- the integral type can use
31+
// at most log2(alignof(T)) bits. This technique allows storing the integral type without additional storage
32+
// beyond that of the pointer itself, at the cost of some bit twiddling.
33+
34+
_LIBCPP_BEGIN_NAMESPACE_STD
35+
36+
template <class, class = void>
37+
struct _PointerLikeTraits;
38+
39+
template <class _Tp>
40+
struct _PointerLikeTraits<_Tp*, __enable_if_t<!is_void<_Tp>::value> > {
41+
// This is really `__bit_log2`, but we need this to be a constant expression even in C++03, so we can't use that
42+
static const size_t __low_bits_available = numeric_limits<size_t>::digits - 1 - __builtin_clzg(_LIBCPP_ALIGNOF(_Tp));
43+
44+
static _LIBCPP_HIDE_FROM_ABI uintptr_t __to_uintptr(_Tp* __ptr) { return reinterpret_cast<uintptr_t>(__ptr); }
45+
static _LIBCPP_HIDE_FROM_ABI _Tp* __to_pointer(uintptr_t __ptr) { return reinterpret_cast<_Tp*>(__ptr); }
46+
};
47+
48+
template <class _Tp>
49+
struct _PointerLikeTraits<_Tp*, __enable_if_t<is_void<_Tp>::value> > {
50+
static const size_t __low_bits_available = 0;
51+
52+
static _LIBCPP_HIDE_FROM_ABI uintptr_t __to_uintptr(_Tp* __ptr) { return reinterpret_cast<uintptr_t>(__ptr); }
53+
static _LIBCPP_HIDE_FROM_ABI _Tp* __to_pointer(uintptr_t __ptr) { return reinterpret_cast<_Tp*>(__ptr); }
54+
};
55+
56+
enum __integer_width : size_t {};
57+
58+
template <class _Pointer, class _IntType, __integer_width __int_bit_count>
59+
class __pointer_int_pair {
60+
using _PointerTraits = _PointerLikeTraits<_Pointer>;
61+
62+
static const auto __int_width = static_cast<size_t>(__int_bit_count);
63+
64+
static_assert(__int_width <= _PointerTraits::__low_bits_available,
65+
"Not enough bits available for requested bit count");
66+
static_assert(is_integral<_IntType>::value, "_IntType has to be an integral type");
67+
static_assert(is_unsigned<_IntType>::value,
68+
"__pointer_int_pair doesn't work for signed types since that would require handling the sign bit");
69+
70+
static const size_t __extra_bits = _PointerTraits::__low_bits_available - __int_width;
71+
static const uintptr_t __int_mask = static_cast<uintptr_t>(1 << _PointerTraits::__low_bits_available) - 1;
72+
static const uintptr_t __ptr_mask = ~__int_mask;
73+
74+
uintptr_t __value_ = 0;
75+
76+
public:
77+
__pointer_int_pair() = default;
78+
79+
_LIBCPP_HIDE_FROM_ABI __pointer_int_pair(_Pointer __ptr_value, _IntType __int_value)
80+
: __value_(_PointerTraits::__to_uintptr(__ptr_value) | (__int_value << __extra_bits)) {
81+
_LIBCPP_ASSERT_INTERNAL((__int_value & (__int_mask >> __extra_bits)) == __int_value, "integer is too large!");
82+
_LIBCPP_ASSERT_INTERNAL(
83+
(_PointerTraits::__to_uintptr(__ptr_value) & __ptr_mask) == _PointerTraits::__to_uintptr(__ptr_value),
84+
"Pointer alignment is too low!");
85+
}
86+
87+
_LIBCPP_HIDE_FROM_ABI _IntType __get_value() const { return (__value_ & __int_mask) >> __extra_bits; }
88+
_LIBCPP_HIDE_FROM_ABI _Pointer __get_ptr() const { return _PointerTraits::__to_pointer(__value_ & __ptr_mask); }
89+
90+
template <class, class>
91+
friend struct _PointerLikeTraits;
92+
};
93+
94+
template <class _Pointer, __integer_width __int_bit_count, class _IntType>
95+
struct _PointerLikeTraits<__pointer_int_pair<_Pointer, _IntType, __int_bit_count> > {
96+
private:
97+
using _PointerIntPair = __pointer_int_pair<_Pointer, _IntType, __int_bit_count>;
98+
99+
public:
100+
static inline const size_t __low_bits_available = _PointerIntPair::__extra_bits;
101+
102+
static _LIBCPP_HIDE_FROM_ABI uintptr_t __to_uintptr(_PointerIntPair __ptr) { return __ptr.__value_; }
103+
104+
static _LIBCPP_HIDE_FROM_ABI _PointerIntPair __to_pointer(uintptr_t __ptr) {
105+
_PointerIntPair __tmp;
106+
__tmp.__value_ = __ptr;
107+
return __tmp;
108+
}
109+
};
110+
111+
#ifndef _LIBCPP_CXX03_LANG
112+
113+
// Make __pointer_int_pair tuple-like
114+
115+
template <class _Pointer, class _IntType, __integer_width __int_bit_count>
116+
struct tuple_size<__pointer_int_pair<_Pointer, _IntType, __int_bit_count> > : integral_constant<size_t, 2> {};
117+
118+
template <class _Pointer, class _IntType, __integer_width __int_bit_count>
119+
struct tuple_element<0, __pointer_int_pair<_Pointer, _IntType, __int_bit_count> > {
120+
using type = _Pointer;
121+
};
122+
123+
template <class _Pointer, class _IntType, __integer_width __int_bit_count>
124+
struct tuple_element<1, __pointer_int_pair<_Pointer, _IntType, __int_bit_count> > {
125+
using type = _IntType;
126+
};
127+
128+
template <size_t __i>
129+
struct __pointer_int_pair_getter;
130+
131+
template <>
132+
struct __pointer_int_pair_getter<0> {
133+
template <class _Pointer, class _IntType, __integer_width __int_bit_count>
134+
static _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Pointer
135+
__get(__pointer_int_pair<_Pointer, _IntType, __int_bit_count> __pair) {
136+
return __pair.__get_ptr();
137+
}
138+
};
139+
140+
template <>
141+
struct __pointer_int_pair_getter<1> {
142+
template <class _Pointer, class _IntType, __integer_width __int_bit_count>
143+
static _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _IntType
144+
__get(__pointer_int_pair<_Pointer, _IntType, __int_bit_count> __pair) {
145+
return __pair.__get_value();
146+
}
147+
};
148+
149+
template <size_t __i, class _Pointer, class _IntType, __integer_width __int_bit_count>
150+
_LIBCPP_HIDE_FROM_ABI typename tuple_element<__i, __pointer_int_pair<_Pointer, _IntType, __int_bit_count> >::type
151+
get(__pointer_int_pair<_Pointer, _IntType, __int_bit_count> __pair) {
152+
return __pointer_int_pair_getter<__i>::__get(__pair);
153+
}
154+
155+
#endif // _LIBCPP_CXX03_LANG
156+
157+
_LIBCPP_END_NAMESPACE_STD
158+
159+
#endif // _LIBCPP___UTILITY_POINTER_INT_PAIR_H

libcxx/include/module.modulemap.in

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2167,6 +2167,11 @@ module std [system] {
21672167
module no_destroy { header "__utility/no_destroy.h" }
21682168
module pair { header "__utility/pair.h" }
21692169
module piecewise_construct { header "__utility/piecewise_construct.h" }
2170+
module pointer_int_pair {
2171+
header "__utility/pointer_int_pair.h"
2172+
2173+
export std_core.fwd.tuple
2174+
}
21702175
module priority_tag { header "__utility/priority_tag.h" }
21712176
module private_constructor_tag { header "__utility/private_constructor_tag.h" }
21722177
module rel_ops { header "__utility/rel_ops.h" }
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
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+
// REQUIRES: has-unix-headers
10+
// REQUIRES: libcpp-hardening-mode=debug
11+
// XFAIL: availability-verbose_abort-missing
12+
13+
#include "test_macros.h"
14+
15+
TEST_DIAGNOSTIC_PUSH
16+
TEST_CLANG_DIAGNOSTIC_IGNORED("-Wprivate-header")
17+
#include <__utility/pointer_int_pair.h>
18+
TEST_DIAGNOSTIC_POP
19+
20+
#include <cassert>
21+
22+
#include "check_assertion.h"
23+
24+
struct [[gnu::packed]] Packed {
25+
char c;
26+
int i;
27+
};
28+
29+
int main(int, char**) {
30+
TEST_LIBCPP_ASSERT_FAILURE(
31+
(std::__pointer_int_pair<int*, size_t, std::__integer_width{1}>{nullptr, 2}), "integer is too large!");
32+
33+
TEST_DIAGNOSTIC_PUSH
34+
TEST_CLANG_DIAGNOSTIC_IGNORED("-Waddress-of-packed-member") // That's what we're trying to test
35+
TEST_GCC_DIAGNOSTIC_IGNORED("-Waddress-of-packed-member")
36+
alignas(int) Packed p;
37+
TEST_LIBCPP_ASSERT_FAILURE(
38+
(std::__pointer_int_pair<int*, size_t, std::__integer_width{1}>{&p.i, 0}), "Pointer alignment is too low!");
39+
TEST_DIAGNOSTIC_POP
40+
41+
return 0;
42+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
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
14+
15+
// clang-format off
16+
17+
#include <__utility/pointer_int_pair.h>
18+
#include <cstddef>
19+
20+
template <class Ptr, class UnderlyingType>
21+
using single_bit_pair = std::__pointer_int_pair<Ptr, UnderlyingType, std::__integer_width{1}>;
22+
23+
constinit int ptr = 0;
24+
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: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
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+
// XFAIL: FROZEN-CXX03-HEADERS-FIXME
10+
11+
#include <__utility/pointer_int_pair.h>
12+
#include <cassert>
13+
#include <cstddef>
14+
#include <type_traits>
15+
16+
#include "test_macros.h"
17+
18+
template <class Ptr, class UnderlyingType>
19+
using single_bit_pair = std::__pointer_int_pair<Ptr, UnderlyingType, std::__integer_width(1)>;
20+
21+
template <class Ptr, class UnderlyingType>
22+
using two_bit_pair = std::__pointer_int_pair<Ptr, UnderlyingType, std::__integer_width(2)>;
23+
24+
#if _LIBCPP_STD_VER >= 20
25+
constinit single_bit_pair<int*, size_t> continitiable_pointer_int_pair;
26+
#endif
27+
28+
int main(int, char**) {
29+
#if TEST_STD_VER >= 11
30+
{ // __pointer_int_pair() constructor
31+
single_bit_pair<int*, size_t> pair = {};
32+
assert(pair.__get_value() == 0);
33+
assert(pair.__get_ptr() == nullptr);
34+
}
35+
#endif
36+
37+
{ // __pointer_int_pair(pointer, int) constructor
38+
single_bit_pair<int*, size_t> pair(nullptr, 1);
39+
assert(pair.__get_value() == 1);
40+
assert(pair.__get_ptr() == nullptr);
41+
}
42+
43+
{ // pointer is correctly packed/unpacked (with different types and values)
44+
int i;
45+
single_bit_pair<int*, size_t> pair(&i, 0);
46+
assert(pair.__get_value() == 0);
47+
assert(pair.__get_ptr() == &i);
48+
}
49+
{
50+
int i;
51+
two_bit_pair<int*, size_t> pair(&i, 2);
52+
assert(pair.__get_value() == 2);
53+
assert(pair.__get_ptr() == &i);
54+
}
55+
{
56+
short i;
57+
single_bit_pair<short*, size_t> pair(&i, 1);
58+
assert(pair.__get_value() == 1);
59+
assert(pair.__get_ptr() == &i);
60+
}
61+
62+
{ // check that a __pointer_int_pair<__pointer_int_pair> works
63+
int i;
64+
single_bit_pair<single_bit_pair<int*, size_t>, size_t> pair(single_bit_pair<int*, size_t>(&i, 1), 0);
65+
assert(pair.__get_value() == 0);
66+
assert(pair.__get_ptr().__get_ptr() == &i);
67+
assert(pair.__get_ptr().__get_value() == 1);
68+
}
69+
70+
#if _LIBCPP_STD_VER >= 17
71+
{ // check that the tuple protocol is correctly implemented
72+
int i;
73+
two_bit_pair<int*, size_t> pair{&i, 3};
74+
auto [ptr, value] = pair;
75+
assert(ptr == &i);
76+
assert(value == 3);
77+
}
78+
#endif
79+
80+
{ // check that the (pointer, int) constructor is implicit
81+
int i;
82+
two_bit_pair<int*, size_t> pair(&i, 3);
83+
assert(pair.__get_ptr() == &i);
84+
assert(pair.__get_value() == 3);
85+
}
86+
87+
{ // check that overaligned types work as expected
88+
struct TEST_ALIGNAS(32) Overaligned {
89+
int i;
90+
};
91+
92+
Overaligned i;
93+
std::__pointer_int_pair<Overaligned*, size_t, std::__integer_width(4)> pair(&i, 13);
94+
assert(pair.__get_ptr() == &i);
95+
assert(pair.__get_value() == 13);
96+
}
97+
98+
{ // check that types other than size_t work as well
99+
int i;
100+
single_bit_pair<int*, bool> pair(&i, true);
101+
assert(pair.__get_ptr() == &i);
102+
assert(pair.__get_value());
103+
static_assert(std::is_same<decltype(pair.__get_value()), bool>::value, "");
104+
}
105+
{
106+
int i;
107+
single_bit_pair<int*, unsigned char> pair(&i, 1);
108+
assert(pair.__get_ptr() == &i);
109+
assert(pair.__get_value() == 1);
110+
static_assert(std::is_same<decltype(pair.__get_value()), unsigned char>::value, "");
111+
}
112+
113+
return 0;
114+
}

0 commit comments

Comments
 (0)