[libcxx-commits] [libcxx] [libc++] Add __pointer_int_pair (PR #94324)
Louis Dionne via libcxx-commits
libcxx-commits at lists.llvm.org
Thu Jun 6 09:15:29 PDT 2024
================
@@ -0,0 +1,154 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___UTILITY_POINTER_INT_PAIR_H
+#define _LIBCPP___UTILITY_POINTER_INT_PAIR_H
+
+#include <__assert>
+#include <__bit/bit_log2.h>
+#include <__concepts/derived_from.h>
+#include <__config>
+#include <__tuple/tuple_element.h>
+#include <__tuple/tuple_size.h>
+#include <__type_traits/is_pointer.h>
+#include <__type_traits/is_unsigned.h>
+#include <__type_traits/is_void.h>
+#include <__type_traits/remove_pointer.h>
+#include <__utility/swap.h>
+#include <cstddef>
+#include <cstdint>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+#if _LIBCPP_STD_VER >= 23
+
+// A __pointer_int_pair is a pair of a pointer and an integral type. The lower bits of the pointer that are free
+// due to the alignment requirement of the pointee are used to store the integral type.
+//
+// This imposes a constraint on the number of bits available for the integral type -- the integral type can use
+// at most log2(alignof(T)) bits. This technique allows storing the integral type without additional storage
+// beyond that of the pointer itself, at the cost of some bit twiddling.
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class>
+struct _PointerLikeTraits;
+
+template <class _Tp>
+ requires(!is_void_v<_Tp>)
+struct _PointerLikeTraits<_Tp*> {
+ static constexpr size_t __low_bits_available = std::__bit_log2(alignof(_Tp));
+
+ static _LIBCPP_HIDE_FROM_ABI uintptr_t __to_uintptr(_Tp* __ptr) { return reinterpret_cast<uintptr_t>(__ptr); }
+ static _LIBCPP_HIDE_FROM_ABI _Tp* __to_pointer(uintptr_t __ptr) { return reinterpret_cast<_Tp*>(__ptr); }
+};
+
+template <class _Tp>
+ requires is_void_v<_Tp>
+struct _PointerLikeTraits<_Tp*> {
+ static constexpr size_t __low_bits_available = 0;
+
+ static _LIBCPP_HIDE_FROM_ABI uintptr_t __to_uintptr(_Tp* __ptr) { return reinterpret_cast<uintptr_t>(__ptr); }
+ static _LIBCPP_HIDE_FROM_ABI _Tp* __to_pointer(uintptr_t __ptr) { return reinterpret_cast<_Tp*>(__ptr); }
+};
+
+enum class __integer_width : size_t {};
+
+template <class _Pointer, class _IntType, __integer_width __int_bit_count>
+class __pointer_int_pair {
+ using _PointerTraits = _PointerLikeTraits<_Pointer>;
+
+ static constexpr auto __int_width = static_cast<size_t>(__int_bit_count);
+
+ static_assert(__int_width <= _PointerTraits::__low_bits_available,
+ "Not enough bits available for requested bit count");
+ static_assert(is_integral_v<_IntType>, "_IntType has to be an integral type");
+ static_assert(is_unsigned_v<_IntType>, "__pointer_int_pair doesn't work for signed types");
+
+ static constexpr size_t __extra_bits = _PointerTraits::__low_bits_available - __int_width;
+ static constexpr uintptr_t __int_mask = static_cast<uintptr_t>(1 << _PointerTraits::__low_bits_available) - 1;
+ static constexpr uintptr_t __ptr_mask = ~__int_mask;
+
+ uintptr_t __value_ = 0;
+
+public:
+ __pointer_int_pair() = default;
+ __pointer_int_pair(const __pointer_int_pair&) = default;
+ __pointer_int_pair(__pointer_int_pair&&) = default;
+ __pointer_int_pair& operator=(const __pointer_int_pair&) = default;
+ __pointer_int_pair& operator=(__pointer_int_pair&&) = default;
+ ~__pointer_int_pair() = default;
+
+ _LIBCPP_HIDE_FROM_ABI __pointer_int_pair(_Pointer __ptr_value, _IntType __int_value)
+ : __value_(_PointerTraits::__to_uintptr(__ptr_value) | (__int_value << __extra_bits)) {
+ _LIBCPP_ASSERT_INTERNAL((__int_value & (__int_mask >> __extra_bits)) == __int_value, "integer is too large!");
----------------
ldionne wrote:
In principle, yes. However, since this type is used as an implementation detail of libc++ and we are likely to store things like booleans or other values that we know the required size for, I think an internal assertion makes sense. If we wanted to store a user-provided value in a `pointer_int_pair`, the place where such usage is done could have something like a valid-element-access check to ensure that we don't blow up past the number of low bits available.
https://github.com/llvm/llvm-project/pull/94324
More information about the libcxx-commits
mailing list